2011-11-22 2 views
6

Considerate Ho una dichiarazioni di funzioni come questi:Recuperare gli argomenti della funzione come una tupla in C++

void foo(int x, float y); 

class X { 
    void anotherFoo(double a, int c); 
}; 

Come posso ottenere una tupla che corrisponde gli argomenti della funzione? Nel caso di cui sopra sarebbe:

boost::tuple<int, float> 
boost::tuple<X*, double, int> 

o meglio ancora con il risultato tipo di elemento come 0a:

boost::tuple<void, int, float> 
boost::tuple<void, X*, double, int> 

so che boost::function_types::parameter_types può fare esattamente questo. Tuttavia, sono interessato al principio di come viene implementato.

+0

Vorrei usare una 'struct'. Qual è lo scopo/vantaggio dell'uso di tuple? – kol

+0

@kol less codice boilerplate, meno rumore – daramarak

+0

Vuoi dire che vuoi generare automaticamente il tipo di tupla in fase di compilazione o in fase di esecuzione, o se vuoi estrarre i parametri dal regs/stack, o da entrambi, o qualcos'altro? – Rup

risposta

9

È possibile ottenere la tupla tipo corrispondente al vostro argomento tipi, in questo modo:

template <typename R, typename... T> 
std::tuple<T...> function_args(R (*)(T...)) 
{ 
    return std::tuple<T...>(); 
} 

// get the tuple type 
typedef decltype(function_args(foo)) FooArgType; 
// create a default-initialised tuple 
auto args = function_args(foo); 

E 'questo che vuoi? Nota potrebbe essere necessario aggiungere uno o più overload di function_args, ad es. prendendo un parametro di tipo classe per i metodi di classe.

+0

Il 'decltype' funziona ancora se la funzione non può essere istanziata perché uno o più dei suoi tipi di parametro non possono essere inizializzato di default? E se no, c'è qualche soluzione con un puntatore che lo farebbe? –

+0

Puoi ancora usare il 'decltype (function_args (foo))' in un typedef o qualsiasi altra cosa, ma non puoi mai veramente _call_ 'function_args 'in questo caso. – Useless

+0

Funziona, grazie. C'è un modo per evitare decltype (dato che questo è solo C++ 11?) Boost funziona anche su compilatori non C++ 11, quindi questo dovrebbe essere possibile in qualche modo .. –

2

Finalmente trovato un modo come farlo in C++ 03 usando la specializzazione parziale. Un sacco di overload per diverso numero di argomenti e funzioni const/volatili sono necessarie ma l'idea è seguente: Utilizzo

/* An empty template struct, this gets chosen if the given template parameter is not a member function */ 
template <typename _Func> 
struct MemberFunctionInfo { }; 

/* Specialization for parameterless functions */ 
template <typename _Result, typename _Class> 
struct MemberFunctionInfo<_Result (_Class::*)()> { 
    typedef _Class class_type; 
    typedef _Result result_type; 
    typedef boost::tuple<> parameter_types; 
    enum { arity = 0 }; 
}; 

/* Specialization for parameterless const functions */ 
template <typename _Result, typename _Class> 
struct MemberFunctionInfo<_Result (_Class::*)() const> : MemberFunctionInfo<_Result (_Class::*)()> { }; 

/* Specialization for functions with one parameter */ 
template <typename _Result, typename _Class, typename P0> 
struct MemberFunctionInfo<_Result (_Class::*) (P0)> { 
    typedef _Class class_type; 
    typedef _Result result_type; 
    typedef boost::tuple<P0> parameter_types; 
    enum { arity = 1 }; 
}; 

/* Specialization for const functions with one parameter */ 
template <typename _Result, typename _Class, typename P0> 
struct MemberFunctionInfo<_Result (_Class::*) (P0) const> : MemberFunctionInfo<_Result (_Class::*) (P0)> { }; 

. 
. 
. 

Esempio:

template <typename MemFunc> 
int getArity(MemFunc fn) { 
    // Can also use MemberFunctionInfo<MemFunc>::parameter_types with boost::mpl 
    return MemberFunctionInfo<MemFunc>::arity; 
} 

La soluzione sopra ha alcuni difetti. Non gestisce i riferimenti alle funzioni, le funzioni non membri o le funzioni membro volatile/const volatile, ma è facile tenerne conto aggiungendo ulteriori specializzazioni.

Per C++ 11, l'approccio menzionato da @Useless è molto più pulito e dovrebbe essere preferito.