सी में डिबग प्रिंटिंग के लिए # डेफिन मैक्रो?


209

एक मैक्रो बनाने की कोशिश की जा रही है जो डीबग परिभाषित होने पर प्रिंट डिबग संदेशों के लिए उपयोग किया जा सकता है, निम्न छद्म कोड की तरह:

#define DEBUG 1
#define debug_print(args ...) if (DEBUG) fprintf(stderr, args)

यह एक मैक्रो के साथ कैसे पूरा किया जाता है?


यदि कंपाइलर (gcc) स्टेटमेंट्स को ऑप्टिमाइज़ करेगा जैसे कि (DEBUG) {...} आउट, अगर प्रोडक्शन कोड में DEBUG मैक्रो 0 पर सेट है? मैं समझता हूं कि संकलक को दिखाई देने वाले डिबग बयानों को छोड़ने के लिए अच्छे कारण हैं, लेकिन एक बुरी भावना बनी हुई है। -पट
पाट

1
इसके अलावा त्रुटि
jww

जवाबों:


410

यदि आप 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 उप-निर्देशिका।


1
मुझे लगता है कि जीसीसी ## - gcc.gnu.org/onbuildocs/cpp/Variadic-Macros.html से दृष्टिकोण "एकल तर्क C99 संस्करण" शीर्षक के तहत उल्लेख करने के लिए योग्य होगा।
ह्यूगो इडेलर

2
वर्षों बाद, और यह उत्तर अभी भी सभी इंटर्नेट से सबसे अधिक उपयोगी है, कैसे उर्फ ​​प्रिंट पर! vfprintf कर्नेल स्थान में काम नहीं करता है क्योंकि stdio उपलब्ध नहीं है। धन्यवाद! #define debug(...) \ do { if (DEBUG) \ printk("DRIVER_NAME:"); \ printk(__VA_ARGS__); \ printk("\n"); \ } while (0)
केविनफ जुले

6
कीवर्ड के साथ आपके उदाहरण में __FILE__, __LINE__, __func__, __VA_ARGS__, यह संकलित नहीं करेगा यदि आपके पास कोई प्रिंटफ पैरामीटर नहीं है, अर्थात यदि आप सिर्फ कॉल करते हैं तो आप ## __ VA_ARGS__ debug_print("Some msg\n"); का उपयोग करके इसे ठीक कर सकते हैं fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__);
mc_electron

1
@ लॉजिकटम: अंतर #define debug_print(fmt, ...)और के बीच है #define debug_print(...)। इनमें से पहले कम से कम एक तर्क, प्रारूप स्ट्रिंग ( fmt) और शून्य या अधिक अन्य तर्क की आवश्यकता होती है; दूसरे को शून्य या अधिक तर्कों की आवश्यकता होती है। यदि आप debug_print()पहले के साथ प्रयोग करते हैं, तो आपको मैक्रो के दुरुपयोग के बारे में प्रीप्रोसेसर से एक त्रुटि मिलती है, जबकि दूसरा नहीं करता है। हालाँकि, आपको अभी भी संकलन त्रुटियां हैं क्योंकि प्रतिस्थापन पाठ मान्य नहीं है। इसलिए, यह वास्तव में बहुत अंतर नहीं है - इसलिए 'सीमित जाँच' शब्द का उपयोग।
जोनाथन लेफ्लर

1
दिखाया गया संस्करण, @ सेंटऑन्टारियो, पूरे एप्लिकेशन में एकल सक्रिय डिबगिंग स्तर का उपयोग करता है, और मैं आमतौर पर कमांड लाइन विकल्प का उपयोग करता हूं ताकि प्रोग्राम चलने पर डिबगिंग स्तर सेट करने की अनुमति दी जा सके। मेरे पास एक ऐसा संस्करण भी है जो कई अलग-अलग उप-प्रणालियों को पहचानता है, जिनमें से प्रत्येक को एक नाम और अपना डिबगिंग स्तर दिया जाता है, ताकि मैं -D input=4,macros=9,rules=2इनपुट सिस्टम के डिबग स्तर को 4 पर सेट करने के लिए उपयोग कर सकूं, मैक्रो सिस्टम को 9 (गहन जांच के तहत) ) और नियम प्रणाली 2. विषय पर अंतहीन विविधताएं हैं; जो भी आपको सूट करे उसका उपयोग करें।
जोनाथन लेफ़लर 16

28

मैं कुछ इस तरह का उपयोग करें:

#ifdef DEBUG
 #define D if(1) 
#else
 #define D if(0) 
#endif

क्या मैं केवल उपसर्ग के रूप में D का उपयोग करता हूं:

D printf("x=%0.3f\n",x);

