변수 인수 목록을 사용하는 디버그 전용 함수는 어떻게 생성합니까?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이며 컴파일되고 일반적으로 부작용 없이 실행되지만 불필요한 코드를 생성합니다.이 문제를 해결하기 위해 주로 사용하는 방법은 다음과 같습니다.
XTrace 함수가 int를 반환하도록 합니다(0만 반환하면 반환 값은 상관 없음).
#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로 앞에 놓을 필요 없이 대부분의 유니코드 스크립트에서 벗어날 수 있기 때문에 "더 많든 적든"이라고 말합니다.
질문에 나온 내용은 정확하지 않습니다. 하지만 이 코드는 디버깅에 도움이 될 것입니다. 각 변수의 값과 이름을 함께 인쇄합니다.이것은 완전히 유형에 독립적이며 변수 개수의 인수를 지원합니다.또한 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
'codememo' 카테고리의 다른 글
| Oracle 자동 증분 (0) | 2023.07.02 |
|---|---|
| 마리아의 재귀적 부모 자식 문제DB (0) | 2023.07.02 |
| SQL Server의 조건을 기준으로 카운트 (0) | 2023.07.02 |
| 목록의 제품 반환 (0) | 2023.07.02 |
| SQL 데이터베이스 테이블에 날짜 시간을 삽입하는 방법은 무엇입니까? (0) | 2023.07.02 |