2011-01-22 9 views
11

Sto provando a scrivere una metafunzione di nome signature_of che, dato il tipo di funzione (puntatore), functor o lambda, restituisce la sua firma.Come ottenere la firma di un'espressione di collegamento C++ (...)

Ecco quello che ho finora:

#include <boost/mpl/pop_front.hpp> 
#include <boost/mpl/push_front.hpp> 
#include <boost/function_types/is_member_function_pointer.hpp> 
#include <boost/function_types/function_type.hpp> 
#include <boost/function_types/result_type.hpp> 
#include <boost/function_types/parameter_types.hpp> 

#include <type_traits> 

template <typename F> 
struct signature_of_member 
{ 
    typedef typename boost::function_types::result_type<F>::type result_type; 
    typedef typename boost::function_types::parameter_types<F>::type parameter_types; 
    typedef typename boost::mpl::pop_front<parameter_types>::type base; 
    typedef typename boost::mpl::push_front<base, result_type>::type L; 
    typedef typename boost::function_types::function_type<L>::type type; 
}; 

template <typename F, bool is_class> 
struct signature_of_impl 
{ 
    typedef typename boost::function_types::function_type<F>::type type; 
}; 

template <typename F> 
struct signature_of_impl<F, true> 
{ 
    typedef typename signature_of_member<decltype(&F::operator())>::type type; 
}; 

template <typename F> 
struct signature_of 
{ 
    typedef typename signature_of_impl<F, std::is_class<F>::value>::type type; 
}; 

E 'piuttosto semplice, con la maggior parte del lavoro vero e proprio stato fatto dalla libreria boost :: function_types. L'idea generale è:

  • uso std :: is_class di discriminare tra le funzioni built-in (tra cui lambda) e funtori
  • per i tipi di funzioni built-in, uso boost :: :: function_types tipo_funzione per ottenere la sua firma
  • per funtori, ottenere il tipo di loro operatore(), ottenere la sua firma, e medico per rimuovere il "questo" parametro

questo funziona per le funzioni built-in:

int f(int); 
typedef signature_of<decltype(f)>::type Sig; // Sig is int(int) 

per lambda:

auto f = [](int) { return 0; } 
typedef signature_of<decltype(f)>::type Sig; // Sig is int(int) 

e per funtori:

struct A 
{ 
    int operator()(int); 
}; 
typedef signature_of<A>::type Sig; // Sig is int(int) 

Tuttavia, non funziona per bind() espressioni (che sono un caso particolare di funtori). Se provo questo:

#include <functional> 
int g(int); 
typedef signature_of<decltype(std::bind(g, 0))>::type Sig; 

ottengo un errore di compilazione:

In file included from test.cpp:3:0: 
signature_of.hpp: In instantiation of 'signature_of_impl< 
     _Bind<int (*(int))(int)>, true 
    >': 
signature_of.hpp:45:74: instantiated from 'signature_of< 
     _Bind<int (*(int))(int)> 
    >' 
test.cpp:21:52: instantiated from here 
signature_of.hpp:39:74: error: type of '& _Bind< 
     int (*)(int)({int} ...) 
    >::operator()' is unknown 

Il problema è che l'operatore() del funtore restituito da bind() è un modello, e quindi il suo tipo non può essere determinato.

È possibile ottenere la firma di un'espressione bind() in un altro modo?

+0

VC10 non può compilare questo, gli errori sono stupidi come al solito ('errore C2039: 'tipo': non è un membro di 'boost :: function_types :: result_type ''), non gli piace questa specializzazione: 'typedef typename signature_of_member :: tipo tipo; ' –

+0

@Andy T: come si converte in std :: function senza menzionare il parametro template di std :: function, che * è * la firma ? – HighCommander4

+0

sì, colpa mia, cancellazione ... –

risposta

11

Hai più problemi del fatto che l'operatore di un raccoglitore() ha un modello, ma ha anche un conteggio arbitrario dei parametri. Ricorda che dovresti essere in grado di invocare il risultato del binding con un numero qualsiasi di argomenti extra. Ad esempio:

int f(int); 

auto bound = boost::bind(f, _2); 

bound può ora essere chiamato con qualsiasi numero di argomenti di 2 o più, solo il secondo viene effettivamente trasmessa al funzione.

Quindi sostanzialmente, come un'altra risposta ha detto, questo oggetto non ha firma. La sua firma è definita solo da come viene utilizzata.

+0

Buon punto, in realtà non sapevo che si potesse usare bind() in questo modo. – HighCommander4

4

Se l'operatore è un modello, quindi non ha una firma.

+0

O guardando in modo leggermente diverso, non ha una firma univoca. 'operator()' può essere istanziato con diversi argomenti del template, e partecipa alla firma di quell'istanza di 'operator()'. –

+2

Sì, ma se g ha la firma int (int), quindi concettualmente, bind (g, 0) dovrebbe avere la firma int (void). Il fatto che l'implementazione std :: bind usi un operatore basato su modelli() è solo un dettaglio di implementazione. – HighCommander4

+0

@ HighCommander4: Concettualmente? Che importa? Il fatto è che non succederà. Se 'bind' produce un funtore con un operatore di template(), sei fregato, e puoi concettualizzare quello che vuoi, e non cambierà questo. – Puppy

Problemi correlati