كيف يمكنك إنشاء وظيفة تصحيح الأخطاء فقط التي تأخذ قائمة وسيطات متغيرة؟مثل برينتف ()

StackOverflow https://stackoverflow.com/questions/15240

  •  08-06-2019
  •  | 
  •  

سؤال

أرغب في إنشاء وظيفة تسجيل التصحيح باستخدام نفس المعلمات printf.ولكن يمكن إزالتها بواسطة المعالج المسبق أثناء عمليات الإنشاء المحسنة.

على سبيل المثال:

Debug_Print("Warning: value %d > 3!\n", value);

لقد ألقيت نظرة على وحدات الماكرو المتنوعة ولكنها غير متوفرة على جميع الأنظمة الأساسية. gcc يدعمهم، msvc لا.

هل كانت مفيدة؟

المحلول

ما زلت أفعل ذلك بالطريقة القديمة، من خلال تحديد ماكرو (XTRACE، أدناه) يرتبط إما بـ no-op أو باستدعاء دالة مع قائمة وسيطات متغيرة.داخليًا، اتصل بـ vsnprintf حتى تتمكن من الاحتفاظ ببناء جملة printf:

#include <stdio.h>

void XTrace0(LPCTSTR lpszText)
{
   ::OutputDebugString(lpszText);
}

void XTrace(LPCTSTR lpszFormat, ...)
{
    va_list args;
    va_start(args, lpszFormat);
    int nBuf;
    TCHAR szBuffer[512]; // get rid of this hard-coded buffer
    nBuf = _vsnprintf(szBuffer, 511, lpszFormat, args);
    ::OutputDebugString(szBuffer);
    va_end(args);
}

ثم التبديل #ifdef النموذجي:

#ifdef _DEBUG
#define XTRACE XTrace
#else
#define XTRACE
#endif

حسنًا، يمكن تنظيف ذلك قليلاً ولكنها الفكرة الأساسية.

نصائح أخرى

هذه هي الطريقة التي أقوم بها بتصحيح أخطاء الطباعة في C++.حدد "dout" (تصحيح الأخطاء) مثل هذا:

#ifdef DEBUG
#define dout cout
#else
#define dout 0 && cout
#endif

في الكود أستخدم "dout" تمامًا مثل "cout".

dout << "in foobar with x= " << x << " and y= " << y << '\n';

إذا استبدل المعالج المسبق "dout" بـ "0 && cout" لاحظ أن << له أسبقية أعلى من && وتقييم الدائرة القصيرة لـ && يجعل تقييم السطر بأكمله إلى 0.نظرًا لعدم استخدام 0، لا يقوم المترجم بإنشاء أي كود على الإطلاق لهذا السطر.

إليك شيء أفعله في C/C++.أولاً، تكتب دالة تستخدم عناصر varargs (انظر الرابط الموجود في منشور Stu).ثم افعل شيئًا مثل هذا:


 int debug_printf( const char *fmt, ... );
 #if defined( DEBUG )
  #define DEBUG_PRINTF(x) debug_printf x
 #else
   #define DEBUG_PRINTF(x)
 #endif

 DEBUG_PRINTF(( "Format string that takes %s %s\n", "any number", "of args" ));

كل ما عليك أن تتذكره هو استخدام الأقواس المزدوجة عند استدعاء وظيفة تصحيح الأخطاء، وستتم إزالة السطر بأكمله في تعليمات برمجية غير تصحيح الأخطاء.

هناك طريقة ممتعة أخرى لإيقاف الوظائف المتنوعة وهي:

#define function sizeof

@CodingTheWheel:

هناك مشكلة واحدة بسيطة في أسلوبك.النظر في مكالمة مثل

XTRACE("x=%d", x);

يعمل هذا بشكل جيد في إصدار تصحيح الأخطاء، ولكن في إصدار الإصدار سيتم توسيعه إلى:

("x=%d", x);

