5

Iterating over a tuple is a common question in the C++ world and I know the various ways it can be done - usually with recursion and variadic templates.
The reason is that a tuple usually holds different element types and the only way to correctly handle every type is to have a specific overload that matches this type. then we let the compiler dispatch every element to the correct function using recursion.

My case goes like this: I have a tuple of elements, every element is an instantiation of a template class:

std::tuple<my_class<int>, my_class<string>, my_class<float>>

Also, my_class<T> is a derived class of my_class_base, which isn't a template.
Considering this constraint, is it possible to write an at(tuple_type& tuple, size_t n) function that returns the nth element of the tuple, as a reference to the base class, in O(1) steps?

Then I can write

for (size_t i = 0; i < N; i++){
   auto& o = at<my_base_class>(tuple, i);
   o.call_base_method(...);
} 

Thanks.

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • sounds like a job for std::make_index_sequence or https://stackoverflow.com/a/37227316/14237276 – Abel Jul 01 '21 at 12:01

1 Answers1

1

You might just using std::apply:

std::apply([](auto&...args){ (args.call_base_method(), ...); }, tuple);

Else to answer your question, you might do something like:

template <std::size_t... Is, typename tuple_type>
my_class_base& at_impl(std::index_sequence<Is...>, tuple_type& tuple, size_t n)
{
    my_class_base* bases[] = {&std::get<Is>(tuple)...};
    *return bases[n];
}

template <typename tuple_type>
my_class_base& at(tuple_type& tuple, size_t n)
{
    auto seq = std::make_index_sequence<std::tuple_size<tuple_type>::value>();
    return at_impl(seq, tuple, n);
}

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • @KonradRudolph: typo fixed and demo added. thanks – Jarod42 Jul 01 '21 at 12:27
  • For complexity, I think returning `std::array, N>` once and iterating over it would be more secure. – Jarod42 Jul 01 '21 at 12:29
  • great solution. MSVC compiles this function to assembly that runs in O(n) (filling a stack array with memory addresses), because MSVC optimizations are naive at best. nevertheless I still went with this solution because it's so compact and elegant. well done. – David Haim Jul 03 '21 at 11:16