다음 의사 코드와 같이 DEBUG가 정의 될 때 인쇄 디버그 메시지에 사용할 수있는 매크로를 작성하려고합니다.
#define DEBUG 1
#define debug_print(args ...) if (DEBUG) fprintf(stderr, args)
매크로로 어떻게이 작업을 수행 할 수 있습니까?
다음 의사 코드와 같이 DEBUG가 정의 될 때 인쇄 디버그 메시지에 사용할 수있는 매크로를 작성하려고합니다.
#define DEBUG 1
#define debug_print(args ...) if (DEBUG) fprintf(stderr, args)
매크로로 어떻게이 작업을 수행 할 수 있습니까?
답변:
#define debug_print(fmt, ...) \
do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0)
C99를 사용한다고 가정합니다 (이전 버전에서는 변수 인수 목록 표기법이 지원되지 않음). 그만큼do { ... } while (0)
숙어 보장하는 코드는 문 (함수 호출)와 같은 역할을 것을. 무조건 코드를 사용하면 컴파일러가 항상 디버그 코드가 유효한지 확인하지만 DEBUG가 0 일 때 옵티마이 저가 코드를 제거합니다.
#ifdef DEBUG로 작업하려면 테스트 조건을 변경하십시오.
#ifdef DEBUG
#define DEBUG_TEST 1
#else
#define DEBUG_TEST 0
#endif
그런 다음 DEBUG를 사용한 DEBUG_TEST를 사용하십시오.
형식 문자열에 대해 문자열 리터럴을 고집하는 경우 (아마도 좋은 생각 일 것입니다) __FILE__
, __LINE__
및 __func__
출력을 소개 하여 진단을 향상시킬 수 있습니다.
#define debug_print(fmt, ...) \
do { if (DEBUG) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
__LINE__, __func__, __VA_ARGS__); } while (0)
이것은 문자열 연결에 의존하여 프로그래머가 쓰는 것보다 더 큰 형식의 문자열을 만듭니다.
C89가 붙어 있고 유용한 컴파일러 확장이 없다면 특별히 처리 할 수있는 방법이 없습니다. 내가 사용한 기술은 다음과 같습니다.
#define TRACE(x) do { if (DEBUG) dbg_printf x; } while (0)
그런 다음 코드에서 다음을 작성하십시오.
TRACE(("message %d\n", var));
이중 괄호는 중요하며 매크로 확장에서 재미있는 표기법을 사용하는 이유입니다. 이전과 마찬가지로 컴파일러는 항상 코드의 구문 유효성 (좋은)을 검사하지만 DEBUG 매크로가 0이 아닌 것으로 평가되는 경우 옵티마이 저는 인쇄 기능 만 호출합니다.
여기에는 'stderr'과 같은 것을 처리하기위한 지원 함수 (예 : dbg_printf ())가 필요합니다. varargs 함수를 작성하는 방법을 알아야하지만 어렵지 않습니다.
#include <stdarg.h>
#include <stdio.h>
void dbg_printf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
물론 C99에서도이 기술을 사용할 수 있지만 __VA_ARGS__
이중 괄호 해킹이 아닌 일반 함수 표기법을 사용하므로이 기술이 더 깔끔합니다.
[ 다른 답변에 대한 해시 코멘트. ]
위의 C99 및 C89 구현의 기본 개념 중 하나는 컴파일러에서 항상 printf와 같은 디버깅 문을 보는 것입니다. 이것은 장기 코드 (10 년 또는 2 년 지속되는 코드)에 중요합니다.
코드 조각이 수년 동안 대부분 휴면 상태 (안정적) 였지만 이제는 변경해야한다고 가정합니다. 디버깅 추적을 다시 활성화하십시오. 그러나 안정적인 유지 관리 기간 동안 이름이 바뀌거나 다시 입력 된 변수를 참조하기 때문에 디버깅 (추적) 코드를 디버깅해야하는 것은 실망 스럽습니다. 컴파일러 (사전 프로세서)가 항상 print 문을 보게되면 주변의 모든 변경 사항이 진단을 무효화하지 않았는지 확인합니다. 컴파일러에서 인쇄 문이 보이지 않으면 자신의 부주의 (또는 동료 또는 공동 작업자의 부주의)로부터 보호 할 수 없습니다. '참조 프로그래밍의 연습을 커니 핸과 파이크, 특히 제 8 장에 의해'(에도 위키 백과를 참조 TPOP ).
이것은 '그것이 거기에 있었고, 그 경험이 있습니다.'경험-필자는 비 디버그 빌드가 몇 년 동안 (10 년 이상) printf와 같은 문장을 보지 못하는 다른 답변에 설명 된 기술을 사용했습니다. 그러나 나는 TPOP (내 이전 의견 참조)에 대한 조언을 듣고 몇 년 후에 디버깅 코드를 활성화했으며 변경된 컨텍스트가 디버깅을 깨뜨리는 문제가 발생했습니다. 여러 번 인쇄를 항상 검증하면 나중에 문제를 피할 수있었습니다.
NDEBUG를 사용하여 어설 션 만 제어하고 별도의 매크로 (일반적으로 DEBUG)를 사용하여 디버그 추적이 프로그램에 내장되어 있는지 여부를 제어합니다. 디버그 추적이 내장되어 있어도 디버그 출력이 무조건 표시되는 것을 원하지 않기 때문에 출력의 표시 여부 (디버그 레벨 및 호출 대신)를 제어하는 메커니즘이 있습니다.fprintf()
직접 조건부로만 인쇄하는 디버그 인쇄 기능을 호출 함). 동일한 코드 빌드가 프로그램 옵션에 따라 인쇄되거나 인쇄되지 않을 수 있습니다). 또한 더 큰 프로그램을위한 코드의 '다중 서브 시스템'버전을 가지고 있으므로 런타임 제어 하에서 다른 양의 추적을 생성하는 프로그램의 다른 섹션을 가질 수 있습니다.
모든 빌드에서 컴파일러가 진단 문을보아야한다고 주장합니다. 그러나 디버그가 사용 가능하지 않으면 컴파일러는 디버깅 추적 명령문에 대한 코드를 생성하지 않습니다. 기본적으로 이는 컴파일 할 때마다 릴리스 또는 디버깅 여부에 관계없이 컴파일러가 모든 코드를 검사 함을 의미합니다. 이것은 좋은 일입니다!
/*
@(#)File: $RCSfile: debug.h,v $
@(#)Version: $Revision: 1.2 $
@(#)Last changed: $Date: 1990/05/01 12:55:39 $
@(#)Purpose: Definitions for the debugging system
@(#)Author: J Leffler
*/
#ifndef DEBUG_H
#define DEBUG_H
/* -- Macro Definitions */
#ifdef DEBUG
#define TRACE(x) db_print x
#else
#define TRACE(x)
#endif /* DEBUG */
/* -- Declarations */
#ifdef DEBUG
extern int debug;
#endif
#endif /* DEBUG_H */
/*
@(#)File: $RCSfile: debug.h,v $
@(#)Version: $Revision: 3.6 $
@(#)Last changed: $Date: 2008/02/11 06:46:37 $
@(#)Purpose: Definitions for the debugging system
@(#)Author: J Leffler
@(#)Copyright: (C) JLSS 1990-93,1997-99,2003,2005,2008
@(#)Product: :PRODUCT:
*/
#ifndef DEBUG_H
#define DEBUG_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
/*
** Usage: TRACE((level, fmt, ...))
** "level" is the debugging level which must be operational for the output
** to appear. "fmt" is a printf format string. "..." is whatever extra
** arguments fmt requires (possibly nothing).
** The non-debug macro means that the code is validated but never called.
** -- See chapter 8 of 'The Practice of Programming', by Kernighan and Pike.
*/
#ifdef DEBUG
#define TRACE(x) db_print x
#else
#define TRACE(x) do { if (0) db_print x; } while (0)
#endif /* DEBUG */
#ifndef lint
#ifdef DEBUG
/* This string can't be made extern - multiple definition in general */
static const char jlss_id_debug_enabled[] = "@(#)*** DEBUG ***";
#endif /* DEBUG */
#ifdef MAIN_PROGRAM
const char jlss_id_debug_h[] = "@(#)$Id: debug.h,v 3.6 2008/02/11 06:46:37 jleffler Exp $";
#endif /* MAIN_PROGRAM */
#endif /* lint */
#include <stdio.h>
extern int db_getdebug(void);
extern int db_newindent(void);
extern int db_oldindent(void);
extern int db_setdebug(int level);
extern int db_setindent(int i);
extern void db_print(int level, const char *fmt,...);
extern void db_setfilename(const char *fn);
extern void db_setfileptr(FILE *fp);
extern FILE *db_getfileptr(void);
/* Semi-private function */
extern const char *db_indent(void);
/**************************************\
** MULTIPLE DEBUGGING SUBSYSTEMS CODE **
\**************************************/
/*
** Usage: MDTRACE((subsys, level, fmt, ...))
** "subsys" is the debugging system to which this statement belongs.
** The significance of the subsystems is determined by the programmer,
** except that the functions such as db_print refer to subsystem 0.
** "level" is the debugging level which must be operational for the
** output to appear. "fmt" is a printf format string. "..." is
** whatever extra arguments fmt requires (possibly nothing).
** The non-debug macro means that the code is validated but never called.
*/
#ifdef DEBUG
#define MDTRACE(x) db_mdprint x
#else
#define MDTRACE(x) do { if (0) db_mdprint x; } while (0)
#endif /* DEBUG */
extern int db_mdgetdebug(int subsys);
extern int db_mdparsearg(char *arg);
extern int db_mdsetdebug(int subsys, int level);
extern void db_mdprint(int subsys, int level, const char *fmt,...);
extern void db_mdsubsysnames(char const * const *names);
#endif /* DEBUG_H */
카일 브란트가 물었다.
어쨌든이 작업을 수행하면
debug_print
인수가 없더라도 여전히 작동합니까? 예를 들면 다음과 같습니다.debug_print("Foo");
간단하고 구식 인 해킹이 있습니다.
debug_print("%s\n", "Foo");
아래에 표시된 GCC 전용 솔루션도이를 지원합니다.
그러나 다음을 사용하여 straight C99 시스템으로 수행 할 수 있습니다.
#define debug_print(...) \
do { if (DEBUG) fprintf(stderr, __VA_ARGS__); } while (0)
첫 번째 버전과 비교할 때 'fmt'인수가 필요한 제한된 검사를 잃어 버렸습니다. 즉, 인수없이 'debug_print ()'를 호출하려고 시도 할 수 있습니다 (그러나 인수 목록의 마지막 쉼표는 fprintf()
컴파일에 실패합니다) . 점검 손실이 문제인지 여부는 논란의 여지가 있습니다.
일부 컴파일러는 매크로에서 가변 길이 인수 목록을 처리하는 다른 방법에 대한 확장을 제공 할 수 있습니다. 특히 Hugo Ideler 의 의견에서 처음 언급 한 것처럼 GCC를 사용하면 매크로에 대한 마지막 '고정 된'인수 뒤에 일반적으로 표시되는 쉼표를 생략 할 수 있습니다. 또한 ##__VA_ARGS__
매크로 대체 텍스트에서 사용할 수 있습니다 . 이는 이전 토큰이 쉼표 인 경우에만 표기법 앞에 쉼표를 삭제합니다.
#define debug_print(fmt, ...) \
do { if (DEBUG) fprintf(stderr, fmt, ##__VA_ARGS__); } while (0)
이 솔루션은 형식 뒤에 옵션 인수를 허용하면서 형식 인수를 요구하는 이점을 유지합니다.
이 기술은 CCC 호환성을 위해 Clang 에서도 지원됩니다 .
do while
여기 의 목적은 무엇입니까 ?
매크로를 사용하여 함수 호출처럼 보이게하려면 세미콜론이옵니다. 따라서 매크로 본문을 맞게 패키지해야합니다. if
주변없이 진술 을 사용하면 다음 do { ... } while (0)
이 가능합니다.
/* BAD - BAD - BAD */
#define debug_print(...) \
if (DEBUG) fprintf(stderr, __VA_ARGS__)
이제 다음과 같이 작성한다고 가정하십시오.
if (x > y)
debug_print("x (%d) > y (%d)\n", x, y);
else
do_something_useful(x, y);
불행하게도, 전처리 기는 이것과 동등한 코드를 생성하기 때문에 (들여 져서 실제 의미를 강조하기 위해 들여 쓰기와 괄호가 추가되어 있기 때문에) 들여 쓰기는 흐름의 실제 제어를 반영하지 않습니다.
if (x > y)
{
if (DEBUG)
fprintf(stderr, "x (%d) > y (%d)\n", x, y);
else
do_something_useful(x, y);
}
매크로에서 다음 시도는 다음과 같습니다.
/* BAD - BAD - BAD */
#define debug_print(...) \
if (DEBUG) { fprintf(stderr, __VA_ARGS__); }
그리고 동일한 코드 조각은 이제 다음을 생성합니다.
if (x > y)
if (DEBUG)
{
fprintf(stderr, "x (%d) > y (%d)\n", x, y);
}
; // Null statement from semi-colon after macro
else
do_something_useful(x, y);
그리고 else
지금은 구문 오류입니다. do { ... } while(0)
루프 피합니다 모두 이러한 문제.
작동하는 매크로를 작성하는 다른 방법이 있습니다.
/* BAD - BAD - BAD */
#define debug_print(...) \
((void)((DEBUG) ? fprintf(stderr, __VA_ARGS__) : 0))
이렇게하면 프로그램 조각이 유효한 것으로 남습니다. (void)
캐스트는 값이 요구되는 상황에서 이용하지 못하도록 - 있지만 여기서 콤마 연산자의 왼쪽 피연산자로서 사용될 수 do { ... } while (0)
버전 수 없다. 이러한 표현식에 디버그 코드를 포함시킬 수 있어야한다고 생각하면이 코드를 선호 할 수 있습니다. 디버그 인쇄가 전체 명령문으로 작동하도록하려면 do { ... } while (0)
버전이 더 좋습니다. 매크로 본문에 세미콜론이 포함 된 경우 (대략 말하면) do { ... } while(0)
표기법 만 사용할 수 있습니다 . 항상 작동합니다. 식 문 메커니즘을 적용하기가 더 어려울 수 있습니다. 컴파일러가 피하기 원하는 표현 형식으로 경고를받을 수도 있습니다. 컴파일러와 사용하는 플래그에 따라 다릅니다.
TPOP는 이전에 http://plan9.bell-labs.com/cm/cs/tpop 및 http://cm.bell-labs.com/cm/cs/tpop에 있었지만 현재는 둘 다 (2015-08-10) 부서진.
있는 거 호기심 당신이, 당신이 내에서 GitHub의 코드를 볼 수 있습니다 SOQ 파일로 (스택 오버플로 질문) 저장소 debug.c
, debug.h
그리고 mddebug.c
의
SRC / libsoq
하위 디렉토리.
#define debug(...) \ do { if (DEBUG) \ printk("DRIVER_NAME:"); \ printk(__VA_ARGS__); \ printk("\n"); \ } while (0)
__FILE__, __LINE__, __func__, __VA_ARGS__
당신은 즉, 더 printf와 매개 변수를가없는 경우 그냥 호출하는 경우는 컴파일되지 않습니다 debug_print("Some msg\n");
당신은 사용하여이 문제를 해결할 수 fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__);
## __ VA_ARGS__ 함수에 매개 변수를 전달하지 허용합니다.
#define debug_print(fmt, ...)
와 #define debug_print(...)
. 이들 중 첫 번째는 하나 이상의 인수, 형식 문자열 ( fmt
) 및 0 개 이상의 다른 인수가 필요합니다. 두 번째는 총 0 개 이상의 인수가 필요합니다. debug_print()
첫 번째와 함께 사용 하면 전 처리기에서 매크로 오용에 대한 오류가 발생하지만 두 번째는 그렇지 않습니다. 그러나 대체 텍스트가 유효하지 않기 때문에 여전히 컴파일 오류가 발생합니다. 따라서 실제로 큰 차이는 없습니다. 따라서 '제한된 검사'라는 용어를 사용합니다.
-D input=4,macros=9,rules=2
입력 시스템의 디버그 수준을 4로 설정하고 매크로 시스템을 9로 설정하는 데 사용할 수 있습니다 (강렬한 조사 진행 중) ) 및 2에 대한 규칙 시스템. 주제에는 끝없는 변형이 있습니다. 자신에게 맞는 것을 사용하십시오.
나는 이와 같은 것을 사용한다 :
#ifdef DEBUG
#define D if(1)
#else
#define D if(0)
#endif
D를 접두사로 사용하는 것보다 :
D printf("x=%0.3f\n",x);
컴파일러는 디버그 코드를보고 쉼표 문제가 없으며 모든 곳에서 작동합니다. 또한 printf
배열이 덤프되거나 프로그램 자체에 중복되는 진단 값을 계산해야 할 때 충분하지 않은 경우에도 작동 합니다.
편집 : 좋아, else
근처에 어딘가에이 주입으로 가로 챌 수 있는 문제가 발생할 수 있습니다 if
. 이것은 그것을 넘어서는 버전입니다.
#ifdef DEBUG
#define D
#else
#define D for(;0;)
#endif
for(;0;)
당신이 뭔가를 쓸 때, 그것은 문제를 생성 할 수 있습니다 D continue;
또는 D break;
.
이식 가능한 (ISO C90) 구현의 경우 다음과 같이 이중 괄호를 사용할 수 있습니다.
#include <stdio.h>
#include <stdarg.h>
#ifndef NDEBUG
# define debug_print(msg) stderr_printf msg
#else
# define debug_print(msg) (void)0
#endif
void
stderr_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
int
main(int argc, char *argv[])
{
debug_print(("argv[0] is %s, argc is %d\n", argv[0], argc));
return 0;
}
또는 (hackish, 권장하지 않음)
#include <stdio.h>
#define _ ,
#ifndef NDEBUG
# define debug_print(msg) fprintf(stderr, msg)
#else
# define debug_print(msg) (void)0
#endif
int
main(int argc, char *argv[])
{
debug_print("argv[0] is %s, argc is %d"_ argv[0] _ argc);
return 0;
}
나는 같은 것을 할 것입니다
#ifdef DEBUG
#define debug_print(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#else
#define debug_print(fmt, ...) do {} while (0)
#endif
나는 이것이 더 깨끗하다고 생각합니다.
assert()
stdlib에서 동일한 방식으로 작동하며 일반적으로 NDEBUG
매크로를 내 디버깅 코드에 재사용 합니다.
http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html 에 따르면 ##
이전 버전 이 있어야합니다 __VA_ARGS__
.
그렇지 않으면, 매크로 #define dbg_print(format, ...) printf(format, __VA_ARGS__)
는 다음 예제를 컴파일하지 않습니다 : dbg_print("hello world");
.
#define debug_print(FMT, ARGS...) do { \
if (DEBUG) \
fprintf(stderr, "%s:%d " FMT "\n", __FUNCTION__, __LINE__, ## ARGS); \
} while (0)
이것이 내가 사용하는 것입니다 :
#if DBG
#include <stdio.h>
#define DBGPRINT printf
#else
#define DBGPRINT(...) /**/
#endif
추가 인수 없이도 printf를 올바르게 처리하면 좋은 이점이 있습니다. DBG == 0 인 경우 dumbest 컴파일러조차도 씹을 것이 없으므로 코드가 생성되지 않습니다.
아래에서 내가 가장 좋아하는 것은입니다 var_dump
.
var_dump("%d", count);
다음과 같은 출력을 생성합니다.
patch.c:150:main(): count = 0
@ "Jonathan Leffler"에게 감사드립니다. 모두 C89- 행복합니다 :
암호
#define DEBUG 1
#include <stdarg.h>
#include <stdio.h>
void debug_vprintf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
/* Call as: (DOUBLE PARENTHESES ARE MANDATORY) */
/* var_debug(("outfd = %d, somefailed = %d\n", outfd, somefailed)); */
#define var_debug(x) do { if (DEBUG) { debug_vprintf ("%s:%d:%s(): ", \
__FILE__, __LINE__, __func__); debug_vprintf x; }} while (0)
/* var_dump("%s" variable_name); */
#define var_dump(fmt, var) do { if (DEBUG) { debug_vprintf ("%s:%d:%s(): ", \
__FILE__, __LINE__, __func__); debug_vprintf ("%s = " fmt, #var, var); }} while (0)
#define DEBUG_HERE do { if (DEBUG) { debug_vprintf ("%s:%d:%s(): HERE\n", \
__FILE__, __LINE__, __func__); }} while (0)
따라서 gcc를 사용할 때 나는 다음을 좋아합니다.
#define DBGI(expr) ({int g2rE3=expr; fprintf(stderr, "%s:%d:%s(): ""%s->%i\n", __FILE__, __LINE__, __func__, #expr, g2rE3); g2rE3;})
코드에 삽입 할 수 있기 때문입니다.
디버그하려고한다고 가정하십시오.
printf("%i\n", (1*2*3*4*5*6));
720
그런 다음 다음과 같이 변경할 수 있습니다.
printf("%i\n", DBGI(1*2*3*4*5*6));
hello.c:86:main(): 1*2*3*4*5*6->720
720
그리고 어떤 표현이 무엇으로 평가되었는지 분석 할 수 있습니다.
이중 평가 문제로부터 보호되지만 gensyms가 없으면 이름 충돌에 노출됩니다.
그러나 그것은 중첩합니다 :
DBGI(printf("%i\n", DBGI(1*2*3*4*5*6)));
hello.c:86:main(): 1*2*3*4*5*6->720
720
hello.c:86:main(): printf("%i\n", DBGI(1*2*3*4*5*6))->4
따라서 g2rE3을 변수 이름으로 사용하지 않는 한 괜찮습니다.
확실히 나는 그것을 (그리고 문자열에 대한 연합 버전과 디버그 레벨에 대한 버전 등) 귀중한 것을 발견했습니다.
나는 몇 년 동안 이것을하는 방법을 연구 해 왔으며 마침내 해결책을 제시했습니다. 그러나 이미 다른 솔루션이 있다는 것을 몰랐습니다. 첫째, Leffler의 답변 과는 달리 디버그 인쇄가 항상 컴파일되어야한다는 그의 주장을 보지 못했습니다. 테스트 할 필요가 있고 최적화되지 않을 수있는 경우 필요하지 않은 경우 프로젝트에서 불필요한 코드를 실행하지 않아도됩니다.
매번 컴파일하지 않으면 실제보다 더 나쁘게 들릴 수 있습니다. 때로는 컴파일되지 않는 디버그 프린트로 마무리하지만 프로젝트를 마무리하기 전에 컴파일하고 테스트하는 것은 그리 어렵지 않습니다. 이 시스템을 사용하면 세 가지 레벨의 디버그를 사용하는 경우 디버그 메시지 레벨 3에 놓고 컴파일 오류를 수정하고 다른 코드가 있는지 확인한 후에 코드를 완성하십시오. (물론 디버그 명령문을 컴파일한다고해서 여전히 의도 한대로 작동한다는 보장은 없습니다.)
내 솔루션은 디버그 세부 수준도 제공합니다. 가장 높은 수준으로 설정하면 모두 컴파일됩니다. 최근에 높은 디버그 세부 수준을 사용했다면 모두 당시에 컴파일 할 수있었습니다. 최종 업데이트는 매우 쉬워야합니다. 나는 3 개 이상의 레벨을 필요로하지 않았지만 Jonathan은 9를 사용했다고 말합니다. Leffler와 같은이 방법은 여러 레벨로 확장 될 수 있습니다. 내 방법의 사용법이 더 간단 할 수 있습니다. 코드에서 사용될 때 두 개의 문장 만 필요합니다. 그러나 CLOSE 매크로도 코딩하고 있지만 아무것도하지 않습니다. 파일로 보내는 중일 수 있습니다.
비용에 대비하여 테스트 전에 추가 테스트 단계를 수행하여 배포 전에 컴파일되는지 확인합니다.
브랜치는 현대 프리 페칭 프로세서에서 실제로 상당히 비쌉니다. 앱이 시간이 중요한 앱이 아닌 경우 큰 문제는 아닙니다. 그러나 성능이 문제라면, 다소 빠른 실행 디버그 코드 (그리고 드문 경우이지만, 언급 된 바와 같이 더 빠른 릴리스)를 선택하는 것을 선호 할만큼 충분히 큰 거래입니다.
그래서 내가 원했던 것은 인쇄하지 않으면 컴파일되지 않지만 그렇지 않으면 컴파일되는 디버그 인쇄 매크로입니다. 또한 디버깅 수준을 원했기 때문에 코드의 성능에 중요한 부분을 인쇄하지 않고 다른 시간에 인쇄하려는 경우 디버그 수준을 설정하고 추가 디버그 인쇄를 시작할 수있었습니다. 인쇄가 컴파일되었는지 여부를 결정하는 디버그 레벨을 구현하는 방법을 발견했습니다. 나는 이런 식으로 그것을 달성했다 :
// FILE: DebugLog.h
// REMARKS: This is a generic pair of files useful for debugging. It provides three levels of
// debug logging, currently; in addition to disabling it. Level 3 is the most information.
// Levels 2 and 1 have progressively more. Thus, you can write:
// DEBUGLOG_LOG(1, "a number=%d", 7);
// and it will be seen if DEBUG is anything other than undefined or zero. If you write
// DEBUGLOG_LOG(3, "another number=%d", 15);
// it will only be seen if DEBUG is 3. When not being displayed, these routines compile
// to NOTHING. I reject the argument that debug code needs to always be compiled so as to
// keep it current. I would rather have a leaner and faster app, and just not be lazy, and
// maintain debugs as needed. I don't know if this works with the C preprocessor or not,
// but the rest of the code is fully C compliant also if it is.
#define DEBUG 1
#ifdef DEBUG
#define DEBUGLOG_INIT(filename) debuglog_init(filename)
#else
#define debuglog_init(...)
#endif
#ifdef DEBUG
#define DEBUGLOG_CLOSE debuglog_close
#else
#define debuglog_close(...)
#endif
#define DEBUGLOG_LOG(level, fmt, ...) DEBUGLOG_LOG ## level (fmt, ##__VA_ARGS__)
#if DEBUG == 0
#define DEBUGLOG_LOG0(...)
#endif
#if DEBUG >= 1
#define DEBUGLOG_LOG1(fmt, ...) debuglog_log (fmt, ##__VA_ARGS__)
#else
#define DEBUGLOG_LOG1(...)
#endif
#if DEBUG >= 2
#define DEBUGLOG_LOG2(fmt, ...) debuglog_log (fmt, ##__VA_ARGS__)
#else
#define DEBUGLOG_LOG2(...)
#endif
#if DEBUG == 3
#define DEBUGLOG_LOG3(fmt, ...) debuglog_log (fmt, ##__VA_ARGS__)
#else
#define DEBUGLOG_LOG3(...)
#endif
void debuglog_init(char *filename);
void debuglog_close(void);
void debuglog_log(char* format, ...);
// FILE: DebugLog.h
// REMARKS: This is a generic pair of files useful for debugging. It provides three levels of
// debug logging, currently; in addition to disabling it. See DebugLog.h's remarks for more
// info.
#include <stdio.h>
#include <stdarg.h>
#include "DebugLog.h"
FILE *hndl;
char *savedFilename;
void debuglog_init(char *filename)
{
savedFilename = filename;
hndl = fopen(savedFilename, "wt");
fclose(hndl);
}
void debuglog_close(void)
{
//fclose(hndl);
}
void debuglog_log(char* format, ...)
{
hndl = fopen(savedFilename,"at");
va_list argptr;
va_start(argptr, format);
vfprintf(hndl, format, argptr);
va_end(argptr);
fputc('\n',hndl);
fclose(hndl);
}
사용하려면 다음을 수행하십시오.
DEBUGLOG_INIT("afile.log");
로그 파일에 쓰려면 다음을 수행하십시오.
DEBUGLOG_LOG(1, "the value is: %d", anint);
닫으려면 다음을 수행하십시오.
DEBUGLOG_CLOSE();
현재로서는 기술적으로 말하면 아무것도하지 않기 때문에 필요하지 않습니다. 그러나 여전히 작동 방식에 대한 내 생각이 바뀌고 로깅 문 사이에 파일을 열어 두려는 경우를 대비하여 여전히 CLOSE를 사용하고 있습니다.
그런 다음 디버그 인쇄를 켜려면 헤더 파일에서 첫 번째 #define을 편집하십시오.
#define DEBUG 1
로깅 명령문을 컴파일하지 않으려면
#define DEBUG 0
자주 실행되는 코드 조각 (예 : 높은 수준의 세부 정보)에서 정보가 필요한 경우 다음과 같이 작성할 수 있습니다.
DEBUGLOG_LOG(3, "the value is: %d", anint);
DEBUG를 3으로 정의하면 로깅 레벨 1, 2 및 3이 컴파일됩니다. 2로 설정하면 로깅 레벨 1 & 2가 표시됩니다. 1로 설정하면 로깅 레벨 1 명령문 만 표시됩니다.
do-while 루프에 대해서는 if 문 대신 단일 함수로 평가되거나 아무것도 계산하지 않기 때문에 루프가 필요하지 않습니다. 좋아, C ++ IO 대신 C를 사용하여 나를 캐스트하십시오 (그리고 Qt의 QString :: arg ()는 Qt에서도 변수를 포맷하는 더 안전한 방법입니다. 꽤 매끄럽지 만 더 많은 코드가 필요하며 서식 문서는 구성되어 있지 않습니다) 그럴 수도 있지만 여전히 바람직한 경우를 찾았지만 원하는 .cpp 파일에 코드를 넣을 수 있습니다. 또한 클래스 일 수도 있지만 인스턴스화하여 최신 상태로 유지하거나 new ()를 수행하여 저장해야합니다. 이런 식으로 #include, init 및 close 문을 소스에 드롭하면 바로 사용할 수 있습니다. 그러나 당신이 그렇게 기울어지면 좋은 수업을 만들 것입니다.
나는 이전에 많은 해결책을 보았지만 이것뿐만 아니라 내 기준에 적합한 것은 없습니다.
대단히 중요하지는 않지만 추가로 :
DEBUGLOG_LOG(3, "got here!");
. 따라서 Qt의 안전한 .arg () 형식과 같은 사용을 허용합니다. MSVC에서 작동하므로 아마도 gcc입니다. Leffler가 지적했듯이 비표준 인 s ##
에서 사용 #define
되지만 널리 지원됩니다. ( ##
필요한 경우 사용하지 않도록 코드를 다시 작성할 수 있지만, 제공 한 것과 같은 핵을 사용해야합니다.)경고 : 로깅 수준 인수를 제공하지 않은 경우 MSVC는 식별자가 정의되어 있지 않다고 주장합니다.
일부 소스에서도 해당 기호를 정의하기 때문에 DEBUG 이외의 전 처리기 기호 이름을 사용할 수도 있습니다 (예 : ./configure
빌드 준비 명령을 사용하는 progs ). 내가 그것을 개발했을 때 그것은 자연스럽게 보였다. DLL을 다른 용도로 사용하는 응용 프로그램에서 개발했으며 로그 인쇄를 파일로 보내는 것이 더 수월합니다. 그러나 vprintf ()로 변경하면 잘 작동합니다.
이것이 디버그 로깅을 수행하는 가장 좋은 방법을 찾는 데 많은 슬픔을 덜어주기를 바랍니다. 또는 당신이 선호하는 것을 보여줍니다. 나는 이것을 진심으로 수십 년 동안 알아 내려고 노력했습니다. MSVC 2012 및 2015에서 작동하므로 아마도 gcc에서 작동합니다. 아마도 다른 많은 사람들과 함께 일할 것이지만, 나는 그것들을 테스트하지 않았습니다.
이것도 언젠가는 스트리밍 버전을 만드는 것을 의미합니다.
참고 : Leffler에게 감사의 말을 전합니다. Leffler는 StackOverflow를 위해 내 메시지를 더 잘 형식화하도록 도와주었습니다.
if (DEBUG)
런타임에서 수십 또는 수백 개의 문장을 실행하는데, 최적화되지 않습니다"- 풍차에서 기울어지고 있습니다. 필자가 설명한 시스템의 요점은 컴파일러가 코드를 검사하지만 (중요하고 자동-특별한 빌드가 필요하지 않음) 디버그 코드 는 최적화되어 있기 때문에 전혀 생성되지 않으므로 런타임에 아무런 영향을 미치지 않습니다. 코드가 런타임에 존재하지 않기 때문에 코드 크기 또는 성능).
((void)0)
쉽게 변경하십시오 .
테마의 이러한 변형은 카테고리마다 별도의 매크로 이름을 가질 필요없이 디버그 카테고리를 제공한다고 생각합니다.
프로그램 공간이 32K로 제한되고 동적 메모리가 2K로 제한되는 Arduino 프로젝트 에서이 변형을 사용했습니다. 디버그 문과 추적 디버그 문자열을 추가하면 공간이 빨리 소모됩니다. 따라서 컴파일 타임에 포함 된 디버그 추적을 코드가 작성 될 때마다 필요한 최소값으로 제한 할 수 있어야합니다.
#ifndef DEBUG_H
#define DEBUG_H
#define PRINT(DEBUG_CATEGORY, VALUE) do { if (DEBUG_CATEGORY & DEBUG_MASK) Serial.print(VALUE);} while (0);
#endif
#define DEBUG_MASK 0x06
#include "Debug.h"
...
PRINT(4, "Time out error,\t");
...