The use of invariants in metaprogramming

I would like to be able to check invariants for classes that are used in metaprograms. My first naive approach was

template <int N>
struct digit
{
  static_assert((N >= 0) && (N < 10), "bad invariant");
};

using boom = digit<99>;

However, this compiles without any problems. A static statement is only triggered when an illegal class is created.

This is possible by adding an additional template parameter:

#include <type_traits>

template <int N, 
          typename = typename std::enable_if<(N >= 0) && (N < 10)>::type>
struct digit;

using crash = digit<-7>;

When I wanted to apply this technique to a class that is used as a list of types:

#include <type_traits>

template <typename ...> struct are_integral;

template <typename T, typename ...Ts>
struct are_integral<T, Ts...>
{
  static const bool value = std::is_integral<T>::value &&
                            are_integral<Ts...>::value;
};

template <>
struct are_integral<> : std::true_type { };



template <typename ...Ts,
          typename = typename std::enable_if<are_integral<Ts...>::value>::type>
struct list;

using ok = list<int, long, char>;
using bad = list<double>;

It just doesn't work, since gcc complains that

error: parameter package "Ts" should be at the end of the template a list of structural parameter lists;

Even if this works, the class is useless since the template parameter package does not reflect the list of types.

So, I tried to use the “illegal” base class:

template <typename> struct check;

template <typename ...Ts>
struct list : check<typename std::enable_if<are_integral<Ts...>::value>::type>
{ };

using ok = list<int, long, char>;
using bad = list<double>;

This compiles without a problem.

- ++ 11 ?

+4
1

- , , , static_assert .

, , . metafunction .

template <typename ...Ts>
struct make_list
{ 
    static_assert(are_integral<Ts...>::value, "all types must be integral");
    typedef list<Ts...> type;
};

using ok = make_list<int, long, char>::type;
using bad = make_list<double>::type;

.

// dummy wrapper
template <typename ...>
struct pack;

template <typename ...> struct are_integral;

// specialization for wrapped packs
template <typename ...Ts>
struct are_integral<pack<Ts...>> : are_integral<Ts...>
{
};

template <typename T, typename ...Ts>
struct are_integral<T, Ts...>
{
  static const bool value = std::is_integral<T>::value &&
                            are_integral<Ts...>::value;
};

template <>
struct are_integral<> : std::true_type { };

// helper type which performs the check
template <typename Pack,
          typename = typename std::enable_if<are_integral<Pack>::value>::type>
struct list_helper;

// actual type (alias) you will expose
template <typename ...Ts>
using list = list_helper<pack<Ts...>>;

using ok = list<int, long, char>;
using bad = list<double>; // compiler error

, : - , , , , ..

+1

All Articles