在开发静态库的过程中,我遇到了测试库函数的必要性。 功能检查不是问题。主要问题是测试库提供的每个宏定义。
我开始使用像
这样的代码/* For non-vital macro (OS/WORDSIZE detections) */
# if defined(BXI_ARCH_X32)
printf(" defined : BXI_ARCH_X32\n");
# endif
# if defined(BXI_ARCH_X64)
printf(" defined : BXI_ARCH_X64\n");
# endif
<...>
/* For vital macro */
#if defined(BXI_OS)
printf(" defined : BXI_OS : \"%s\"\n", BXI_OS);
#else
print_failed();
#endif
#if defined(BXI_BITS)
printf(" defined : BXI_BITS: %d\n", BXI_BITS);
#else
print_failed();
#endif
#if defined(BXI_ARCH)
printf(" defined : BXI_ARCH: \"%s\"\n", BXI_ARCH);
#else
print_failed();
#endif
这很酷,但非常耗时。我想要一个可以为我生成代码的工具,或者一些技巧,可以让我通过像这样的宏自动生成测试
TEST_MACRO(BXI_OS)
但是,如您所知,宏定义无法生成#if /#else / #endif指令。
我需要一个解决方案,它不仅会检查运行时是否定义宏,还会将其值打印到输出中。
答案 0 :(得分:0)
但是,由于这是Q&amp; A风格的文章,我找到了解决方案。
最终结果如下:
TEST_BXI_MACRO_STRING(BXI_OS);
TEST_BXI_MACRO_STRING(BXI_ARCH);
TEST_BXI_MACRO_I32 (BXI_BITS);
TEST_BXI_MACRO_EXISTS_WEAK(BXI_ARCH_X32); // _WEAK as we don't need to fail
TEST_BXI_MACRO_EXISTS_WEAK(BXI_ARCH_X64);
让我们密切关注每一个
这个很简单:
#define TEST_BXI_MACRO_STRING(name) \
do \
{ \
print_macro_name(#name); \
if (!TEST_BXI_MACRO_DEFINED(#name, name(1))) \
print_macro_undefined_exit(); \
if (strlen(name "") == 0) \
print_macro_undefined_exit(); \
print_macro_value_string(name ""); \
} \
while (0)
我们只是使用C允许const字符串自动连接的想法。因此,当宏存在时,我们将收到
#define MACRO "Content"
"Content" "" = "Content"
当它不
时 "" = ""
然后我们查看结果字符串的长度,当它为0-bingo时,未定义宏。这不适用于“”宏,但可以使用TEST_BXI_MACRO_EXISTS检查这种特殊情况
#define TEST_BXI_MACRO_I32(name) \
do \
{ \
print_macro_name(#name); \
if (!TEST_BXI_MACRO_DEFINED(#name, name(1))) \
print_macro_undefined_exit(); \
if ((5 * name + 1) == 5) \
print_macro_undefined_exit(); \
print_macro_value_signed(name + 0); \
} \
while (0)
注意:您可以通过更换打印机格式化器来类似地创建... MACRO_U32版本。
这里我们使用'+'运算符可以是一元AND二进制的事实。 让我们模拟三种情况:
在这种情况下,完整的公式将如下所示:
5 * 10 + 1 => 50 + 1 => 51
在这种情况下,乘法淡出:
5 * 0 + 1 => 0 + 1 => 1
在某些情况下,如果定义的宏为0(如预处理选项和内容),可以使用它进行额外检查
这个案例展示了一些数学魔术:
5 * + 1 => 5 * (+1) => 5 * 1 => 5
由于+1被解释为简单1,我们收到5。
#define TEST_BXI_MACRO_DEFINED_I(strstr, fnc) (strcmp(#fnc, strstr "(1)"))
#define TEST_BXI_MACRO_DEFINED(str, fnc) TEST_BXI_MACRO_DEFINED_I(str, fnc)
#define TEST_BXI_MACRO_EXISTS(name) \
do \
{ \
print_macro_name(#name); \
if (!TEST_BXI_MACRO_DEFINED(#name, name(1))) \
print_macro_undefined_exit(); \
else \
print_macro_defined(); \
} \
while (0)
此实现使用以下事实:宏的字符串值不应与其名称相同(因为#define A A
是useles)
对于那些想要打印功能的人来说,这里是:
void print_macro_name(const char * name)
{
printf(" checking: %-20s: ", name);
}
void print_macro_undefined_exit(void)
{
printf("\033[1;31mUNDEFINED\033[0m\n");
exit(1);
}
void print_macro_defined(void)
{
printf("\033[1;33mDEFINED\033[0m\n");
}
void print_macro_undefined(void)
{
printf("\033[1;32mUNDEFINED\033[0m\n");
}
void print_macro_value_string(const char * value)
{
printf("\"%s\"\n", value);
}
void print_macro_value_signed(i32 value)
{
printf("%d\n", value);
}
void print_macro_value_unsigned(u32 value)
{
printf("%u\n", value);
}
答案 1 :(得分:0)
我遇到了一个类似的问题,并且发现了另一个不错的技巧,可以在没有TEST_BXI_MACRO_EXISTS
且不需要额外的函数调用的情况下实现您的string.h
:
#define STRINGIZE_I(x) #x
#define TEST_BXI_MACRO_EXISTS(name) (#name [0] != STRINGIZE_I(name) [0])
此技巧使用相同的假设,即已定义宏的字符串化值与该宏的字符串化名称不匹配。但是对于我来说,我只需要检查宏是否定义为数字常量,字符串文字或空值。没有类似函数的宏和其他内容。
它是这样工作的:
#define MACRO "Content"
TEST_BXI_MACRO_EXISTS(MACRO)
// translates to ("MACRO"[0] != "\"Content\""[0])
// first letter of a valid macro name can never be equal to '"'
#define MACRO 3.14
TEST_BXI_MACRO_EXISTS(MACRO)
// translates to ("MACRO"[0] != "3.14"[0])
// first letter of a valid macro name can never be equal to a digit
#define MACRO
TEST_BXI_MACRO_EXISTS(MACRO)
// translates to ("MACRO"[0] != ""[0])
// first letter of a valid macro name can never be equal to a '\0'
通过检查STRINGIZE_I(name) [0]
的值,该方法也可以轻松地用于测试宏是否定义了数字常量,字符串文字或空值。
到目前为止,我还不知道如何以这种方式测试类似函数的宏,但是我认为与他人共享仍然很有用。