2012-01-19 8 views
13

Ho bisogno di un tipo di tratto che riporti il ​​tipo di parametro operator() del funtore in base al tipo di funtore e al tipo di argomento passato. Fondamentalmente, ho bisogno di determinare con precisione di che tipo sarà convertito l'argomento quando lo si passa al functor. Per semplicità, supponiamo che io sia interessato solo a un (potenzialmente tentato, potenzialmente sovraccarico) operator() con un singolo argomento. Sfortunatamente, sono limitato a C++ 03. Può essere fatto? Altrimenti, che ne dici di C++ 11?Come determinare il tipo di un parametro di funzione dato il tipo di argomento passato ad esso?

Ecco un esempio:

#include <cassert> 
#include <type_traits> 

template<typename Functor, typename Argument> 
    struct parameter_type 
{ 
    // what goes here? 
    typedef ... type; 
}; 

struct takes_float_cref 
{ 
    void operator()(const float &); 
}; 

int main() 
{ 
    // when calling takes_float_cref::operator() with an int, 
    // i'd expect a conversion to const float & 
    assert(std::is_same(parameter_type<takes_float_cref, int>::type, const float &>::value); 

    return 0; 
} 

A related question (la cui risposta non mi dà abbastanza quello che mi serve) fornisce il contesto per che necessitano di tale caratteristica. Ho effettuato ulteriori test unitari su ideone.

+0

Non puoi semplicemente aggiungere lo stesso 'typedef' all'interno di ogni functor? Qualcosa come 'typedef float const e TParameter;' all'interno di 'takes_float_cref' e quindi testare per' Functor :: TParameter'? – lapk

+0

@AxxA, grazie per il suggerimento. Sì, questa è una soluzione, ma trovo pesante da richiedere quella dei clienti. Non sono sicuro che sarebbe in grado di adattarsi bene al caso generale dei parametri N. –

+0

Beh, in realtà potrebbe non essere poi così male. È troppo lungo da scrivere qui, lo posterò come risposta. Vedi se ti aiuta. – lapk

risposta

1

Per iniziare vorrei andare con questo:

template<typename F> 
struct parameter_type_impl; 

// may be with variadic arguments 
template<typename R, typename A, typename F> 
struct parameter_type_impl<R (F::*)(A)> { 
    typedef A type; 
}; 

template<typename F> 
struct parameter_type { 
    typedef typename parameter_type_impl<decltype(&F::operator())>::type type; 
}; 

non vedo perché si dovrebbe passare il tipo di argomento attuale. Se la conversione non è possibile, è necessario utilizzare le misure speciali (ad esempio SFINAE) in seguito. Penso che le due cose siano ortogonali: deducendo il tipo di argomento, quindi decidendo se l'argomento da convertire in è convertibile.

Il decltype non C++ 03 è difficile da eliminare. Specificando una funzione il tipo richiede sempre la conoscenza degli argomenti. Appena avresti spiegato gli argomenti, il tutto sarebbe stato discutibile.

Lo stesso problema si verificava con Boost.Function Types.

+1

Grazie. Mi aspetto che io debba passare il tipo di argomento effettivo per chiarire quale sovraccarico verrebbe invocato, e anche quale '' 'operatore()' '' sarebbe istanziato. –

+0

Oh, la tua domanda non ha nulla di sovraccarico. Se è così, hai già salpato per fallire. Le funzioni sovraccaricate devono sempre essere disambiguate da un 'static_cast' che richiede sempre all'utente di specificare il tipo. Quindi devi anche passare il tipo di reso. Questo diventa brutto molto velocemente senza C++ 11. – pmr

+0

Il mio male. Il codice che ho postato su ideone chiarisce tutti i casi che mi interessano. Brutto che posso affrontare, voglio solo sapere se è possibile o meno :) –

1
#include <iostream> 

    template< typename PParameter00 = void, typename PParameter01 = void, typename PParameter02 = void, typename PParameter03 = void > 
    struct TIdentityParameter // Users need to inherit from it. Add more types as needed. 
    { 
     typedef PParameter00 TType00; 
     typedef PParameter01 TType01; 
     typedef PParameter02 TType02; 
     typedef PParameter03 TType03; 
    }; 

    struct TUserFunctor00 : public TIdentityParameter< float const &, int, void * > 
    { 
     void operator()(float const &, int, void *); 
     // or they can do 
     //void operator()(TType00, TType01, TType02); 
    }; 

    struct TUserFunctor01 : public TIdentityParameter< char const *, double > 
    { 
     void operator()(char const*, double); 
     // or they can do 
     //void operator()(TType00, TType01); 
    }; 

    template< bool pValue > 
    struct TValueBool 
    { 
     static bool const sValue = pValue; 
    }; 

    template< typename PType00, typename PType01 > 
    struct TIsSame : public TValueBool<false> 
    { 
    }; 

    template< typename PType > 
    struct TIsSame< PType, PType > : public TValueBool<true> 
    { 
    }; 

    int main(void) 
    { 
    std::cout << TIsSame< TUserFunctor00::TType02, void * >::sValue << std::endl; 
    std::cout << TIsSame< TUserFunctor01::TType00, double >::sValue << std::endl; 

    return (0); 
    } 

Code on [ideone][1]. I don't think it's asking too much from users to inherit from your struct in a pattern explained to them. After all, they want to work with your library. Anyway, maybe it's not what you are looking for. 

+++++++++++++++++++++++++++++++++++++++++++ +++++++

