2015-01-27 2 views
6

Attualmente ho un modello in questo modo:C'è un modo per suddividere un puntatore a un modello in una funzione?

template<typename func, typename ret, typename... args> class Entry{ 
public: 
    PVOID Address; 
    ret operator()(args...){ 
     return ((func) this->Address)(args...); 
    } 
}; 

e sto usando in questo modo:

Entry<int(*)(int), int, int> func; 
// ^func  ^ret ^args 
func.Address = (PVOID) 0xDEADC0DE; 
func(123); // calls 0xDEADC0DE with '123' as argument 

Tuttavia, mi chiedevo se è possibile avere solo questo:

Entry<int(*)(int)> func; 
// ^only specifying the function's prototype once instead of breaking it down 
func(123); 

Se ce l'ho, non sarei in grado di sovraccaricare operator() in quanto non riesco a dividere il tipo di puntatore di funzione in argomenti e restituire il tipo (in modo che io possa scrivere return_type operator()(args...)).

Esiste un modo per raggiungere questo obiettivo?

sto usando VS2013 Nov 2013 CTP

+1

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

+0

@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

risposta

5

È possibile farlo con una specializzazione in questo modo:

// Entry has one template argument 
template<typename func> class Entry; 

// and if it's a function type, this specialization is used as best fit. 
template<typename ret, typename... args> class Entry<ret(args...)>{ 
public: 
    PVOID Address; 
    ret operator()(args... a){ 
    return ((ret(*)(args...)) this->Address)(a...); 
    } 
}; 

int main() { 
    Entry<int(int)> foo; 
    foo.Address = (PVOID) 0xdeadc0de; 
    func(123); 
} 

di utilizzarlo con un tipo di puntatore a funzione, come nel tuo esempio (anche se mi piace il tipo di sintassi delle funzioni meglio), scrivere

//           here ------v 
template<typename ret, typename... args> class Entry<ret(*)(args...)>{ 

Addendum: una cosa mi è venuta quando ero fuori sempre la cena: c'è una (lieve) problema con il operator() tha t può interessare o meno: non si verificano problemi di inoltro con parametri che vengono passati per valore o per riferimento lvalue perché sono appena passati mentre sono passati (perché l'elenco degli argomenti è esattamente lo stesso per puntatore di funzione e operator()), ma se si prevede di utilizzare parametri di riferimento di rvalore, ciò non funziona in modo implicito. Per questo motivo,

Entry<int(int&&)> foo; 
foo(123); 

non viene compilato. Se si prevede di utilizzare questo con funzioni che richiedono riferimenti rvalue, operator() può essere risolto in questo modo:

ret operator()(args... a){ 
    //     explicit forwarding ----v 
    return ((ret(*)(args...)) this->Address)(std::forward<args>(a)...); 
} 
+0

Posso specificare una convenzione di chiamata con questo? Come 'Entry pippo;' AND con la sintassi del tipo? O devo usare un puntatore a funzione? – rev

+1

Puoi usare 'Entry foo;' con MSVC 2013. '__stdcall' non fa parte dello standard, quindi altri compilatori probabilmente non lo supportano. – Wintermute

+0

Incredibile. Ora, ho provato ad aggiungere un costruttore alla mia classe in questo modo: 'Entry foo (0xC0DE);', ma dice 'tipo incompleto non è permesso'. Sto usando la sintassi del tipo. Cosa mi manca? – rev

1

specializzazione parziale come mostrato nella @Wintermutes post è possibile.
Tuttavia, ciò che si sta cercando dovrebbe essere possibile senza di essa:

template <typename func> 
class Entry{ 
public: 
    PVOID Address; 

    template <typename... Args> 
    auto operator()(Args&&... args) 
    -> decltype(((func*) Address)(std::forward<Args>(args)...)) { 
     return ((func*) Address)(std::forward<Args>(args)...); 
    } 
}; 

L'argomento modello deve essere un tipo di funzione. Tuttavia, è possibile farlo funzionare con funzione e puntatore a funzione tipi con una leggera modifica nell'espressione ritorno: Invece di utilizzare func* come tipo di destinazione del cast, utilizzare typename std::remove_pointer<func>::type*, cioè

template <typename... Args> 
auto operator()(Args&&... args) 
-> decltype(((typename std::remove_pointer<func>::type*) Address)(std::forward<Args>(args)...)) { 
    return ((typename std::remove_pointer<func>::type*) Address)(std::forward<Args>(args)...); 
} 

Demo.

0

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.