C宏中的可变参数函数

早上好

我想将此代码块放入C宏中:

void* instance = frm_config_get_instance("inside");
int (*func)(void* inst, double value) = frm_config_get_function("comp123_work2");
int res = func(instance, 0);

所以我可以这样使用它:

res = FRM_CONFIG_CALL("inside", "comp123_work2", 0.0)

到目前为止,这是我能想到的:

#define FRM_CONFIG_CALL(instance, function, ...) \
  ((int*)(void*, __VA_ARGS__))frm_config_get_function(function) \
  (frm_config_get_instance(instance), __VA_ARGS__)

有任何想法吗?

格罗

您尝试实现的方法意味着程序员必须事先知道每个“动态可调用”功能的参数数量和类型,这首先使采用这种机制的观点不合时宜。例如,您必须知道comp123_work2接受一个type的参数double

尽管这种违反OOP原则的行为可能不会让您感到那么麻烦,但实际上这意味着您将用编译错误来解决难以发现的运行时问题。读取通过宏传递给函数的不正确的varargs值,是一场灾难,等待发生。

您可以采用几种方法来实现自己现在正在做的事情:

1.将参数传递为 void*

这种方法并不能提供更多的编译时间安全性,但是对于毫无戒心的代码维护者而言,这并不令人惊讶。

在C中处理不同参数类型的一种常用方法是简单地通过void指针传递自定义参数,即:

typedef int(*voidvoidfunc_t)(void*, void*);
extern void* frm_config_get_instance(const char* name);
extern voidvoidfunc_t frm_config_get_function(const char* name);

然后,每个实现都可以转换为所需的内容:

int printf_wrapper(void * ch, void * p)
{
    double val = *(double*)p;
    return printf(ch, val);
}

voidvoidfunc_t frm_config_get_function(const char* name)
{
    return printf_wrapper;
}

您只需使用以下命令即可调用它:

void * parameter = frm_config_get_instance("inside");
voidvoidfunc_t func = frm_config_get_function("comp123_work2");
double val = 0.0;
int result = func(parameter, &val);

2.可变参数函数指针

另外,请注意,如果所有函数都使用varargs,则也可以定义变量参数函数指针。同样,没有编译时安全性(与C中的任何可变args函数一样):

typedef int(*varargfunc_t)(void*, ...);
extern void* frm_config_get_instance(const char* name);
extern varargfunc_t frm_config_get_function(const char* name);

接着:

void * parameter = frm_config_get_instance("inside");
varargfunc_t func = frm_config_get_function("comp123_work2");
int result = func(parameter, 0.0);

但是,您所有的“工作者”函数每次都必须“解压”参数,例如:

int printf_wrapper(void * ch, ...)
{
    va_list va;
    va_start(va, ch);
    int ret = vfprintf(stdout, ch, va);
    va_end(va);
    return ret;
}

varargfunc_t frm_config_get_function(const char* name)
{
    return printf_wrapper;
}

3.每种参数类型都有单独的功能

如果您没有那么多参数,则实现编译时类型安全的一种方法是为每种参数类型使用单独的函数,即:

typedef int(*intfunc_t)(void*, int);
typedef int(*floatfunc_t)(void*, float);
typedef int(*stringfunc_t)(void*, const char*);

这样就可以避免以错误的方式解释args:

int printf_wrapper(void * ch, float val)
{
    return printf(ch, val);
}

floatfunc_t frm_config_get_float_function(const char* name)
{
    return printf_wrapper;
}

这意味着您在调用后有一个强类型的fn指针frm_config_get_xxxxx_function

void * parameter = frm_config_get_instance("inside");
floatfunc_t func = frm_config_get_float_function("comp123_work2");
int result = func(parameter, 0.0f);

4.使用类似GValueGLib的变体联合类型

这也许不太常见,并且不提供完整的类型安全性,但是它至少提供了元数据,可以帮助您在错误调用的情况下引发错误。想法是将多个值与值类型进行并集,以便您的函数可以检查参数是否符合其期望。

int printf_wrapper(void * ch, GValue *val)
{
    // this checks that the parameter has the correct type
    if (!G_VALUE_HOLDS_FLOAT(val))
        error();

    float f = g_value_get_float(val);
    return printf(ch, f);
}

typedef int(*gvalue_func_t)(void*, GValue *val);
extern void* frm_config_get_instance(const char* name);
extern gvalue_func_t frm_config_get_function(const char* name);

然后GValue在运行时构造对象:

void * parameter = frm_config_get_instance("inside");
gvalue_func_t func = frm_config_get_function("comp123_work2");

GValue val = G_VALUE_INIT;
g_value_set_float(&val, 0.0f);
func(parameter, &val);

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章