我已经定义了接口:
typedef DataExchg_Status_T (*DataExchg_Iface_ReceiveDataClbk_T)(
void * p_self, uint8_t const * p_data, size_t size);
typedef struct DataExchg_Iface_Tag {
DataExchg_Status_T (*SendData)(void * p_self, uint8_t const * p_data, size_t size);
void (*RegisterReceiveDataClbk)(void * p_self,
DataExchg_Iface_ReceiveDataClbk_T receive_data_clbk,
void * p_receiver_instance);
void * pSelf;
} DataExchg_Iface_T;
,我在Object_A
中使用它。
typedef struct Object_A_Tag {
DataExchg_Iface_T * pIface;
} Object_A_T;
在Object_B
模块中
我已经实现了此接口:
typedef struct Object_B_Tag {
int value;
...
} Object_B_T;
DataExchg_Status_T SendData(Object_B_T * p_self, uint8_t const * p_data, size_t size) {
...
}
void RegisterReceiveDataClbk(Object_B_T * p_self,
DataExchg_Iface_ReceiveDataClbk_T receive_data_clbk,
void * p_receiver_instance) {
...
}
一切正常,直到我想将上述功能分配给Object_A中的接口为止
Object_B object_b = {...};
Object_A object_a = {
.pIface = &(DataExchg_Iface_T){
.SendData = SendData,
.RegisterReceiveDataClbk = RegisterReceiveDataClbk,
.pSelf = &object_b
}
};
问题是我收到不兼容的指针警告,因为在接口实现中参数之一void * p_self
不等于Object_B_T * p_self
。
有一些解决此问题的方法:
1。强制转换为接口函数指针:
SendData = (DataExchg_Status_T(*)(void *, DataExchg_Iface_ReceiveDataClbk_T, void *)SendData
这是最方便的解决方案,但是有强有力的证据表明该解决方案可能导致不确定的行为:Casting a function pointer to another type
2。完全按照声明的方式实现接口并将参数强制转换为函数体:
这是最安全的解决方案,但不是最方便的。
DataExchg_Status_T SendData(void * p_self, uint8_t const * p_data, size_t size) {
Object_B_T pSelf = p_self;
}
3。初始化接口时将其转换为接口函数指针(解决方案1),并在每次使用接口时将其转换为实现的类型:
据我所知,此解决方案不应导致不确定的行为。
object_b.pIface->(DataExchg_Status_T(*)(Object_B *, DataExchg_Iface_ReceiveDataClbk_T, void *)SendData(...)
最后是一个问题:
1)就我而言,解决方案1真的会导致不确定的行为吗?
2)有什么解决方案可以使我具有带有无效实例指针的泛型接口声明和具有specyfic实例指针的specyfic实现? (类似于解决方案1)
答案 0 :(得分:0)
第二个选项,更改函数定义以匹配指针类型,并将void *
转换为正确的类型,这是实现此类回调的正确且首选的方式。
以一个示例为例,看一下man page for qsort
。该函数的声明如下:
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
并提供了回调函数的示例:
static int
cmpstringp(const void *p1, const void *p2)
{
/* The actual arguments to this function are "pointers to
pointers to char", but strcmp(3) arguments are "pointers
to char", hence the following cast plus dereference */
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
如您所见,此示例将void *
强制转换为正确的类型以便使用。这是C回调函数的常见用法。