यदि आप C99 या बाद के संकलक का उपयोग करते हैं
#define debug_print(fmt, ...) \
do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0)
यह मानता है कि आप C99 का उपयोग कर रहे हैं (चर तर्क सूची संकेतन पिछले संस्करणों में समर्थित नहीं है)। do { ... } while (0)
मुहावरा सुनिश्चित कोड एक बयान (समारोह कॉल) की तरह काम करता है। कोड का बिना शर्त उपयोग सुनिश्चित करता है कि कंपाइलर हमेशा यह जांचता है कि आपका डिबग कोड वैध है - लेकिन ऑप्टबाइज़र कोड को हटा देगा जब DEBUG 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));
डबल-कोष्ठक महत्वपूर्ण हैं - और यही कारण है कि आपके पास मैक्रो विस्तार में अजीब संकेतन है। पहले की तरह, कंपाइलर हमेशा सिंथैटिक वैलिडिटी (जो अच्छा है) के लिए कोड की जाँच करता है, लेकिन ऑप्टिमाइज़र केवल प्रिंटिंग फ़ंक्शन को इनवॉइस करता है अगर DEBUG मैक्रो नॉन-जीरो का मूल्यांकन करता है।
उदाहरण के लिए 'stderr' जैसी चीजों को संभालने के लिए इसे एक समर्थन फ़ंक्शन - 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);
}
आप निश्चित रूप से C99 में भी इस तकनीक का उपयोग कर सकते हैं, लेकिन __VA_ARGS__
तकनीक neater है क्योंकि यह नियमित रूप से फ़ंक्शन नोटेशन का उपयोग करती है, न कि डबल-कोष्ठक हैक का।
यह क्यों महत्वपूर्ण है कि कंपाइलर हमेशा डिबग कोड देखें?
[ एक और जवाब के लिए की गई टिप्पणियों को फिर से पढ़ना। ]
ऊपर C99 और C89 दोनों कार्यान्वयन के पीछे एक केंद्रीय विचार यह है कि कंपाइलर उचित हमेशा डिबगिंग प्रिंटफ जैसे बयान देखता है। यह दीर्घकालिक कोड - कोड के लिए महत्वपूर्ण है जो एक या दो दशक तक चलेगा।
मान लीजिए कि कई वर्षों से कोड का एक टुकड़ा ज्यादातर निष्क्रिय (स्थिर) है, लेकिन अब इसे बदलने की आवश्यकता है। आप डिबगिंग ट्रेस को फिर से सक्षम करते हैं - लेकिन डिबगिंग (अनुरेखण) कोड को डीबग करना निराशाजनक होता है क्योंकि यह स्थिर चर के वर्षों के दौरान उन चरों को संदर्भित करता है जिनका नाम बदला गया है या उन्हें हटा दिया गया है। यदि कंपाइलर (पोस्ट-प्री-प्रोसेसर) हमेशा प्रिंट स्टेटमेंट देखता है, तो यह सुनिश्चित करता है कि आसपास के किसी भी परिवर्तन ने निदान को अमान्य नहीं किया है। यदि कंपाइलर प्रिंट स्टेटमेंट नहीं देखता है, तो यह आपकी खुद की लापरवाही (या आपके सहकर्मियों या सहयोगियों की लापरवाही) से आपकी रक्षा नहीं कर सकता है। कर्निघन और पाइक द्वारा ' प्रोग्रामिंग का अभ्यास ' देखें , विशेष रूप से अध्याय 8 ( टीपीओपी पर विकिपीडिया भी देखें )।
यह 'वहाँ किया गया है, उस अनुभव' - मैंने अनिवार्य रूप से अन्य उत्तरों में वर्णित तकनीक का उपयोग किया है जहां गैर-डीबग बिल्ड कई वर्षों के लिए प्रिंटफ़-जैसे कथन नहीं देखता है (एक दशक से अधिक)। लेकिन मैं टीपीओपी (मेरी पिछली टिप्पणी देखें) में सलाह के बाद आया, और फिर कई वर्षों के बाद कुछ डिबगिंग कोड को सक्षम किया, और डिबगिंग को तोड़ने के संदर्भ में परिवर्तित संदर्भ की समस्याओं में भाग लिया। कई बार, मुद्रण हमेशा मान्य होने से मुझे बाद की समस्याओं से बचा लिया गया है।
मैं NDEBUG का उपयोग केवल कथनों को नियंत्रित करने के लिए करता हूं, और एक अलग मैक्रो (आमतौर पर DEBUG) को नियंत्रित करने के लिए कि क्या डिबग ट्रेसिंग प्रोग्राम में बनाया गया है। यहां तक कि जब डिबग ट्रेसिंग का निर्माण किया जाता है, तो मैं अक्सर डिबग आउटपुट को बिना शर्त प्रकट नहीं करना चाहता हूं, इसलिए मेरे पास यह नियंत्रित करने के लिए तंत्र है कि क्या आउटपुट दिखाई देता है (डिबग स्तर, और fprintf()
सीधे कॉल करने के बजाय , मैं एक डीबग प्रिंट फ़ंक्शन को कॉल करता हूं जो केवल सशर्त प्रिंट करता है इसलिए कोड का एक ही निर्माण प्रोग्राम विकल्पों के आधार पर प्रिंट या प्रिंट नहीं कर सकता है)। मेरे पास बड़े कार्यक्रमों के लिए कोड का एक also मल्टीपल-सबसिस्टम ’संस्करण भी है, ताकि मैं अलग-अलग मात्रा में ट्रेस बनाने वाले कार्यक्रम के विभिन्न खंड रख सकूं - रनटाइम कंट्रोल के तहत।
मैं वकालत कर रहा हूं कि सभी बिल्ड के लिए, कंपाइलर को नैदानिक विवरण देखना चाहिए; हालाँकि, संकलक डिबगिंग ट्रेस स्टेटमेंट के लिए कोई कोड उत्पन्न नहीं करेगा जब तक कि डिबग सक्षम न हो। मूल रूप से, इसका मतलब है कि आपके सभी कोड को कंपाइलर द्वारा हर बार आपके द्वारा संकलित किए जाने पर चेक किया जाता है - चाहे रिलीज़ या डिबगिंग के लिए। यह एक अच्छी बात है!
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 (2008-02-11)
/*
@(#)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 */
C99 या बाद के लिए एकल तर्क संस्करण
काइल ब्रांट ने पूछा:
वैसे भी ऐसा करने के लिए debug_print
अभी भी काम करता है, भले ही कोई तर्क न हो? उदाहरण के लिए:
debug_print("Foo");
एक साधारण, पुराने जमाने की हैक है:
debug_print("%s\n", "Foo");
नीचे दिखाया गया GCC- केवल समाधान भी उस के लिए समर्थन प्रदान करता है।
हालाँकि, आप इसका उपयोग करके सीधा C99 सिस्टम के साथ कर सकते हैं:
#define debug_print(...) \
do { if (DEBUG) fprintf(stderr, __VA_ARGS__); } while (0)
पहले संस्करण की तुलना में, आप सीमित जांच को खो देते हैं, जिसके लिए 'fmt' तर्क की आवश्यकता होती है, जिसका अर्थ है कि कोई व्यक्ति बिना किसी तर्क के 'debug_print ()' को कॉल करने का प्रयास कर सकता है (लेकिन तर्क सूची में अनुगामी कॉमा fprintf()
संकलन करने में विफल होगा) । चेकिंग की हानि एक समस्या है या नहीं यह बहस का विषय है।
एकल तर्क के लिए जीसीसी-विशिष्ट तकनीक
कुछ संकलक मैक्रो में चर-लंबाई तर्क सूचियों को संभालने के अन्य तरीकों के लिए एक्सटेंशन प्रदान कर सकते हैं। विशेष रूप से, जैसा कि पहली बार ह्यूगो इडेलर द्वारा टिप्पणियों में उल्लेख किया गया है , जीसीसी आपको उस अल्पविराम को छोड़ने की अनुमति देता है जो आम तौर पर मैक्रो को अंतिम 'निश्चित' तर्क के बाद दिखाई देगा। यह आपको ##__VA_ARGS__
स्थूल प्रतिस्थापन पाठ में भी उपयोग करने की अनुमति देता है , जो संकेतन से पहले अल्पविराम को हटा देता है यदि, लेकिन केवल यदि, तो पिछला टोकन अल्पविराम है:
#define debug_print(fmt, ...) \
do { if (DEBUG) fprintf(stderr, fmt, ##__VA_ARGS__); } while (0)
यह समाधान प्रारूप के बाद वैकल्पिक तर्कों को स्वीकार करते हुए प्रारूप तर्क की आवश्यकता के लाभ को बरकरार रखता है।
यह तकनीक जीसीसी अनुकूलता के लिए क्लैंग द्वारा भी समर्थित है ।
क्यों करते-करते पाश?
यहाँ क्या उद्देश्य 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)
नोटेशन का उपयोग कर सकते हैं । यह हमेशा काम करता है; अभिव्यक्ति कथन तंत्र को लागू करना अधिक कठिन हो सकता है। आपको कंपाइलर से अभिव्यक्ति फॉर्म के साथ चेतावनी भी मिल सकती है जिसे आप बचना पसंद करेंगे; यह संकलक और आपके द्वारा उपयोग किए जाने वाले झंडे पर निर्भर करेगा।
TPOP पहले http://plan9.bell-labs.com/cm/cs/tpop और http://cm.bell-labs.com/cm/cs/tpop पर था, लेकिन अब दोनों हैं (2015-08-10) टूटा हुआ।
GitHub में कोड
अगर आपका उत्सुक, आप GitHub में इस कोड मेरे में देख सकते हैं SOQ (स्टैक ओवरफ़्लो प्रश्न) फ़ाइलों के रूप में भंडार debug.c
, debug.h
और mddebug.c
में
src / libsoq
उप-निर्देशिका।