وهو لغة C مشروعة تمامًا وسيتم تجميعها وتشغيلها عادةً بدون آثار جانبية ولكنها تولد تعليمات برمجية غير ضرورية.الطريقة التي أستخدمها عادةً للتخلص من هذه المشكلة هي:

  1. اجعل الدالة XTrace ترجع int (فقط قم بإرجاع 0، القيمة المرجعة لا تهم)

  2. قم بتغيير #define في جملة #else إلى:

    0 && XTrace
    

الآن سيتم توسيع نسخة الإصدار إلى:

0 && XTrace("x=%d", x);

وأي مُحسِّن لائق سوف يتخلص من الأمر برمته نظرًا لأن تقييم الدائرة القصيرة كان سيمنع أي شيء بعد تنفيذ &&.

بالطبع، تمامًا كما كتبت تلك الجملة الأخيرة، أدركت أنه ربما يمكن تحسين النموذج الأصلي بعيدًا أيضًا وفي حالة الآثار الجانبية، مثل تمرير استدعاءات الوظائف كمعلمات إلى XTrace، فقد يكون هذا حلاً أفضل لأنه سيكون كذلك تأكد من أن إصدارات التصحيح والإصدار ستعمل بنفس الطريقة.

في لغة C++، يمكنك استخدام عامل البث لتبسيط الأمور:

#if defined _DEBUG

class Trace
{
public:
   static Trace &GetTrace () { static Trace trace; return trace; }
   Trace &operator << (int value) { /* output int */ return *this; }
   Trace &operator << (short value) { /* output short */ return *this; }
   Trace &operator << (Trace &(*function)(Trace &trace)) { return function (*this); }
   static Trace &Endl (Trace &trace) { /* write newline and flush output */ return trace; }
   // and so on
};

#define TRACE(message) Trace::GetTrace () << message << Trace::Endl

#else
#define TRACE(message)
#endif

واستخدامها مثل:

void Function (int param1, short param2)
{
   TRACE ("param1 = " << param1 << ", param2 = " << param2);
}

يمكنك بعد ذلك تنفيذ إخراج التتبع المخصص للفئات بنفس الطريقة التي تستخدمها للإخراج إليها std::cout.

آه، vsprintf() هو الشيء الذي كنت أفتقده.يمكنني استخدام هذا لتمرير قائمة الوسائط المتغيرة مباشرةً إلى printf():

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

void DBG_PrintImpl(char * format, ...)
{
    char buffer[256];
    va_list args;
    va_start(args, format);
    vsprintf(buffer, format, args);
    printf("%s", buffer);
    va_end(args);
}

ثم لف كل شيء في الماكرو.

ما هي المنصات التي لا تتوفر عليها؟stdarg جزء من المكتبة القياسية:

http://www.opengroup.org/onlinepubs/009695399/basedefs/stdarg.h.html

أي نظام أساسي لا يوفره ليس تطبيقًا قياسيًا للغة C (أو قديمًا جدًا).بالنسبة لهؤلاء، سيتعين عليك استخدام varargs:

http://opengroup.org/onlinepubs/007908775/xsh/varargs.h.html

جزء من المشكلة في هذا النوع من الوظائف هو أنه في كثير من الأحيان يتطلب وحدات الماكرو المتنوعة.تم توحيدها مؤخرًا إلى حد ما (C99) ، والكثير من المترجمين C القديم لا يدعمون المعيار ، أو لديهم عمل خاص بهم.

يوجد أدناه رأس تصحيح الأخطاء الذي كتبته والذي يحتوي على العديد من الميزات الرائعة:

  • يدعم بناء الجملة C99 وC89 لتصحيح وحدات الماكرو
  • تمكين/تعطيل الإخراج بناءً على وسيطة الوظيفة
  • الإخراج إلى واصف الملف (ملف io)

ملحوظة:لسبب ما واجهت بعض المشكلات الطفيفة في تنسيق التعليمات البرمجية.

#ifndef _DEBUG_H_
#define _DEBUG_H_
#if HAVE_CONFIG_H
#include "config.h"
#endif

#include "stdarg.h"
#include "stdio.h"

#define ENABLE 1
#define DISABLE 0

