Andrej Levkovitch
Andrej Levkovitch

Reputation: 255

How to check values in mpl::vector_c?

In the tutorial is exersize, where need:

add error checking to the binary template causes a compilation error if N contains digits other than 0 or 1

It is enough simple to do it by standard features:

template <int N>
struct Binary {
  static_assert(N == 0 || N == 1, "binary have to be 0 or 1");
  const int n = N;
};

But how to do it by mpl::vector_c? For example:

using values = mpl::vector_c<int, 0, 1>;

template<int N,
         typename = typename std::enable_if</*TODO compare N with values from mpl::vector_c*/>::type>
struct Binary {
  const int n = N;
};

Upvotes: 1

Views: 154

Answers (2)

Julius H&#252;lsmann
Julius H&#252;lsmann

Reputation: 21

Building on top of the other answer, below you can find a solution using SFINAE as indicated in the question by the use of enable_if.

This is somewhat more tricky than I originally expected it to be, because somehow the amount of provided arguments for the boost::mpl_vector_c in the partial template specialization does not match the size of the mpl::vector.

Therefore, I define a helper struct below which allows to perform the boolean 'and' operation on a subset of a provided variadic template of boolean values.

If c++17 is available, the lines marked by the comment c++14/c++17 can be excanged. If c++14 is not available, the the index_sequence can for instance be replaced by making use of a recursive #value declaraion in the struct and _v/_t suffixes replaced by [...]::value/type respectively.


#include <boost/mpl/vector_c.hpp>

#include <tuple>

/// Helper struct which enables to evaluate a conjunction of a subset of a set of booleans.
template <typename IndexSequence, bool... v> struct PartialConjunction;
/// Parital template specialization
template <std::size_t... idx, bool... b>
struct PartialConjunction<std::index_sequence<idx...>, b...>
    : std::integral_constant<
          bool, (std::get<idx>(std::forward_as_tuple(b...)) && ...)> {};
/// 'Alias' for the value 
template <std::size_t S, bool... v> constexpr auto PartialConjunction_v =
    PartialConjunction<decltype(std::make_index_sequence<S>()), v...>::value;


/// Actual struct which holds the type of the vector in ::type if it meets the criterion
template <typename VecType, VecType N, typename MplVector> struct Same; //< c++14
//template <auto N, typename MplVector> struct Same;                    //< c++17
template <typename VecType, VecType N, long... a> struct Same<VecType, N, boost::mpl::vector_c<VecType, a...>> {// c++14
//template <typename VecType, VecType N, long... a> struct Same<N, boost::mpl::vector_c<VecType, a...>> {       // c++17
  using type = boost::mpl::vector_c<VecType, a...>;
  static constexpr auto Size = typename type::size();
  static constexpr auto value = PartialConjunction_v<Size, N == static_cast<VecType>(a)...>;
};
/// Alias for the type which performs SFINAE.
template <typename T, T N, typename VectorType, typename = std::enable_if_t<Same<T, N, VectorType>::value>>  // c++14..
using Same_t = typename Same<T, N, VectorType>::type;
//template <auto N, typename VectorType, typename = std::enable_if_t<Same<N, VectorType>::value>>            // c++17..
//using Same_t = typename Same<N, VectorType>::type;



int main() {

  // For the c++17 version, the first 'int' in the parameter list can be omitted

  //Same_t<int, 1, boost::mpl::vector_c<int, 1, 1, 2>> fails;

  Same_t<int, 1, boost::mpl::vector_c<int, 1, 1, 1>> ok;
  Same_t<int, 1, boost::mpl::vector_c<int, 1, 1>> okok;
  Same_t<int, 1, boost::mpl::vector_c<int, 1>> okokok;
}

The code example can be found here in CompilerExplorer.

Upvotes: 2

mutableVoid
mutableVoid

Reputation: 1526

Partial template specialization might help you here:

The trick is to first define the template the way you use it (with an int (N) and the type (which will be the mpl::vector_c), and to then 'break it down' into the components you want to obtain access to (in this case two integers of which the vector_c is comprised). Below you can find the example which works for a vector_c with exactly two parameters. You can also extend this to work with an arbitrary amount of parameters (which might be a fun second exercise :) and could be achieved using a parameter pack).

#include <boost/mpl/vector_c.hpp>

using namespace boost;
using values = mpl::vector_c<int, 1, 1>;

template <int N, typename T> struct Binary;
template <int N, typename t, long a, long b> struct Binary<N, boost::mpl::vector_c<t, a, b>> {
  static_assert(N == a && b == N, "invalid vector_c");
};

int main() {
  //Binary<1, boost::mpl::vector_c<int, 1, 2>> koo; // commented out as assertion fails
  Binary<1, values> kooo;
}

Related Links you might find interesting:

Note that this solution does not work with standards <= c++03

Upvotes: 2

Related Questions