2015-04-06 17 views
6

Considerare un'ipotetica metafunzione arity, che accetta qualsiasi metafunzione come argomento e restituisce l'arit effettivo.Come introspezionare l'arità di un argomento modello template variadic?

Il seguente approccio ovvio non è possibile, poiché gli standard di linguaggio denominati parametri del modello di modello interno sono definiti solo localmente.

template<template<typename... args> class f> 
struct arity 
{ 
    static constexpr std::size_t value = sizeof...(args); //ERROR: undefined 'args' 
}; 

Nemmeno specializzazioni esaustivi sono un'alternativa, poiché un tipo di modello di prendere un altro tipo di modello non può essere parzialmente specializzato rispetto al numero di argomenti del modello interno.

Questo mi porta alla domanda, la cui risposta temo di essere no.

Esiste un modo ragionevole per analizzare l'arità effettiva di un tipo di modello?

non mi aspetto una effettiva attuazione arity per assumere la forma di un tipo di modello, come nel ovvio approccio, cioè, tutto ciò che può essere calcolato durante la fase di compilazione è accettabile come " ragionevole "soluzione, purché non dipenda dagli argomenti reali.

Nota: per semplicità si presuppone che solo metafunzioni non-variadic siano consentiti come argomenti per arity.

+1

ha un modello di primaria prendere un 'T', poi specializzarsi con' arity > '' dove Args ... 'è un parametro di modello. Quindi ottieni la dimensione con 'sizeof ... (Args)'. – 0x499602D2

+0

Sfortunatamente, ciò significa che non puoi passare solo il modello di classe, però. – chris

+0

@ 0x499602D2 come indicato da chris, sfortunatamente non è "ragionevole" nel senso della domanda originale, poiché gli argomenti reali potrebbero non essere disponibili al punto in cui è necessario l'arità di un tipo di modello. – brunocodutra

risposta

2

Mentre è vero che i tipi di modello che utilizzano i parametri del modello di modello potrebbero non essere parzialmente specializzati rispetto all'arità dei relativi parametri del modello di modello, le funzioni potrebbero essere sovraccariche in questo modo.

template<template<typename> class f> 
constexpr std::size_t _arity(){return 1;} 
template<template<typename, typename> class f> 
constexpr std::size_t _arity(){return 2;} 
template<template<typename, typename, typename> class f> 
constexpr std::size_t _arity(){return 3;} 
//... 
template<template<typename...> class f> 
constexpr std::size_t _arity(){return 0;} 

template<template<typename... args> class f> 
struct arity 
{ 
    static constexpr std::size_t value = _arity<f>(); 
}; 

Anche se non ideale, questo approccio funziona entro limiti ragionevoli ed è più vicino a una soluzione "ragionevole" che mi veniva in mente. Comunque sto ancora cercando una soluzione variadica pura che non richieda un'enumerazione esauriente di funzioni/tipi.

3
template<class...> struct foo; 
template<class X> struct foo<X>:std::true_type {}; 
template<class X, class Y, class Z> struct foo<X,Y,Z>:std::false_type {}; 

sotto qualsiasi abbinamento di modelli naive, foo ha un'aerea infinita.

In pratica, ha un'aria di 1 o 3.

In generale, la domanda "qual è l'aria di questo modello" è la domanda sbagliata. Piuttosto, "questi tipi possono essere passati a questo modello" o "quanti di questi tipi possono essere passati a questo modello" è più utile.

Cercare l'aria di un modello è come voler estrarre la firma da un oggetto callable. Se sai come chiamerai un oggetto, chiedendo "Posso chiamarlo in questo modo? è ragionevole; chiedere "dimmi come chiamarti" è quasi sempre fuorviato.

template<class...>struct types{using type=types;}; 
template<class types>struct types_length; 
template<class...Ts>struct types_length<types<Ts...>>: 
    std::integral_constant<size_t, sizeof...(Ts)> 
{}; 

template<class...>struct voider{using type=void;}; 
template<class...Ts>using void_t=typename voider<Ts...>::type; 

