1

I have a class that receive variadic templates of the same type. Each of these types have a nested value that should be unique:

template <SomeEnum _se, int _val>
struct Pack
{
  enum {val_se = _se};
  enum {val = _val};
};

int main()
{
  TypeMe<Pack<A_SE, 1>, Pack<B_SE, 2>, Pack<C_SE, 3>> tm_abc; // OK
  TypeMe<Pack<A_SE, 1>, Pack<B_SE, 2>, Pack<A_SE, 3>> tm_aba; // Should fail (first and last Pack are templated with A_SE)

  (void)tm_abc;
  (void)tm_aba;
  return (0);
}

The entire test code :

#include <cstdio>

template <typename ... ArgPacks>
class TypeMe
{
public:
  TypeMe();
private:
  template <typename ... APs>
  void cycleAPs();

  template <typename AP>
  void cycleAP();
};

template <typename ... ArgPacks>
TypeMe<ArgPacks...>::TypeMe()
{
  // Maybe the static assertion should go here
  cycleAPs<ArgPacks...>();
}

template <typename ... ArgPacks>
template <typename ... APs>
void TypeMe<ArgPacks...>::cycleAPs()
{
  int _[] = {0, (cycleAP<APs>(), 0)...};
  (void)_;
  return ;
}

template <typename ... ArgPacks>
template <typename AP>
void TypeMe<ArgPacks...>::cycleAP()
{
  printf("SomeEnum = %d, Val = %d\n", static_cast<int>(AP::val_se), AP::val);
  return ;
}

enum SomeEnum
{
  A_SE,
  B_SE,
  C_SE,
  MAX
};

template <SomeEnum _se, int _val>
struct Pack
{
  enum {val_se = _se};
  enum {val = _val};
};

int main()
{
  TypeMe<Pack<A_SE, 1>, Pack<B_SE, 2>, Pack<C_SE, 3>> tm_abc; // OK
  TypeMe<Pack<A_SE, 1>, Pack<B_SE, 2>, Pack<A_SE, 3>> tm_aba; // Should fail (first and last Pack are templated with A_SE)

  (void)tm_abc;
  (void)tm_aba;
  return (0);
}

Is there a way, in C++0x, to check at compile time that each of the Pack::val_se are different ? or with C++11 ?

Thanks for reading

edit:

Same code but with @MadScientist answer, thanks

#include <cstdio>


template <typename ...Ts>
struct are_mutually_different;

template <typename T>
struct are_mutually_different<T>
{
  static const bool value = true;
};

template <typename T1, typename T2, typename ...Ts>
struct are_mutually_different<T1, T2, Ts...>
{
  static const bool value = (T1::val_se != T2::val_se) &&
                            are_mutually_different<T1, Ts...>::value &&
                            are_mutually_different<T2, Ts...>::value;
};


template <typename ... ArgPacks>
class TypeMe
{
public:
  TypeMe();
private:
  template <typename ... APs>
  void cycleAPs();

  template <typename AP>
  void cycleAP();
};

template <typename ... ArgPacks>
TypeMe<ArgPacks...>::TypeMe()
{
  static_assert(are_mutually_different<ArgPacks...>::value, "!"); // <3
  // Maybe the static assertion should go here
  cycleAPs<ArgPacks...>();
}

template <typename ... ArgPacks>
template <typename ... APs>
void TypeMe<ArgPacks...>::cycleAPs()
{
  int _[] = {0, (cycleAP<APs>(), 0)...};
  (void)_;
  return ;
}

template <typename ... ArgPacks>
template <typename AP>
void TypeMe<ArgPacks...>::cycleAP()
{
  printf("SomeEnum = %d, Val = %d\n", static_cast<int>(AP::val_se), AP::val);
  return ;
}

enum SomeEnum
{
  A_SE,
  B_SE,
  C_SE,
  MAX
};

template <SomeEnum _se, int _val>
struct Pack
{
  enum {val_se = _se};
  enum {val = _val};
};

