আপনি যদি C99 বা তার পরে সংকলক ব্যবহার করেন
#define debug_print(fmt, ...) \
do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0)
এটি ধরে নিয়েছে যে আপনি সি 99 ব্যবহার করছেন (ভেরিয়েবল যুক্তি তালিকার তালিকাটি পূর্ববর্তী সংস্করণগুলিতে সমর্থিত নয়)। do { ... } while (0)
বাগ্ধারা নিশ্চিত কোড একটি বিবৃতি (ফাংশন কল) -এর মত কাজ করে যে। কোডের নিঃশর্ত ব্যবহার নিশ্চিত করে যে কম্পাইলার সর্বদা আপনার ডিবাগ কোডটি বৈধ কিনা তা পরীক্ষা করে - তবে ডিইবিইউজি 0 হলে অপ্টিমাইজার কোডটি সরিয়ে ফেলবে।
আপনি যদি #ifdef DEBUG এর সাথে কাজ করতে চান তবে পরীক্ষার শর্তটি পরিবর্তন করুন:
#ifdef DEBUG
#define DEBUG_TEST 1
#else
#define DEBUG_TEST 0
#endif
এবং তারপরে DEBUG_TEST ব্যবহার করুন যেখানে আমি DEBUG ব্যবহার করেছি।
আপনি ফরম্যাট স্ট্রিং (সম্ভবত একটি ভাল ধারণা যাহাই হউক না কেন), তবে আপনাকে ভালো জিনিস পরিচয় করিয়ে দিতে পারেন জন্য একটি স্ট্রিং আক্ষরিক পীড়াপীড়ি যদি __FILE__
, __LINE__
এবং __func__
আউটপুট, যা ডায়গনিস্টিক উন্নত করতে পারেন মধ্যে:
#define debug_print(fmt, ...) \
do { if (DEBUG) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
__LINE__, __func__, __VA_ARGS__); } while (0)
এটি প্রোগ্রামার লেখার চেয়ে বড় ফর্ম্যাট স্ট্রিং তৈরি করতে স্ট্রিং কনটেনটেশনের উপর নির্ভর করে।
আপনি যদি C89 সংকলক ব্যবহার করেন
আপনি যদি C89 এর সাথে আটকে থাকেন এবং কোনও কার্যকর সংকলক এক্সটেনশান নেই, তবে এটি হ্যান্ডেল করার কোনও বিশেষ উপায় নেই। আমি যে কৌশলটি ব্যবহার করতাম তা হ'ল:
#define TRACE(x) do { if (DEBUG) dbg_printf x; } while (0)
এবং তারপরে কোডটিতে লিখুন:
TRACE(("message %d\n", var));
দ্বৈত-বন্ধনীগুলি গুরুত্বপূর্ণ - এবং ম্যাক্রো প্রসারণে আপনার মজার স্বরলিপি রয়েছে have আগের মতো, সংকলক সর্বদা সিন্ট্যাকটিক বৈধতার কোডটি যাচাই করে (যা ভাল) তবে অপ্টিমাইজার কেবল তখনই মুদ্রণ ফাংশনটির জন্য অনুরোধ করে যদি ডিইবিইউজি ম্যাক্রো অ-শূন্যে মূল্যায়ন করে।
এটির জন্য একটি সমর্থন ফাংশন প্রয়োজন - উদাহরণস্বরূপ dbg_printf () - 'স্ট্যাডার' এর মতো জিনিসগুলি পরিচালনা করতে। ভ্যারাগস ফাংশনগুলি কীভাবে লিখবেন তা আপনার জানা দরকার, তবে এটি কঠিন নয়:
#include <stdarg.h>
#include <stdio.h>
void dbg_printf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
আপনি অবশ্যই এই কৌশলটি সি 99 এ ব্যবহার করতে পারেন, তবে __VA_ARGS__
কৌশলটি আরও সুন্দর কারণ এটি নিয়মিত ফাংশন স্বরলিপি ব্যবহার করে, ডাবল-বন্ধনী হ্যাক করে না।
সংকলক সর্বদা ডিবাগ কোডটি দেখেন কেন এটি গুরুত্বপূর্ণ?
[ অন্য উত্তরে মন্তব্য করা পুনরায় পোস্ট করা। ]
উপরোক্ত C99 এবং C89 উভয় বাস্তবায়নের পিছনে একটি কেন্দ্রীয় ধারণা হ'ল সংকলক যথাযথভাবে ডিবাগিং প্রিন্টফ-এর মতো বিবৃতি দেখে। এটি দীর্ঘমেয়াদী কোডের জন্য গুরুত্বপূর্ণ - কোড যা এক বা দুই দশক চলবে।
ধরুন একটি টুকরা কোড বেশিরভাগ বছর ধরে বেশিরভাগ সুপ্ত (স্থিতিশীল) ছিল তবে এখন এটি পরিবর্তন করা দরকার। আপনি ডিবাগিং ট্রেসটিকে পুনরায় সক্ষম করেছেন - তবে ডিবাগিং (ট্রেসিং) কোডটি ডিবাগ করতে পেরে হতাশাজনক কারণ এটি স্থিতিশীল রক্ষণাবেক্ষণের বছরগুলিতে নাম পরিবর্তন করা বা পুনরায় টাইপ করা পরিবর্তনশীলগুলিকে বোঝায়। যদি সংকলক (পোস্ট প্রসেসর পোস্টকারী) সর্বদা মুদ্রণ বিবৃতিটি দেখে, এটি নিশ্চিত করে যে কোনও পার্শ্ববর্তী পরিবর্তনগুলি ডায়াগনস্টিকগুলিকে অকার্যকর করেনি। সংকলক মুদ্রণ বিবৃতিটি না দেখলে, এটি আপনাকে আপনার নিজের অসতর্কতা (বা আপনার সহকর্মীদের বা সহযোগীদের অসতর্কতা) থেকে রক্ষা করতে পারে না। কার্নিগান এবং পাইকের ' প্রোগ্রামিং অনুশীলন ' দেখুন , বিশেষ করে অধ্যায় 8 ( টিপিওপিতে উইকিপিডিয়া দেখুন )।
এটি 'সেখানে রয়েছে, এটি হয়েছে' অভিজ্ঞতা - আমি মূলত অন্যান্য উত্তরে বর্ণিত কৌশলটি ব্যবহার করেছি যেখানে নন-ডিবাগ বিল্ডটি বেশ কয়েক বছর ধরে (এক দশকেরও বেশি সময়) মুদ্রণের মতো বিবৃতি দেখতে পায় না। তবে আমি টিপিওপিতে পরামর্শটি পেয়েছিলাম (আমার পূর্ববর্তী মন্তব্যটি দেখুন), এবং তারপরে বেশ কয়েকটি বছর পরে কিছু ডিবাগিং কোড সক্ষম করেছিলাম এবং ডিবাগটি ভেঙে পরিবর্তিত প্রসঙ্গে সমস্যার মধ্যে পড়েছিলাম। বেশিরভাগ সময়, মুদ্রণটিকে সর্বদা বৈধ করা আমাকে পরবর্তী সমস্যাগুলি থেকে বাঁচিয়েছে।
আমি কেবলমাত্র দৃser়তাগুলি নিয়ন্ত্রণ করতে NDEBUG এবং ডিবাগ ট্রেসিং প্রোগ্রামে অন্তর্নির্মিত কিনা তা নিয়ন্ত্রণ করতে একটি পৃথক ম্যাক্রো (সাধারণত ডিইবিইউজি) ব্যবহার করি। এমনকি যখন ডিবাগ ট্রেসিং অন্তর্নির্মিত হয়, আমি প্রায়শই চাই না যে ডিবাগ আউটপুটটি নিঃশর্তভাবে প্রদর্শিত হোক, তাই আউটপুট প্রদর্শিত হবে কিনা তা নিয়ন্ত্রণ করার আমার কাছে ব্যবস্থা আছে (ডিবাগ স্তরগুলি, এবং fprintf()
সরাসরি কল করার পরিবর্তে , আমি একটি ডিবাগ প্রিন্ট ফাংশন কল করি যা কেবল শর্তসাপেক্ষে মুদ্রণ করে সুতরাং কোডের একই বিল্ডটি প্রোগ্রাম বিকল্পগুলির উপর ভিত্তি করে মুদ্রণ করতে বা মুদ্রণ করতে পারে)। বড় প্রোগ্রামগুলির জন্য আমার কোডটির একটি 'মাল্টিপল-সাবসিস্টেম' সংস্করণও রয়েছে, যাতে আমার কাছে প্রোগ্রামের বিভিন্ন বিভাগ বিভিন্ন পরিমাণে ট্রেস তৈরি করতে পারে - রানটাইম নিয়ন্ত্রণের অধীনে।
আমি পরামর্শ দিচ্ছি যে সমস্ত বিল্ডের জন্য, সংকলকটি ডায়াগনস্টিক স্টেটমেন্টগুলি দেখতে হবে; যাইহোক, সংকলক ডিবাগ সক্ষম করা না থাকলে ডিবাগিং ট্রেস স্টেটমেন্টগুলির জন্য কোনও কোড উত্পন্ন করবে না। মূলত, এর অর্থ হ'ল প্রতিটি বার সংকলক দ্বারা আপনার কোডগুলি যাচাই বা ডিবাগিংয়ের জন্য পরীক্ষা করা হয়। এটি একটি ভাল জিনিস!
debug.h - সংস্করণ 1.2 (1990-05-01)
/*
@(#)File: $RCSfile: debug.h,v $
@(#)Version: $Revision: 1.2 $
@(#)Last changed: $Date: 1990/05/01 12:55:39 $
@(#)Purpose: Definitions for the debugging system
@(#)Author: J Leffler
*/
#ifndef DEBUG_H
#define DEBUG_H
/* -- Macro Definitions */
#ifdef DEBUG
#define TRACE(x) db_print x
#else
#define TRACE(x)
#endif /* DEBUG */
/* -- Declarations */
#ifdef DEBUG
extern int debug;
#endif
#endif /* DEBUG_H */
debug.h - সংস্করণ 3.6 (২০০৮-০২-১১)
/*
@(#)File: $RCSfile: debug.h,v $
@(#)Version: $Revision: 3.6 $
@(#)Last changed: $Date: 2008/02/11 06:46:37 $
@(#)Purpose: Definitions for the debugging system
@(#)Author: J Leffler
@(#)Copyright: (C) JLSS 1990-93,1997-99,2003,2005,2008
@(#)Product: :PRODUCT:
*/
#ifndef DEBUG_H
#define DEBUG_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
/*
** Usage: TRACE((level, fmt, ...))
** "level" is the debugging level which must be operational for the output
** to appear. "fmt" is a printf format string. "..." is whatever extra
** arguments fmt requires (possibly nothing).
** The non-debug macro means that the code is validated but never called.
** -- See chapter 8 of 'The Practice of Programming', by Kernighan and Pike.
*/
#ifdef DEBUG
#define TRACE(x) db_print x
#else
#define TRACE(x) do { if (0) db_print x; } while (0)
#endif /* DEBUG */
#ifndef lint
#ifdef DEBUG
/* This string can't be made extern - multiple definition in general */
static const char jlss_id_debug_enabled[] = "@(#)*** DEBUG ***";
#endif /* DEBUG */
#ifdef MAIN_PROGRAM
const char jlss_id_debug_h[] = "@(#)$Id: debug.h,v 3.6 2008/02/11 06:46:37 jleffler Exp $";
#endif /* MAIN_PROGRAM */
#endif /* lint */
#include <stdio.h>
extern int db_getdebug(void);
extern int db_newindent(void);
extern int db_oldindent(void);
extern int db_setdebug(int level);
extern int db_setindent(int i);
extern void db_print(int level, const char *fmt,...);
extern void db_setfilename(const char *fn);
extern void db_setfileptr(FILE *fp);
extern FILE *db_getfileptr(void);
/* Semi-private function */
extern const char *db_indent(void);
/**************************************\
** MULTIPLE DEBUGGING SUBSYSTEMS CODE **
\**************************************/
/*
** Usage: MDTRACE((subsys, level, fmt, ...))
** "subsys" is the debugging system to which this statement belongs.
** The significance of the subsystems is determined by the programmer,
** except that the functions such as db_print refer to subsystem 0.
** "level" is the debugging level which must be operational for the
** output to appear. "fmt" is a printf format string. "..." is
** whatever extra arguments fmt requires (possibly nothing).
** The non-debug macro means that the code is validated but never called.
*/
#ifdef DEBUG
#define MDTRACE(x) db_mdprint x
#else
#define MDTRACE(x) do { if (0) db_mdprint x; } while (0)
#endif /* DEBUG */
extern int db_mdgetdebug(int subsys);
extern int db_mdparsearg(char *arg);
extern int db_mdsetdebug(int subsys, int level);
extern void db_mdprint(int subsys, int level, const char *fmt,...);
extern void db_mdsubsysnames(char const * const *names);
#endif /* DEBUG_H */
সি 99 বা তার পরে একক আর্গুমেন্ট বৈকল্পিক
কাইল ব্র্যান্ড জিজ্ঞাসা করেছে:
যাইহোক এটি করার debug_print
জন্য কোনও যুক্তি না থাকলেও এখনও কাজ করে? উদাহরণ স্বরূপ:
debug_print("Foo");
একটি সাধারণ, পুরানো কালের হ্যাক রয়েছে:
debug_print("%s\n", "Foo");
নীচে প্রদর্শিত জিসিসির একমাত্র সমাধানও এর জন্য সমর্থন সরবরাহ করে।
তবে আপনি এটি ব্যবহার করে সরাসরি সি 99 সিস্টেমের মাধ্যমে এটি করতে পারেন:
#define debug_print(...) \
do { if (DEBUG) fprintf(stderr, __VA_ARGS__); } while (0)
প্রথম সংস্করণের তুলনায়, আপনি সীমিত চেকিংটি হারাতে পারেন যার জন্য 'fmt' আর্গুমেন্টের প্রয়োজন, যার অর্থ হল যে কেউ 'ডিবাগ_প্রিন্ট ()' কল করতে চেষ্টা করতে পারে কোনও যুক্তি ছাড়াই (তবে আর্গুমেন্ট তালিকার পিছনে থাকা কমাটি fprintf()
সংকলন করতে ব্যর্থ হবে) । যাচাইয়ের ক্ষতি একেবারেই সমস্যা কিনা তা বিতর্কযোগ্য।
একক যুক্তির জন্য জিসিসি-নির্দিষ্ট কৌশল
কিছু সংকলক ম্যাক্রোগুলিতে পরিবর্তনশীল-দৈর্ঘ্যের আর্গুমেন্ট তালিকাগুলি হ্যান্ডল করার অন্যান্য উপায়ে এক্সটেনশনের প্রস্তাব দিতে পারে। বিশেষত, হুগো ইডেলারের মন্তব্যগুলিতে প্রথম হিসাবে উল্লেখ করা হয়েছে , জিসিসি আপনাকে ম্যাক্রোর সর্বশেষ 'স্থির' যুক্তির পরে সাধারণত যে কমাটি উপস্থিত হবে তা বাদ দিতে দেয়। এটি আপনাকে ##__VA_ARGS__
ম্যাক্রো প্রতিস্থাপন পাঠ্যেও ব্যবহার করতে দেয় , যা স্বরলিখনের পূর্ববর্তী কমাটি মুছে ফেলে তবে কেবল যদি পূর্ববর্তী টোকনটি কমা হয়:
#define debug_print(fmt, ...) \
do { if (DEBUG) fprintf(stderr, fmt, ##__VA_ARGS__); } while (0)
বিন্যাসের পরে alচ্ছিক আর্গুমেন্ট গ্রহণ করার সময় এই সমাধানটি ফর্ম্যাট আর্গুমেন্টের প্রয়োজনীয়তার সুবিধা ধরে রাখে।
এই কৌশলটি এছাড়াও দ্বারা সমর্থিত ঝনঝন জিসিসি সামঞ্জস্যের জন্য।
কেন ডু-লুপ?
এখানকার উদ্দেশ্য কী do while
?
আপনি ম্যাক্রোটি ব্যবহার করতে সক্ষম হতে চান তাই এটি কোনও ফাংশন কলের মতো দেখাচ্ছে, যার অর্থ এটি একটি অর্ধ-কোলন অনুসরণ করবে। অতএব, আপনি মামলা করতে ম্যাক্রো বডি প্যাকেজ করতে হবে। আপনি যদি if
আশেপাশের জায়গা ছাড়াই কোনও বিবৃতি ব্যবহার করেন তবে আপনার কাছে do { ... } while (0)
থাকবে:
/* BAD - BAD - BAD */
#define debug_print(...) \
if (DEBUG) fprintf(stderr, __VA_ARGS__)
এখন, ধরুন আপনি লিখেছেন:
if (x > y)
debug_print("x (%d) > y (%d)\n", x, y);
else
do_something_useful(x, y);
দুর্ভাগ্যক্রমে, সেই ইন্ডেন্টেশনটি প্রবাহের প্রকৃত নিয়ন্ত্রণকে প্রতিফলিত করে না, কারণ প্রিপ্রসেসর এটির সমান কোড তৈরি করে (প্রকৃত অর্থের উপর জোর দেওয়ার জন্য অভিযুক্ত এবং ধনুর্বন্ধনী):
if (x > y)
{
if (DEBUG)
fprintf(stderr, "x (%d) > y (%d)\n", x, y);
else
do_something_useful(x, y);
}
ম্যাক্রোতে পরবর্তী প্রচেষ্টা হতে পারে:
/* BAD - BAD - BAD */
#define debug_print(...) \
if (DEBUG) { fprintf(stderr, __VA_ARGS__); }
এবং একই কোড খণ্ড এখন উত্পাদন করে:
if (x > y)
if (DEBUG)
{
fprintf(stderr, "x (%d) > y (%d)\n", x, y);
}
; // Null statement from semi-colon after macro
else
do_something_useful(x, y);
এবং এটি else
এখন একটি সিনট্যাক্স ত্রুটি। do { ... } while(0)
লুপ এড়াতে উভয় এই সমস্যা।
ম্যাক্রো লেখার অন্য একটি উপায় রয়েছে যা কাজ করতে পারে:
/* BAD - BAD - BAD */
#define debug_print(...) \
((void)((DEBUG) ? fprintf(stderr, __VA_ARGS__) : 0))
এটি প্রোগ্রামের খণ্ডটিকে বৈধ হিসাবে দেখায়। (void)
ঢালাই প্রতিরোধ এটা প্রেক্ষিতে যেখানে একটি মান প্রয়োজন বোধ করা হয় ব্যবহৃত হচ্ছে - কিন্তু এটি একটি কমা অপারেটর যেখানে বাম প্রতীক হিসাবে ব্যবহার করা যেতে পারে do { ... } while (0)
সংস্করণ করতে পারেন না। আপনি যদি ভাবেন যে এই জাতীয় অভিব্যক্তিগুলির মধ্যে আপনার ডিবাগ কোড এম্বেড করতে সক্ষম হওয়া উচিত তবে আপনি এটি পছন্দ করতে পারেন। আপনি যদি পূর্ণ বিবৃতি হিসাবে কাজ করতে ডিবাগ প্রিন্টের প্রয়োজন পছন্দ করেন তবে do { ... } while (0)
সংস্করণটি আরও ভাল। মনে রাখবেন যে যদি ম্যাক্রোর শরীরে কোনও আধা-কলোন জড়িত (মোটামুটিভাবে বলা হয়), তবে আপনি কেবল do { ... } while(0)
স্বরলিপিটি ব্যবহার করতে পারেন । এটি সর্বদা কাজ করে; এক্সপ্রেশন বিবৃতি প্রক্রিয়া প্রয়োগ করা আরও কঠিন হতে পারে। আপনি যে সংজ্ঞাটি এড়াতে পছন্দ করবেন তার সংকলক থেকে সতর্কতাও পেতে পারেন; এটি আপনার ব্যবহৃত সংকলক এবং পতাকাগুলির উপর নির্ভর করবে।
টিপিওপি আগে http://plan9.bell-labs.com/cm/cs/tpop এবং http://cm.bell-labs.com/cm/cs/tpop এ ছিল তবে উভয়ে এখন (2015-08-10) ভাঙ্গা।
গিটহাবে কোড
যদি তুমি জানতে আগ্রহী, আপনি GitHub এই কোড আমার এ সন্ধান করতে পারেন SOQ (স্ট্যাক ওভারফ্লো প্রশ্নাবলি) ফাইল হিসেবে সংগ্রহস্থলের debug.c
, debug.h
এবং mddebug.c
মধ্যে
src / libsoq
উপ-নির্দেশিকা।