簡體   English   中英

如何覆蓋C中的斷言宏?

[英]How to override assert macro in C?

我想創建我自己的assert版本,其中它會打印一些日志,以防在NDEBUG模式下調用斷言。

我嘗試使用LD_PRELOAD技巧並重新定義 assert 宏,但它似乎完全忽略了宏定義並且覆蓋__assert_fail無關緊要,因為在NDEBUG情況下不會調用它。

如何覆蓋libc assert宏?

我不想創建不同的函數,因為 assert 已經在項目中大量使用。

在 Cygwin/Windows 和 Linux 上使用 gcc 時遇到了同樣的問題。

我的解決方案是覆蓋實際斷言失敗處理函數的(弱)定義。 這是代碼:

/*!
 * Overwrite the standard (weak) definition of the assert failed handling function.
 *
 * These functions are called by the assert() macro and are named differently and
 * have different signatures on different systems.
 * - On Cygwin/Windows its   __assert_func()
 * - On Linux its            __assert_fail()
 *
 * - Output format is changed to reflect the gcc error message style
 *
 * @param filename    - the filename where the error happened
 * @param line        - the line number where the error happened
 * @param assert_func - the function name where the error happened
 * @param expr        - the expression that triggered the failed assert
 */
#if defined( __CYGWIN__ )

void __assert_func( const char *filename, int line, const char *assert_func, const char *expr )

#elif defined( __linux__ )

void __assert_fail ( const char* expr, const char *filename, unsigned int line, const char *assert_func )

#else
# error "Unknown OS! Don't know how to overwrite the assert failed handling function. Follow assert() and adjust!"
#endif
{
    // gcc error message style output format:
    fprintf( stdout, "%s:%d:4: error: assertion \"%s\" failed in function %s\n",
             filename, line, expr, assert_func );

    abort();
}

C99 基本原理在第 113 頁上提供了有關如何以良好方式重新定義斷言的示例:

 #undef assert #ifdef NDEBUG #define assert(ignore) ((void)0) #else extern void __gripe(char *_Expr, char *_File, int _Line, const char *_Func); #define assert(expr) \\ ((expr) ? (void)0 :\\ __gripe(#expr, _ _FILE_ _,_ _LINE_ _,_ _func_ _)) #endif

我會在此代碼之前包含 assert.h 以確保使用 assert.h。 另請注意,它調用了一個執行報告邏輯的函數,因此您的代碼會更小。

這是一件非常簡單的事情,因為assert是一個宏。 鑒於你有這個代碼:

#define NDEBUG
#include <assert.h>

int main( void )
{
  assert(0);

  return 0;
}

然后就這樣做:

#ifdef NDEBUG
#undef assert
#define assert(x) if(!(x)){printf("hello world!");} // whatever code you want here
#endif

請注意,這必須#include <assert.h>之后完成。

因此,如果您想將自己的定義粘貼到一個公共頭文件中,然后使用該頭文件修改現有代碼,那么您的頭文件必須包含在 assert.h 之后。

my_assert.h

#include <assert.h>
#include <stdio.h>

#ifdef NDEBUG
#undef assert
#define assert(x) if(!(x)){printf("hello world!");}
#endif

主文件

#define NDEBUG    
#include <assert.h>
#include "my_assert.h"

int main( void )
{
  assert(0); // prints "hello world!"
  assert(1); // does nothing

  return 0;
}

嘗試覆蓋大型代碼庫中的 assert() 宏可能很困難。 例如,假設您有如下代碼:

#include <assert.h>
#include "my_assert.h"
#include "foo.h" // directly or indirectly includes <assert.h>

在此之后,任何對 assert() 的使用都將再次使用系統 assert() 宏,而不是您在“my_assert.h”中定義的宏(這顯然是 assert 宏的 C 設計的一部分)。

有很多方法可以避免這種情況,但是您必須使用討厭的技巧,例如將您自己的 assert.h 標頭放在系統 assert.h 之前的包含路徑中,這有點容易出錯且不可移植。

我建議使用與 assert 不同的命名宏,並使用正則表達式技巧或 clang-rewriter 將代碼庫中的各種斷言宏重命名為您可以控制的斷言宏。 例子:

perl -p -i -e 's/\bassert\b *\( */my_assert( /;' `cat list_of_filenames`

(然后在修改的每個文件中添加“my_assert.h”或類似的東西)

您可以檢查NDEBUG是否已定義,然后打印您想要打印的任何日志。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM