# Convenient approach to composite pattern in C++

This topic is 1354 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi,

I have a class (A) which contains 'Listeners' or 'Observers' which need to be informed when certain things happen in class A. This currently involves iterating over all listeners and calling a certain notification method for example:

for(Listener listener : listeners)
{
listener->InformOfSomething(1, 2, 3, 4);
}


I was wondering if there is any convenient way in c++11 to reduce this to a one-liner without having to create additional functions or use lambdas (or even remove the brackets ;)

The approach I'm looking for would be something similar to jQuery where functions act on collections of objects as opposed to single items. For e.g. maybe something as simple as this:

$(listeners).InformOfSomething(1, 2, 3, 4);  (please forgive the shameless copy of jQuery syntax) Where the$ syntax acts in a similar way to range-based for loops and requires begin() and end() functions on the 'listeners' param which then iterates the collection and substitutes the item in the collection with first parameter of the proceeding function (or more specifically the 'this' pointer when using a class member function).

Anything like this in c++11 or any better way to do it?

Thanks.

##### Share on other sites
You could write a horrible macro to condense your loop to:
DOLLAR(listeners, InformOfSomething(1, 2, 3, 4));

:D (obviously call it something other than \$ :lol:)

##### Share on other sites
It may be worth considering a more off-the-shelf solution to the problem, such as boost::signals2.

As for the code in question, C++'s (sometimes questionable) rules around braces and line breaks allow for an obvious one-liner:
for (Listener listener : listeners) listener->InformOfSomething(1, 2, 3, 4);
If you are willing to allow the use of lambdas, std::for_each becomes an option:
std::for_each(listeners.begin(), listeners(), [](Listener &l) {l->InformOfSomething(1, 2, 3, 4);});

There isn't any standard way to forward functions called against containers to the members of those containers (and this is in general a *very* tricky topic for compiled languages). Edited by swiftcoder

##### Share on other sites

Just write some templated helper functions, if std::for_each() doesn't do what you want.

Feel free to take mine. The header needs to be cleaned up a bit, but meh.

ForEach(array, DoSomething, 17, "Text"); //Global functions, functors, or lambdas.
ForEach(array, &Element::DoSomething, 17, "Text"); //Member functions
ForEachPtr(arrayOfPtrs, &Element::DoSomething, 17, "Text"); //Vector of smart pointers.


If you really wanted to, you could probably cobble together something that looked like this:

ForEach(array, &Element::DoSomething)(17, "Text");

Or this:

ForEach(array).(&Element::DoSomething, 17, "Text");

...but I don't think this is possible:

ForEach(array).DoSomething(17, "Text"); //Not possible.


Not in straight C++ anyway - with the preprocessor you could do:

#define ForEach(container) for(auto &element : container) element

ForEach(arrayOfObjects).DoSomething(17, "Test");


[ideone]

You'd need a separate macro for const iteration. I'd stick to the templated functions, though.

Edited by Servant of the Lord

##### Share on other sites

#include <iostream>
#include <vector>

class Listener {
public:
void InformOfSomething(int a, int b, int c, int d) {
std::cout << "Thank you for informing me about " << a << ", " << b << ", " << c << ", " << d << '\n';
}
};

template<typename C, typename F, typename ...Args>
static inline void callVector(C &&it, F &&f, Args&& ...args) {
for(auto &t : it)
(t.*f)(std::forward<Args>(args)...);
}

int main() {
std::vector<Listener> listeners(3);

callVector(listeners, &Listener::InformOfSomething, 7, 8, 9, 0);
}


Edited by Erik Rufelt

##### Share on other sites

Its probably something of an oversight that there's no version of for_each that simply takes a container and a function to apply to all its elements -- it isn't much more inconvenient to simply use the iterator-pair version, or a short range-for form, like "for (auto l in listeners) l->do_stuff(1,2,3); but a version of for_each that doesn't use an iterator pair would eliminate unnecessary verbosity of the most-common use case:

Luckily, with non-member begin() and non-member end(), I think (that is to say **disclaimer** no warranty expressed or implied) you can implement this easily in those terms and in the terms of the existing iterator-pair version of for_each. This is not tested, but it would look something like this:

template<class Input, class Function>

Function for_each(Input in, Function fn)

{

return for_each(std::begin(in), std::end(in), fn);

}

and usage would look something like so:

for_each(listeners, [](Listener &l) {l->InformOfSomething(1, 2, 3, 4);}

I think then, that you could reduce out the lambda with a function object template of some kind -- something like a "call_member_with_args" template, who's usage could look something like this:

for_each(listners, call(Listener::InformOfSomething)(1,2,3,4));

But I'll leave implementation as an exercise to the reader. (I think probably you could use std::function, but its not lightweight -- I wonder, though, if its exactly as heavy as an encapsulated member function anyways)

[EDIT] ... and Erik's C++11 variadic-template-fu is both stronger and quicker than my outdated C++06 template-fu

Edited by Ravyne

##### Share on other sites

template<typename C, typename F>
static inline Caller<C, F> operator->*(C &c, F &&f) {


Yeah, okay. I'm totally okay with what just happened and am totally fine with how readable this is.

static ::operator->*... Yup!

##### Share on other sites
I've been playing with Erik's code a bit, and another candidate is the almost-never overloaded comma operator. I didn't post the code since I'm unfamiliar with troubles that might arise. I suspect its safe, as comma has lowest precedence, and this use is similar to other uses in C++ DSL implementations, or as its used to collect terms in parts of boost, I'm just unfamiliar with potential hazards that might be found.

Usage looks like:

(Listeners, &Listener::InformOfSomething)(1,2,3,4);

Which doesn't read too poorly is you read the first set of parens as something like "cartesean product of this collection and this member function"

1. 1
Rutin
42
2. 2
3. 3
4. 4
5. 5

• 9
• 27
• 20
• 14
• 14
• ### Forum Statistics

• Total Topics
633385
• Total Posts
3011600
• ### Who's Online (See full list)

There are no registered users currently online

×