我想创建自己的版本,assert
以防在NDEBUG
模式下调用 assert 时它会打印一些日志。
我试图做到这一点LD_PRELOAD
并重新定义 assert 宏,但它似乎完全忽略了宏定义并且覆盖__assert_fail
是无关紧要的,因为在NDEBUG
.
如何覆盖libc
assert
宏?
我不想创建不同的函数,因为 assert 已经在项目中大量使用。
我想创建自己的版本,assert
以防在NDEBUG
模式下调用 assert 时它会打印一些日志。
我试图做到这一点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 之前的包含路径中,这有点容易出错且不可移植。
我建议使用与断言不同的命名宏,并使用正则表达式技巧或 clang-rewriter 将代码库中的各种断言宏重命名为您可以控制的断言宏。例子:
perl -p -i -e 's/\bassert\b *\( */my_assert( /;' `cat list_of_filenames`
(然后将“my_assert.h”或类似的东西添加到每个修改的文件中)
您可以检查是否NDEBUG
已定义,如果已定义,则打印您要打印的任何日志。