कंपाइलर डिबग कोड देखता है, कोई अल्पविराम समस्या नहीं है और यह हर जगह काम करता है। यह तब भी काम करता है जब printfपर्याप्त नहीं है, कहो जब आपको किसी सरणी को डंप करना होगा या कुछ निदान मूल्य की गणना करना होगा जो प्रोग्राम के लिए अनावश्यक है।

संपादित करें: ठीक है, यह एक समस्या उत्पन्न कर सकता है जब elseकहीं पास है कि इस इंजेक्शन द्वारा रोका जा सकता है if। यह एक संस्करण है जो इस पर जाता है:

#ifdef DEBUG
 #define D 
#else
 #define D for(;0;)
#endif

3
के लिए के रूप में for(;0;), यह एक समस्या उत्पन्न हो सकती है जब आप की तरह कुछ लिखने D continue;या D break;
ACcreator

1
मुझे मिला; हालांकि ऐसा लगता है कि दुर्घटना की संभावना नहीं है।
mbq

11

एक पोर्टेबल (आईएसओ C90) कार्यान्वयन के लिए, आप इस तरह डबल कोष्ठक का उपयोग कर सकते हैं;

#include <stdio.h>
#include <stdarg.h>

#ifndef NDEBUG
#  define debug_print(msg) stderr_printf msg
#else
#  define debug_print(msg) (void)0
#endif

void
stderr_printf(const char *fmt, ...)
{
  va_list ap;
  va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  va_end(ap);
}

int
main(int argc, char *argv[])
{
  debug_print(("argv[0] is %s, argc is %d\n", argv[0], argc));
  return 0;
}

या (hackish, यह सुझा नहीं होगा)

#include <stdio.h>

#define _ ,
#ifndef NDEBUG
#  define debug_print(msg) fprintf(stderr, msg)
#else
#  define debug_print(msg) (void)0
#endif

int
main(int argc, char *argv[])
{
  debug_print("argv[0] is %s, argc is %d"_ argv[0] _ argc);
  return 0;
}

3
@LB: प्रीप्रोसेसर बनाने के लिए 'सोचें' केवल एक तर्क है, जबकि _ को बाद के चरण में विस्तारित किया जा सकता है।
मार्सिन कोइज़ुक

10

यहाँ संस्करण मैं उपयोग कर रहा हूँ:

#ifdef NDEBUG
#define Dprintf(FORMAT, ...) ((void)0)
#define Dputs(MSG) ((void)0)
#else
#define Dprintf(FORMAT, ...) \
    fprintf(stderr, "%s() in %s, line %i: " FORMAT "\n", \
        __func__, __FILE__, __LINE__, __VA_ARGS__)
#define Dputs(MSG) Dprintf("%s", MSG)
#endif

9

मैं कुछ ऐसा करूंगा

#ifdef DEBUG
#define debug_print(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#else
#define debug_print(fmt, ...) do {} while (0)
#endif

मुझे लगता है कि यह क्लीनर है।


मुझे वास्तव में ध्वज के रूप में परीक्षण के अंदर मैक्रो का उपयोग करने का विचार पसंद नहीं है। क्या आप बता सकते हैं कि डिबग प्रिंटिंग की हमेशा जाँच क्यों की जानी चाहिए?
LB40

1
@ जोनाथन: यदि कोड केवल कभी भी डिबग मोड में निष्पादित होता है, तो गैर-डीबग मोड में संकलित करने पर आपको क्यों ध्यान देना चाहिए? assert()स्टडलिब उसी तरह से काम करता है और मैं आमतौर पर सिर्फ NDEBUGअपने डिबगिंग कोड के लिए मैक्रो का फिर से उपयोग करता हूं ...
क्रिस्टोफ

परीक्षण में DEBUG का उपयोग करते हुए, यदि कोई अनियंत्रित अपंग DEBUG करता है, तो आपका कोड अब संकलित नहीं होता है। सही ?
LB40

4
डिबगिंग को सक्षम करने के लिए निराशा होती है और फिर डिबगिंग कोड को डीबग करना पड़ता है क्योंकि यह उन वेरिएबल्स को संदर्भित करता है जिनका नाम बदल दिया गया है या फिर से हटा दिया गया है, आदि। यदि कंपाइलर (पोस्ट-प्री-प्रोसेसर) हमेशा प्रिंट स्टेटमेंट देखता है, तो यह सुनिश्चित करता है कि आसपास के कुछ बदलाव हुए हैं। डायग्नोस्टिक्स को अमान्य नहीं किया गया। यदि कंपाइलर प्रिंट स्टेटमेंट नहीं देखता है, तो यह आपकी खुद की लापरवाही (या आपके सहकर्मियों या सहयोगियों की लापरवाही) से आपकी रक्षा नहीं कर सकता है। कर्निघन और पाइक द्वारा 'प्रोग्रामिंग का अभ्यास' देखें - plan9.bell-labs.com/cm/cs/tpop
जोनाथन लेफलर

