如何指定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和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” #包括 国
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_