首页 > 解决方案 > C++-17:将函数指针转换为具有不同参数指针类型的函数

问题描述

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

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

我现在的问题是:将reinterpret_casttype 的包装函数指针指向具有 typefunc_wrp的遗留函数指针是否安全func_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

标签: c++c++17function-pointers

解决方案


不,这是不安全的,并且在[expr.call]/6中被明确称为未定义行为

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

reinterpret_cast这在有关函数指针转换 的文档中也得到了加强[expr.reinterpret.cast]/6

函数指针可以显式转换为不同类型的函数指针。[注:通过指向与函数定义中使用的类型不同的函数类型([dcl.fct])的指针调用函数的效果是未定义的([expr.call])。— 尾注] 除了将类型为“指向 T1 的指针”的纯右值转换为类型“指向 T2 的指针”(其中 T1 和 T2 是函数类型)并返回其原始类型会产生原始指针值,这样的结果未指定指针转换。[ 注意:有关指针转换的更多详细信息,另请参阅 [conv.ptr]。——尾注]


推荐阅读