1
@Christoph: ठीक है, की तरह ... मैं NDEBUG का उपयोग केवल अभिकथन को नियंत्रित करने के लिए करता हूं, और डिबग ट्रेसिंग को नियंत्रित करने के लिए एक अलग मैक्रो (आमतौर पर DEBUG)। मैं अक्सर डिबग आउटपुट को बिना शर्त प्रकट नहीं करना चाहता हूं, इसलिए मेरे पास यह नियंत्रित करने के लिए तंत्र है कि क्या आउटपुट दिखाई देता है (डिबग स्तर, और सीधे fprintf को कॉल करने के बजाय), मैं एक डीबग प्रिंट फ़ंक्शन को कॉल करता हूं जो केवल सशर्त रूप से प्रिंट करता है ताकि उसी का निर्माण हो सके कोड प्रोग्राम विकल्पों के आधार पर प्रिंट या प्रिंट नहीं कर सकता है)। मैं वकालत कर रहा हूं कि सभी बिल्ड के लिए, कंपाइलर को नैदानिक ​​विवरण देखना चाहिए; हालाँकि, यह कोड उत्पन्न नहीं करेगा जब तक कि डिबग सक्षम न हो।
जोनाथन लेफ़लर

8

Http://gcc.gnu.org/oniltocs/cpp/Variadic-Macros.html के अनुसार , ##पहले होना चाहिए __VA_ARGS__

अन्यथा, एक मैक्रो #define dbg_print(format, ...) printf(format, __VA_ARGS__)निम्नलिखित उदाहरण को संकलित नहीं करेगा dbg_print("hello world");:।


1
ढेर अतिप्रवाह में आपका स्वागत है। आप सही हैं कि जीसीसी में आपके द्वारा निर्दिष्ट गैर-मानक एक्सटेंशन है। वर्तमान में स्वीकृत उत्तर वास्तव में इसका उल्लेख करता है, जिसमें आपके द्वारा दिए गए संदर्भ URL भी शामिल है।
जोनाथन लेफलर

7
#define debug_print(FMT, ARGS...) do { \
    if (DEBUG) \
        fprintf(stderr, "%s:%d " FMT "\n", __FUNCTION__, __LINE__, ## ARGS); \
    } while (0)

C का कौन सा संस्करण उस संकेतन का समर्थन करता है? और, अगर यह काम करता है, तो उस तरह के सभी तर्कों को चिपकाने का मतलब है कि आपके पास प्रारूप स्ट्रिंग के लिए केवल बहुत सीमित विकल्प हैं, है न?
जोनाथन लेफलर

@ जोनाथन: gcc (डेबियन 4.3.3-13) 4.3.3
पलक

1
ठीक है - सहमत: यह एक पुराने GNU एक्सटेंशन (GCC 4.4.1 मैनुअल के खंड 5.17) के रूप में प्रलेखित है। लेकिन आपको शायद यह दस्तावेज़ देना चाहिए कि यह केवल जीसीसी के साथ काम करेगा - या शायद हमने इन टिप्पणियों में हमारे बीच किया है।
जोनाथन लेफ्लर

1
मेरा इरादा आर्ग का उपयोग कर और मुख्य रूप से के उपयोग का प्रदर्शन करने का एक और शैली को दिखाने के लिए था फंक्शन और लाइन
eyalm

2

यही है वह जो मेरे द्वारा उपयोग किया जाता है:

#if DBG
#include <stdio.h>
#define DBGPRINT printf
#else
#define DBGPRINT(...) /**/  
#endif

अतिरिक्त तर्कों के बिना भी, ठीक से प्रिंटफ़ को संभालने का अच्छा लाभ है। डीबीजी == 0 के मामले में, यहां तक ​​कि डंबेस्ट कंपाइलर को चबाने के लिए कुछ भी नहीं मिलता है, इसलिए कोई कोड उत्पन्न नहीं होता है।


यह बेहतर है कि कंपाइलर हमेशा डिबग कोड की जांच करें।
जोनाथन लेफ़लर

1

नीचे का मेरा पसंदीदा है var_dump, जिसे जब बुलाया जाता है:

var_dump("%d", count);

जैसे उत्पादन का उत्पादन:

patch.c:150:main(): count = 0

@ "जोनाथन लेफ़लर" को श्रेय। सभी C89- खुश हैं:

कोड

#define DEBUG 1
#include <stdarg.h>
#include <stdio.h>
void debug_vprintf(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
}

/* Call as: (DOUBLE PARENTHESES ARE MANDATORY) */
/* var_debug(("outfd = %d, somefailed = %d\n", outfd, somefailed)); */
#define var_debug(x) do { if (DEBUG) { debug_vprintf ("%s:%d:%s(): ", \
    __FILE__,  __LINE__, __func__); debug_vprintf x; }} while (0)

