0

I am reading Modern C++ design by Andrei Alexandrescu and I am trying to use some of the type list examples that he his giving. In the example below I want to create a list of a Option struct that holds a type and an integer. Later I want to create a typelist of those Options and then pass that to a another struct FindTypeForMapping along with an integer. If the integer match any of the integer set in the list of Options, then the expression should evaluate to the type for that Options, otherwise it should evaluate to my custom type NullType.

First approach that works is that the OptionsList is created with macros, and there I have macros for each number of Options that the list holds, where each macro for n Options is using the macro for n-1 Options.

Then I wanted to use a parameter pack in the template parameters to the list. This version of the list is named OptionsList2. In OptionsList2 I recursively build up the list, but then I get an compilation time error (see below) when passing this list to 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;
  */    
}
max66
  • 65,235
  • 10
  • 71
  • 111
tuple_cat
  • 1,165
  • 2
  • 7
  • 22
  • 1
    Regardless of how complicated the error message is, surely you understand there is no specialization of `FindTypeForMapping` that accepts an `OptionsList2`, only `OptionsList`. Those are **different** templates! – StoryTeller - Unslander Monica Oct 08 '17 at 21:31
  • Yes, thanks. I added a copy of `FindTypeMapping` accepting a `OptionsList2`. Still an error but slightly different. /* error: invalid use of incomplete type ‘struct FindTypeForMapping – tuple_cat Oct 08 '17 at 21:54
  • IMO that's going about it the wrong way. Instead of duplicating stuff, just focus on getting rid of the macro. Add a `MakeOptionList` meta-function that exposes an `OptionsList` as its `::type`. Then you'll get rid of the macro. – StoryTeller - Unslander Monica Oct 08 '17 at 21:58
  • Ok, something like: `template struct MakeOptionsList { typedef OptionsList type; };` ? – tuple_cat Oct 08 '17 at 22:22
  • Not quite. It should be recursive, like the macro definition. You can work it out. – StoryTeller - Unslander Monica Oct 08 '17 at 22:24
  • ok, I understand but I can't figure it out. I'm gonna go with @max66 answer which removes many unnecessary things that I have above using already existing std structs. – tuple_cat Oct 09 '17 at 10:54

1 Answers1

1

Sorry but... why don't you simply use std::tuple instead a variadic OptionList2 ?

Your FindTypeForMapping type traits can be written simply as (sorry if I short the name in 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; };

The following is a full working (well... compiling) example

#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>>{}, "!" );
 }
max66
  • 65,235
  • 10
  • 71
  • 111