在 C++ 中实现呼叫多路复用的优雅方式聚合类?

发布于 2024-11-08 19:07:25 字数 710 浏览 0 评论 0原文

当多路调用许多子对象时,防止循环样板代码的优雅方法是什么?

问题描述示例:

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 成员函数的实现中删除样板文件的优雅方法是什么

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

月朦胧 2024-11-15 19:07:25

您可以按照@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 作为:

#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)();
            }
        }
   };    
};

阅读有关函数对象来理解第二个示例。


在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
};

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
};
又怨 2024-11-15 19:07:25

您可以使用 Boost.Bind 来传递 boost ::function 对象放入指定调用哪个方法的调度方法中。那么您只需要一种可以使用不同目标方法作为参数来调用的调度方法。

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.

絕版丫頭 2024-11-15 19:07:25

我将采用 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); }

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); }
故人如初 2024-11-15 19:07:25

纳瓦兹的回答很有趣,但还有其他解决方案。

首先,您应该认识到您的聚合器在很大程度上是一种Composite 模式。

其次,我会选择 for:

  • 外部迭代
  • 类似 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;
};

然后,您的聚合仅具有 BeginEnd 方法,并且由调用者与您的迭代器交互。

注意:您可以将其设置为模板,以便一次性实现可变/常量实现

不过,外部迭代仍然非常庞大,因为 C++ 缺乏使事情变得简单的生成器语法。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文