/* var_dump("%s" variable_name); */
#define var_dump(fmt, var) do { if (DEBUG) { debug_vprintf ("%s:%d:%s(): ", \
    __FILE__,  __LINE__, __func__); debug_vprintf ("%s = " fmt, #var, var); }} while (0)

#define DEBUG_HERE do { if (DEBUG) { debug_vprintf ("%s:%d:%s(): HERE\n", \
    __FILE__,  __LINE__, __func__); }} while (0)

1

इसलिए, जब gcc का उपयोग करते हैं, तो मुझे पसंद है:

#define DBGI(expr) ({int g2rE3=expr; fprintf(stderr, "%s:%d:%s(): ""%s->%i\n", __FILE__,  __LINE__, __func__, #expr, g2rE3); g2rE3;})

क्योंकि इसे कोड में डाला जा सकता है।

मान लीजिए आप डिबग करने की कोशिश कर रहे हैं

printf("%i\n", (1*2*3*4*5*6));

720

तो आप इसे बदल सकते हैं:

printf("%i\n", DBGI(1*2*3*4*5*6));

hello.c:86:main(): 1*2*3*4*5*6->720
720

और आप एक विश्लेषण प्राप्त कर सकते हैं कि किस अभिव्यक्ति का मूल्यांकन किया गया था।

यह दोहरे मूल्यांकन की समस्या से सुरक्षित है, लेकिन गेंसो की अनुपस्थिति इसे नाम-टकराव के लिए खुला छोड़ देती है।

हालाँकि यह घोंसला बनाता है:

DBGI(printf("%i\n", DBGI(1*2*3*4*5*6)));

hello.c:86:main(): 1*2*3*4*5*6->720
720
hello.c:86:main(): printf("%i\n", DBGI(1*2*3*4*5*6))->4

इसलिए मुझे लगता है कि जब तक आप एक चर नाम के रूप में g2rE3 का उपयोग करने से बचते हैं, आप ठीक होंगे।

निश्चित रूप से मैंने इसे पाया है (और स्ट्रिंग्स के लिए संबद्ध संस्करण, और डिबग स्तर आदि के लिए संस्करण) अमूल्य।


1

मैं वर्षों से ऐसा करने के बारे में बात कर रहा था, और अंत में एक समाधान के साथ आया। हालाँकि, मुझे नहीं पता था कि यहाँ पहले से ही अन्य समाधान थे। पहले, लेफ़लर के जवाब के साथ अंतर पर , मुझे उनका तर्क नहीं दिखता कि डिबग प्रिंट हमेशा संकलित किए जाएं। मुझे अपनी परियोजना में निष्पादित होने वाले अनावश्यक कोड के टन नहीं चाहिए, जब जरूरत नहीं होगी, उन मामलों में जहां मुझे परीक्षण करने की आवश्यकता है और वे अनुकूलित नहीं हो सकते हैं।

हर बार संकलन नहीं करने से यह वास्तविक व्यवहार में खराब हो सकता है। आप डिबग प्रिंट के साथ हवा करते हैं जो कभी-कभी संकलन नहीं करते हैं, लेकिन किसी प्रोजेक्ट को अंतिम रूप देने से पहले उन्हें संकलित करना और परीक्षण करना इतना कठिन नहीं है। इस प्रणाली के साथ, यदि आप डिबग के तीन स्तरों का उपयोग कर रहे हैं, तो बस डिबग संदेश स्तर तीन पर रखें, अपनी संकलित त्रुटियों को ठीक करें और इससे पहले कि आप yer कोड को अंतिम रूप दें, किसी भी अन्य के लिए जाँच करें। (बेशक, डीबग कथनों का संकलन इस बात की कोई गारंटी नहीं है कि वे अभी भी उद्देश्य के अनुसार काम कर रहे हैं।)

मेरा समाधान डिबग विस्तार के स्तरों के लिए भी प्रदान करता है; और यदि आप इसे उच्चतम स्तर पर सेट करते हैं, तो वे सभी संकलन करते हैं। यदि आप हाल ही में उच्च डीबग विवरण स्तर का उपयोग कर रहे हैं, तो वे सभी उस समय संकलन करने में सक्षम थे। अंतिम अद्यतन बहुत आसान होना चाहिए। मुझे तीन स्तरों से अधिक की आवश्यकता नहीं है, लेकिन जोनाथन का कहना है कि उसने नौ का उपयोग किया है। इस विधि (जैसे लेफ़लर की) को किसी भी स्तर के स्तर तक बढ़ाया जा सकता है। मेरी विधि का उपयोग सरल हो सकता है; आपके कोड में उपयोग किए जाने पर केवल दो कथनों की आवश्यकता है। हालाँकि, मैं क्लॉस मैक्रो कोडिंग भी कर रहा हूँ - हालाँकि यह कुछ भी नहीं करता है। यह हो सकता है अगर मैं एक फाइल को भेज रहा था।

