在 C++ 聚合类中实现调用多路复用的优雅方式?
Posted
技术标签:
【中文标题】在 C++ 聚合类中实现调用多路复用的优雅方式?【英文标题】:Elegant way of implementing call multiplexing in a C++ aggregate class? 【发布时间】:2011-08-29 12:12:55 【问题描述】:当多路复用对许多子对象的调用时,防止循环样板代码的优雅方法是什么?
问题举例说明:
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 成员函数的实现中删除样板的优雅方法
【问题讨论】:
这 ´e=m_foos.size(); i!=e´ 看起来像是过早的优化。 @Henrik:不是这样。这只是惯用的编程。所有循环看起来都一样。 【参考方案1】:我将采用 Nawaz 的第一个很好的例子并进一步简化:
(记住,我想减少样板,而不是引入最花哨的功能。)
// 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);
【讨论】:
【参考方案2】:Nawaz 的回答很有趣,但还有其他解决方案。
首先,您应该认识到您的聚合器在很大程度上是一个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
方法,由调用者与您的迭代器进行交互。
注意:您可以将其模板化为一次性实现可变/常量
但外部迭代仍然非常庞大,因为 C++ 缺少生成器语法来使事情变得简单。
【讨论】:
【参考方案3】:您可以按照@Space_C0wb0y 的建议使用Boost.Bind
。但是如果你因为某种原因不能使用它,那么你可以做这样的事情:
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
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)();
;
;
阅读 Function object 以了解第二个示例。
在 C++0x(即 C++11)中,它非常简单。您可以在std::for_each
中使用 lamda 作为:
#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
;
【讨论】:
这是朝着正确方向迈出的一步,除了:1)有std::foreach()
,2)如果函数的签名不同,这将需要一些std/boost::bind()
魔法。或者使用 lambda 函数,如果你的编译器有这些。
@sbi:但是,foreach
需要将条件包装到一个额外的函数中。
@Space_C0wb0y:这就是我提出bind
和 lambda 的原因。 @Nawaz:太好了!赞成。
+1 用于第一个示例。我不喜欢第二个。他们要冗长。 (因为我想减少样板,而不是引入看起来不同的样板:-)【参考方案4】:
您可以使用Boost.Bind 将boost::function
对象传递给指定调用哪个方法的调度方法。那么您只需要一个可以使用不同目标方法作为参数调用的调度方法。
【讨论】:
以上是关于在 C++ 聚合类中实现调用多路复用的优雅方式?的主要内容,如果未能解决你的问题,请参考以下文章