sourcecode

C++에서의 디버깅 매크로

copyscript 2022. 8. 10. 22:16
반응형

C++에서의 디버깅 매크로

C에서 DEBUG 매크로가 발견되어 매우 마음에 듭니다.

#ifdef DEBUG_BUILD
#  define DEBUG(x) fprintf(stderr, x)
#else
#  define DEBUG(x) do {} while (0)
#endif

C++의 아날로그는 다음과 같습니다.

#ifdef DEBUG_BUILD
#  define DEBUG(x) cerr << x
#else
#  define DEBUG(x) do {} while (0)
#endif
  1. 두 번째 코드 스니펫은 C의 코드 스니펫과 유사합니까?
  2. C++의 디버깅 매크로 중에서 마음에 드는 것이 있습니까?

EDIT : "디버깅 매크로"란 "디버깅 모드에서 프로그램을 실행할 때 편리할 수 있는 매크로"를 의미합니다.

두 번째 코드 스니펫은 C의 코드 스니펫과 유사합니까?

체로로그그그그그더 강력합니다.<<내 - 하면 C 의 수 있습니다.반면에, 사람들이 논쟁에 세미콜론을 포함시킴으로써 그것을 남용할 가능성은 희박하다. 콜도 있습니다. 이걸 blockdo block에

#define DEBUG(x) do { std::cerr << x; } while (0)

마음에 드는 C++ 디버깅 매크로가 있나요?

저는 위의 것이 마음에 들어서 자주 사용하고 있습니다.내 무수술은 보통 그냥 읽기만 해

#define DEBUG(x)

컴파일러 최적화에도 같은 효과가 있습니다.다음 @Tony D에 의한 코멘트는 맞지만 일부 구문 오류가 검출되지 않을 수 있습니다.

런타임 체크도 포함하여 디버깅플래그의 형식을 지정할 수 있습니다.@Tony D가 상기시켰듯이, 엔들(Endl)이 있는 것도 도움이 됩니다.

#define DEBUG(x) do { \
  if (debugging_enabled) { std::cerr << x << std::endl; } \
} while (0)

다음과 같은 표현도 인쇄하고 싶을 때가 있습니다.

#define DEBUG2(x) do { std::cerr << #x << ": " << x << std::endl; } while (0)

에 따라서는, 「마크로」라고 하는 것도 .__FILE__,__LINE__ ★★★★★★★★★★★★★★★★★」__func__단, 이것들은 단순한 디버깅매크로가 아닌 어설션인 경우가 많습니다.

여기 내가 제일 좋아하는 게 있어

#ifdef DEBUG 
#define D(x) x
#else 
#define D(x)
#endif

매우 편리하고, 깨끗한 코드(중요한 릴리스 모드에서는 고속)를 실현합니다.

★★★#ifdef DEBUG_BUILD over (code of 는 꽤 몇 의 행을 ('debug related blocks code')로.D().

사용방법:

D(cerr << "oopsie";)

그래도 너무 못생기거나 이상하거나 길면

#ifdef DEBUG
#define DEBUG_STDERR(x) (std::cerr << (x))
#define DEBUG_STDOUT(x) (std::cout << (x))
//... etc
#else 
#define DEBUG_STDERR(x)
#define DEBUG_STDOUT(x)
//... etc
#endif

(사용하지 않는 것이 좋습니다.using namespace std;using std::cout; using std::cerr;은은 、 도도))은각 )

「디버깅」을 생각하고 있을 때는, stderr에 인쇄하는 것 이상의 작업을 실시할 필요가 있는 경우가 있습니다.창의력을 발휘하여 프로그램 내의 가장 복잡한 상호작용에 대한 통찰력을 제공하는 구성을 구축할 수 있습니다.또한 디버깅 인스트루먼테이션에 의해 방해받지 않고 매우 효율적인 버전의 구축으로 신속하게 전환할 수 있습니다.

예를 들어, 최근 프로젝트 중 하나에서 디버깅 전용 블록이 하나 있었는데, 이 블록은 다음과 같습니다.FILE* file = fopen("debug_graph.dot");데이터 구조 내의 큰 트리를 시각화하기 위해 도트 형식의 그래프와 호환되는 그래프를 계속 내보냈습니다.더욱 쿨한 것은 OS X Graphviz 클라이언트가 변경 시 디스크에서 파일을 자동으로 읽기 때문에 프로그램이 실행될 때마다 그래프가 새로 고쳐진다는 것입니다.