लागत के खिलाफ परीक्षण के अतिरिक्त चरण को देखने के लिए कि वे प्रसव से पहले संकलित करेंगे, वह है

  1. यदि आप एक पर्याप्त अनुकूलन स्तर रखते हैं, तो आपको उन पर भरोसा करना चाहिए, जो कि अनुकूलित रूप से होने चाहिए।
  2. इसके अलावा, वे शायद नहीं करेंगे यदि आप परीक्षण उद्देश्यों के लिए बंद किए गए अनुकूलन के साथ एक रिलीज संकलन बनाते हैं (जो स्वाभाविक रूप से दुर्लभ है); और वे लगभग निश्चित रूप से डिबग के दौरान बिल्कुल नहीं करेंगे - जिससे रनटाइम के दौरान "अगर (DEBUG)" के दर्जनों या सैकड़ों को निष्पादित किया जा सके; इस प्रकार निष्पादन को धीमा करना (जो मेरा सिद्धांत आपत्ति है) और कम महत्वपूर्ण बात, अपने निष्पादन योग्य या डीएल आकार को बढ़ाना; और इसलिए निष्पादन और संकलन समय। जोनाथन, हालांकि, मुझे सूचित करता है कि उसका तरीका भी बयानों को संकलित नहीं किया जा सकता है।

शाखाएं वास्तव में आधुनिक प्री-प्रेशरिंग प्रोसेसर में अपेक्षाकृत बहुत महंगी हैं। हो सकता है कि कोई बड़ी बात न हो अगर आपका ऐप टाइम-क्रिटिकल नहीं है; लेकिन अगर प्रदर्शन एक मुद्दा है, तो, हाँ, एक बड़ा पर्याप्त सौदा जिसे मैं कुछ तेज़ी से निष्पादित डिबग कोड (और संभवतः तेज़ मामलों में, दुर्लभ मामलों में, जैसा कि उल्लेख किया गया है) के लिए चुनना पसंद करूंगा।

इसलिए, मैं जो चाहता था वह एक डिबग प्रिंट मैक्रो है जो संकलन नहीं करता है यदि इसे मुद्रित नहीं किया जाना है, लेकिन यदि यह है तो। मैं डिबगिंग के स्तर भी चाहता था, ताकि उदाहरण के लिए, अगर मैं चाहता था कि कोड के प्रदर्शन-महत्वपूर्ण हिस्से कुछ समय में प्रिंट न हों, लेकिन दूसरों पर प्रिंट करने के लिए, मैं डिबग स्तर सेट कर सकता था, और अतिरिक्त डीबग प्रिंट को किक कर सकता हूं। " डिबग स्तर लागू करने का एक तरीका है जो निर्धारित करता है कि प्रिंट भी संकलित था या नहीं। मैंने इसे इस तरह हासिल किया:

DebugLog.h:

// FILE: DebugLog.h
// REMARKS: This is a generic pair of files useful for debugging.  It provides three levels of 
// debug logging, currently; in addition to disabling it.  Level 3 is the most information.
// Levels 2 and 1 have progressively more.  Thus, you can write: 
//     DEBUGLOG_LOG(1, "a number=%d", 7);
// and it will be seen if DEBUG is anything other than undefined or zero.  If you write
//     DEBUGLOG_LOG(3, "another number=%d", 15);
// it will only be seen if DEBUG is 3.  When not being displayed, these routines compile
// to NOTHING.  I reject the argument that debug code needs to always be compiled so as to 
// keep it current.  I would rather have a leaner and faster app, and just not be lazy, and 
// maintain debugs as needed.  I don't know if this works with the C preprocessor or not, 
// but the rest of the code is fully C compliant also if it is.

#define DEBUG 1

#ifdef DEBUG
#define DEBUGLOG_INIT(filename) debuglog_init(filename)
#else
#define debuglog_init(...)
#endif

#ifdef DEBUG
#define DEBUGLOG_CLOSE debuglog_close
#else
#define debuglog_close(...)
#endif

#define DEBUGLOG_LOG(level, fmt, ...) DEBUGLOG_LOG ## level (fmt, ##__VA_ARGS__)

#if DEBUG == 0
#define DEBUGLOG_LOG0(...)
#endif

#if DEBUG >= 1
#define DEBUGLOG_LOG1(fmt, ...) debuglog_log (fmt, ##__VA_ARGS__)
#else
#define DEBUGLOG_LOG1(...)
#endif

#if DEBUG >= 2
#define DEBUGLOG_LOG2(fmt, ...) debuglog_log (fmt, ##__VA_ARGS__)
#else
#define DEBUGLOG_LOG2(...)
#endif

#if DEBUG == 3
#define DEBUGLOG_LOG3(fmt, ...) debuglog_log (fmt, ##__VA_ARGS__)
#else
#define DEBUGLOG_LOG3(...)
#endif

