6

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

Martin Ba
  • 37,187
  • 33
  • 183
  • 337

4 Answers4

6

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
};
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 2
    This is a step into the right direction except: 1) There's `std::foreach()` for that, 2) this would need some `std/boost::bind()` magic if the functions aren't of the same signature. Or use a lambda function, if your compiler has those. – sbi May 20 '11 at 09:21
  • @sbi: However, `foreach` would require wrapping the condition into an extra function. – Björn Pollex May 20 '11 at 09:25
  • 1
    @Space_C0wb0y: That's why I brought up `bind` and lambda. @Nawaz: Great! Upvoted. – sbi May 20 '11 at 10:32
  • +1 for the 1st example. I don't like the 2nd two. They're to verbose. (As I wanted to reduce boilerplate, not introduce different-looking boilerplate :-) – Martin Ba May 21 '11 at 19:14
1

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.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
1

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); }
Martin Ba
  • 37,187
  • 33
  • 183
  • 337
0

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

First of all, you should recognize that your aggregator is very much a Composite pattern.

Second of all, I would go either for:

  • external iteration
  • a for_each-like member method to which a functor is passed (2 actually, because of the const overload).

For external iteration, read on :)

It's relatively unfortunate that C++ iterator syntax is not really geared toward "skipping" iterators, but it is achievable nonetheless.

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 it's up to the caller to interact with your iterators.

Note: you can make it template to have mutable/const implementations in one go

External iteration remains very bulky though, because C++ lacks a generator syntax to make things simple.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722