An elegant way to implement call multiplexing in an aggregate C ++ class?

When multiplexing calls to many sub-objects, what is an elegant way to prevent a cyclic pattern?

Description of the problem using an example:

struct Foo { void Boo(); void Hoo(); bool IsActivated(); }; struct FooAggregator { ... void Boo(); void Hoo(); ... std::vector<Foo> m_foos; }; FooAggregator::Boo() { for(size_t i=0, e=m_foos.size(); i!=e; ++i) { if(m_foos[i].IsActivated()) { m_foos[i].Boo(); } } } FooAggregator::Hoo() { for(size_t i=0, e=m_foos.size(); i!=e; ++i) { if(m_foos[i].IsActivated()) { m_foos[i].Hoo(); } } } 

As you can see, FooAggregator implements the same (similar) interface as one Foo, iterating over all Foo objects that call their corresponding member functions.

As you can also see, the iteration loop is a complete pattern repeating for each member function of FooAggregator.

What is an elegant way to remove a template from the implementation of member functions of FooAggregators

+6
source share
4 answers

I'll take Nawaz a good 1st example and simplify a few more:

(Remember, I want to reduce the template, and not introduce the most interesting functions.)

 // FooAggregator.h struct FooAggregator { template<typename MemFn> void CallForEachFoo(MemFn fun); void Boo(); void Hoo(); }; // FooAggregator.cpp template<typename MemFn> void FooAggregator::CallForEachFoo(MemFn fun) { BOOST_FOREACH(Foo& o, m_foos) { if(o.IsActivated()) { (o.*fun)(); } } } void Boo() { CallForEachFoo(&Foo::Boo); } void Hoo() { CallForEachFoo(&Foo::Hoo); } 
+1
source

You can use Boost.Bind as suggested by @ Space_C0wb0y. But if you cannot use this for any reason, then you can do something like this:

 struct FooAggregator { typedef void (Foo::*Fun)(); void Boo() { CallForEach(m_foos.begin(), m_foos.end(), &Foo::Boo); } void Hoo() { CallForEach(m_foos.begin(), m_foos.end(), &Foo::Hoo); } template<typename FwdIterator> void CallForEach(FwdIterator first, FwdIterator last, Fun fun) { while (first != last ) { if(first->IsActivated()) { (first->*fun)(); } first++; } } }; 

Or you can use std::for_each from <algorithm> like:

 #include <algorithm> struct FooAggregator { typedef void (Foo::*Fun)(); void Boo() { std::for_each(m_foos.begin(), m_foos.end(), Call(&Foo::Boo)); } void Hoo() { std::for_each(m_foos.begin(), m_foos.end(), Call(&Foo::Hoo)); } struct Call { Fun m_fun; Call(Fun fun) : m_fun(fun) {} void operator()(Foo & foo) { if(foo.IsActivated()) { (foo.*m_fun)(); } } }; }; 

Read the function object to understand the second example.


In C ++ 0x (i.e. C ++ 11) its very simple. You can use lamda in std::for_each like:

 #include <algorithm> struct FooAggregator { void Boo() { std::for_each(m_foos.begin(), m_foos.end(), [](Foo &foo){ if (foo.IsActivated()) foo.Boo(); } ); } void Hoo() { std::for_each(m_foos.begin(), m_foos.end(), [](Foo &foo){ if (foo.IsActivated()) foo.Hoo(); } ); } //other code }; 
+6
source

You can use Boost.Bind to pass the boost::function object to a dispatch method that indicates which method to call. Then you need only one sending method, which can be called using different target methods as a parameter.

+1
source

Nawaz's answer is interesting, but there are alternative solutions.

First of all, you must admit that your aggregator is very similar to the Composite pattern.

Secondly, I would go for either:

  • external iteration
  • a for_each a for_each member method to which a functor is passed (in fact, due to const overload).

For external iteration read :)

It is relatively unfortunate that the syntax of the C ++ iterator is not really oriented toward skipping iterators, but it is nonetheless achievable.

 class ActiveIterator { public: friend class FooAggregator; friend bool operator==(ActiveIterator lhs, ActiveIterator rhs) { return lhs._it == rhs._it; } ActiveIterator& operator++() { this->next(); return *this; } Foo* operator->() const { return _it::operator->(); } Foo& operator*() const { return *_it; } private: typedef std::vector<Foo>::iterator base; ActivateIterator(base begin, base end): _it(begin), _end(end) { if (_it == _end || _it->IsActive()) { return; } this->next(); } void next() { ++it; while (_it != _end && !_it->IsActive()) { ++_it; } } base _it, _end; }; 

Then your aggregate simply has Begin and End methods, and before the caller interacts with your iterators.

Note: you can make it a template to implement mutable / const at a time

External iteration remains very cumbersome because in C ++ there is no generator syntax to make things simple.

0
source

Source: https://habr.com/ru/post/888625/


All Articles