void debuglog_init(char *filename);
void debuglog_close(void);
void debuglog_log(char* format, ...);

DebugLog.cpp:

// FILE: DebugLog.h
// REMARKS: This is a generic pair of files useful for debugging.  It provides three levels of 
// debug logging, currently; in addition to disabling it.  See DebugLog.h's remarks for more 
// info.

#include <stdio.h>
#include <stdarg.h>

#include "DebugLog.h"

FILE *hndl;
char *savedFilename;

void debuglog_init(char *filename)
{
    savedFilename = filename;
    hndl = fopen(savedFilename, "wt");
    fclose(hndl);
}

void debuglog_close(void)
{
    //fclose(hndl);
}

void debuglog_log(char* format, ...)
{
    hndl = fopen(savedFilename,"at");
    va_list argptr;
    va_start(argptr, format);
    vfprintf(hndl, format, argptr);
    va_end(argptr);
    fputc('\n',hndl);
    fclose(hndl);
}

मैक्रोज़ का उपयोग करना

इसका उपयोग करने के लिए, बस करें:

DEBUGLOG_INIT("afile.log");

लॉग फ़ाइल में लिखने के लिए, बस करें:

DEBUGLOG_LOG(1, "the value is: %d", anint);

इसे बंद करने के लिए, आप करते हैं:

DEBUGLOG_CLOSE();

हालाँकि वर्तमान में यह आवश्यक नहीं है, तकनीकी रूप से बोलना, क्योंकि यह कुछ भी नहीं करता है। मैं अभी भी CLOSE का उपयोग कर रहा हूं, हालाँकि, अगर मैं इस बारे में अपना दिमाग बदल देता हूं कि यह कैसे काम करता है, और लॉगिंग स्टेटमेंट के बीच फाइल को खुला छोड़ना चाहते हैं।

फिर, जब आप डिबग प्रिंटिंग चालू करना चाहते हैं, तो हेडर फ़ाइल में पहले #define को संपादित करने के लिए कहें, उदा

#define DEBUG 1

लॉगिंग स्टेटमेंट्स को कुछ भी नहीं करने के लिए संकलित करें

#define DEBUG 0

यदि आपको बार-बार निष्पादित कोड की जानकारी चाहिए (अर्थात उच्च स्तर का विवरण), तो आप लिखना चाह सकते हैं:

 DEBUGLOG_LOG(3, "the value is: %d", anint);

यदि आप DEBUG को 3 मानते हैं, तो लॉगिंग स्तर 1, 2 और 3 संकलित होते हैं। यदि आप इसे 2 पर सेट करते हैं, तो आपको लॉगिंग स्तर 1 और 2 मिलते हैं। यदि आप इसे 1 पर सेट करते हैं, तो आपको केवल लॉगिंग स्तर 1 स्टेटमेंट मिलते हैं।

जैसे-जैसे लूप होता है, चूंकि यह एक एकल फ़ंक्शन या कुछ भी का मूल्यांकन करता है, अगर एक स्टेटमेंट के बजाय, लूप की आवश्यकता नहीं है। ठीक है, मुझे C ++ IO (और Qt के QString :: arg () के बजाय C का उपयोग करने के लिए कैस्टरेट करें जब Qt में भी चर को स्वरूपित करने का एक सुरक्षित तरीका है, तो - यह बहुत चालाक है, लेकिन अधिक कोड लेता है और प्रारूपण प्रलेखन व्यवस्थित नहीं है। जैसा कि यह हो सकता है - लेकिन फिर भी मुझे ऐसे मामले मिल गए हैं जहां इसके बेहतर) हैं, लेकिन आप जो चाहें कोड में डाल सकते हैं। यह एक वर्ग भी हो सकता है, लेकिन तब आपको इसे तुरंत करने और इसके साथ बने रहने की आवश्यकता होगी, या एक नया () करें और इसे स्टोर करें। इस तरह, आप बस अपने स्रोत में #include, init और वैकल्पिक रूप से नज़दीकी स्टेटमेंट ड्रॉप करते हैं, और आप इसका उपयोग शुरू करने के लिए तैयार हैं। यह एक अच्छा वर्ग बना सकता है, हालांकि, यदि आप बहुत इच्छुक हैं।

