Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elegant way of implementing call multiplexing in a C++ aggregate class?

When multiplexing calls to many sub-objects, what is an elegant way of preventing looping-boilerplate code?

Problem description by 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, the FooAggregator implements the same (similar) interface as a single Foo, iterating over all Foo objects calling their respective member functions.

As you also can see, the iteration loop is complete boilerplate, repeated for every member function of FooAggregator.

What is an elegant way of removing the boilerplate from the implementation of FooAggregators member functions

like image 966
Martin Ba Avatar asked May 20 '11 09:05

Martin Ba


3 Answers

You could use Boost.Bind as @Space_C0wb0y suggested. But if you cannot use that for whatever reason, then you can do something of this sort:

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> as:

#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 about 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 as:

#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
};
like image 107
Nawaz Avatar answered Nov 20 '22 12:11

Nawaz


You could use Boost.Bind to pass a boost::function object into the dispatching method that specifies which method to call. Then you would only need one dispatch-method that could be called with different target methods as parameter.

like image 31
Björn Pollex Avatar answered Nov 20 '22 12:11

Björn Pollex


I'll take Nawaz's good 1st example and simplify some more:

(Remember, I want to reduce the boilerplate, not introduce the fanciest features.)

// 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); }
like image 1
Martin Ba Avatar answered Nov 20 '22 11:11

Martin Ba