extern FILE* debug_fd;

int debug_file_init(char *file);
int debug_file_close(void);

#if HAVE_C99
#define PRINT(x, format, ...) \
if ( x ) { \
if ( debug_fd != NULL ) { \
fprintf(debug_fd, format, ##__VA_ARGS__); \
} \
else { \
fprintf(stdout, format, ##__VA_ARGS__); \
} \
}
#else
void PRINT(int enable, char *fmt, ...);
#endif

#if _DEBUG
#if HAVE_C99
#define DEBUG(x, format, ...) \
if ( x ) { \
if ( debug_fd != NULL ) { \
fprintf(debug_fd, "%s : %d " format, __FILE__, __LINE__, ##__VA_ARGS__); \
} \
else { \
fprintf(stderr, "%s : %d " format, __FILE__, __LINE__, ##__VA_ARGS__); \
} \
}

#define DEBUGPRINT(x, format, ...) \
if ( x ) { \
if ( debug_fd != NULL ) { \
fprintf(debug_fd, format, ##__VA_ARGS__); \
} \
else { \
fprintf(stderr, format, ##__VA_ARGS__); \
} \
}
#else /* HAVE_C99 */

void DEBUG(int enable, char *fmt, ...);
void DEBUGPRINT(int enable, char *fmt, ...);

#endif /* HAVE_C99 */
#else /* _DEBUG */
#define DEBUG(x, format, ...)
#define DEBUGPRINT(x, format, ...)
#endif /* _DEBUG */

#endif /* _DEBUG_H_ */

إلقاء نظرة على هذا الموضوع:

ينبغي أن يجيب على سؤالك.

بعد أن واجهت المشكلة اليوم، الحل الخاص بي هو الماكرو التالي:

    static TCHAR __DEBUG_BUF[1024]
    #define DLog(fmt, ...)  swprintf(__DEBUG_BUF, fmt, ##__VA_ARGS__); OutputDebugString(__DEBUG_BUF) 

يمكنك بعد ذلك استدعاء الوظيفة مثل هذا:

    int value = 42;
    DLog(L"The answer is: %d\n", value);

هذا ما أستخدمه:

inline void DPRINTF(int level, char *format, ...)
{
#    ifdef _DEBUG_LOG
        va_list args;
        va_start(args, format);
        if(debugPrint & level) {
                vfprintf(stdout, format, args);
        }
        va_end(args);
#    endif /* _DEBUG_LOG */
}

والذي لا يكلف شيئًا على الإطلاق في وقت التشغيل عند إيقاف تشغيل علامة _DEBUG_LOG.

هذه نسخة TCHAR من إجابة المستخدم، لذا ستعمل كـ ASCII (طبيعي)، أو وضع Unicode (أكثر أو أقل).

#define DEBUG_OUT( fmt, ...) DEBUG_OUT_TCHAR(       \
            TEXT(##fmt), ##__VA_ARGS__ )
#define DEBUG_OUT_TCHAR( fmt, ...)                  \
            Trace( TEXT("[DEBUG]") #fmt,            \
            ##__VA_ARGS__ )
void Trace(LPCTSTR format, ...)
{
    LPTSTR OutputBuf;
    OutputBuf = (LPTSTR)LocalAlloc(LMEM_ZEROINIT,   \
            (size_t)(4096 * sizeof(TCHAR)));
    va_list args;
    va_start(args, format);
    int nBuf;
    _vstprintf_s(OutputBuf, 4095, format, args);
    ::OutputDebugString(OutputBuf);
    va_end(args);
    LocalFree(OutputBuf); // tyvm @sam shaw
}

أقول، "أكثر أو أقل"، لأنه لن يقوم تلقائيًا بتحويل وسيطات سلسلة ASCII إلى WCHAR، ولكن يجب أن يخرجك من معظم قصاصات Unicode دون الحاجة إلى القلق بشأن التفاف سلسلة التنسيق في TEXT() أو يسبقها بـ L .

مستمدة إلى حد كبير من شبكة MSDN:استرداد رمز الخطأ الأخير

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top