EDIT: Ecco qualcosa, forse, un po 'più vicino alla funzionalità che JAred sta cercando, ma, ho capito, lo stile non lo attrae. Sebbene, all'interno di C++ 03, non vedo come si possa fare diversamente. Nota, puoi prendere TIdentityParameter prendere, diciamo 16 argomenti del template per coprire 16 possibili tipi. Ancora una volta, sì, l'utente deve ereditare e specificare i tipi. Ideone:

#include <iostream> 

struct TOneCrazyStruct 
{ 
}; 

template< typename PParameter00 = TOneCrazyStruct, typename PParameter01 = TOneCrazyStruct, typename PParameter02 = TOneCrazyStruct, 
    typename PParameter03 = TOneCrazyStruct, typename PParameter04 = TOneCrazyStruct > 
struct TIdentityParameter //Users will need to inherit from this struct as shown below. 
{ 
    typedef PParameter00 TType00; 
    typedef PParameter01 TType01; 
    typedef PParameter02 TType02; 
    typedef PParameter03 TType03; 
    typedef PParameter04 TType04; 
}; 

struct TUserFunctor00 : public TIdentityParameter< float const &, int, void *, double > 
{ 
    void operator()(float const &, int, void *); 
    void operator()(double); 
}; 

template< bool pValue > 
struct TValueBool 
{ 
    static bool const sValue = pValue; 
}; 

template< typename PType00, typename PType01 > 
struct TIsSame : public TValueBool<false> 
{ 
}; 

template< typename PType > 
struct TIsSame< PType, PType > : public TValueBool<true> 
{ 
}; 

template< typename PFunctor, typename PParameter > 
struct THasType : public TValueBool< 
    TIsSame< typename PFunctor::TType00, PParameter >::sValue || TIsSame< typename PFunctor::TType01, PParameter >::sValue 
    || TIsSame< typename PFunctor::TType02, PParameter >::sValue || TIsSame< typename PFunctor::TType03, PParameter >::sValue > 
{ 
}; 

int main(void) 
{ 
std::cout << THasType< TUserFunctor00, void * >::sValue << std::endl; 
std::cout << THasType< TUserFunctor00, long double >::sValue << std::endl; 

return (0); 
} 
+0

Quindi, perché non ereditare solo da 'std :: binary_function' o' std :: unary_function' e averlo fatto? Anche questo non copre i sovraccarichi. – pmr

+0

@AzzA Grazie per il suggerimento, ma come note di pmr, l'intera ragione per questo esercizio è di evitare questo genere di cose. Inoltre, C++ 11 depreca '' 'std :: unary_function''' e' '' std :: binary_function'''. –

+0

@JaredHoberock Oh, beh, mi dispiace non ha aiutato. – lapk

2

temo che questo non è esattamente possibile senza l'aiuto di vostro cliente.

TL; DR: unit test fail (grrr gcc).

Il caso generale della vostra domanda è questa funtore:

struct Functor { 
    template <typename T> 
    typename std::enable_if<std::is_integral<T>::value>::type 
    operator()(T t) const; 

    void operator(double d) const; 
}; 

Esso combina le due questioni principali qui:

  1. Se c'è un sovraccarico, poi prendendo &F::operator() richiede un static_cast ad una data digitare per disambiguare quale sovraccarico deve essere utilizzato
  2. I modelli (e le condizioni arbitrarie per esprimerli) non possono essere espressi succintamente come s

Pertanto, il client (Functor qui) deve fornire ganci aggiuntivi se si desidera veramente ottenere questo tipo. E senza decltype non vedo come ottenerlo (nota, gcc fornisce typeof come estensione in C++ 03).

Ottenere il cliente a darci suggerimenti:

// 1. Make use of the return value: 
struct Functor { 
    template <typename T> 
    typename std::enable_if<std::is_integral<T>::value, T>::type 
    operator()(T t) const; 

    double operator(double d) const; 
}; 

// 2. Double up the work (but leave the return value as is) 
struct Functor { 
    template <typename T> 
    static typename std::enable_if<std::is_integral<T>::value, T>::type Select(T); 

    static double Select(T); 

    template <typename T> 
    typename std::enable_if<std::is_integral<T>::value>::type 
    operator()(T t) const; 

    void operator(double d) const; 
}; 

Diciamo che andiamo per il secondo caso (lasciando il valore di ritorno libero per un altro uso).

template <typename F, typename T> 
struct parameter { 
    static T t; 
    typedef decltype(F::Select(t)) type; 
}; 

In C++ 03, sostituire decltype da typeof con gcc.

Non vedo un modo per annullare decltype. sizeof fornisce un contesto non valutato ma sembra che non aiuti molto qui.

Unit Tests Here.

Purtroppo, c'è un bug gcc sembra con i riferimenti, e float& viene ridotto a float (e qualsiasi altro riferimento in realtà), il bug rimane con decltype quindi è solo un'implementazione buggy:/Clang 3.0 non ha alcun problema con la la versione C++ 11 (decltype) ma non implementa typeof penso.

Questo può essere aggirato richiedendo al client di utilizzare una classe ref<float> e quindi di srotolarlo. Solo un po 'più di peso ...

+0

Si noti che uno dei test unitari è apparentemente buggato: il test di 'takes_float_cref' è scritto come se si aspettasse un' const di barre & '. Come sarebbe mai passato? – Xeo

+0

@Xeo: esatto, l'uso di "assert" significa che non andiamo mai così lontano. Cercherò il test –

+0

@Xeo: test patch, sono andato per errore in fase di compilazione e mostra che 'gcc' ignora solo il riferimento di livello superiore: x –

Problemi correlati