C++ C++-17:将函数指针强制转换为具有不同参数指针类型的函数

C++ C++-17:将函数指针强制转换为具有不同参数指针类型的函数,c++,c++17,function-pointers,C++,C++17,Function Pointers,我有一个遗留的C-API,它为异步操作提供回调。 由于这个库需要许多编译器标志,并且充满了宏,而且包含的头文件会生成大量警告,因此我决定为这个库创建一个封装专有C库的包装器 但由于这个库是异步的,所以它提供了回调函数。 问题是,回调需要指向结构的指针(X\u leg)。 但是由于这个结构是旧API的一部分,我不想包括这个结构, 我决定创建一个布局相同的结构X\u wrp。 在main()中,我确保两个结构的大小相等 我现在的问题是: 重新解释类型为func\u wrp的包装函数指针到类型为fun

我有一个遗留的C-API,它为异步操作提供回调。 由于这个库需要许多编译器标志,并且充满了宏,而且包含的头文件会生成大量警告,因此我决定为这个库创建一个封装专有C库的包装器

但由于这个库是异步的,所以它提供了回调函数。 问题是,回调需要指向结构的指针(
X\u leg
)。 但是由于这个结构是旧API的一部分,我不想包括这个结构, 我决定创建一个布局相同的结构
X\u wrp
。 在main()中,我确保两个结构的大小相等

我现在的问题是:
重新解释类型为
func\u wrp
的包装函数指针到类型为
func\u leg
的旧函数指针是否安全? 还是C++17中未定义的行为

我有以下最简单的工作示例:

#include <iostream>
#include <cstdint>

//begin of wrapper decls
struct X_wrp {
    std::uint32_t member;
};

using func_wrp = void (*)(const X_wrp* arg);

void caller_wrp(func_wrp func);
//end of wrapper decls

//Legacy C-Code
typedef struct {
    std::uint32_t member;
} X_leg;

typedef void (*func_leg)(const X_leg* arg);

void caller_leg(func_leg func) {
    static X_leg inst{10};
    func(&inst);
}
//End of Legacy C-Code

void callback(const X_wrp* arg) {
    std::cout << arg->member << std::endl;
}

int main() {
    static_assert(sizeof(X_leg)==sizeof(X_wrp));//ensures that there is no oops
    caller_wrp(callback);
    return EXIT_SUCCESS;
}

//begin of wrapper implementations
void caller_wrp(func_wrp func) {
    caller_leg(reinterpret_cast<func_leg>(func)); //is this cast safe?

}
//end of wrapper implementations
#包括
#包括
//行首
结构X_wrp{
标准:uint32_t成员;
};
使用func_wrp=void(*)(常量X_wrp*arg);
无效调用方(func_wrp func);
//包装袋底端
//遗留C代码
类型定义结构{
标准:uint32_t成员;
}X_腿;
typedef void(*func_leg)(const X_leg*arg);
无效调用方函数(函数函数函数){
静态X_腿装置{10};
func(和inst);
}
//遗留C代码的结束
无效回调(常量X_wrp*arg){

std::cout memberNo,这是不安全的,在

通过函数类型不同于被调用函数定义的函数类型的表达式调用函数会导致未定义的行为

关于函数指针转换的
reinterpret\u cast
文档也加强了这一点

函数指针可以显式转换为不同类型的函数指针。[注意:通过指向与函数定义中使用的类型不同的函数类型([dcl.fct])的指针调用函数的效果是未定义的([expr.call])。-结束注意]除了转换类型的prvalue之外“指向T1的指针”指向类型“指向T2的指针”(其中T1和T2是函数类型),返回到其原始类型将生成原始指针值,此类指针转换的结果未指定。[注:有关指针转换的更多详细信息,请参见[conv.ptr]。-结束注]


迂腐地说,至少要打破严格的别名规则,这会导致UB。你能改为创建一个代理吗?这意味着包装器对象包含
X_leg
,可以将调用委托给旧的API,使旧的结构和API不可见,但没有潜在的布局问题。作为hack(因为一旦定义/令牌不同,您可能就会违反ODR),您可以在包装器头中复制/粘贴
X_leg
定义。@Scis:创建包装器对象到底是什么意思?@Jarod42,是的,我想阻止这种黑客行为。但这可能是最后一种可能性