如何指定C++;指向具有未知参数数的C函数的指针? 我正在编写一个C库,我希望它可以从C和C++中使用。在某一时刻,它应该从具有0-3个参数的用户处接收回调,稍后将在某个指针处调用该回调。如下所示(代码的副本也可用): //app_c.c #包括 #包括“lib.h” 双f0(无效){ 返回123; } 双f2(双a,双b){ 返回a+b; } int main(){ cb_arity=0; cb_func=f0; printf(“%f\n”,cb_call()); cb_arity=2; cb_func=f2; printf(“%f\n”,cb_call()); }

如何指定C++;指向具有未知参数数的C函数的指针? 我正在编写一个C库,我希望它可以从C和C++中使用。在某一时刻,它应该从具有0-3个参数的用户处接收回调,稍后将在某个指针处调用该回调。如下所示(代码的副本也可用): //app_c.c #包括 #包括“lib.h” 双f0(无效){ 返回123; } 双f2(双a,双b){ 返回a+b; } int main(){ cb_arity=0; cb_func=f0; printf(“%f\n”,cb_call()); cb_arity=2; cb_func=f2; printf(“%f\n”,cb_call()); },c++,c,language-lawyer,ffi,reinterpret-cast,C++,C,Language Lawyer,Ffi,Reinterpret Cast,我能够创建一个指向C函数的指针,该函数接受未知(但仍然固定)数量的参数,注意它是void(*cb_func)(),而不是void(*cb_func)(void): //lib.h #ifndef LIB_H_ #定义LIB_H_ #ifdef_uucplusplus 外部“C”{ #恩迪夫 外部国际合作; 外部双(*cb_func)(); 双重cb_调用(无效); #ifdef_uucplusplus } #恩迪夫 #endif//LIB_H_ //lib.c #包括“lib.h” #包括 国

我能够创建一个指向C函数的指针,该函数接受未知(但仍然固定)数量的参数,注意它是
void(*cb_func)(
),而不是
void(*cb_func)(void)

//lib.h
#ifndef LIB_H_
#定义LIB_H_
#ifdef_uucplusplus
外部“C”{
#恩迪夫
外部国际合作;
外部双(*cb_func)();
双重cb_调用(无效);
#ifdef_uucplusplus
}
#恩迪夫
#endif//LIB_H_
//lib.c
#包括“lib.h”
#包括
国际合作;
双(*cb_func)();
双cb_呼叫(无效){
开关(断路器){
案例0:
返回cb_func();
案例1:
返回cb_func(10.0);
案例2:
返回cb_func(10.0,20.0);
案例3:
返回cb_func(10.0,20.0,30.0);
违约:
中止();
}
}
它在我的机器和上编译并成功运行。据我所知,未调用UB

现在我想让它在C++中也能工作。不幸的是,我现在需要的是<代码> RealTytCase< /Cord>,因为<代码>()>代码>在C++中是“没有参数”,而不是“未知的参数数”:

//app_cpp.cpp
#包括
#包括“lib.h”
int main(){
cb_arity=0;
cb_func=[](){return 123.0;};
printf(“%f\n”,cb_call());
cb_arity=2;
cb_func=重新解释强制转换(静态强制转换)(
[](双a,双b){返回a+b;}
));
printf(“%f\n”,cb_call());
}

据我所知,这里也没有调用ub:尽管我在C++中将函数指针<代码> double(*)(double,double)< /> >转换为<代码> double(*)(空)>代码>,但是在调用之前,它在C代码中被转换回<代码> double(*)(double,double)< /> >。


有没有办法在C++代码中去掉这些丑陋的模型?我已经尝试了指定< <代码> CBSFUNC< /C> >作为代码>空白(*)(…)< />代码,但是C++仍然不会隐式地将<代码> double(*)(double,double)< /> >它。

,而不是擦除回调中的参数数目,您可以保留它。

// lib.h
#ifndef LIB_H_
#define LIB_H_

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
    int arity;
    union {
        void(*zero)(void);
        void(*one)(double);
        void(*two)(double, double);
        void(*three)(double, double, double);
    }
} cb_type;
extern cb_type cb;

double cb_call(void);

#ifdef __cplusplus
}
#endif

#endif  // LIB_H_

无参数函数声明是一种过时的C特性。我建议避免这样做

本质上,您试图绕过类型检查:您希望在
cb_func
变量中存储一个几乎任意的函数,然后能够在没有任何危险迹象(如显式强制转换)的情况下自由调用它。然而,这段代码本身就是危险的:如果你搞砸了
cb_arity
,行为就没有定义。此外,您甚至可以在没有任何警告的情况下将
double(*)(int,char*)
存储到
cb_func
,这在您的示例中永远都是不正确的

在更深层次上,您的
cb_arity
/
cb_func
看起来非常像一个。在C++中实现它的规范方法是(或类似于函数的特定的)。在C中实现它的规范方法是一个带有“tag”字段和匿名
联合的结构,如下所示:

struct cb\u s{
内在性;
联合{
双(*0)(无效);
双(*func1)(双);
双(*func2)(双,双);
双人(*func3)(双人、双人、双人);
};
};
外部结构cb_s cb;
现在,您编写的不是
cb\u func=f0
,而是
cb.func0=f0
,而不是
cb\u func=f2
您编写的是
cb.func2=f2
。C++中的Similary和lambda的所有演员现在都不见了。唯一剩下的危险迹象是潜在的
工会

您必须在两个地方更改代码:

  • 图书馆。这里您已经知道了
    func0
    /
    func1
    /。。。打电话,没什么大不了的
  • 写入
    cb_func
    的用户代码。现在它必须知道该写信给哪个工会成员。据推测,同样的代码也会编写一个编译时常量
    cb_arity
    ,所以这不是什么大问题。如果从其他人处收到
    cb_arity
    ,则该人也应使用标记的并集,而不是单独传递
    cb_arity
    cb_func
    ,以提高类型安全性

  • StEnoTo: C++(与C相反)禁止访问非活动成员的联合。这不应影响代码的正确性,因为通过指向

    func1
    的指针调用
    func0
    本身就是UB。

    注意:这样的“无参数”函数声明是“过时的特性”,至少从C99开始,请参阅中的“6.11.6函数声明”。您可能希望重新查看。这也是一个过时的C功能。根据可能的删除情况,作为备用,您可以去掉
    静态\u cast
    。将
    +
    应用于非捕获lambda会导致转换为函数指针。因此,
    +[](双a,双b){return a+b;}
    就可以了。作为另一种(更常见的)方法,您可以有一个带有单个参数的回调函数,一个指向同时包含值数和值的结构的指针。请注意,
    cbu t
    是。
    // lib.c
    #include "lib.h"
    #include <stdlib.h>
    
    cb_type cb;
    
    double cb_call(void) {
        switch (cb.arity) {
            case 0:
                return cb.zero();
            case 1:
                return cb.one(10.0);
            case 2:
                return cb.two(10.0, 20.0);
            case 3:
                return cb.three(10.0, 20.0, 30.0);
            default:
                abort();
        }
    }
    
    // lib.h
    #ifndef LIB_H_
    #define LIB_H_
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    void register_zero(void(*)(void));
    void register_one(void(*)(double));
    void register_two(void(*)(double, double));
    void register_three(void(*)(double, double, double));
    
    double cb_call(void);
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif  // LIB_H_