2017-10-08 9 views
0

Je suis en train de lire le design C++ moderne d'Andrei Alexandrescu et j'essaie d'utiliser certains des exemples de liste de types qu'il a donnés. Dans l'exemple ci-dessous, je veux créer une liste de struct Option qui contient un type et un entier. Plus tard, je veux créer une liste de types de ces options, puis passer à une autre struct FindTypeForMapping avec un entier. Si l'entier correspond à l'un des entiers définis dans la liste des options, l'expression doit alors correspondre au type de cette option, sinon elle doit correspondre à mon type personnalisé NullType.Utilisation du pack de paramètres de template au lieu de la macro

Première approche qui fonctionne est que le OptionsList est créé avec des macros, et je macros pour chaque numéro de Option s que la liste contient, où chaque macro utilise la macro pour nOption s pour n-1Option s. Puis j'ai voulu utiliser un pack de paramètres dans les paramètres du template pour la liste. Cette version de la liste est nommée OptionsList2. Dans OptionsList2 je récursivement construire la liste, mais ensuite j'obtiens une erreur de temps de compilation (voir ci-dessous) lors du passage de cette liste à FindTypeForMapping.

struct NullType { }; 

template<class T, class U> 
struct OptionsList 
{ 
    typedef T Head; 
    typedef U Tail; 
}; 

template<class T, class... U> 
struct OptionsList2 
{ 
    typedef T Head; 
    typedef typename std::conditional<sizeof...(U) == 0, NullType, OptionsList2<U...>>::type Tail; 
}; 

template<int n, typename N> 
struct Option 
{ 
    enum { 
    int_mapping = n 
    }; 
    typedef N MappedType; 
}; 

template<int, int> struct CheckMappedInt; 

template<int n> 
struct CheckMappedInt<n, n> 
{ 
    enum { is_the_same = 1}; 
}; 

template<int n, int m> 
struct CheckMappedInt 
{ 
    enum { is_the_same = 0}; 
}; 

template<typename OLT, int n> struct FindTypeForMapping; 

template<int n> 
struct FindTypeForMapping<NullType, n> 
{ 
    typedef NullType mapped_type; 
}; 


template<typename OP, typename Tail, int n> 
struct FindTypeForMapping<OptionsList<OP, Tail>, n> 
{ 
private: 
    enum {temp = CheckMappedInt<OP::int_mapping, n>::is_the_same }; 
    typedef typename FindTypeForMapping<Tail, n>::mapped_type temp_type; 
public: 
    typedef typename std::conditional< 
      temp == 1, 
      typename OP::MappedType, 
      temp_type>::type mapped_type; 
}; 

// Added this after SoryTellers comment 
template<typename OP, typename Tail, int n> 
struct FindTypeForMapping<OptionsList2<OP, Tail>, n> 
{ 
private: 
    enum {temp = CheckMappedInt<OP::int_mapping, n>::is_the_same }; 
    typedef typename FindTypeForMapping<Tail, n>::mapped_type temp_type; 
public: 
    typedef typename std::conditional< 
      temp == 1, 
      typename OP::MappedType, 
      temp_type>::type mapped_type; 
}; 

#define OPTION_LIST_1(op1)           OptionsList<op1, NullType> 
#define OPTION_LIST_2(op1, op2)          OptionsList<op1, OPTION_LIST_1(op2)> 
#define OPTION_LIST_3(op1, op2, op3)        OptionsList<op1, OPTION_LIST_2(op2, op3)> 
#define OPTION_LIST_4(op1, op2, op3, op4)       OptionsList<op1, OPTION_LIST_3(op2, op3, op4)> 
#define OPTION_LIST_5(op1, op2, op3, op4, op5)      OptionsList<op1, OPTION_LIST_4(op2, op3, op4, op5)> 
#define OPTION_LIST_6(op1, op2, op3, op4, op5, op6)     OptionsList<op1, OPTION_LIST_5(op2, op3, op4, op5, op6)> 
#define OPTION_LIST_7(op1, op2, op3, op4, op5, op6, op7)   OptionsList<op1, OPTION_LIST_6(op2, op3, op4, op5, op6, op7)> 
#define OPTION_LIST_8(op1, op2, op3, op4, op5, op6, op7, op8, op9) OptionsList<op1, OPTION_LIST_7(op2, op3, op4, op5, op6, op7, op8)> 
#define OPTION_LIST_9(op1, op2, op3, op4, op5, op6, op7, op8, op9) OptionsList<op1, OPTION_LIST_8(op2, op3, op4, op5, op6, op7, op8, op9)> 