디버깅 전용 멤버와 함수로 클래스/구조를 확장하는 것도 특히 좋아합니다.이를 통해 버그를 추적하는 데 도움이 되는 기능 및 상태를 구현할 수 있습니다.또한 디버깅매크로로 둘러싸인 다른 모든 것과 마찬가지로 빌드 파라미터를 전환하여 삭제할 수 있습니다.주정부 업데이트 때마다 구석구석 사건을 꼼꼼히 체크하는 거대한 루틴? 때리다D()하는 것을 삭제해 주세요.-DDEBUG빌드 스크립트(릴리스용 빌드)로부터, 유닛이나 그 외의 것을 위해서, 즉시 재활성화할 수 있습니다.

이 개념의 (어느 정도 지나치게 열성적인) 사용을 설명하기 위해 크고 다소 완전한 예를 다음에 제시하겠습니다.

#ifdef DEBUG
#  define D(x) x
#else
#  define D(x)
#endif // DEBUG

#ifdef UNITTEST
#  include <UnitTest++/UnitTest++.h>
#  define U(x) x // same concept as D(x) macro.
#  define N(x)
#else
#  define U(x)
#  define N(x) x // N(x) macro performs the opposite of U(x)
#endif

struct Component; // fwd decls
typedef std::list<Component> compList;

// represents a node in the graph. Components group GNs
// into manageable chunks (which turn into matrices which is why we want
// graph component partitioning: to minimize matrix size)
struct GraphNode {
    U(Component* comp;) // this guy only exists in unit test build
    std::vector<int> adj; // neighbor list: These are indices
    // into the node_list buffer (used to be GN*)
    uint64_t h_i; // heap index value
    U(int helper;) // dangling variable for search algo to use (comp node idx)
    // todo: use a more space-efficient neighbor container?
    U(GraphNode(uint64_t i, Component* c, int first_edge):)
    N(GraphNode(uint64_t i, int first_edge):)
        h_i(i) {
        U(comp = c;)
        U(helper = -1;)
        adj.push_back(first_edge);
    }
    U(GraphNode(uint64_t i, Component* c):)
    N(GraphNode(uint64_t i):)
        h_i(i)
    {
        U(comp=c;)
        U(helper=-1;)
    }
    inline void add(int n) {
        adj.push_back(n);
    }
};

// A component is a ugraph component which represents a set of rows that
// can potentially be assembled into one wall.
struct Component {
#ifdef UNITTEST // is an actual real struct only when testing
    int one_node; // any node! idx in node_list (used to be GN*)
    Component* actual_component;
    compList::iterator graph_components_iterator_for_myself; // must be init'd
    // actual component refers to how merging causes a tree of comps to be
    // made. This allows the determination of which component a particular
    // given node belongs to a log-time operation rather than a linear one.

    D(int count;) // how many nodes I (should) have

    Component(): one_node(-1), actual_component(NULL) {
        D(count = 0;)
    }
#endif
};

#ifdef DEBUG
// a global pointer to the node list that makes it a little
// easier to reference it
std::vector<GraphNode> *node_list_ptr;

#  ifdef UNITTEST
std::ostream& operator<<(std::ostream& os, const Component& c) {
    os << "<s=" << c.count << ": 1_n=" << node_list_ptr->at(c.one_node).h_i;
    if (c.actual_component) {
        os << " ref=[" << *c.actual_component << "]";
    }
    os << ">";
    return os;
}
#  endif
#endif

합니다.#ifdef가독성이 다소 향상되기 때문에 조건이 충족됩니다.대블록의 경우 극히 짧은 매크로를 사용하는 것이 오히려 방해가 됩니다.

「 」의 N(x)macro must exist는 unit-interval이 디세이블일 때 추가할 항목을 지정하는 것입니다.

이 파트에서는:

U(GraphNode(uint64_t i, Component* c, int first_edge):)
N(GraphNode(uint64_t i, int first_edge):)

이런 말을 할 수 있으면 좋을 텐데

GraphNode(uint64_t i, U(Component* c,) int first_edge):

그러나 콤마는 프리프로세서 구문의 일부이기 때문에 사용할 수 없습니다.쉼표를 생략하면 C++ 구문이 비활성화됩니다.

디버깅용으로 컴파일되지 않은 경우에 사용할 추가 코드가 있는 경우 이러한 유형의 대응 역디버깅 매크로를 사용할 수 있습니다.

이 코드가 '진짜 좋은 코드'의 예는 아닐지 모르지만매크로의 교묘한 어플리케이션으로 달성할 수 있는 것 중 몇 가지를 나타내고 있습니다.이것들에 대해 훈련을 계속한다고 해서 반드시 나쁜 것만은 아닙니다.

