Jak utworzyć funkcję debugowania, która pobiera listę zmiennych argumentów? Jak printf()

Chciałbym utworzyć funkcję logowania debugowania o takich samych parametrach jak printf. Ale taki, który może zostać usunięty przez pre-procesor podczas optymalizacji kompilacji.

Na przykład:

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

Przyjrzałem się różnym makrom, ale nie są one dostępne na wszystkich platformach. gcc wspiera je, msvc nie.

Author: Brian Tompsett - 汤莱恩, 2008-08-19

13 answers

Nadal robię to w stary sposób, definiując makro (xtrace, poniżej), które koreluje z no-op lub wywołaniem funkcji z listą zmiennych argumentów. Wewnętrznie, wywołaj vsnprintf, aby zachować składnię 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);
}

Następnie typowy przełącznik # ifdef:

#ifdef _DEBUG
#define XTRACE XTrace
#else
#define XTRACE
#endif
Można to trochę posprzątać, ale to podstawowa idea.
 23
Author: Gunther Struyf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-09 23:06:06

Tak robię debugowanie wydruków w C++. Define ' dout '(debug out) like this:

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

W kodzie używam 'dout' tak jak 'cout'.

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

Jeśli preprocesor zastąpi' dout 'na' 0 & & cout ' zauważ, że

 21
Author: snstrand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-03 19:48:02

Oto coś, co robię w C / C++. Po pierwsze, piszesz funkcję, która używa rzeczy varargs (patrz link w postach Stu). To zrób coś takiego:


 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" ));

Wszystko, co musisz pamiętać, to użycie podwójnego parena podczas wywoływania funkcji debug, a cała linia zostanie usunięta w kodzie nie-DEBUG.

 11
Author: Graeme Perrow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-18 21:35:49

Innym fajnym sposobem na stubowanie zmiennych funkcji jest:

#define function sizeof
 4
Author: Mat Noguchi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-03 15:07:33

@CodingTheWheel:

Jest jeden mały problem z twoim podejściem. Rozważ wywołanie takie jak
XTRACE("x=%d", x);

To działa dobrze w kompilacji debugowania, ale w kompilacji wydania rozszerzy się do:

("x=%d", x);

Który jest całkowicie legalnym C i kompiluje się i zwykle działa bez efektów ubocznych, ale generuje niepotrzebny kod. Podejście, którego zwykle używam do wyeliminowania tego problemu, to:

  1. Spraw, aby funkcja XTrace zwracała wartość int (zwróć tylko 0, zwracaną wartość nie ma znaczenia)

  2. Zmień # define w klauzuli # else na:

    0 && XTrace
    

Teraz wersja wydania rozszerzy się do:

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

I każdy porządny optymalizator wyrzuci wszystko, ponieważ ocena zwarcia uniemożliwiłaby cokolwiek po wykonaniu&&.

Oczywiście, tak jak napisałem to ostatnie zdanie, zdałem sobie sprawę, że być może oryginalna forma może być również zoptymalizowana, a w przypadku skutków ubocznych, takich jak wywołania funkcji przekazywane jako parametry do XTrace, może to być lepsze rozwiązanie, ponieważ upewni się, że wersje debug i release będą zachowywać się tak samo.

 3
Author: Ferruccio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-11 01:47:17

W C++ możesz użyć operatora strumieniowego do uproszczenia rzeczy:

#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

I używaj go jak:

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

Możesz następnie zaimplementować spersonalizowane wyjście śledzenia dla klas w taki sam sposób, jak w przypadku wyjścia do std::cout.

 2
Author: Skizz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-03 13:03:30

Ah, vsprintf () było czymś, czego mi brakowało. Mogę użyć tego, aby przekazać listę argumentów zmiennych bezpośrednio do 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);
}

Następnie zawiń całość w makro.

 2
Author: hyperlogic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-09 23:20:00

Na jakich platformach nie są dostępne? stdarg jest częścią biblioteki standardowej:

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

Każda platforma nie dostarczająca jej nie jest standardową implementacją C (lub bardzo, bardzo starą). W tym celu musisz użyć varargs:

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

 1
Author: Stu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-18 21:27:18

Część problemu z tego rodzaju funkcjonalnością polega na tym, że często wymaga zmienne makra. Zostały one znormalizowane dość niedawno (C99), a wiele stare kompilatory C nie obsługują standardu, lub mają swoją specjalną pracę w pobliżu.

Poniżej jest nagłówek debugowania, który napisałem, że ma kilka fajnych funkcji:

  • obsługuje składnię C99 i C89 dla makr debugowania
  • Włącz / Wyłącz wyjście na podstawie argumentu funkcji
  • Wyjście Do deskryptora pliku(Plik io)

Uwaga: z jakiegoś powodu miałem drobne problemy z formatowaniem kodu.

#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_ */
 1
Author: David Bryson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-17 05:49:34

Spójrz na ten wątek:

Powinno odpowiedzieć na twoje pytanie.

 1
Author: Community,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:30:59

Po napotkaniu problemu dzisiaj, moim rozwiązaniem jest następujące makro:

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

Możesz wywołać funkcję w następujący sposób:

    int value = 42;
    DLog(L"The answer is: %d\n", value);
 0
Author: Koffiman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-22 21:40:41

Tego używam:

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 */
}

Który nie kosztuje absolutnie nic w czasie wykonywania, gdy flaga _DEBUG_LOG jest wyłączona.

 0
Author: mousomer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-08 15:00:42

Jest to wersja TCHAR odpowiedzi użytkownika, więc będzie działać jako ASCII (normal ) lub tryb Unicode (mniej więcej).

#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
}

mówię, "mniej więcej", ponieważ nie będzie automatycznie konwertować argumentów ASCII na WCHAR, ale powinno to wydostać się z większości scrapów Unicode bez konieczności martwienia się o zawijanie ciągu formatu w TEXT () lub poprzedzającego go za pomocą L.

W dużej mierze pochodzi z MSDN: pobieranie kodu ostatniego błędu

 0
Author: Orwellophile,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-29 03:02:30