2008-08-18 1 views
35

Я хотел бы сделать функцию регистрации отладки с теми же параметрами, что и printf. Но тот, который может быть удален предварительным процессором во время оптимизированных сборок.Как создать функцию отладки, которая принимает список переменных аргументов? Как printf()

Например:

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

Я посмотрел на вариативный макрос, но те не доступны на всех платформах. gcc поддерживает их, msvc нет.

+0

Stu, MSVC поддерживает функции VARIADIC, он не поддерживает VARIADIC макросы. Edit: My bad: поддержка Variadic макросов была представлена ​​в Visual C++ 2005. – hyperlogic 2008-08-18 22:15:41

+0

См. Также макрос C# # define для отладочной печати] (https://stackoverflow.com/questions/1644868/c-define-macro-for -debug-печать). Обратите внимание, в частности, что лучше всего обеспечить компиляцию (но оптимизирует) код из макроса отладки, так что код всегда проверяется и, следовательно, всегда корректен. В противном случае бит-гниль может установить и когда вы повторно активируете макрос отладки через десятилетие, вы обнаружите, что он больше не компилируется. – 2016-04-09 23:18:05

ответ

22

Я все еще делаю это по-старому, определяя макрос (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 

Ну что может быть очищено совсем немного, но это основная идея.

+0

Отличный ответ, но вы должны использовать ** _ vsntprintf **, чтобы сделать его совместимым с Unicode. Я добавил свою собственную версию, потому что у меня было требование добавить строку (например, [DEBUG]). http://stackoverflow.com/a/39186784/912236 – Orwellophile 2016-08-27 23:50:47

+0

`_vsnprintf()` специфичен для конкретных (совместимых с Microsoft) реализаций - и устарел. `vsnprintf()` является стандартным. – Peter 2016-08-28 00:11:42

1

На каких платформах они недоступны? stdarg является частью стандартной библиотеки:

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

Любой платформа не обеспечивает его не стандартная реализация C (или очень, очень старая). Для тех, вам придется использовать с переменным числом аргументов:

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

+0

http://msdn.microsoft.com/en-us/library/kb57fad8(VS.71).aspx - В этой статье эта статья даже показывает вам, как использовать варганы старой школы в качестве бонуса. – Stu 2008-08-18 22:13:32

+0

Похоже, что поддержка Variadic макросов была представлена ​​в Visual C++ 2005. – hyperlogic 2008-08-18 22:15:41

11

Вот то, что я делаю в 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")); 

Все, что вам нужно помнить, чтобы использовать двойные скобки при вызове функции отладки, и вся линия получит снятую в неотладочной коде.

2

Ах, 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); 
} 

Затем заверните все это в макрос.

4

Другой интересный способ гашу VARIADIC функции является:

#define function sizeof 
2

В 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.

1

Часть проблемы с такой функциональностью заключается в том, что для этого требуется variadic macros.Они были стандартизованы сравнительно недавно (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_ */ 
+0

Кажется странным использовать `` debug_fd` 'для хранения указателя файла, а не файлового дескриптора; было бы более условно использовать «debug_fp». – 2009-07-17 05:50:45

3

@CodingTheWheel:

Существует одна небольшая проблема с вашим подходом. Рассмотрим такой вызов

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

Это прекрасно работает в отладочных, но в выпуске строить будет расширяться:

("x=%d", x); 

Что вполне законно C и компилирует и обычно работают без стороны -эффекты, но генерирует ненужный код. Подход, который я обычно использую, чтобы устранить эту проблему, является:

  1. Сделать функция XTrace возвращает Int (просто возвращает 0, возвращаемое значение не имеет значения)

  2. Измените #define в # еще статья на:

    0 && XTrace 
    

Теперь версия выпуск будет расширяться:

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

и любой достойный оптимизатор выбросят все это, так как оценка короткого замыкания предотвратила бы что-либо после того, как & & никогда не выполнялся.

Конечно, так же, как я написал это последнее предложение, я понял, что, возможно, исходная форма также может быть оптимизирована, а в случае побочных эффектов, таких как вызовы функций, переданные в качестве параметров XTrace, может быть лучше потому что он будет следить за тем, чтобы отладочные и выпускные версии будут вести себя одинаково.

20

Вот как я отлаживаю отпечатки на C++. Определение «DOUT» (отладочный выход), как это:

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

В коде я использую «DOUT» так же, как «соиЬ».

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

Если препроцессор заменяет «DOUT» с пометкой «0 & & COUT», что < < имеет более высокий приоритет, чем & & и оценки короткого замыкания & & делает всю линию оценки 0.Поскольку 0 не используется, компилятор не генерирует никакого кода для этой строки.

0

Столкнувшись проблемы сегодня, мое решение следующий макрос:

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

Это то, что я использую:

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 выключен.

0

Это версия ответа пользователя TCHAR, поэтому она будет работать как ASCII (normal), или режим 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 передряги без необходимости беспокоиться о оберточной строке формата в ТЕКСТ() или предшествующий ему с L.

Во многом производный от MSDN: Retrieving the Last-Error Code