namespace details { 
    template<template<class...>class Z, class types, class=void> 
    struct can_apply : std::false_type {}; 

    template<template<class...>class Z, class...Ts> 
    struct can_apply<Z,types<Ts...>,void_t<Z<Ts...>>>: std::true_type {}; 
}; 
template<template<class...>class Z, class...Ts> 
struct can_apply : details::can_apply<Z,types<Ts...>> {}; 

Quanto sopra risponde alla domanda "Posso applicare alcuni tipi a un modello".

Ora, il più lungo prefisso di una serie di tipi è possibile applicare a un modello:

template<class T>struct tag{using type=T;}; 

namespace details { 
    template<class types, class=types<>> 
    struct pop_back {}; 
    template<class T0, class...rhs> 
    struct pop_back<types<T0>, types<rhs...>>:types<rhs...> {}; 
    template<class T0, class...Ts, class...rhs> 
    struct pop_back<types<T0, Ts...>, types<rhs...>>: 
    pop_back<types<T0,Ts...>,types<rhs...,T0>> 
    {}; 
    template<class types> 
    using pop_back_t = typename pop_back<types>::type; 
} 
template<class types> 
using pop_back = details::pop_back_t<types>; 

namespace details { 
    template<template<class...>class Z, class types, class=void> 
    struct longest_prefix {}; 
    template<template<class...>class Z, class...Ts> 
    struct longest_prefix< 
    Z,types<Ts...>, 
    std::enable_if_t<can_apply<Z,Ts...>> 
    >: 
    types<Ts...> 
    {}; 
    template<template<class...>class Z,class T0, class...Ts> 
    struct longest_prefix< 
    Z,types<T0, Ts...>, 
    std::enable_if_t<!can_apply<Z, T0, Ts...>> 
    >: 
    longest_prefix<Z,pop_back_t<types<T0,Ts...>>> 
    {}; 
} 
template<template<class...>class Z, class...Ts> 
using longest_prefix = 
    typename details::longest_prefix<Z, types<Ts...>>::type; 

namespace details { 
    template<class types> 
    struct pop_front; 
    template<> 
    struct pop_front<types<>> {}; 
    template<class T0, class...Ts> 
    struct pop_front<types<T0,Ts...>>:types<Ts...>{}; 
    template<class types> 
    using pop_front_t=typename pop_front<types>::type; 
} 

codice simile che prende un fascio di tipi e di un modello, e ripetutamente fette fuori il prefisso più lungo del fascio di tipi che possono essere passati al modello può essere scritto.

(Il codice sopra riportato contiene certamente errori di battitura).

template<class types> 
using pop_front = details::pop_front_t<types>; 
template<size_t n, template<class...>class Z, class T> 
struct repeat : repeat< n-1, Z, Z<T> > {}; 
template<template<class...>class Z, class T> 
struct repeat<0,Z,T> : tag<T> {}; 
template<size_t n, template<class...>class Z, class T> 
using repeat_t = typename repeat<n,Z,T>::type; 
template<template<class...>class Z, class types> 
using longest_prefix_tail = 
    repeat_t< 
    types_length<longest_prefix<Z,Ts...>>{}, 
    pop_front, 
    types<Ts...> 
    >; 

ora si può prendere un modello e un sacco di tipi, e costruire un fascio di tipi derivanti dall'applicazione del modello per il più lungo prefisso del gruppo di tipi a sua volta.

Se siamo pazzi, potremmo persino fare il backtracking, in modo che se il nostro modello prende 2 o 3 elementi e lo alimentiamo 4, non proverebbe ad alimentarlo 3, quindi fallisce quando rimane 1 elemento - - Invece, potrebbe trovare il prefisso più lungo di ogni applicazione che consente alla coda di essere raggruppata in modo simile.

+0

