定义一个函数名称被宏掩盖的函数

时间:2019-01-19 02:11:47

标签: c gcc macros masking

有时,用同名宏掩盖函数可能很方便。 在这个人为的示例中,宏使调用function()时可以插入其他验证。

int function(int i);

#define function(i)  ( assert(i>0), function(i) )

其他更常见的用法使宏转换为更快的代码,例如直接表入口点,从而提高了性能。 在所有情况下,目标都是在库中保留相同名称的符号,以确保它存在并且可以由另一个程序从库中调用,而无需使用*.h(通常从包装器以与C

在相应的单元中,现在定义function()会遇到问题:function现在是宏。因此int function(int i) { ... }将会扩展,并且会严重失败。

此问题的解决方案很简单:只需在定义前#undef function,然后实现该功能。

一切都很好,除了...

如果在同一单元的后续部分中,另一个函数调用function(),则它将直接调用符号,而不是*.h中的宏,从而失去了提供的额外功能宏,这可能是不可取的。

gcc中使用编译器扩展名#pragma push_macro提供了解决此问题的另一种方法。 它可以工作,但是很难看,在函数定义周围至少添加三行,这不利于可读性。这还不包括可移植性的恶作剧,这将增加更多的复杂性。

但是,用相同名称的宏屏蔽函数并不是什么新鲜事。我敢肯定,我早在C90标准库中就读过建议这种设计的书籍。

我怀疑可能存在针对此问题的更好解决方案。 有什么想法吗?

2 个答案:

答案 0 :(得分:4)

类似函数的宏的另一种技巧是在定义的声明符周围放置多余的括号:

int (function)(int i) {
    ...
}

这防止了类似于函数的宏的匹配和扩展,但对定义没有影响。

答案 1 :(得分:0)

  

有时候,用同名宏掩盖函数可能很方便。

类似宏的包装函数,如

static inline int actual_function(int i)
{
    /* extra stuff */
    return function(i);
}
由于最小惊喜原则,

在编译时是一个更好的选择。 (例如,使开发人员感到惊讶的是,他们做愚蠢的事情。因此,惊喜越少,他们可能犯的愚蠢错误就越少。)

不幸的是,这对名称更改没有帮助。我个人并不在乎,因为无论如何,重命名仅需快速find . -name '*.[ch]' -exec sed -e 's|\bOLDNAME\b|NEWNAME|g' -i '{}' ';'


对于动态链接的符号,我们可以在运行时使用我们自己的符号将它们包装或插入。这是高度特定于OS和工具链的。

在Linux中,有两个选项:使用动态链接器工具(dlsym())或--wrap链接器选项。它仅适用于动态链接的符号,但通常,至少标准库是动态链接的。

简而言之,如果您想将动态链接的malloc()替换为自己的函数,则可以使用

#define  _GNU_SOURCE
#include <stdlib.h>
#include <dlfcn.h>

static void *(*real_malloc)(size_t) = NULL;

void *malloc(size_t size)
{
    if (!real_malloc)
        real_malloc = dlsym(RTLD_NEXT, "malloc");
    /* extra stuff */
    return real_malloc(size);
}

如果您想以线程安全的方式执行上述操作,它将变得更加复杂。 (我使用__atomic_load_n()等,因此仅多了几行代码。)

更简单的方法是通过在编译和链接二进制文件时为其提供选项-Wl,-wrap,malloc来告诉GCC为我们做符号魔术。然后,

#include <stdlib.h>

void *__real_malloc(size_t);

void *__wrap_malloc(size_t size)
{
    /* extra stuff */
    return __real_malloc(size);
}

这次,插入/包装是在符号表级别完成的。