programing

#C에서 디버깅 인쇄를 위한 매크로 정의

randomtip 2022. 8. 31. 22:22
반응형

#C에서 디버깅 인쇄를 위한 매크로 정의

다음 의사 코드와 같이 DEBUG 정의 시 디버깅메시지 인쇄에 사용할 수 있는 매크로를 작성하려고 합니다.

#define DEBUG 1
#define debug_print(args ...) if (DEBUG) fprintf(stderr, args)

어떻게 매크로를 사용하여 이 작업을 수행할 수 있습니까?

C99 이후의 컴파일러를 사용하는 경우

#define debug_print(fmt, ...) \
            do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0)

C99 를 사용하고 있는 것을 전제로 하고 있습니다(이전 버전에서는 변수 인수 리스트 표기는 지원되지 않습니다).do { ... } while (0) 호출와 같이 하도록 합니다.idiom 은 、 om 、 om 、 ( om idi ) 。를 무조건 단, 때 를 삭제합니다.가가가 、 DEBUG 、 0 일일일 。

#ifdef DEBUG를 사용하는 경우 테스트 조건을 변경합니다.

#ifdef DEBUG
#define DEBUG_TEST 1
#else
#define DEBUG_TEST 0
#endif

그런 다음 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 컴파일러를 사용하는 경우

C89를 사용할 수 없고 컴파일러 확장기능이 없는 경우 특별히 깔끔하게 처리할 수 있는 방법은 없습니다.내가 사용하던 기술은 다음과 같다.

#define TRACE(x) do { if (DEBUG) dbg_printf x; } while (0)

그리고 코드에 다음과 같이 적습니다.

TRACE(("message %d\n", var));

이중 부모 표기는 매우 중요합니다.이 때문에 매크로 확장에서는 재미있는 표기가 사용됩니다.이전과 같이 컴파일러는 항상 코드의 구문 유효성을 체크하지만(이는 양호합니다), 옵티마이저는 DEBUG 매크로가 0이 아닌 것으로 평가되었을 경우에만 인쇄 함수를 호출합니다.

여기에는 'stderr'과 같은 기능을 처리하기 위한 지원 함수(예시에서는 dbg_printf())가 필요합니다.vargs 함수를 작성하는 방법을 알고 있어야 하지만, 이는 어렵지 않습니다.

#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」는 「C99」입니다.__VA_ARGS__기법은 이중 문자 해킹이 아닌 일반 함수 표기법을 사용하기 때문에 더 깔끔합니다.

컴파일러가 항상 디버깅코드를 확인하는 것이 중요한 이유는 무엇입니까?

[또 다른 답변에 댓글 달기]

위의 C99와 C89의 양쪽 구현의 배후에 있는 하나의 중심 아이디어는 컴파일러가 항상 debugging printf와 같은 스테이트먼트를 올바르게 인식한다는 것입니다.이것은 장기 코드, 즉 10~20년간 지속되는 코드에 중요합니다.

코드 조각이 몇 년 동안 대부분 휴지 상태(안정 상태)였지만 이제는 변경해야 한다고 가정합니다.디버깅 트레이스를 다시 유효하게 할 수 있지만, 디버깅(트레이싱) 코드를 디버깅해야 하는 번거로움이 있습니다.이는 안정된 유지보수가 이루어지는 동안 이름 변경 또는 재입력된 변수를 참조하기 때문입니다.컴파일러(post-pre-processor)가 항상 인쇄문을 참조하는 경우 주변 변경으로 인해 Diagnostics가 무효화되지 않았는지 확인합니다.컴파일러에 인쇄문이 표시되지 않으면 자신의 부주의(또는 동료나 공동작업자의 부주의)로부터 보호할 수 없습니다.Kernighan과 Pike의 '프로그래밍의 실천', 특히 8장을 참조하십시오(TPOP에 관한 위키피디아 참조).