나는 방금 이 보석을 우연히 발견했다.do{} while(0)이 매크로에 있는 모든 화려함도 정말 원하는군요!

이 예에서 C++ 코드를 개선하기 위해 할 수 있는 몇 가지 현명한 방법에 대한 통찰력을 얻을 수 있기를 바랍니다.무슨 일이 일어나고 있는지 모를 때 다시 코드를 작성하기보다는 코드를 작성하는 동안 계측하는 것이 매우 중요합니다.하지만 항상 견고하게 하는 것과 제시간에 완료시키는 것 사이에서 균형을 이루어야 합니다.

추가 디버깅 빌드 건전성 검사는 유닛 테스트와 마찬가지로 툴박스의 다른 툴이라고 생각합니다.유닛 테스트에 온전성 체크 로직을 도입해 실장으로부터 격리하는 것이 아니라 실장에 포함되어 자유롭게 조작할 수 있는 경우, 체크 기능을 유효하게 해, 만일의 경우에 실행할 수 있기 때문에, 완전한 테스트가 필요 없기 때문에, 한층 더 강력해질 가능성이 있다고 생각합니다.

저는 다음 마이크로 사용해요.

#if DEBUG
#define LOGE2(x,y) std::cout << "ERRO : " << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << x <<":"<< y <<std::endl;
#define LOGI2(x,y) std::cout << "INFO : " << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << x <<":"<< y << std::endl;
#define LOGD2(x,y) std::cout << "DEBG : " << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << x <<":"<< y << std::endl;
#define LOGE(x) std::cout << "ERRO : " << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << x << std::endl;
#define LOGI(x) std::cout << "INFO : " << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << x << std::endl;
#define LOGD(x) std::cout << "DEBG : " << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << x << std::endl;
#else
#define LOGE2(x,y) NULL
#define LOGI2(x,y) NULL
#define LOGD2(x,y) NULL
#define LOGE(x) NULL
#define LOGI(x) NULL
#define LOGD(x) NULL
#endif

용도:

LOGE("ERROR.");
LOGE2("ERROR1","ERROR2");

이것은 현재 사용하고 있는 로그 매크로입니다.

#ifndef DEBUG 
#define DEBUG 1 // set debug mode
#endif

#if DEBUG
#define log(...) {\
    char str[100];\
    sprintf(str, __VA_ARGS__);\
    std::cout << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << str << std::endl;\
    }
#else
#define log(...)
#endif

사용방법:

log(">>> test...");

출력:

xxxx/proj.ios_mac/Classes/IntroScene.cpp][gotoNextScene][Line 58] >>> test...

모든 응답에 대한 부록으로 다음과 같은 것이 있습니다.

는 개인적으로 절대 .DEBUG디버깅 릴리스 하기 위해 "를 사용합니다.NDEBUG릴리스 빌드를 정의해야 합니다.assert() 콜을 사용합니다.assert()'''가) 않은 매크로를 더할 필요가 또, 「!」라고 하는 경우에 할 수 있습니다.DEBUG ★★★★★★★★★★★★★★★★★」NDEBUG을 사용하다

질문 1] 답은 '그렇다'입니다.메시지를 표준 오류 스트림으로 출력합니다.

질문 2] 많이 있어요.나의 가장 좋아하는 것은

#define LOG_ERR(...) fprintf(stderr, __VA_ARGS__)

이를 통해 임의의 수의 변수를 디버깅메시지에 포함할 수 있습니다.

템플릿 「Variadic」을 한 제입니다.print★★★★

template<typename... ArgTypes>
inline void print(ArgTypes... args)
{
  // trick to expand variadic argument pack without recursion
  using expand_variadic_pack = int[];
  // first zero is to prevent empty braced-init-list
  // void() is to prevent overloaded operator, messing things up
  // trick is to use the side effect of list-initializer to call a function
  // on every argument.
  // (void) is to suppress "statement has no effect" warnings
  (void)expand_variadic_pack{0, ((cout << args), void(), 0)... };
}

#ifndef MYDEBUG
#define debug_print(...)
#else
#define debug_print(...) print(__VA_ARGS__)
#endif

가 만든 입니다.debug_print실행 시 출력할 출력 종류를 선택할 수 있는 디버깅레벨을 받아들이는 바리에딕템플릿 함수:

template<typename... ArgTypes>
inline void debug_print(debug::debug level, ArgTypes... args)
{
  if(0 != (debug::level & level))
    print(args...);
}