int main(int argc, char* argv[]) 
{ 
    typedef Option<1, char> o1; 
    typedef Option<2, int> o2; 

    // Works 
    typedef OPTION_LIST_2(o1, o2) ol; 
    typedef typename FindTypeForMapping<ol, 1>::mapped_type ResolvedType; // Works 

    typedef OptionsList2<o1, o2> ol2; 
    typedef typename FindTypeForMapping<ol2, 1>::mapped_type ResolvedType2; 
    /* 
error: invalid use of incomplete type ‘struct FindTypeForMapping<Option<2, int>, 1>’ 
    typedef typename FindTypeForMapping<Tail, n>::mapped_type temp_type; 
    */  
} 
+1

Quel que soit le degré de complexité du message d'erreur, vous comprenez sûrement qu'il n'y a pas de spécialisation de 'FindTypeForMapping' qui accepte un' OptionsList2', seulement 'OptionsList'. Ce sont des modèles ** différents **! – StoryTeller

+0

Oui, merci. J'ai ajouté une copie de 'FindTypeMapping' acceptant un' OptionsList2'. Toujours une erreur mais légèrement différente. /* erreur: utilisation non valide de type incomplet « struct FindTypeForMapping

+0

IMO ça va mal. Au lieu de dupliquer des choses, il suffit de se concentrer sur la suppression de la macro. Ajoutez une méta-fonction 'MakeOptionList ' qui expose un 'OptionsList' comme son' :: type'. Ensuite, vous allez vous débarrasser de la macro. – StoryTeller

Répondre

1

Désolé, mais ... pourquoi ne pas utiliser simplement std::tuple à la place un OptionList2 variadique?

Vos traits de type FindTypeForMapping peuvent être écrits simplement (désolé si je court le nom dans FTFM)

template <typename, int> 
struct FTFM; 

template <int n, int no, typename TypeO, typename ... Ts> 
struct FTFM<std::tuple<Option<no, TypeO>, Ts...>, n> 
{ using type = typename FTFM<std::tuple<Ts...>, n>::type; }; 

template <int n, typename TypeO, typename ... Ts> 
struct FTFM<std::tuple<Option<n, TypeO>, Ts...>, n> 
{ using type = TypeO; }; 

template <int n> 
struct FTFM<std::tuple<>, n> 
{ using type = NullType; }; 

Ce qui suit est un travail complet (bien ... compilation) exemple

#include <tuple> 
#include <type_traits> 

struct NullType 
{ }; 

template <int n, typename T> 
struct Option : public std::integral_constant<int, n> 
{ using type = T; }; 

template <typename, int> 
struct FTFM; 

template <int n, int no, typename TypeO, typename ... Ts> 
struct FTFM<std::tuple<Option<no, TypeO>, Ts...>, n> 
{ using type = typename FTFM<std::tuple<Ts...>, n>::type; }; 

template <int n, typename TypeO, typename ... Ts> 
struct FTFM<std::tuple<Option<n, TypeO>, Ts...>, n> 
{ using type = TypeO; }; 

template <int n> 
struct FTFM<std::tuple<>, n> 
{ using type = NullType; }; 

template <typename T, int I> 
using FTFM_t = typename FTFM<T, I>::type; 

int main() 
{ 
    using opt0 = Option<0, void>; 
    using opt1 = Option<1, char>; 
    using opt2 = Option<2, short>; 
    using opt3 = Option<3, int>; 
    using opt4 = Option<4, long>; 
    using opt5 = Option<5, long long>; 

    using optList = std::tuple<opt0, opt1, opt2, opt3, opt4, opt5>; 

    static_assert (std::is_same<void,  FTFM_t<optList, 0>>{}, "!"); 
    static_assert (std::is_same<char,  FTFM_t<optList, 1>>{}, "!"); 
    static_assert (std::is_same<short,  FTFM_t<optList, 2>>{}, "!"); 
    static_assert (std::is_same<int,  FTFM_t<optList, 3>>{}, "!"); 
    static_assert (std::is_same<long,  FTFM_t<optList, 4>>{}, "!"); 
    static_assert (std::is_same<long long, FTFM_t<optList, 5>>{}, "!"); 
    static_assert (std::is_same<NullType, FTFM_t<optList, 6>>{}, "!"); 
} 
+0

Merci. Belle solution, qui supprime OptionsList et CheckedMappedInt. – Andreas