Junsuk Park
Junsuk Park

Reputation: 193

Is there any elegant way? (type parameter pack)

I want to make a function that accepts types and call itself again by looping or recursion.

What I had already tried was below, but it only called the base function (Called base function with AModule class and didn't call the function with BModule.

class AModule {
};
class BModule {
};

auto main() -> int {
  init<AModule, BModule>()
  return 0;
}

template<typename Module>
void init() {
  // BASE FUNCTION
  // Do something
}

template<typename... Modules>
void init() {
  (init<Modules>(), ...)
}

Upvotes: 0

Views: 67

Answers (2)

max66
max66

Reputation: 66230

In the code in your example you're using template-folding, a new C++17 feature that permit you to avoid recursion.

But, to avoid a name collision, I suggest to call the base version in a different way; say do_something()

template<typename Module>
void do_something() {
  // BASE FUNCTION
  // Do something
}

template<typename... Modules>
void init() {
  (do_something<Modules>(), ...);
}

If you really want use recursive way, you can do something as follows

template <int = 0>
void init ()
 {
  // stop recursion; do nothing
 }

template <typename Head, typename ... Tail>
void init () 
 {
   // do something with Head

   // recursively call init()
   init<Tail...>();
 }

The trick is that calling init<Tail...>();, until Tail... isn't empty is called the Head/Tail... recursive version.

When Tail... is empty, init<Tail...>() is init<>() so the Head/Tail... version doesn't matches anymore (there isn't a Head) but matches the int = 0 version; so init<>() become init<0>() and the ground-do-nothing case stops recursion.

Upvotes: 3

xaxxon
xaxxon

Reputation: 19771

In your code (once syntax errors were fixed), both init() calls were valid for 1 template parameter, so the call was ambiguous. By making the multi-init call require at least 2 parameters, that ambiguity is removed.

// bogus type that just prints an error message including the type it is parameterized with
template<typename T>
struct printer;

template<typename Module>
void init() {
 printer<Module>{}; // this is an error just to show you that it's getting called with both types
}

// only call this if it's called with 2 or more types, otherwise just use the other init()
template<typename T1, typename T2, typename... Modules>
void init() {
    init<T1>();
    init<T2>();
  (init<Modules>(), ...);
}

class AModule {
};
class BModule {
};

auto main() -> int {
  init<AModule, BModule>();
  return 0;
}

live: https://godbolt.org/z/D-eh2G

Upvotes: 1

Related Questions