更好的 C 语言断言
Better Assert in C
有时我必须通过 canbus 发送断言的结果,有时是本地的。
我只使用 C,Embitz 编译器使用 GCC、STM32F407 或 STM32F103。
我现在的主张是:
.h 文件:
extern char *astrbuf;
#define assert(left,operator,right)\
if(!((left) operator (right))) \
{asprintf(&astrbuf,"\nAssert error %s %d %ld %ld\n",__FILE__, __LINE__,\
(u32)(left),(u32)(right));\
asserted();\
}
.c 文件:
void asserted(void)
{ dprint("%s",astrbuf);
后跟显示代码或 canbus 代码。
例子:
断言(毫秒,<,最大时间);
这工作得很好,但如果可以指示操作员会更好。
我根本看不出如何显示或发送运算符,可以是 ==
、<
或 >
.
您可以使用字符串化运算符#
将宏参数operator
转换为字符串#operator
:
extern char *astrbuf;
#define assert(left,operator,right)\
if(!((left) operator (right))) \
{asprintf(&astrbuf,"\nAssert error %s %d %ld %s %ld\n",__FILE__, __LINE__,\
(u32)(left), #operator, (u32)(right));\
asserted();\
}
为什么不使用标准 assert
接口并包含整个表达式?
#define assert(EXPR) \
if (!(EXPR)) \
{asprintf(&astrbuf, "\nAssert error %s %d %s\n",__FILE__, __LINE__, #EXPR); \
asserted(); \
}
... 使用 #
宏字符串化运算符。
顺便问一下,为什么你的代码一半在宏中,另一半在 asserted
函数中?为什么不在一个地方全部完成?
#define assert(EXPR) \
if (!(EXPR)) \
{ \
asserted(__FILE__, __LINE__, #EXPR); \
}
和
void asserted(const char *file, int line, const char *expr) {
char *astrbuf;
asprintf(&astrbuf, "%s: %d: assertion failed: %s\n", file, line, expr);
dprint("%s", astrbuf);
...
}
现在您不再需要全局变量了。
还有一个潜在的问题。如果您这样使用宏:
if (foo())
assert(x > 42);
else
bar();
... else bar();
部分将附加到隐藏在 assert
中的 if
语句,而不是外部 if
。要解决此问题,您可以将整个内容包装在 do
while
循环中:
#define assert(EXPR) \
do { \
if (!(EXPR)) { \
asserted(__FILE__, __LINE__, #EXPR); \
} \
} while (0)
或者确保整个宏扩展为单个表达式:
#define assert(EXPR) \
((void)((EXPR) || (asserted(__FILE__, __LINE__, #EXPR), 0)))
当然你也可以把条件逻辑放在函数中:
#define assert(EXPR) asserted(!!(EXPR), __FILE__, __LINE__, #expr)
void asserted(int cond, const char *file, int line, const char *expr) {
if (cond) {
return;
}
...
}
也许您可以尝试稍微不同的方法来实现相同的目标。
不要将 (left, operator, right) 传递到宏中,而是尝试传递单个布尔条件。您可以在实际断言函数中使用条件,也可以使用宏将其字符串化。这样您仍然可以将整个情况报告给您的调试模块 (canbus)。
这也适用于更复杂的表达式,例如 ((a-b)< 0)
#define assert( condition ) custom_assert( condition , STRINGIFY_CONSTANT( condition ), __FILE__, __LINE__)
stringify 宏在其自己的头文件中,并基于此 link 派生。 https://gcc.gnu.org/onlinedocs/gcc-3.4.3/cpp/Stringification.html
#define STRINGIFY_CONSTANT(a) STRINGIFY_CONSTANT_DO_NOT_USE(a)
#define STRINGIFY_CONSTANT_DO_NOT_USE(a) #a
显然,不要使用 STRINGIFY_CONSTANT_DO_NOT_USE
void custom_assert( int condition , const char * condition_string, const char * file_name, int line_number)
{
if (!condition)
{
dprint("Assert Failed:'%s' File:'%s' Line:'%d'",condition_string, file_name, line_number);
}
}
我会避免在您的断言#define 中放置超过单个函数调用的任何内容,因为这可能难以调试,并且还会增加代码的大小。我建议将任何逻辑放入函数中。
我称我的断言为 custom_assert。我还有许多#defines 将调试输出放到不同的通道,如 usb、rs232、屏幕显示等。在发布模式下,断言只是重新启动嵌入式设备,这在我的应用程序中是可以接受的。
Assert是一个宏,按照我的理解,它总是内联的。
机器的代码有很多断言来防止损坏,因此需要尽可能快地保持代码,即。正常情况下内联。
当出现任何问题时,使用断言的功能,速度不再是问题,安全才是。断言的功能会关闭电机等,并通过 canbus 或本地显示进行报告。我不想在宏中完成所有这些。
这就是为什么部分代码在宏中,部分在函数中的原因。
如果我的理解有误,请纠正我。
没有全局变量是一个明确的优势,我添加了 while (0),就像在其他宏中一样。
有时我必须通过 canbus 发送断言的结果,有时是本地的。 我只使用 C,Embitz 编译器使用 GCC、STM32F407 或 STM32F103。 我现在的主张是: .h 文件:
extern char *astrbuf;
#define assert(left,operator,right)\
if(!((left) operator (right))) \
{asprintf(&astrbuf,"\nAssert error %s %d %ld %ld\n",__FILE__, __LINE__,\
(u32)(left),(u32)(right));\
asserted();\
}
.c 文件:
void asserted(void)
{ dprint("%s",astrbuf);
后跟显示代码或 canbus 代码。
例子:
断言(毫秒,<,最大时间);
这工作得很好,但如果可以指示操作员会更好。
我根本看不出如何显示或发送运算符,可以是 ==
、<
或 >
.
您可以使用字符串化运算符#
将宏参数operator
转换为字符串#operator
:
extern char *astrbuf;
#define assert(left,operator,right)\
if(!((left) operator (right))) \
{asprintf(&astrbuf,"\nAssert error %s %d %ld %s %ld\n",__FILE__, __LINE__,\
(u32)(left), #operator, (u32)(right));\
asserted();\
}
为什么不使用标准 assert
接口并包含整个表达式?
#define assert(EXPR) \
if (!(EXPR)) \
{asprintf(&astrbuf, "\nAssert error %s %d %s\n",__FILE__, __LINE__, #EXPR); \
asserted(); \
}
... 使用 #
宏字符串化运算符。
顺便问一下,为什么你的代码一半在宏中,另一半在 asserted
函数中?为什么不在一个地方全部完成?
#define assert(EXPR) \
if (!(EXPR)) \
{ \
asserted(__FILE__, __LINE__, #EXPR); \
}
和
void asserted(const char *file, int line, const char *expr) {
char *astrbuf;
asprintf(&astrbuf, "%s: %d: assertion failed: %s\n", file, line, expr);
dprint("%s", astrbuf);
...
}
现在您不再需要全局变量了。
还有一个潜在的问题。如果您这样使用宏:
if (foo())
assert(x > 42);
else
bar();
... else bar();
部分将附加到隐藏在 assert
中的 if
语句,而不是外部 if
。要解决此问题,您可以将整个内容包装在 do
while
循环中:
#define assert(EXPR) \
do { \
if (!(EXPR)) { \
asserted(__FILE__, __LINE__, #EXPR); \
} \
} while (0)
或者确保整个宏扩展为单个表达式:
#define assert(EXPR) \
((void)((EXPR) || (asserted(__FILE__, __LINE__, #EXPR), 0)))
当然你也可以把条件逻辑放在函数中:
#define assert(EXPR) asserted(!!(EXPR), __FILE__, __LINE__, #expr)
void asserted(int cond, const char *file, int line, const char *expr) {
if (cond) {
return;
}
...
}
也许您可以尝试稍微不同的方法来实现相同的目标。
不要将 (left, operator, right) 传递到宏中,而是尝试传递单个布尔条件。您可以在实际断言函数中使用条件,也可以使用宏将其字符串化。这样您仍然可以将整个情况报告给您的调试模块 (canbus)。
这也适用于更复杂的表达式,例如 ((a-b)< 0)
#define assert( condition ) custom_assert( condition , STRINGIFY_CONSTANT( condition ), __FILE__, __LINE__)
stringify 宏在其自己的头文件中,并基于此 link 派生。 https://gcc.gnu.org/onlinedocs/gcc-3.4.3/cpp/Stringification.html
#define STRINGIFY_CONSTANT(a) STRINGIFY_CONSTANT_DO_NOT_USE(a)
#define STRINGIFY_CONSTANT_DO_NOT_USE(a) #a
显然,不要使用 STRINGIFY_CONSTANT_DO_NOT_USE
void custom_assert( int condition , const char * condition_string, const char * file_name, int line_number)
{
if (!condition)
{
dprint("Assert Failed:'%s' File:'%s' Line:'%d'",condition_string, file_name, line_number);
}
}
我会避免在您的断言#define 中放置超过单个函数调用的任何内容,因为这可能难以调试,并且还会增加代码的大小。我建议将任何逻辑放入函数中。
我称我的断言为 custom_assert。我还有许多#defines 将调试输出放到不同的通道,如 usb、rs232、屏幕显示等。在发布模式下,断言只是重新启动嵌入式设备,这在我的应用程序中是可以接受的。
Assert是一个宏,按照我的理解,它总是内联的。 机器的代码有很多断言来防止损坏,因此需要尽可能快地保持代码,即。正常情况下内联。 当出现任何问题时,使用断言的功能,速度不再是问题,安全才是。断言的功能会关闭电机等,并通过 canbus 或本地显示进行报告。我不想在宏中完成所有这些。 这就是为什么部分代码在宏中,部分在函数中的原因。 如果我的理解有误,请纠正我。 没有全局变量是一个明确的优势,我添加了 while (0),就像在其他宏中一样。