मैंने पहले बहुत सारे समाधान देखे थे, लेकिन किसी ने भी मेरे मापदंड के अनुकूल नहीं था।

  1. इसे आप जितने चाहें उतने स्तर तक बढ़ा सकते हैं।
  2. यह मुद्रण न होने पर कुछ भी करने के लिए संकलित करता है।
  3. यह एक आसान-से-संपादन स्थान में IO को केंद्रीकृत करता है।
  4. यह प्रिंटफ़ स्वरूपण का उपयोग करके लचीला है।
  5. फिर, यह डिबग रन को धीमा नहीं करता है, जबकि हमेशा-संकलित डिबग प्रिंट हमेशा डिबग मोड में निष्पादित होते हैं। यदि आप कंप्यूटर विज्ञान कर रहे हैं, और सूचना प्रसंस्करण लिखना आसान नहीं है, तो आप खुद को सीपीयू-उपभोग करने वाला सिम्युलेटर चला सकते हैं, उदाहरण के लिए जहां डीबगर इसे एक वेक्टर के लिए सीमा से बाहर सूचकांक के साथ रोकता है। ये पहले से ही डिबग मोड में अतिरिक्त-धीरे-धीरे चलते हैं। सैकड़ों डिबग प्रिंटों का अनिवार्य निष्पादन अनिवार्य रूप से ऐसे रन को धीमा कर देगा। मेरे लिए, इस तरह के रन असामान्य नहीं हैं।

बहुत महत्वपूर्ण नहीं है, लेकिन इसके अलावा:

  1. इसके लिए तर्क के बिना किसी हैक की आवश्यकता नहीं है (उदाहरण के लिए DEBUGLOG_LOG(3, "got here!");); इस प्रकार आप का उपयोग करने के लिए अनुमति देता है, उदाहरण के लिए Qt का सुरक्षित .arg () स्वरूपण। यह MSVC पर काम करता है, और इस प्रकार, शायद gcc। यह एस में उपयोग करता ##है #define, जो गैर-मानक है, जैसा कि लेफ़लर बताते हैं, लेकिन व्यापक रूप से समर्थित है। ( ##यदि आवश्यक हो तो इसका उपयोग नहीं करने के लिए आप इसे फिर से बना सकते हैं , लेकिन आपको एक हैक का उपयोग करना होगा जैसे कि वह प्रदान करता है।)

चेतावनी: यदि आप लॉगिंग स्तर तर्क प्रदान करना भूल जाते हैं, तो MSVC अनपेक्षित रूप से दावा करता है कि पहचानकर्ता परिभाषित नहीं है।

आप DEBUG के अलावा एक प्रीप्रोसेसर प्रतीक नाम का उपयोग करना चाह सकते हैं, क्योंकि कुछ स्रोत उस प्रतीक को भी परिभाषित करते हैं (उदाहरण के ./configureलिए, भवन निर्माण की तैयारी के लिए कमांड का उपयोग करते हुए )। जब मैंने इसे विकसित किया तो यह मुझे स्वाभाविक लगा। मैंने इसे एक ऐसे एप्लिकेशन में विकसित किया जहां DLL का उपयोग किसी और चीज के द्वारा किया जा रहा है, और यह लॉग प्रिंट्स को एक फ़ाइल में भेजने के लिए अधिक कॉन्वेंट है; लेकिन इसे बदलकर vprintf () ठीक काम करेगा।

मुझे आशा है कि यह आप में से कई लोगों को डिबग लॉगिंग करने का सबसे अच्छा तरीका पता लगाने के बारे में बताता है; या आप जो पसंद करते हैं वह आपको दिखाता है। मैं आधे-अधूरे मन से दशकों से यह जानने की कोशिश कर रहा हूं। MSVC 2012 और 2015 में काम करता है, और इस प्रकार संभवतः gcc पर; के रूप में अच्छी तरह से शायद कई अन्य लोगों पर काम कर रहा है, लेकिन मैंने उन पर इसका परीक्षण नहीं किया है।

मेरा मतलब है कि इस एक दिन का एक स्ट्रीमिंग संस्करण भी बनाया जाए।

नोट: धन्यवाद Leffler के लिए, जिन्होंने सौहार्दपूर्ण ढंग से मेरे संदेश को StackOverflow के लिए बेहतर बनाने में मेरी मदद की है।


2
आप कहते हैं " if (DEBUG)रनटाइम पर दर्जनों या सैकड़ों बयानों को निष्पादित करना, जो बाहर अनुकूलित नहीं होते हैं" - जो पवनचक्कियों पर झुका हुआ है । प्रणाली मैं वर्णित के पूरे मुद्दे कि कोड संकलक द्वारा जाँच की है (महत्वपूर्ण स्वचालित, और - कोई विशेष निर्माण आवश्यक) लेकिन डिबग कोड सभी में उत्पन्न नहीं है क्योंकि यह है बाहर अनुकूलित (तो वहाँ पर शून्य क्रम प्रभाव है कोड आकार या प्रदर्शन क्योंकि कोड रनटाइम पर मौजूद नहीं है)।
जोनाथन लेफ़लर

