Adi*_*vit 10 c++ function-pointers cdecl calling-convention stdcall
我有一个第三方C API,需要一个__stdcall
回调函数.
我的代码有一个外部提供的 __cdecl
回调函数.
我无法将函数指针传递给C-API,因为它们被认为是不同的类型.
绕过类型系统并reinterpret_cast<>
自然地使用会导致运行时错误.
下面是一个例子在这里:
// C-API
// the stdcall function pointer type:
typedef CTMuint(__stdcall *CTMwritefn)(const void *aBuf, CTMuint aCount, void *aUserData);
// A function needing the callback:
CTMEXPORT void __stdcall ctmSaveCustom(CTMcontext aContext, CTMwritefn aWriteFn, void *aUserData, int *newvertexindex);
^^^^^^^^^^^^^^^^^^^
//////////////////////////////////////////////////////////////////////////////
// C++
CTMuint __cdecl my_func(const void *aBuf, CTMuint aCount, void *aUserData);
// I want to call here:
ctmSaveCustom(context, my_func, &my_data, nullptr);
// ^^^^^^^
Run Code Online (Sandbox Code Playgroud)
有没有办法安全地将一个调用约定的函数转换和/或包装到另一个调用约定中?
我确实通过传递一个调用第二个捕获lambda的casted captureless-lambda来找到一种方法.第一个作为回调传递,第二个作为回调传递void* user_data
.这是有效的,并且是类型安全的.但对于看似如此简单的事情来说,这是令人费解的.
krz*_*zaq 11
您可以为不同的调用约定之间的转换创建一个包装器:
template<typename Func, Func* callback>
auto make_callback()
{
return &detail::callback_maker<Func, callback>::call;
}
Run Code Online (Sandbox Code Playgroud)
与callback_maker
定义为
template<typename T, T*>
struct callback_maker;
template<typename R, typename... Params, R(*Func)(Params...)>
struct callback_maker<R(Params...), Func>
{
static R __stdcall call(Params... ps)
{
return Func(std::forward<Params>(ps)...);
}
};
Run Code Online (Sandbox Code Playgroud)
这是一个相当通用的解决方案,允许您指定函数原型.您可以按如下方式使用它:
// external_api(¬_stdcall_func); // error
external_api(make_callback<void(int,int), ¬_stdcall_func>());
Run Code Online (Sandbox Code Playgroud)
如果要在运行时确定指针,则可以将回调保留在用户数据中.你必须正确地管理它的生命周期,但你可能已经需要这样做了.再次,尝试通用的解决方案.进行回调并告诉它哪个参数是用户数据指针:
template<typename Callback, size_t N>
auto make_callback()
{
using callback_maker = detail::callback_maker<Callback, N>;
return &callback_maker::call;
}
Run Code Online (Sandbox Code Playgroud)
随着callback_maker
定义为
template<typename T, size_t N>
struct callback_maker;
template<typename R, typename... Params, size_t N>
struct callback_maker<R(*)(Params...), N>
{
using function_type = R(Params...);
static R __stdcall call(Params... ps)
{
void const* userData = get_nth_element<N>(ps...);
auto p = static_cast<pair<function_type*, void*> const*>(userData);
return p->first(ps...);
}
};
Run Code Online (Sandbox Code Playgroud)
而get_nth_element
作为
template<size_t N, typename First, typename... Ts>
decltype(auto) get_nth_element_impl(false_type, First&& f, Ts&&...);
template<size_t N, typename First, typename... Ts>
decltype(auto) get_nth_element_impl(true_type, First&&, Ts&&... ts)
{
return get_nth_element_impl<N-1>(integral_constant<bool, (N > 1)>{}, forward<Ts>(ts)...);
}
template<size_t N, typename First, typename... Ts>
decltype(auto) get_nth_element_impl(false_type, First&& f, Ts&&...)
{
return forward<First>(f);
}
template<size_t N, typename... Ts>
decltype(auto) get_nth_element(Ts&&... ts)
{
return get_nth_element_impl<N>(integral_constant<bool, (N > 0)>{}, forward<Ts>(ts)...);
}
Run Code Online (Sandbox Code Playgroud)
现在,在通话网站上
using callback_t = CTMuint(*)(const void *aBuf, CTMuint aCount, void *aUserData);
auto runtime_ptr = ¬_stdcall_func;
pair<callback_t, void*> data;
data.first = runtime_ptr;
data.second = nullptr; // actual user data you wanted
auto callback = make_callback<callback_t, 2>();
ctmSaveCustom({}, callback, &data, nullptr);
Run Code Online (Sandbox Code Playgroud)
根据Andrey Turkin的建议,您可以替换参数列表中的用户数据指针.与此同时forward_as_tuple
,它不再需要get_nth_element
.升级的通话功能:
static R __stdcall call(Params... ps)
{
auto params_tuple = forward_as_tuple(ps...);
void const* userData = get<N>(params_tuple);
auto p = static_cast<pair<function_type*, void*> const*>(userData);
get<N>(params_tuple) = p->second;
return apply(p->first, move(params_tuple));
}
Run Code Online (Sandbox Code Playgroud)
这里是C++ 17的简单实现apply
:
template<typename Func, typename T, size_t... Is>
decltype(auto) apply_impl(Func f, T&& t, index_sequence<Is...>)
{
return f(get<Is>(t)...);
}
template<typename Func, typename... Ts>
decltype(auto) apply(Func f, tuple<Ts...>&& tup)
{
return apply_impl(f, move(tup), index_sequence_for<Ts...>{});
}
Run Code Online (Sandbox Code Playgroud)