How do you statically set values ​​in the package of parameters of a variational template?

I am creating a variation template.
Let's say I have something like this:

template<typename T, T ... Numbers> class Sequence final { // Unpack parameter pack into a constexpr array constexpr static T count = sizeof...(Numbers); constexpr static T numbers[count] = { Numbers... }; // ... } 

Instances of this class can be created like this:

 Sequence<uint32_t, 1, 2, 3, 42, 25> seq; 

I would like to make sure that during compilation using static_assert , the numbers parameter package contains only certain numbers. For this example, suppose I only want to allow 0 or 1 .

So, I would like to do something like:

 for (size_t i = 0; i < count; i++) { static_assert(numbers[i] == 1 || numbers[i] == 0, "Only ones and zeroes are allowed."); } 

But obviously static_assert does not work with a for loop. I am sure there must be some syntax for this, but I could not figure it out.

I would prefer to use something that compiles with the C ++ 11 compiler (or perhaps the C ++ 14 compiler if it is not executable in C ++ 11).

+12
c ++ c ++ 11 static-assert variadic-templates
source share
6 answers

I will write @Columbo bool_pack trick .

 template<bool...> struct bool_pack; template<bool... bs> using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>; static_assert(all_true<(Numbers == 0 || Numbers == 1)...>::value, ""); 

Extract expression into constexpr function if it becomes complicated.

+20
source share

A simple C ++ 14 solution:

 template <typename T, T ... Numbers> class Sequence final { static constexpr bool is_all_zero_or_one(std::initializer_list<T> list) { for (auto elem : list) { if (elem != 0 && elem != 1) return false; } return true; } static_assert(is_all_zero_or_one({Numbers...}), "Only zeroes and ones are allowed."); }; 
+5
source share

You cannot use the traditional for loop with compile-time values, but there are many ways you can iterate over the compile-time assembly. In your case, however, there is no need to explicitly iterate over each single number: you can use the package extension to make sure that only the digits are 0 or 1 :

coliru example

 #include <type_traits> // We define a `conjunction<...>` helper that will evaluate to // a true boolean `std::integral_constant` if all passed types evaluate // to true. template <typename...> struct conjunction : std::true_type { }; template <typename T> struct conjunction<T> : T { }; template <typename T, typename... Ts> struct conjunction<T, Ts...> : std::conditional_t<T::value != false, conjunction<Ts...>, T> { }; // Define `constexpr` predicates: template <int T> constexpr bool is_zero_or_one() { return T == 0 || T == 1; } template <int... Ts> constexpr bool all_are_zero_or_one() { // Using variadic pack expansion and `conjunction` we can // simulate an `and` left fold over the parameter pack: return conjunction< std::integral_constant<bool, is_zero_or_one<Ts>()>... >{}; } int main() { static_assert(all_are_zero_or_one<0, 1, 0, 1, 0, 0>(), ""); static_assert(!all_are_zero_or_one<2, 1, 0, 1, 0, 0>(), ""); } 

If you are looking for an explicit way to iterate over a set of compile-time elements, I suggest you explore the following resources:

boost :: hana is a modern metaprogramming library that allows compilation time calculations using the "traditional" imperative syntax.

My CppCon 2015 talk: for_each_argument is explained and expanded - using std::tuple and the encoding type paradigm that you can store, temporary numeric values ​​in a tuple and iterate over it at compile time. My conversation shows a possible way to iterate this way.

+2
source share

You can implement your static validation using a recursive template helper, for example. Then, when you try to compile the code with a sequence containing invalid numbers, you will get a compiler error with a static statement if you want.

 #include <iostream> template<typename T, T... Numbers> struct ValidateSequence; template<typename T> struct ValidateSequence<T>{}; template<typename T, T Number, T... Numbers> struct ValidateSequence<T, Number, Numbers...> { static_assert(Number == 0 || Number == 1, "Invalid Number"); ValidateSequence<T, Numbers...> rest; }; template<typename T, T... Numbers> class Sequence { public: constexpr static unsigned count = sizeof...(Numbers); constexpr static T numbers[] = {Numbers...}; ValidateSequence<T, Numbers...> validate; }; int main() { Sequence <int, 1, 2, 1, 2> sec; std::cout << sec.count << std::endl; return 0; } 
+2
source share
  • C ++ 11
  • msvc2015u3, gcc5.4, clang3.8

     #include <cstdint> #include <algorithm> namespace utility { template <typename T0> inline constexpr bool is_all_true(T0 && v0) { return std::forward<T0>(v0) ? true : false; } template <typename T0, typename... Args> inline constexpr bool is_all_true(T0 && v0, Args &&... args) { return (std::forward<T0>(v0) ? true : false) && is_all_true(std::forward<Args>(args)...); } template <typename T0> inline constexpr bool is_all_false(T0 && v0) { return std::forward<T0>(v0) ? false : true; } template <typename T0, typename... Args> inline constexpr bool is_all_false(T0 && v0, Args &&... args) { return (std::forward<T0>(v0) ? false : true) && is_all_false(std::forward<Args>(args)...); } template <typename T0> inline constexpr bool is_any_true(T0 && v0) { return std::forward<T0>(v0) ? true : false; } template <typename T0, typename... Args> inline constexpr bool is_any_true(T0 && v0, Args &&... args) { return (std::forward<T0>(v0) ? true : false) || is_any_true(std::forward<Args>(args)...); } template <typename T0> inline constexpr bool is_any_false(T0 && v0) { return std::forward<T0>(v0) ? false : true; } template <typename T0, typename... Args> inline constexpr bool is_any_false(T0 && v0, Args &&... args) { return (std::forward<T0>(v0) ? false : true) || is_any_false(std::forward<Args>(args)...); } } 

    ''

Gcc clank

  static_assert(utility::is_all_true((Numbers == 0 || Numbers == 1)...), "Only ones and zeroes are allowed."); 

msvc2015u3 (with workaround for: error C2059: syntax error: '...' )

  static constexpr const bool boo = utility::is_all_true((Numbers == 0 || Numbers == 1)...); static_assert(boo, "Only ones and zeroes are allowed."); 

https://godbolt.org/z/hcS9FY

+1
source share

Another solution:

 template<typename T> constexpr bool IsOneOrZero(T&& t) { return t == 0 || t == 1; } template<typename T, typename... Args> constexpr bool IsOneOrZero(T&& first, Args&&... args) { return IsOneOrZero(std::forward<T>(first)) && IsOneOrZero(std::forward<Args>(args)...); } template<typename T, T First, T... Numbers> class Sequence final { // Unpack parameter pack into a constexpr array constexpr static T count = sizeof...(Numbers); constexpr static T numbers[count] = { Numbers... }; static_assert(IsOneOrZero(First, Numbers...), "ERROR"); }; 
0
source share

All Articles