이것은 '그곳에 있는, 이미 끝난' 경험입니다.저는 기본적으로 디버깅 이외의 빌드에서는 수년간(10년 이상) printf와 같은 스테이트먼트가 표시되지 않는 다른 답변에서 설명한 기술을 사용했습니다.그러나 TPOP에서 조언을 얻었고(이전 코멘트를 참조), 몇 년 후 몇 가지 디버깅 코드를 활성화하여 컨텍스트가 변경되어 디버깅이 중단되는 문제에 부딪혔습니다.인쇄가 항상 유효하게 되어, 그 후의 문제로부터 해방되는 일이 몇번이나 있었습니다.

NDEBUG는 어설션만 제어하고 디버깅트레이스가 프로그램에 내장되어 있는지 여부를 제어하기 위해 별도의 매크로(일반적으로 DEBUG)를 사용합니다.무조건 않기 할 수 있는 또, 「디버깅 트레이스」를 호출하는 에, 「디버깅 레벨」을 사용할 수 있습니다.fprintf()디버깅 인쇄 기능을 호출합니다.이 기능을 조건부로만 인쇄하면 동일한 코드 빌드가 프로그램 옵션에 따라 인쇄 또는 인쇄되지 않습니다).또한 더 큰 프로그램을 위한 '복수-하위 시스템' 버전의 코드를 가지고 있기 때문에 프로그램의 다른 섹션이 런타임 제어 하에 다양한 양의 트레이스를 생성할 수 있습니다.

모든 빌드에 대해 컴파일러가 진단문을 표시해야 하지만 디버깅이 활성화되지 않는 한 디버깅 트레이스문에 대한 코드를 생성하지 않습니다.기본적으로 모든 코드는 컴파일러에 의해 릴리즈 또는 디버깅용으로 체크됩니다.이건 좋은 일이야!

debug.h - 버전 1.2(1990-05-01)

/*
@(#)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 */

debug.h - 버전 3.6 (2008-02-11)

/*
@(#)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 */

C99 이후의 단일 인수 바리안트

Kyle Brandt가 물었다:

그렇게 위해서debug_print쟁이 없없 ?? ??? ????예를 들어 다음과 같습니다.

    debug_print("Foo");

한 가지 간단하고 구식 해킹이 있습니다.

debug_print("%s\n", "Foo");

다음에 나타내는 GCC 전용 솔루션에서도 이 기능이 지원됩니다.

단, 스트레이트 C99 시스템에서는 다음을 사용하여 실행할 수 있습니다.

#define debug_print(...) \
            do { if (DEBUG) fprintf(stderr, __VA_ARGS__); } while (0)

버전과 하여 ' '할 수 단, 인수 에 있는 는 'fmt'로 호출할 수 있습니다).fprintf()컴파일에 실패합니다).확인의 손실이 문제가 되는지는 논란의 여지가 있다.

단일 인수에 대한 GCC 고유의 기술

일부 컴파일러는 매크로에서 가변 길이 인수 목록을 처리하는 다른 방법을 위한 확장을 제공할 수 있습니다.특히 Hugo Ideler의 코멘트에서 처음 언급했듯이 GCC에서는 매크로의 마지막 '고정' 인수 뒤에 나타나는 콤마를 생략할 수 있습니다.또한 매크로 치환 텍스트에서 를 사용할 수도 있습니다.이 경우 이전 토큰이 콤마일 경우에만 표기법 앞의 콤마가 삭제됩니다.