जोनाथन लेफ़लर: मेरे गलत शब्द को इंगित करने के लिए Thx। मैंने अपने विचारों को अपनी अंगुलियों से तेज दौड़ने दिया, ऐसा होने पर मुझे बहुत खुशी हुई। मैंने अपनी आपत्तियों को "... 1) के साथ संशोधित किया है। आपको उन्हें अनुकूलित करने के लिए उन पर भरोसा करना चाहिए, जो कि आपके पास पर्याप्त अनुकूलन स्तर होने पर होना चाहिए। 2) इसके अलावा, यदि आप अनुकूलन के साथ कोई संकलन तैयार करते हैं तो वे नहीं करेंगे। परीक्षण के उद्देश्यों के लिए बंद कर दिया गया है, और वे संभवतः डिबग के दौरान बिल्कुल भी नहीं होंगे - जिससे दर्जनों या सैकड़ों 'यदि (DEBUG)' स्टेटमेंट रनटाइम पर निष्पादित हो रहे हैं - जिससे आपके निष्पादन योग्य या dll का आकार बढ़ जाएगा, और निष्पादन समय। "
कोडलकरर

आपके लिए मेरा दूसरा महत्वपूर्ण काम करने के लिए, आपके पास डिबग स्तर होना चाहिए। जबकि मुझे अक्सर उनमें से बहुत से चालू करने की आवश्यकता नहीं होती है, कुछ एप्लिकेशन वास्तव में एक सरल "#define 3" के साथ समय-महत्वपूर्ण लूप के बारे में विस्तार का एक बड़ा स्तर प्राप्त करने में सक्षम होते हैं, और फिर वापस जाने के लिए "#define डेबग 1" के साथ बहुत कम वाचाल जानकारी। मुझे तीन स्तरों से अधिक की आवश्यकता नहीं है, और इस प्रकार, मेरे डिबग के कम से कम 1/3 भाग पहले से ही रिलीज पर संकलित हैं। यदि मैंने हाल ही में स्तर 3 का उपयोग किया है, तो वे शायद सभी करते हैं।
कोडलेकर

YMMV। मैंने जिस आधुनिक प्रणाली को दिखाया है, वह डिबग स्तरों की गतिशील (रनटाइम) सेटिंग का समर्थन करती है, इसलिए आप क्रमिक रूप से तय कर सकते हैं कि रनटाइम में डिबग का कितना उत्पादन होता है। मैंने आमतौर पर 1-9 के स्तर का इस्तेमाल किया है, हालांकि इसकी कोई ऊपरी सीमा नहीं है (या निचली सीमा; डिफ़ॉल्ट स्तर 0 है जो आमतौर पर बंद है, लेकिन सक्रिय विकास के दौरान स्पष्ट रूप से अनुरोध किया जा सकता है यदि उपयुक्त हो - यह दीर्घकालिक कार्य के लिए उपयुक्त नहीं है)। मैंने 3 का डिफ़ॉल्ट स्तर चुना; चीजों को ट्यून किया जा सकता है। इससे मुझे बहुत नियंत्रण मिलता है। यदि आप निष्क्रिय होने पर डिबग कोड का परीक्षण नहीं करना चाहते हैं, तो विकल्प को बदल दें ((void)0)- यह आसान है।
जोनाथन लेफ़लर

1
आह। इससे पूरी बात पढ़ने में मदद मिली होगी। यह एक लंबी पोस्ट है। मुझे लगता है कि इस तरह अब तक आवश्यक बिंदु मिल गए हैं। यह तुम्हारा है, मेरी तरह, सभी डीबग प्रिंटों को संकलित करने या न करने के लिए उपयोग किया जा सकता है, और स्तरों का समर्थन कर सकता है; हालांकि माना जाता है, आपका स्तर उन स्तरों को संकलित कर सकता है जिनका आप उपयोग नहीं कर रहे हैं - डिबग के दौरान लागत पर।
कोडालुकर 18

0

मेरा मानना ​​है कि विषय की यह भिन्नता प्रति श्रेणी के लिए एक अलग मैक्रो नाम रखने की आवश्यकता के बिना डिबग श्रेणियां देती है।

मैंने इस भिन्नता का उपयोग एक Arduino प्रोजेक्ट में किया जहां प्रोग्राम स्पेस 32K तक सीमित है और डायनेमिक मेमोरी 2K तक सीमित है। डीबग कथनों और ट्रेस डीबग स्ट्रिंग्स के अतिरिक्त स्थान का उपयोग करता है। इसलिए यह जरूरी है कि डिबग ट्रेस को सीमित करने के लिए सक्षम किया जाए, जो संकलन समय पर न्यूनतम आवश्यक हर बार कोड के निर्माण में शामिल हो।

debug.h

#ifndef DEBUG_H
#define DEBUG_H

#define PRINT(DEBUG_CATEGORY, VALUE)  do { if (DEBUG_CATEGORY & DEBUG_MASK) Serial.print(VALUE);} while (0);

#endif

कॉलिंग .cpp फ़ाइल

#define DEBUG_MASK 0x06
#include "Debug.h"

...
PRINT(4, "Time out error,\t");
...
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.