Элегантный способ реализации мультиплексирования вызовов в агрегатном классе C++?
При мультиплексировании вызовов ко многим подобъектам, что является элегантным способом предотвращения зацикливания кода?
Описание проблемы на примере:
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();
}
}
}
Как видите, FooAggregator реализует тот же (аналогичный) интерфейс, что и отдельный Foo, итерируя по всем объектам Foo, вызывая их соответствующие функции-члены.
Как вы также можете видеть, цикл итерации завершен, повторяется для каждой функции-члена FooAggregator.
Что представляет собой элегантный способ удаления шаблона из реализации функций-членов FooAggregators
4 ответа
Я возьму хороший первый пример Наваза и упросту еще немного:
(Помните, я хочу уменьшить шаблон, а не вводить самые модные функции.)
// 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); }
Вы могли бы использовать Boost.Bind
как @Space_C0wb0y предложил. Но если вы не можете использовать это по какой-либо причине, то вы можете сделать что-то вроде этого:
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++;
}
}
};
Или вы можете использовать std::for_each
от <algorithm>
как:
#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)();
}
}
};
};
Прочитайте об объекте Function, чтобы понять второй пример.
В C++0x (т.е. C++11) это очень просто. Вы можете использовать лямду в std::for_each
как:
#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
};
Вы можете использовать Boost.Bind для передачи boost::function
Объект в диспетчерский метод, который указывает, какой метод вызывать. Тогда вам потребуется только один метод отправки, который можно вызывать с разными целевыми методами в качестве параметра.
Ответ Наваза интересен, но есть альтернативные решения.
Прежде всего, вы должны признать, что ваш агрегатор очень Composite
шаблон.
Во-вторых, я бы выбрал:
- внешняя итерация
for_each
-подобный метод-член, которому передается функтор (на самом деле 2, из-заconst
перегрузки).
Для внешней итерации читайте дальше:)
Относительно прискорбно, что синтаксис итераторов C++ на самом деле не ориентирован на "пропуск" итераторов, но, тем не менее, он достижим.
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;
};
Тогда ваша совокупность просто имеет Begin
а также End
методы, и это до вызывающей стороны, чтобы взаимодействовать с вашими итераторами.
Примечание: вы можете сделать это шаблоном, чтобы иметь изменяемые /const реализации за один раз
Внешняя итерация остается очень громоздкой, потому что в C++ отсутствует синтаксис генератора, чтобы упростить задачу.