int main()
{
  TypeMe<Pack<A_SE, 1>, Pack<B_SE, 2>, Pack<C_SE, 3>> tm_abc; // OK
//  TypeMe<Pack<A_SE, 1>, Pack<B_SE, 2>, Pack<A_SE, 3>> tm_aba; // Should fail (first and last Pack are templated with A_SE)

  (void)tm_abc;
//  (void)tm_aba;
  return (0);
}
shorty_ponton
  • 454
  • 4
  • 14
  • 1
    "C++0x" is not *really* a thing, it was just a working name for C++11. – Baum mit Augen Jun 30 '17 at 12:13
  • yeah I wasn't sure about this one :/ but one of my target platform need to be compile with -std=c++0x – shorty_ponton Jun 30 '17 at 12:16
  • 1
    That's an indicator for incomplete C++11 support, so expect some random breakage if you go the C++11 route. For instance, the streams' move semantics probably won't work. – Baum mit Augen Jun 30 '17 at 12:18
  • What have you attempted so far? – Vittorio Romeo Jun 30 '17 at 12:19
  • I tried to derivate from similar answers but I'm new to variadic templates and I kind of fail to bend them to my need. Best shot was this one :https://stackoverflow.com/questions/36933176/how-do-you-static-assert-the-values-in-a-parameter-pack-of-a-variadic-template but even if I had find a way to fit it to my need, this will not work with my compiler – shorty_ponton Jun 30 '17 at 12:22

2 Answers2

2

You can solve this problem recursively:

Given the types (T1, T2, T3, ...., Tn)

  1. T1::value != T2::value must be true
  2. T1::value != Ti::value must be true for i=3,...,n
  3. T2::value != Ti::value must be true for i=3,...,n

In code, you could do it like this:

#include <type_traits>

template <typename ...Ts>
struct are_mutually_different;

template <typename T>
struct are_mutually_different<T>
{
  static const bool value = true;
};

template <typename T1, typename T2, typename ...Ts>
struct are_mutually_different<T1, T2, Ts...>
{
  static const bool value = (T1::value != T2::value) &&
                            are_mutually_different<T1, Ts...>::value &&
                            are_mutually_different<T2, Ts...>::value;
};



void test()
{
  using _1 = std::integral_constant<int, 1>;
  using _2 = std::integral_constant<int, 2>;
  using _3 = std::integral_constant<int, 3>;
  using _4 = std::integral_constant<int, 4>;
  using _5 = std::integral_constant<int, 5>;

  static_assert(are_mutually_different<_1, _2, _3, _4>::value, ":(");
  static_assert(!are_mutually_different<_1, _1, _3, _4>::value, ":(");
  static_assert(!are_mutually_different<_1, _2, _1, _4>::value, ":(");
  static_assert(!are_mutually_different<_1, _2, _3, _2>::value, ":(");
}
MadScientist
  • 3,390
  • 15
  • 19
2

Hoping that someone else can show a more elegant and simpler solution, I propose the insert of a static_assert() in TypeMe and the development with the support of a set of specific (allDifferentEnums) and generics (allDiffs and fistDiffs) type traits.

The following is a simplified but compilable example

#include <type_traits>

enum SomeEnum
 { A_SE, B_SE, C_SE, MAX };

template <SomeEnum _se, int _val>
struct Pack
 { }; 

template <typename T, T ... ts>
struct firstDiffs;

template <typename T, T t>
struct firstDiffs<T, t> : std::true_type
 { };

template <typename T, T t, T ... ts>
struct firstDiffs<T, t, t, ts...> : std::false_type
 { };

template <typename T, T t, T t0, T ... ts>
struct firstDiffs<T, t, t0, ts...> : firstDiffs<T, t, ts...>
 { };

template <typename T, T ... ts>
struct allDiffs;

template <typename T>
struct allDiffs<T> : std::true_type
 { };

template <typename T, T t, T ... ts>
struct allDiffs<T, t, ts...>
   : std::integral_constant<bool, firstDiffs<T, t, ts...>::value && 
                                  allDiffs<T, ts...>::value>
 { };

template <typename...>
struct allDifferentEnums: std::false_type
 { };

template <SomeEnum ... ses, int ... vals>
struct allDifferentEnums<Pack<ses, vals>...> : allDiffs<SomeEnum, ses...>
 { };

template <typename ... ArgPacks>
class TypeMe
 { static_assert(allDifferentEnums<ArgPacks...>::value, "!"); };

int main()
{
  // OK
  TypeMe<Pack<A_SE, 1>, Pack<B_SE, 2>, Pack<C_SE, 3>> tm_abc;

  // static_assert failed "!"
  // TypeMe<Pack<A_SE, 1>, Pack<B_SE, 2>, Pack<A_SE, 3>> tm_aba;

  (void)tm_abc;
  // (void)tm_aba;
}
max66
  • 65,235
  • 10
  • 71
  • 111