3

I noticed the following line in the open-source project FeatherKit:

int _[] = { (SubscribeToType<MessageTypes>( bus, receiver, desubscribers, unsubscribe ), 0)... };

With the following context:

template<class... MessageTypes>
void Subscribe( MessageBus& bus, MessageReceiver<MessageTypes...>& receiver, bool unsubscribe ) {
    std::vector<std::function<void()>> desubscribers;
    int _[] = { (SubscribeToType<MessageTypes>( bus, receiver, desubscribers, unsubscribe ), 0)... };
    (void) _;
    receiver.desubscribers = desubscribers;
}

It's obviously executing the function SubscribeToType for each parameter in the variadic template.

My question is twofold:

  1. How, exactly, does the line work? How come parameter unpacking is allowing that function to execute for each parameter in the variadic template?

  2. I am very certain this line could be replaced by a lambda. How could you replace the line with a lambda expression?

I've contacted the original author of FeatherKit, but he wasn't able to answer my question at that time.

Dmitry Alexandrov
  • 1,693
  • 12
  • 14
Frank
  • 97
  • 2
  • 8

1 Answers1

1
  1. How, exactly, does the line work? How come parameter unpacking is allowing that function to execute for each parameter in the variadic template?

A parameter pack expansion is some pattern involving a parameter pack followed by ...

So expr(T)... is a pack expansion with expr(T) as its pattern, and it expands to expr(T0), expr(T1), expr(T2), ..., expr(TN) for each Ti in the parameter pack.

A pack expansion can only be used in certain contexts, such as argument lists, or initializer lists, so in this case the results of each sub-expression is being used to form an initializer list for the array int _[]. The array is unused and only exists so that its initializer can be used as the context in which to do the pack expansion. Each sub-expression is of the form (SubscribeToType<Ti>(blah, blah), 0) which means the result of the function call is discarded and the expression has the value 0. This is a bit of a kluge to allow the pack expansion to produce a braced-init-list containing N integers, because that's what's needed to initialize the array.

  1. I am very certain this line could be replaced by a lambda. How could you replace the line with a lambda expression?

Why would you want to?

It could, but you'd need a very similar pack expansion in the lambda, so it wouldn't simplify anything.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • To address point #2, I felt that using the initializer of an int array was 'hack'ish and not very clean. I thought that a lambda iterating over the param pack would be very clean and clear to the reader. – Frank Dec 10 '14 at 19:17
  • How would you "iterate" over the pack? Bear in mind it's a pack of types, you don't have an argument pack that you could actually pass to a lambda. The array is hackish, but just saying "use a lambda" doesn't offer a neater solution (or any solution at all ;-) – Jonathan Wakely Dec 10 '14 at 19:35
  • You're right, I just had the feeling that there must've been a cleaner solution that I didn't know to execute a function call for each param in the pack. I know now :) – Frank Dec 10 '14 at 19:39