定义一个函数,函数名被宏屏蔽

Define a function which function name is masked by a macro

有时,用同名的宏来屏蔽一个函数会很方便。 在这个人为的例子中,宏可以在调用 function().

时插入额外的验证
int function(int i);

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

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

在相应的单元中,定义function()现在有一个问题:function现在是一个宏。所以 int function(int i) { ... } 将被扩展,并且失败得很严重。

这个问题的解决方法很简单:在定义前#undef function,然后实现函数即可。

一切都很好,除了...

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

gcc 中提供了另一个解决此问题的方法,使用编译器扩展 #pragma push_macro。 它有效,但非常丑陋,在函数定义周围至少添加了 3 行,这无助于提高可读性。而且这还不包括可移植性恶作剧,这会增加更多的复杂性。

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

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

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

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

这样可以防止类函数宏匹配展开,但对定义没有影响

Sometimes, it can be handy to mask a function by a macro of same name.

类似宏的包装函数,如

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()等,所以就多了几行代码。)

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

#include <stdlib.h>

void *__real_malloc(size_t);

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

这一次,interposing/wrapping 是在交易品种 table 级别完成的。