해 주세요.print함수가 Visual Studio 2013 Preview에 충돌합니다(RC를 테스트하지 않았습니다).에서는) 에서 (Windows에서) (Windows에서보다).ostream""를 오버로드한 "operator<<.

임시로 도 있습니다.stringstream에 inside inside inside print의 typesafe를 경우)printf

를 .__LINE__,__FILE__코드 내의 출력원이 어디인지를 나타내는 인수로서 같은 변수명을 여러 장소에 인쇄하는 것은 드문 일이 아니기 때문에,fprintf(stderr, "x=%d", x);10줄 아래쪽에 같은 것을 하나 더 추가해도 큰 의미가 없습니다.

또, 특정의 기능을 덮어쓰고, 그 기능을 호출한 장소(메모리 할당등)를 보존하는 매크로도 사용하고 있기 때문에, 나중에 유출된 기능을 특정할 수 있습니다.메모리 할당에 대해서는 C++에서는 조금 어렵습니다.새로운/삭제를 사용하는 경향이 있어 쉽게 교체할 수 없기 때문입니다.그러나 잠금/잠금 해제 조작과 같은 다른 리소스는 이 방법을 추적하는 데 매우 유용합니다.물론, 좋은 C++ 프로그래머처럼 구축/파괴를 사용하는 잠금 래퍼가 있다면 이를 컨스트럭터에 추가합니다.o 잠금을 취득하면 내부 구조에 파일/줄을 추가합니다.또한 잠금을 취득할 수 없는 경우에는 다른 장소에 보관되어 있는 위치를 확인할 수 있습니다.

로그는 아래 코드를 사용합니다.몇 가지 장점이 있습니다.

  1. 실행 시 켜거나 끌 수 있습니다.
  2. 특정 로그 수준에서 문장을 컴파일할 수 있습니다.를 들어, 저는 있습니다.KIMI_PRIVATE에 기록되고 있기 에서 컴파일합니다.macro의 경우, macro의 경우, macro의 경우, macro의 경우, macro의 경우, secret source 의 경우, scret 의 경우 등입니다.

이 패턴은 몇 년 동안 나에게 매우 도움이 되었다.주의: 글로벌이 존재합니다.logMessage일반적으로 로그는 로그 스레드에 큐잉됩니다.

#define KIMI_LOG_INTERNAL(level,EXPR)           \
  if(kimi::Logger::loggingEnabled(level))       \
  {                                             \
    std::ostringstream os;                      \
    os << EXPR;                                 \
    kimi::Logger::logMessage(level ,os.str());  \
  }                                             \
  else (void) 0

#define KIMI_LOG(THELEVEL,EXPR)                 \
  KIMI_LOG_INTERNAL(kimi::Logger::LEVEL_ ## THELEVEL,EXPR)

#define KIMI_ERROR(EXPR)   KIMI_LOG(ERROR,EXPR)
#define KIMI_VERBOSE(EXPR) KIMI_LOG(VERBOSE,EXPR)
#define KIMI_TRACE(EXPR)   KIMI_LOG(TRACE,EXPR)
#define KIMI_INFO(EXPR)    KIMI_LOG(INFO,EXPR)
#define KIMI_PROFILE(EXPR) KIMI_LOG(TRACE,EXPR)

// Use KIMI_PRIVATE for sensitive tracing
//#if defined(_DEBUG)
#  define KIMI_PRIVATE(EXPR) KIMI_LOG(PRIVATE,EXPR)
// #else
// #  define KIMI_PRIVATE(EXPR) (void)0
// #endif

다른 답변에서 알 수 있듯이 많은 답변이 있습니다.변수 개수의 인수를 허용하고 인수 이름을 인쇄하며 줄바꿈을 포함하는 것이 마음에 듭니다.

void debug_print() { std::cerr << std::endl; }
template <typename Head, typename... Tail>
void debug_print(Head H, Tail... T) {
    std::cerr << ' ' << H;
    debug_print(T...);
}

#ifdef DEBUGFLAG
#  define DEBUG(...) std::cerr << "dbg  (" << #__VA_ARGS__ << "):", \
                     debug_print(__VA_ARGS__)
#else
#  define DEBUG(...) do {} while(0)
#endif

대부분의 설명은 이 답변과 동일하지만, 여러 변수를 다른 매크로 없이 한 줄에 디버깅하여 인쇄할 때 템플릿을 추가하면 단순해집니다.

언급URL : https://stackoverflow.com/questions/14251038/debug-macros-in-c

반응형