定义一个函数,函数名被宏屏蔽
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 级别完成的。
有时,用同名的宏来屏蔽一个函数会很方便。
在这个人为的例子中,宏可以在调用 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 级别完成的。