Un approccio di metaprogrammazione. Innanzitutto, alcuni tratti di puntatore che cercano di conservare le informazioni sulla convenzione di chiamata:
template<class...>types {using type=types;};
enum class calling_convention {
cdecl,
clrcall,
stdcall,
fastcall,
thiscall,
vectorcall,
};
template<class Sig>
struct signature_properties;
template<class R, class...Args>
struct signature_properties {
using return_type = R;
using argument_types = types<Args...>;
};
template<class FuncPtr>
struct function_properties;
#define CAT_(A,B) A##B
#define CAT(A,B) CAT_(A,B)
#define CALLING_CONVENTION_SUPPORT(CONVENTION) \
template<class R, class... Args> \
struct function_properties< R(CAT(__, CONVENTION) *)(Args...) >: \
signature_properties<R(Args...)> \
{ \
using type = R(CAT(__, CONVENTION) *)(Args...) \
static const calling_convention convention = calling_convention::CONVENTION; \
static type from_pvoid(void const* pvoid) { \
return static_cast<type>(pvoid); \
} \
}
CALLING_CONVENTION_SUPPORT(cdecl);
CALLING_CONVENTION_SUPPORT(clrcall);
CALLING_CONVENTION_SUPPORT(stdcall);
CALLING_CONVENTION_SUPPORT(fastcall);
CALLING_CONVENTION_SUPPORT(thiscall);
CALLING_CONVENTION_SUPPORT(vectorcall);
#undef CAT
#undef CAT_
#undef CALLING_CONVENTION_SUPPORT
Macro ghiacciate. E grave eccessivo. E non testato. Ma tu hai l'idea.
successivo, un aiutante per fare il lavoro:
template<class FuncPtrType, class R, class Args>
struct helper;
template<class FuncPtrType, class R, class... Args>
struct helper<FuncPtrType, R, types<Args...>> {
FuncPtrType ptr;
R operator()(Args...args)const {
return ptr(std::forward<Args>(args)...);
}
helper(FuncPtrType p):ptr(p) {};
helper(helper const&)=default;
helper& operator=(helper const&)=default;
};
Perfetto inoltro nel aiutante sarebbe anche allettante.
Infine, usiamo la classe tratti sopra per far rimbalzare il lavoro da Entry
a helper
:
template<class FuncPtrType>
struct Entry:helper<
FuncPtrType,
typename signature_properties<FuncPtrType>::return_type,
typename signature_properties<FuncPtrTpye>::arguments
> {
using parent = helper<
FuncPtrType,
typename signature_properties<FuncPtrType>::return_type,
typename signature_properties<FuncPtrTpye>::arguments
>;
Entry(void const* pvoid):parent(static_cast<FuncPtrType>(pvoid)) {}
};
tranne includiamo un costruttore in Entry
di prendere una void const*
e in avanti il puntatore digitato per helper
.
Una modifica è che si esegue il cast da void*
al nostro tipo di funzione al primo punto in cui sappiamo che è il tipo di funzione, piuttosto che al punto che chiamiamo.
Ho pensato che ci fosse un problema tecnico con la memorizzazione dei puntatori di funzione in 'void *'? Certamente non su qualsiasi piattaforma VS2013, lo ammetterei. – Yakk
@Yakk, potrebbe essere dovuto alla dimensione dei puntatori di funzione (che può essere molto più grande di un puntatore normale), ma non sto facendo '& function', sto piuttosto prendendo l'indirizzo dalla tabella delle esportazioni e lo getto, che non dovrebbe causare danni – rev