Vous pouvez faire un wrapper pour la traduction entre les différentes conventions d'appel:
template<typename Func, Func* callback>
auto make_callback()
{
return &detail::callback_maker<Func, callback>::call;
}
avec callback_maker
défini comme
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)...);
}
};
Ce programme vise à être une solution assez générale, vous permettant de spécifier la fonction prototype. Vous pouvez l'utiliser comme suit:
// external_api(¬_stdcall_func); // error
external_api(make_callback<void(int,int), ¬_stdcall_func>());
demo
Si le pointeur doit être déterminé lors de l'exécution, vous pouvez garder le rappel dans les données de l'utilisateur. Vous devez gérer correctement la durée de vie de cette fonctionnalité, mais il est probable que vous en ayez déjà besoin. Encore une fois, tenter une solution générique. Faire un rappel et lui dire quel argument est le pointeur de données utilisateur:
template<typename Callback, size_t N>
auto make_callback()
{
using callback_maker = detail::callback_maker<Callback, N>;
return &callback_maker::call;
}
Avec callback_maker
défini comme
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...);
}
};
et get_nth_element
comme
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)...);
}
Maintenant, sur le site d'appel
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);
demo
Selon la suggestion de Andrey Turkin, vous pouvez remplacer le pointeur de données d'utilisateur dans la liste des paramètres. Avec forward_as_tuple
, il évite le besoin de get_nth_element
.La fonction d'appel amélioré:
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));
}
et est ici une implémentation simpliste de 17 de apply
C++:
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...>{});
}
demo
Pouvez-vous pas seulement créer une enveloppe cdecl qui transmettra l'appel à votre rappel réel? – krzaq
@krzaq: Comment feriez-vous cela? Ce n'est pas aussi évident que cela semble ... Je vais ajouter un exemple. –
Pouvez-vous - en dehors du pointeur de fonction - transmettre également des "données utilisateur" (généralement un "void *")? Sinon, une fonction wrapper va être un peu compliqué en ce qui concerne la sécurité des threads/plusieurs rappels. –