如果定义了宏,则在运行时检查

时间:2017-11-25 22:30:49

标签: c c-preprocessor preprocessor

在开发静态库的过程中,我遇到了测试库函数的必要性。 功能检查不是问题。主要问题是测试库提供的每个宏定义。

我开始使用像

这样的代码
/* 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指令。

我需要一个解决方案,它不仅会检查运行时是否定义宏,还会将其值打印到输出中。

2 个答案:

答案 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);

结果:
the test result

让我们密切关注每一个

TEST_BXI_MACRO_STRING

这个很简单:

#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检查这种特殊情况

TEST_BXI_MACRO_I32

#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二进制的事实。 让我们模拟三种情况:

#define MACRO(10)

在这种情况下,完整的公式将如下所示:

5 * 10 + 1 => 50 + 1 => 51

#define MACRO(0)

在这种情况下,乘法淡出:

5 * 0 + 1 => 0 + 1 => 1

在某些情况下,如果定义的宏为0(如预处理选项和内容),可以使用它进行额外检查

#define MACRO

这个案例展示了一些数学魔术:

5 *    + 1 => 5 * (+1) => 5 * 1 => 5

由于+1被解释为简单1,我们收到5。

TEST_BXI_MACRO_EXISTS

#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]的值,该方法也可以轻松地用于测试宏是否定义了数字常量,字符串文字或空值。

到目前为止,我还不知道如何以这种方式测试类似函数的宏,但是我认为与他人共享仍然很有用。