Передача Функтора в качестве параметра в метод - PullRequest
3 голосов
/ 29 марта 2011

У меня есть два следующих класса, которые демонстрируют шаблон Command. (К сожалению, оба имеют уникальное имя метода.)

//pseudocode 
class Subscriber {
    virtual void receive(const Event&) = 0;
}

class Dispatcher {
    virtual void dispatch(const Event&) = 0;
}

У меня есть шаблон класса, у которого есть список некоторого типа с методом, чтобы перебрать этот список.

//pseudocode
template<typename T>
class Registry {
    typedef list<T> ObjectList;
    ObjectList _objects;
    void iterate(const Event& event) {
        for_each(_objects.begin(), _objects.end(), ...);  //not sure what to do here
    }
}

Я хотел бы использовать mem_fun для создания Functor, который вызывает receive или dispatch в зависимости от ситуации. Я могу создать простой вариант использования, где я просто вызываю метод без каких-либо параметров. * 1010 Т.е. *

//pseudocode
class Simple {
    void simple() {/*...*/}
}

и затем я повторяюсь:

for_each(_objects.begin(), _objects.end(), mem_fun(&Simple::simple);

К сожалению, я понятия не имею, как передать параметр event в mem_fun. Глядя на заголовки, кажется, что я могу передать один параметр, но я не очень разбираюсь в C ++, чтобы понять, что мне нужно делать.

В конечном счете, я бы хотел, чтобы метод iterate принимал тип функтора, поэтому он будет запускать этот метод для каждого метода в списке.

Я бы предпочел избегать Boost ... Я думаю, что это вполне возможно без перетаскивания этой структуры в микс.

Спасибо!

Ответы [ 4 ]

6 голосов
/ 29 марта 2011

Это может быть самый простой способ:

class event_forwarder // make private in Registry
{
public:
    event_forwarder(const Event& event) :
    mEvent(event)
    {}

    void operator()(Subscriber& subcriber) const
    {
        subscriber.receive(mEvent);
    }

    void operator()(Dispatcher& dispatcher) const
    {
        dispatcher.dispatch(mEvent);
    }

private:
    const Event& mEvent;
};

Тогда:

for_each(_objects.begin(), _objects.end(), event_forwarder(event));
4 голосов
/ 29 марта 2011

Если я правильно понимаю, вам нужно: std::bind2nd:

std::for_each(_objects.begin(), _objects.end(), 
              std::bind2nd(std::mem_fun_ref(&Subscriber::receive), event));

Функция-член Subscriber::receive имеет два параметра.Первый - это неявный указатель this, а второй - const Event &.std::bind2nd, если функция f принимает два аргумента, возвращает функцию f_1, принимающую один аргумент, которая вызывает исходную функцию f с фиксированным значением для второго аргумента.

Редактировать:

Для обработки различных имен функций диспетчеризации вы можете сделать функцию диспетчеризации параметром шаблона:

//pseudocode
template<typename T, void (T::*dispatch_method)(Event)>
class Registry {
    typedef list<T> ObjectList;
    ObjectList _objects;
    void iterate(const Event& event) {
        std::for_each(_objects.begin(), _objects.end(), 
                      std::bind2nd(std::mem_fun_ref(dispatch_method), event));
    }
}

К сожалению, похоже, чтобыть способ сделать bind2nd ручка const эталонные параметры, так что все мое решение спорно, если копирование Event возразит прекрасно с вами. Это будет работать в C ++ 0x с std::bind хотя,и идея сделать диспетчерскую функцию параметром шаблона все еще действительна.Вы даже можете использовать черты, чтобы сделать этот механизм еще более гибким.

0 голосов
/ 29 марта 2011

Вы можете создать класс функторов, который обернет ваши классы Subscriber и Dispatcher, например,

class MyFunctor {
  public:
    virtual void Execute(const Event& event) = 0;
};

class MySubscriberFunctor : public MyFunctor {
  private:
    Subscriber subscriber_;
  public:
    void Execute(const Event& event) {
      subscriber_.receive(event);    
    }
};

class MyDispatcherFunctor : public MyFunctor {
  private:
    Dispatcher dispatcher_;
  public:
    void Execute(const Event& event) {
      dispatcher_.dispatch(event);    
    }
};

Ваш список объектов может затем хранить эти обертки функторов в виде списка экземпляров MyFunctor.Таким образом, вы можете вызвать Execute () для них и позволить им делегировать базовые классы.Вы должны действительно иметь оператор () вместо Execute (), чтобы получить «настоящий» функтор, но вы поняли идею.

Приветствия

0 голосов
/ 29 марта 2011

Проверьте, есть ли у вас tr1.Если у вас есть tr1, он содержит std::bind, что почти точно соответствует реализации boost.Это должно быть найдено в заголовке <functional>.

Если у вас нет tr1, рассмотрите возможность использования Boost.Я настоятельно рекомендую использовать по крайней мере boost::bind, так как он легкий и только заголовок.

Если у вас нет tr1 и вы не будете использовать Boost, вы хотите смешать bind2nd и mem_fun1.Первый связывает второй параметр (в данном случае ваше событие; объект будет первым), и mem_fun1 совпадает с mem_fun, но он ожидает два аргумента: объект, который будет вызван, и один параметр для передачивызываемая функция-член.Это полный беспорядок.

Если у вас есть доступ к bind, это довольно просто.

for_each(objects.begin(), objects.end(), bind(&Simple::simple, _1, event))

...