首页 > 解决方案 > 在 C 中使用宏进行函数挂钩

问题描述

我想通过编译器开关为函数启用钩子。我想得到以下结果:

#ifdef COMPILER_SWITCH
static inline int Foo_HOOKED(int x, int y);

int Foo(int x, int y)
{
    int rval;

    Foo_PRE_HOOK();
    rval = Foo_HOOKED(x,y);
    Foo_POST_HOOK();

    return rval;
}

static inline int Foo_HOOKED(int x, int y)
#else
int Foo(int x, int y)
#endif  // COMPILER_SWITCH
{
    // Implementation of Foo
}

函数名称、返回类型、参数数量和类型应该是可变的。但是钩子等的名称应始终包含具有相同后缀的函数名称,如图所示。我的梦想是得到这样的东西:

#ifdef COMPILER_SWITCH
#define HOOKED_FUNCTION(x) <magic>
#else
#define HOOKED_FUNCTION(x) #x
#endif

HOOKED_FUNCTION(int Foo(int x, int y))
{
    // Implementation of Foo
}

有没有人有一个想法,如何以一种通用的方式解决这个问题,这不会对可读性产生太大影响?还允许在挂钩函数等之前包含一个文件。

编辑:我的主题就像 C++ 中的守卫。可惜我在...

#define HOOKED_FUNCTION  hook_t my_hook

class hook_t
{
public:
    hook_t() { pre_hook(); }
    ~hook_t() { post_hook(); }
};


int Foo(int x, int y)
{
#if COMPILER_SWITCH
    HOOKED_FUNCTION;
#endif
    // implementation of Foo()...
}

标签: cc-preprocessor

解决方案


这是否足够接近?

//#define COMPILE_SWITCH

#ifdef COMPILE_SWITCH

#define HOOK_FUNCTION(name, rtype, args_defn, args_list) \
    static rtype (name)args_defn; \
    extern void name ## _PRE_HOOK(void); \
    extern void name ## _POST_HOOK(void); \
    static inline rtype name ## _HOOKED args_defn { \
        name ## _PRE_HOOK(); \
        rtype rval = (name)args_list; \
        name ## _POST_HOOK(); \
        return rval; \
    }

HOOK_FUNCTION(Foo, int, (int x, int y), (x, y))
#define Foo(x, y) Foo_HOOKED(x, y)

HOOK_FUNCTION(Bar, double, (int x, int y, int z), (x, y, z))
#define Bar(x, y, z) Bar_HOOKED(x, y, z)

#endif /* COMPILE_SWITCH */

#include <stdio.h>
#include <math.h>

static int (Foo)(int x, int y)
{
    int z = x * x + y * y;
    return z;
}

static double (Bar)(int x, int y, int z)
{
    return sqrt(x * x + y * y + z * z);
}

int main(void)
{
    int x = 3;
    int y = 5;
    int z = Foo(x, y);
    printf("x = %d, y = %d, z = %d\n", x, y, z);
    printf("x = %d, y = %d, z = %d, r = %.3f\n", x, y, z, Bar(x, y, z));
    return 0;
}

#ifdef COMPILE_SWITCH

void Foo_PRE_HOOK(void)
{
    printf("-->> Foo() (%s)\n", __func__);
}

void Foo_POST_HOOK(void)
{
    printf("<<-- Foo() (%s)\n", __func__);
}

void Bar_PRE_HOOK(void)
{
    printf("-->> Bar() (%s)\n", __func__);
}

void Bar_POST_HOOK(void)
{
    printf("<<-- Bar() (%s)\n", __func__);
}

#endif /* COMPILE_SWITCH */

这编译并运行:

$ rmk -u hook67 UFLAGS=-UCOMPILE_SWITCH && hook67
    gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes        -UCOMPILE_SWITCH hook67.c -o hook67  
x = 3, y = 5, z = 34
x = 3, y = 5, z = 34, r = 34.496
$ rmk -u hook67 UFLAGS=-DCOMPILE_SWITCH && hook67
    gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes        -DCOMPILE_SWITCH hook67.c -o hook67  
-->> Foo() (Foo_PRE_HOOK)
<<-- Foo() (Foo_POST_HOOK)
x = 3, y = 5, z = 34
-->> Bar() (Bar_PRE_HOOK)
<<-- Bar() (Bar_POST_HOOK)
x = 3, y = 5, z = 34, r = 34.496
$

rmk程序是make; 该-u标志表示“无条件编译”。有makefile一个 CFLAGS 宏,其中包括${UFLAGS}(用于用户标志)——UFLAGS仅在命令行上设置。

您可以将宏定义HOOK_FUNCTION放入标题中。您仍然需要HOOK_FUNCTION在代码中编写调用和每个挂钩函数的宏定义。您必须为每个挂钩函数定义 pre-hook 和 post-hook 函数。

没有一种简单的方法可以避免宏 AFAIK的args_defnand部分。args_list


推荐阅读