#define debug_print(fmt, ...) \
            do { if (DEBUG) fprintf(stderr, fmt, ##__VA_ARGS__); } while (0)

이 솔루션에서는 format 인수를 요구하면서도 format 뒤에 옵션 인수를 받아들인다는 장점이 있습니다.

이 기술은 GCC 호환성을 위해 Clang에서도 지원됩니다.


왜 do-while 루프일까요?

책의 은 무엇입니까?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에

만약 여러분이 궁금하다면, GitHub에 이 코드를 파일로 내 SOQ(StackOverflow의문들)저장소에 볼 수 있습니다.debug.c,debug.h ★★★★★★★★★★★★★★★★★」mddebug.c그 src/libsoq sub-directory에서.

나는:이 것을 사용한다.

#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

http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html,에 따르면,## 전에__VA_ARGS__.

이외의 경우는, 「」를 합니다.#define dbg_print(format, ...) printf(format, __VA_ARGS__)는, 다음의 하지 .dbg_print("hello world");.

그래서 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

그리고 어떤 표현이 어떤 표현으로 평가되었는지 분석할 수 있습니다.

이중평가 문제로부터 보호되지만, 젠심이 없으면 이름 충돌에 노출될 수 있습니다.

단, 네스트:

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를 사용하지 않는, 당신은 괜찮을 거라고 생각하다.

확실히, 이 파일(및 문자열의 얼라이드 버전, 디버깅레벨의 버전 등)은 매우 귀중한 것입니다.

#define PRINT_LOG(str_format, ...) { \
    time_t curtime=time (NULL); \
    struct tm *ltm = localtime (&curtime); \
    printf("[%d-%02d-%02d %02d:%02d:%02d] " str_format, \
        ltm->tm_year + 1900, ltm->tm_mon + 1, ltm->tm_mday, \
        ltm->tm_hour, ltm->tm_min, ltm->tm_sec, ##__VA_ARGS__); \
}
    
PRINT_LOG("[%d] Serving client, str=%s, number=%d\n", getpid(), "my str", 10);

사용하고 있는 것은 다음과 같습니다.

#if DBG
#include <stdio.h>
#define DBGPRINT printf
#else
#define DBGPRINT(...) /**/  
#endif

추가 인수가 없어도 printf를 적절하게 처리할 수 있는 장점이 있습니다.DBG == 0인 경우, 아무리 멍청한 컴파일러라도 아무 것도 씹을 것이 없으므로 코드가 생성되지 않습니다.

사용하는 버전은 다음과 같습니다.

#ifdef NDEBUG
#define Dprintf(FORMAT, ...) ((void)0)
#define Dputs(MSG) ((void)0)
#else
#define Dprintf(FORMAT, ...) \
    fprintf(stderr, "%s() in %s, line %i: " FORMAT "\n", \
        __func__, __FILE__, __LINE__, __VA_ARGS__)
#define Dputs(MSG) Dprintf("%s", MSG)
#endif

휴대용(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;
}

또는 (어색한, 추천하지 않음)

#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;
}
#define debug_print(FMT, ARGS...) do { \
    if (DEBUG) \
        fprintf(stderr, "%s:%d " FMT "\n", __FUNCTION__, __LINE__, ## ARGS); \
    } while (0)

몇 년 동안 이걸 어떻게 해야 할지 고민하다가 마침내 해결책을 찾아냈어요.하지만 여기에 다른 해결책이 있는지 몰랐습니다.첫째, Leffler의 답변과 달리 디버깅 프린트는 항상 컴파일되어야 한다는 그의 주장을 볼 수 없다.테스트해야 할 경우 최적화되지 않을 수 있는 불필요한 코드를 프로젝트에서 실행하지 않는 것이 좋습니다.

매번 컴파일하지 않는 것은 실제보다 더 나쁘게 들릴 수 있습니다.디버깅 프린트가 컴파일되지 않는 경우도 있지만 프로젝트를 완료하기 전에 컴파일하고 테스트하는 것은 그리 어렵지 않습니다.이 시스템에서 3가지 수준의 디버깅을 사용하는 경우 디버깅메시지 레벨3에 올려놓고 컴파일 에러를 수정하고 다른 에러를 체크한 후 코드를 확정합니다.(물론 debug 스테이트먼트의 컴파일이 의도대로 동작하고 있는 것은 아닙니다.)

이 솔루션에서는 디버깅 상세 수준도 제공합니다.가장 높은 수준으로 설정하면 모두 컴파일됩니다.최근 높은 디버깅 상세 수준을 사용하고 있는 경우 모두 그 시점에서 컴파일할 수 있었습니다.최종 업데이트는 매우 쉬울 것입니다.3단계 이상은 필요없는데 조나단이 9단계는 써봤대이 방법(Leffler의 방법처럼)은 임의의 수의 레벨로 확장할 수 있습니다.코드에 사용할 경우 두 개의 문장이 필요하므로 내 방법을 사용하는 것이 더 간단할 수 있습니다.그러나 CLOSE 매크로도 코딩하고 있습니다.아무것도 하지 않습니다.파일로 보내면 그럴지도 몰라요

납품 전에 컴파일할 수 있는지 확인하기 위해 테스트하는 추가 단계는 다음과 같습니다.

  1. 최적화 레벨이 충분할 경우 최적화될 수 있도록 신뢰해야 합니다.
  2. 게다가 테스트 목적으로 최적화를 해제한 상태로 릴리스 컴파일을 작성하면(확실히 드물지만), 디버깅 중에는 전혀 작성되지 않습니다.이를 통해 실행 시 수십 또는 수백 개의 "if (DEBUG)" 스테이트먼트가 실행되기 때문에 실행 속도가 느려지고 중요도가 낮아집니다.실행 파일 또는 dll 크기를 늘리기 때문에 실행 및 컴파일 시간이 증가합니다.그러나 조나단은 자신의 방법이 진술서를 전혀 컴파일하지 않도록 만들 수 있다고 나에게 말한다.

브랜치는 사실 현대의 프리페치 프로세서에서는 비교적 비용이 많이 듭니다.사용 중인 앱이 시간적으로 중요한 것이 아니라면 큰 문제가 되지 않을 수도 있지만 성능이 문제가 된다면 저는 다소 빠른 실행 디버깅 코드(경우에 따라서는 빠른 릴리스도 있음)를 선택할 수 있습니다.

그래서 제가 원했던 것은 디버깅 인쇄 매크로입니다.인쇄하지 않으면 컴파일하지 않고 인쇄하면 컴파일합니다.또, 디버깅 레벨도 필요했습니다.예를 들어, 코드의 퍼포먼스·크리티컬한 부분을 인쇄하지 않고, 다른 부분에서 인쇄하는 등, 디버깅 레벨을 설정해, 추가의 디버깅 인쇄를 실시할 수 있습니다.인쇄가 컴파일 되었는지 아닌지를 판별하는 디버깅레벨을 실장하는 방법을 알게 되었습니다.저는 이 방법으로 달성했습니다.

디버깅 로그h:

// 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, ...);

DebugLog.cpp:

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

logging 문을 0으로 컴파일하려면

#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에서 변수를 포맷하는 데 더 안전합니다.이것은 매우 슬릭하지만, 포맷 문서는 가능한 한 정리되어 있지 않습니다만, 그래도 코드에 넣을 수 있는 경우는 찾을 수 없습니다.클래스일 수도 있지만 인스턴스화하여 최신 정보를 유지하거나 new()를 실행하여 저장해야 합니다.이렇게 하면 #include, init 및 옵션의 close 스테이트먼트를 소스에 드롭하기만 하면 바로 사용할 수 있습니다.하지만 그렇게 마음이 내키면 훌륭한 수업이 될 것이다.

이전에도 많은 솔루션을 봐왔지만, 이번 솔루션만큼 내 기준에 맞는 솔루션은 없었습니다.

  1. 원하는 레벨까지 확장할 수 있습니다.
  2. 인쇄하지 않으면 아무것도 되지 않는다.
  3. I/O를 편집하기 쉬운 한 곳에 집중시킵니다.
  4. printf 포맷을 사용하여 유연합니다.
  5. 디버깅 실행은 느려지지 않지만 항상 컴파일된 디버깅 출력은 항상 디버깅모드로 실행됩니다.만약 당신이 컴퓨터 과학을 하고 있고 정보처리를 쓰는 것이 쉽지 않다면, 예를 들어 디버거가 벡터의 범위를 벗어난 인덱스로 CPU를 정지시키는 시뮬레이터를 실행하고 있는 자신을 발견할 수 있을 것이다.디버깅 모드에서는 이미 실행 속도가 매우 느립니다.수백 개의 디버깅프린트를 의무적으로 실행하면 이러한 실행 속도가 더 느려질 수밖에 없습니다.저는 그런 런이 흔하지 않아요.

그다지 중요하지 않지만, 추가로 다음과 같은 것이 있습니다.

  1. 하지 않습니다( 인수는 없습니다).DEBUGLOG_LOG(3, "got here!");Qt (Qt) .arg () 입니다.MSVC는 gcc를 사용합니다. it it를 한다.## #define레플러】【레플러레플러】【레플러】【레프러】【레프러】【레프러】【레프러】【레프러】【레프러】【레프러】【레프러】【레프러】【레프러】【레프러】【】【레프러】【사용하지 않도록 재코딩할 수 .##필요하지만, 그가 제공하는 해킹을 사용해야 합니다.)

경고:logging level 인수 입력을 잊은 경우 MSVC는 ID가 정의되어 있지 않다고 주장합니다.

DEBUG 를 들어,「DEBUG using using the DEBUG」( 「DEBUG 이외의 프리프로세서 심볼명을 사용할 ../configure이치노내가 그것을 개발했을 때 그것은 나에게 자연스러운 것처럼 보였다.DLL을 다른 곳에서 사용하고 있는 어플리케이션으로 개발했습니다만, 로그 프린트를 파일에 송신하는 것이 더 편리합니다만, vprintf()로 변경하는 것도 문제 없습니다.

이를 통해 디버깅로깅을 수행하는 최선의 방법을 찾거나 원하는 방법을 찾을 수 있기를 바랍니다.나는 이 문제를 해결하기 위해 수십 년 동안 건성으로 노력해왔다.MSVC 2012 및 2015에서 작업하고 있기 때문에 gcc;에서도 작업하고 있을 뿐만 아니라 다른 많은 작업도 하고 있을 것입니다만, 아직 테스트해 본 적은 없습니다.

언젠가는 스트리밍 버전도 만들 거예요.

주의: Stack Overflow를 위해 메시지를 더 잘 포맷할 수 있도록 도와주신 Leffler 씨에게 감사드립니다.

이 테마의 변형은 카테고리별로 별도의 매크로 이름을 가질 필요 없이 디버깅 카테고리를 제공한다고 생각합니다.

프로그램 공간이 32K로 제한되고 다이내믹 메모리가 2K로 제한된 Arduino 프로젝트에서 이 변형을 사용했습니다.debug 문과 트레이스 디버깅스트링을 추가하면 공간이 빠르게 소모됩니다.따라서 컴파일 시에 포함되는 디버깅트레이스를 코드가 구축될 때마다 필요한 최소값으로 제한할 수 있어야 합니다.

debug.h

#ifndef DEBUG_H
#define DEBUG_H

#define PRINT(DEBUG_CATEGORY, VALUE)  do { if (DEBUG_CATEGORY & DEBUG_MASK) Serial.print(VALUE);} while (0);

#endif

호출, .cpp 파일

#define DEBUG_MASK 0x06
#include "Debug.h"

...
PRINT(4, "Time out error,\t");
...

아래 중에서 내가 가장 좋아하는 것은var_dump ''라고 해요

var_dump("%d", count);

는 다음과 같은 출력을 생성합니다.

patch.c:150:main(): count = 0

@"Jonathan Leffler"의 공로를 인정합니다.모두 C89-happy:

코드

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

저는 이런 걸 할 거예요.

#ifdef DEBUG
#define debug_print(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#else
#define debug_print(fmt, ...) do {} while (0)
#endif

이게 더 깨끗한 것 같아요.

출력이 stdout으로 전송되지 않는 경우 다음을 사용할 수 있습니다.

int doDebug = DEBUG;  // Where DEBUG may be supplied in compiler command
#define trace if (doDebug) printf

trace("whatever %d, %i\n", arg1, arg2);

언급URL : https://stackoverflow.com/questions/1644868/define-macro-for-debug-printing-in-c

반응형