C++ concepts loop

617 views Asked by At

I'd like to ask the compiler to check if a tuple contain only "meta types".

By the way I'm completely new with C++ concepts.

template < typename T >
struct Type {
  using type = T;
};

//! A type can be easily check with a small concept
template < typename T >
concept bool  C_Type = requires {
  typename T::type;
};

//! But how to apply it on a whole tuple?
template < typename T >
void foo(T tuple) {}

int main() {

  constexpr auto  test = std::make_tuple(Type<int>{}, Type<double>{});
  foo(test);
}

So I want to be sure that every type inside the sequence (let's say only something Iterable for this example) is a "meta type".

I'm using Boost Hana if it can simplify the code.

At the moment I'm not even sure if it's possible. I hope it is, I guess I just need to learn more meta-programming stuff. So I'll continue to search and try, but if somebody already has the answer, thanks!

3

There are 3 answers

2
Casey On BEST ANSWER

Concepts are by design too weak to perform metaprogramming, so to do this you need some "metaprogramming help" from the rest of the language. I would use template specialization to decompose a type into a template and its type parameters, and then require all of those parameters to satisfy C_Type:

template <class>
constexpr bool TypeTuple_ = false;
template <template <class...> class Tuple, class... Types>
  requires (C_Type<Types> && ...)
constexpr bool TypeTuple_<Tuple<Types...>> = true;

template <class T>
concept bool TypeTuple = TypeTuple_<T>;

This works with hana::tuple, and std::tuple - any template that takes all type parameters.

0
BeyelerStudios On

Here is an example of how you could check if tuple only holds types that define a typename type. The trick here is to define a tuple type that defines a new type std::pair<std::pair<...std::pair<void, T0>, ...TN>, TM> for the tuple std::tuple<T0, ..., TN, TM>. This works in GCC 7.2. I'd be interested on how one more cleanly combines variadic constraints as I didn't find any references.

#include <array>
#include <tuple>

template<typename T>
struct Type {
    using type = T;
};

template<typename Tuple, size_t I = std::tuple_size<Tuple>::value>
struct TupleType {
    using type = std::pair<typename TupleType<Tuple, I - 1>::type,
                           typename std::tuple_element<I - 1, Tuple>::type>;
};

template<typename Tuple>
struct TupleType<Tuple, 0> {
    using type = void;
};

template<typename T>
concept bool C_TupleType = requires {
    typename TupleType<T>::type;
};

void foo(C_TupleType tuple) { }

int main() {
    constexpr auto test = std::make_tuple(Type<int>{}, Type<double>{});
    foo(test);

    // also works on pair and array
    constexpr auto test1 = std::make_pair(Type<int>{}, Type<double>{});
    foo(test1);
    constexpr std::array<Type<int>, 3> test2;
    foo(test2);

    // and of course TupleType is also a meta type
    constexpr std::array<TupleType<std::pair<int, int>>, 13> test3;
    foo(test3);

    return 0;
}
0
Jason Rice On

I'm not too familiar with concepts, but you can certainly achieve this with Boost.Hana in a number of ways.

From looking at the comments, it should be noted that any tuple type can be made into a hana::Sequence which by convention is also hana::Searchable and hana::Foldable.

Here is an example with std::tuple used as a hana::Searchable:

#include <boost/hana.hpp>
#include <boost/hana/ext/std/tuple.hpp>
#include <tuple>
namespace hana = boost::hana;

template < typename T >
concept bool  C_Type = requires {
  typename T::type;
};

auto is_C_Type = hana::overload_linearly([](C_Type const&) { return hana::true_c; },
                                         [](auto const&)   { return hana::false_c; });

template < typename T >
constexpr bool foo(T const& tuple) {
  return hana::all_of(tuple, is_C_Type);
}

int main() {
  constexpr auto  test = std::tuple{hana::type_c<int>, hana::type_c<double>};
  static_assert(foo(test));
}

https://wandbox.org/permlink/YNZDX7uN6mgUdmje