codememo

변수 인수 목록을 사용하는 디버그 전용 함수는 어떻게 생성합니까?printf()와 같이()

tipmemo 2023. 7. 2. 20:46
반응형

변수 인수 목록을 사용하는 디버그 전용 함수는 어떻게 생성합니까?printf()와 같이()

다음과 같은 매개 변수로 디버그 로깅 기능을 만들고 싶습니다.printf그러나 최적화된 빌드 중에 사전 프로세서에 의해 제거될 수 있습니다.

예:

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

다양한 매크로를 살펴보았지만 일부 플랫폼에서는 사용할 수 없습니다. gcc합니다, 그들을지고하원,고,msvc하지 않다.

저는 여전히 변수 인수 목록이 있는 no-op 또는 함수 호출과 관련된 매크로(XTRACE, 아래)를 정의하여 이전 방식으로 수행합니다.내부적으로 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

코드에서 나는 cout처럼 dout을 사용합니다.

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

전처리기가 'dout'을 '0 & & cout'으로 대체하는 경우, <<이 & & 의 우선 순위가 높고 & &의 단락 평가는 전체 라인을 0으로 평가합니다.0을 사용하지 않기 때문에 컴파일러는 해당 라인에 대한 코드를 전혀 생성하지 않습니다.

여기 제가 C/C++에서 하는 일이 있습니다.먼저, 당신은 vargs 물건을 사용하는 함수를 작성합니다(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" ));

당신이 기억해야 할 것은 디버그 함수를 호출할 때 이중 괄호를 사용하는 것이고, 비 디버그 코드에서는 전체 줄이 제거될 것입니다.

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

그런 다음 전체를 매크로로 포장합니다.

변수 함수를 스텁아웃하는 또 다른 재미있는 방법은 다음과 같습니다.

#define function sizeof

@휠 코딩:

당신의 접근 방식에 한 가지 사소한 문제가 있습니다.다음과 같은 통화를 고려합니다.

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

이것은 디버그 빌드에서는 잘 작동하지만 릴리스 빌드에서는 다음으로 확장됩니다.

("x=%d", x);

이것은 완전히 합법적인 C이며 컴파일되고 일반적으로 부작용 없이 실행되지만 불필요한 코드를 생성합니다.이 문제를 해결하기 위해 주로 사용하는 방법은 다음과 같습니다.

  1. XTrace 함수가 int를 반환하도록 합니다(0만 반환하면 반환 값은 상관 없음).

  2. #else 절의 #define을 다음으로 변경합니다.

    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.

어떤 플랫폼에서 사용할 수 없습니까? stdarg는 표준 라이브러리의 일부입니다.

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

이를 제공하지 않는 모든 플랫폼은 표준 C 구현(또는 매우 오래됨)이 아닙니다.이러한 경우 변수를 사용해야 합니다.

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

이러한 기능의 문제 중 일부는 종종 가변 매크로가 필요하다는 것입니다.이것들은 꽤 최근에 표준화되었고(C99), 많은 오래된 C 컴파일러들은 표준을 지원하지 않거나 자체적으로 특별한 작업을 수행합니다.

다음은 몇 가지 멋진 기능이 있는 디버그 헤더입니다.

  • 디버그 매크로에 대한 C99 및 C89 구문 지원
  • 함수 인수를 기반으로 출력 사용/사용 안 함
  • 파일 설명자(fileio)로 출력

참고: 어떤 이유에서인지 약간의 코드 포맷 문제가 있었습니다.

#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_ */

이 스레드를 확인하십시오.

그것은 당신의 질문에 대답할 것입니다.

다음을 사용합니다.

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 플래그를 해제하면 런타임에 비용이 전혀 들지 않습니다.

이것은 사용자 응답의 TCAR 버전이므로 ASCII(일반) 또는 유니코드 모드(이상)로 작동합니다.

#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로 변환하지는 않지만 형식 문자열을 TEXT()로 묶거나 L로 앞에 놓을 필요 없이 대부분의 유니코드 스크립트에서 벗어날 수 있기 때문에 "더 많든 적든"이라고 말합니다.

MSDN에서 주로 파생된 것: 마지막 오류 코드 검색

질문에 나온 내용은 정확하지 않습니다. 하지만 이 코드는 디버깅에 도움이 될 것입니다. 각 변수의 값과 이름을 함께 인쇄합니다.이것은 완전히 유형에 독립적이며 변수 개수의 인수를 지원합니다.또한 STL의 값을 표시할 수 있습니다. STL의 출력 연산자를 오버로드할 수 있습니다.

#define show(args...) describe(#args,args);
template<typename T>
void describe(string var_name,T value)
{
    clog<<var_name<<" = "<<value<<" ";
}

template<typename T,typename... Args>
void describe(string var_names,T value,Args... args)
{
    string::size_type pos = var_names.find(',');
    string name = var_names.substr(0,pos);
    var_names = var_names.substr(pos+1);
    clog<<name<<" = "<<value<<" | ";
    describe(var_names,args...);
}

샘플 사용:

int main()
{
    string a;
    int b;
    double c;
    a="string here";
    b = 7;
    c= 3.14;
    show(a,b,c);
}

출력:

a = string here | b = 7 | c = 3.14 

오늘 문제를 발견한 저의 해결책은 다음과 같은 매크로입니다.

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

언급URL : https://stackoverflow.com/questions/15240/how-do-you-create-a-debug-only-function-that-takes-a-variable-argument-list-lik

반응형