12

I have an overloaded function that I have to call with many different types. The simple approach is:

uint8_t a;
uint16_t b;
//....
double x;

doSomething(a);
doSomething(b);
//...
doSomething(x);

expressing those calls succinctly can be done with a variadic template as explained at this Q&A. The code will look somewhat like this:

auto doSomethingForAllTypes = [](auto&&... args) {
    (doSomething(args), ...);
};

uint8_t a;
uint16_t b;
//....
double x;

doSomethingForAllTypes(a, b, ... ,x);

But I'll have to do this at many places in the code, so I'd like to define the list of types only once. I'd like to have code that looks conceptually like this:

auto doSomethingForAllTypes = [](auto&&... args) {
    (doSomething(args), ...);
};

someContainer allTypesNeeded(uint8_t, uint16_t, ... double);

doSomethingForAllTypes(allTypesNeeded);

How could this be done?

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
marco
  • 243
  • 1
  • 9
  • Precisely, what code you would like to shorten? You want to call ```doSomethingForAllType``` several times? Types in ```doSomethingForAllType``` are being deduced. – francesco May 06 '19 at 07:38
  • 4
    How should the call look like with arguments? Looks to me like `std::tuple` and `std::apply`. – Quimby May 06 '19 at 07:39

3 Answers3

11

With some boiler-plate to obtain a tuple foreach, you can implement what you want as follows:

#include <tuple>
#include <utility>

namespace detail
{
    template<typename T, typename F, std::size_t... Is>
    void for_each(T&& t, F f, std::index_sequence<Is...>)
    {
        ( static_cast<void>(f(std::get<Is>(std::forward<T>(t)))),... );
    }
}

template<typename... Ts, typename F>
void for_each_in_tuple(std::tuple<Ts...> const& t, F f)
{
    detail::for_each(t, f, std::index_sequence_for<Ts...>{});
}

int main() {
    std::tuple<uint8_t, uint16_t, double> tup{};
    for_each_in_tuple(tup, [](auto&& arg) {
        doSomething(arg);
    });
}

Live Example

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
5

If you want to have a predefined sequence of types you can use TypeList aproach if you don't want to create tuples with arguments:

#include <type_traits>
#include <utility>

void doSomething(int)
{
}

void doSomething(double)
{
}

template <typename... Args>
void doSomething(Args&&... args)
{
    (doSomething(std::forward<Args>(args)), ...);
}

template <typename ...Args>
struct TypeList{};

template <typename T>
struct DoSomethingHelper;

template <typename ...Args>
struct DoSomethingHelper<TypeList<Args...>>
{
    static void doSomething(Args&&... args)
    {
        ::doSomething(std::forward<Args>(args)...);
    }
};

template <typename T, typename ...Args>
void doSomethingForTypes(Args&&... args)
{
    DoSomethingHelper<T>::doSomething(std::forward<Args>(args)...);
}

int main()
{
    using MyTypeList = TypeList<int, double, int>;

    doSomethingForTypes<MyTypeList>(1, 1.0, 2);
}
Dmitry Gordon
  • 2,229
  • 12
  • 20
1

With std::tuple and std::apply

std::tuple<uint8_t, uint16_t, double> tup{};
std::apply([](const auto&... arg) { (doSomething(arg), ...); }, tup);
Jarod42
  • 203,559
  • 14
  • 181
  • 302