0

I'm newbie in c++ template meta programming.

I tried to generate constexpr tuple of specialized class from enum array.

I'm using c++14.

here is my example code.

I tried to use http://en.cppreference.com/w/cpp/utility/integer_sequence but there is problem with specialized class.

#include <iostream>
#include <utility>
#include <tuple>

enum class ClassType{  kA,  kB,  kC };

template<ClassType Type>
class Event;

template<>
class Event<ClassType::kA>{ void func() { std::cout << "A" << std::endl; } };

template<>
class Event<ClassType::kB>{ void func() { std::cout << "B" << std::endl; } };

template<>
class Event<ClassType::kC>{ void func() { std::cout << "C" << std::endl; } };

class EventList
{
 public:
  constexpr EventList() : size_(0), list_{ClassType::kA,} {}
  ClassType list_[255];
  int size_;
};
template <std::size_t Index, EventList&& List>
                             constexpr auto GetEvent()
{
  return Event<List.list_[Index]>();
}

constexpr auto CreateEventList()
{
  EventList list;
  list.list_[0] = ClassType::kA;
  list.list_[1] = ClassType::kB;
  list.list_[2] = ClassType::kC;
  list.size_ = 3;
  return list;
}

int main()
{
  constexpr auto eventlist =CreateEventList();

  constexpr std::tuple<Event<eventlist.list_[0]>,
                       Event<eventlist.list_[1]>,
                       Event<eventlist.list_[2]>
                       // ... until i==listsize-1
                       > gentuple;
  constexpr auto t = CreateTupleFromArray(eventlist);
}

I also tried in some another way like this.

template<std::size_t SIZE>
constexpr auto CreateArray()
{
  std::array<ClassType, SIZE> array = {ClassType::kA,};

  for( auto i=0 ; i<SIZE ; ++i )
    array[i] = static_cast<ClassType>(i % 3);

  return array;
}
template<typename Array, std::size_t... I>
decltype(auto) a2t_impl(const Array& a, std::index_sequence<I...>)
{
  return std::make_tuple(Event<a[I]>()...);
}

template<typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
decltype(auto) a2t(const std::array<T, N>& a)
{
  return a2t_impl(a, Indices{});
}

int main()
{
  auto arr = CreateArray<15>();
  auto tu = a2t(arr);
}

But there is compile error like this.

main.cc:70:32: error: non-type template argument is not a constant expression
  return std::make_tuple(Event<a[I]>()...);
                               ^
main.cc:76:10: note: in instantiation of function template specialization
      'a2t_impl<std::array<ClassType, 15>, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
      13, 14>' requested here
  return a2t_impl(a, Indices{});
         ^
main.cc:82:13: note: in instantiation of function template specialization
      'a2t<ClassType, 15, std::integer_sequence<unsigned long, 0, 1, 2, 3, 4, 5, 6,
      7, 8, 9, 10, 11, 12, 13, 14> >' requested here
  auto tu = a2t(arr);

I think Array& a should be a template argument but there is also compile error about "conflict on cv-qualification".

cpplearner
  • 13,776
  • 2
  • 47
  • 72
ReaLWaKKa
  • 13
  • 4

1 Answers1

0

I understand your question such that you wish to call a function that returns an, e.g., std::tuple<EventA, EventB, EventC, EventA> if you pass in ClassType::kA, ClassType::kB, ClassType::kC, ClassType::kA. The challenge here is the following: The return type of your function (which must be known at compile time) depends on the information you are passing in. However, arguments' values you pass to a constexpr function are not constant expressions.

With this in mind, one may understand the compiler's complaint

main.cc:70:32: error: non-type template argument is not a constant expression
  return std::make_tuple(Event<a[I]>()...);

in the context

template<typename Array, std::size_t... I>
auto a2t_impl(const Array& a, std::index_sequence<I...>) {
  return std::make_tuple(Event<a[I]>()...);
}

A C++17 solution is to wrap the argument in a constexpr lambda:

constexpr auto eventlist = CreateEventList();
constexpr auto tuple_of_events = create_tuple_from_array([=] { return eventlist;});

which allows the compiler to deduce the return type at compile time:

namespace detail {

template<class WrappedEventList, size_t... is>
constexpr auto create_tuple_from_array_impl(
  WrappedEventList wel, std::index_sequence<is...>
) {
  constexpr auto list = wel();
  return std::make_tuple(Event<list[is]>{}...);
}

}// detail

template<class WrappedEventList>
constexpr auto create_tuple_from_array(WrappedEventList wel) {
  return detail::create_tuple_from_array_impl(
    wel, std::make_index_sequence<wel().size()>{}
  );
}

Quick and dirty C++17 online demo - I did not read all your code, but just "made it work"

In C++14 and below one may try to pass the enums as non-type template parameters.

Julius
  • 1,816
  • 10
  • 14