Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call void function for each template type in a variadic templated function?

My goal is to write a simple generic function for registering converters for arbitrary C++ types. For simplicity I'll just print C++ type names. I'd like to be able to call my generic print_type_name function for any types, including multiple types at once (variadic):

template <typename T>
void print_type_name(void)
{
    std::cout << typeid(T).name() << std::endl;
}

This works fine for things like this:

print_type_name<int>();
print_type_name<std::string>();
print_type_name<std::vector<std::complex<float> > >();

However, I need to be able to call this function for each type in a variadic template, e.g. (when expanded):

print_type_name<int, std::string, std::vector<std::complex<float> > >();

Here's what I've come up with, but it's rather clunky:

template <typename ...TS>
void noop(TS... ts) { }

template <typename T>
int real_print_type_name(void) {
    std::cout << typeid(T).name() << std::endl;
    return 0;
}

template <typename ...TS>
void print_type_name(void) {
    noop(real_print_type_name<TS>()...);
}

Which allows for the following:

template <typename ...TS>
void other_function(void) {
    print_type_name<TS...>();
}

Notice the useless noop function and the int return type of real_print_type_name, both of which I had to add in order to expand the parameter pack. Is there a cleaner way of doing this?

like image 776
joe Avatar asked Feb 27 '26 04:02

joe


1 Answers

Here is a helper function. It uses random dark magic, but its name is pretty clear:

void do_in_order() {}

template<class...Fs>
void do_in_order( Fs&&...fs ) {
  using discard=int[];
  (void)discard{0, (void(
    std::forward<Fs>(fs)()
  ),0)... };
}

or in C++17:

template<class...Fs>
void do_in_order( Fs&&...fs ) {
  (void(std::forward<Fs>(fs)())...);
}

(much nicer).

which hides any uglyness. It takes a set of void() callables and calls them left to right -- it does the tasks in order, like it says on the tin.

Then print_type_names becomes:

template<class...Ts>
void print_type_names() {
  do_in_order( print_type_name<Ts>... );
}

or

template<class...Ts>
void print_type_names() {
  do_in_order( [&]{
    std::cout << typeid(Ts).name() << std::endl;
  }... );
}

if you don't want to use the single print_type_name function and want to inline it.

Note that some non-conforming compilers complain about having an entire lambda be expanded in a ....

live example

like image 137
Yakk - Adam Nevraumont Avatar answered Feb 28 '26 19:02

Yakk - Adam Nevraumont