Questo è molto interessante. Non ho pensato a questo modo di testare se alcuni modelli possono accettare un certo numero di argomenti. Il tuo 'can_apply' può quindi essere usato ricorsivamente per verificare se qualche template può far fronte a un numero crescente di argomenti non specificati, mai istanziato, naturalmente, fino a quando fallisce, quindi la sua arità sarebbe n - 1. Naturalmente dovrà essere specializzato per il caso variadico, che altrimenti reciterebbe il compilatore all'oblio. Se aggiorni la tua risposta per rendere chiaro questo approccio, lo accetterò volentieri. – brunocodutra

0

È il mio approccio a questo problema. Calcola l'arit del modello sostituendo i tipi falsi.

is_subs_success controlla se è possibile sostituire i tipi di modello variadic:

#include <boost/mpl/assert.hpp> 

#include <boost/mpl/bool.hpp> 
#include <boost/mpl/integral_c.hpp>  
#include <boost/mpl/identity.hpp> 
#include <boost/mpl/void.hpp> 

#include <boost/mpl/eval_if.hpp> 

using namespace boost; 

/* 
    is_subs_success<F, T...>::value == false 
    ==> F<T...> causes a compile error 
*/ 
template 
< 
    template<typename... FuncArgs> class Func, 
    typename... SubsArgs 
> 
class is_subs_success { 

    typedef int success[1]; 
    typedef int failure[2]; 

    // if it's not possible to substitute overload 
    template<typename...> 
    static failure& test(...); 

    // if it's possible to substitute overload 
    template<typename... U> 
    static success& test(typename mpl::identity<Func<U...> >::type*); 

public: 

    typedef is_subs_success<Func, SubsArgs...> type; 

    static bool const value = 
    sizeof(test<SubsArgs...>(0)) == sizeof(success); 

}; 

arity calcola arity del modello. Sostituisce argomenti falsi nel modello. Se la sostituzione causa un errore di compilazione, continua con un altro argomento:

template 
< 
    template<typename... FuncArgs> class Func 
> 
class arity { 

    // Checks whether `U` is full set of `Func`'s arguments 
    template<typename... U> 
    struct is_enough_args; 

    // Adds one more argument to `U` and continues iterations 
    template<size_t n, typename... U> 
    struct add_arg; 

    template<size_t n, typename... SubsArgs> 
    struct go : mpl::eval_if 
     < 
     is_enough_args<SubsArgs...>, 
     mpl::integral_c<size_t, n>, 
     add_arg<n, SubsArgs...> 
     > {}; 

    template<typename... U> 
    struct is_enough_args : is_subs_success<Func, U...> {}; 

    template<size_t n, typename... U> 
    struct add_arg { 
    typedef typename 
     go<n + 1, mpl::void_, U...>::type type; 
    }; 

public: 

    typedef typename go<0>::type type; 

}; 

Questa soluzione funziona bene solo con i modelli. arity non restituisce mai 0.

controllo semplice:

template<typename A> 
struct t1 {}; 

template<typename A, typename B> 
struct t2 {}; 

template<typename A, typename B, typename C> 
struct t3 {}; 

int main() { 

    BOOST_MPL_ASSERT((mpl::bool_<arity<t1>::type::value == 1>)); 
    BOOST_MPL_ASSERT((mpl::bool_<arity<t2>::type::value == 2>)); 
    BOOST_MPL_ASSERT((mpl::bool_<arity<t3>::type::value == 3>)); 

} 
+0

si sta facendo affidamento su :: tipo in fase di definizione per Func <...>, ma in generale, F <...> non è nemmeno necessario definirlo. – brunocodutra

+0

Quasi lì, tuttavia, l'obiettivo non è quello di trovare il numero di argomenti che sono "sufficienti" per la sostituzione, ma il numero totale di argomenti. Si noti che questi concetti non sono equivalenti per modelli variadici o modelli con argomenti predefiniti. Lasciando da parte i modelli variadici, la strada da percorrere è aumentare in modo ricorsivo il numero di argomenti finché non fallisce, non finché non funziona per la prima volta. – brunocodutra

+0

@brunocodutra Sembra che abbia frainteso la domanda. Forse farò qualcosa con esso più tardi. Grazie :) – wowofbob

Problemi correlati