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

被刻印的时光 ゝ 提交于 2021-02-04 20:05:02

问题


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?


回答1:


template <typename ...TS>
void print_type_name() {
    using expander = int[];
    (void) expander{ 0, (std::cout << typeid(TS).name() << '\n', 0)... };
}

Or, C++17-style:

template <typename ...TS>
void print_type_name(void) {
    (std::cout << ... << (typeid(TS).name() + "\n"s));
}

Demo.




回答2:


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




回答3:


I think you can do this:

void print_type_name()
{
    std::cout<<"\n";
}

template <typename T>
void print_type_name(const T& t)
{
    std::cout<<t<<" : of type "<<typeid(t).name()<<"\n";
}

template <typename T1, typename... Ts>
void print_type_name(const T1& t1, const Ts&... ts)
{
    // Head
    std::cout<<t1<<" : of type "<<typeid(t1).name()<<", ";
    // Tail
    print_type_name(ts...);
}

Whether it's clearer or not I don't know.



来源:https://stackoverflow.com/questions/28547456/call-void-function-for-each-template-type-in-a-variadic-templated-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!