Эффективный метод передачи функции-члена обратного вызова - PullRequest
4 голосов
/ 23 июня 2010

Я разрабатываю метод для класса обработчика ввода.Вот некоторый псевдокод ...

void InputHandler::ScanEvents( boost::function1< void, std::string& > &func ) {
   // Scan keys, determining string to pass
     // If string found, call func with string as its argument on object tied to func
}

Я не совсем уверен, как реализовать это, или если это вообще возможно, так как весь смысл функции состоит в том, чтобы отделить ее от вызывающего.Идея состоит в том, что у объекта есть закрытая функция-член и член boost :: function, который ее содержит.Всякий раз, когда он вызывает ScanEvents для своего InputHandler, он передает эту функцию, поэтому ScanEvents может «активировать его» всякий раз, когда найдено соответствующее событие.и эта функция часто вызывается.

PS Клянусь, я помню, как читал такой пример в одной из книг Скотта Мейера, но не могу найти его для своей жизни.Может быть, это было в современном C ++ Design ... глядя ....

Ответы [ 4 ]

3 голосов
/ 23 июня 2010

boost::function имеет довольно медленные издержки - около 20 нс на вызов . Я бы не стал вызывать его из внутреннего цикла, но при этом я бы не вызвал любой другой вид указателя на функцию. Чтобы вызвать функцию-член, просто используйте boost::lambda::bind для создания анонимной функции-оболочки:

ih->ScanEvents(boost::lambda::bind(&ThisClass::CallbackFunc, this, boost::lambda::_1));

Один из вариантов, если вам действительно нужна действительно высокая производительность, - это использовать встроенную функцию шаблона с параметром boost::lambda functor:

template<typename F>
double democaller(const F &f) {
    double x = 1;
    for (int i = 0; i < 1000000; i++) {
        x = f(x);
    }
    return x;
}

namespace l = boost::lambda;

void demouser() {
    std::cout << democaller(l::_1 + 1);
}

При достаточно высоких настройках оптимизации лямбда-выражение может быть встроено в вызывающий сайт, почти полностью устраняя издержки.

2 голосов
/ 23 июня 2010

Что-то вроде

class Thingy
{
public:
    Thingy() : callback(bind(&Thingy::Callback, this, _1)) {}

    void DoStuff()
    {
        handler.ScanEvents(callback);
    }

private:
    InputHandler handler;
    function<void(string)> callback;

    void Callback(string s)
    {
        cout << "Called with " << s << endl;
    }
};

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

1 голос
/ 23 июня 2010

Возможно, вы ищете реализацию команды в Modern C ++ Design.

boost :: function имеет столько же накладных расходов, сколько необходимо для выполнения того, что она делает.Его цель - позволить вам передать «указатель» на любую функцию, которая может реагировать на ее интерфейс.Если вам не нужно такое поведение, тогда эти накладные расходы являются реальными накладными расходами.Если вы это сделаете, то, честно говоря, я не вижу лучшего подхода, чем boost :: function (он даже оптимизирован, чтобы в некоторых реализациях вы не получили кучу дополнительного использования памяти от виртуальных функций).

Можно создать метод, который приведет к возможно встроенному коду, но как только вы попытаетесь сохранить его в универсальном интерфейсе, вы получите служебные данные boost :: function (возможно, больше).

ЧтоЯ бы порекомендовал просто использовать boost :: function до тех пор, пока вы не обнаружите, что вам действительно нужно заменить его чем-то более быстрым.ТОГДА, и только тогда вы пишете шаблон различной сложности, чтобы это произошло.

Простого сохранения boost :: function может быть вполне достаточно для того, что вам нужно.Вы также можете посмотреть на boost :: сигналы.

0 голосов
/ 23 июня 2010

Обычно вызов частного метода в другом классе - не лучшая идея.

Вы можете реализовать класс в стиле интерфейса и наследовать его, а также написать свой метод ScanEvents, чтобы ожидать ввод такого типа, например:

class Notifyable {
  public:
    virtual void notify(std::string s) = 0;
};

void InputHandler::ScanEvents( Notifyable &n ) {
    // Scan keys, determining string to pass
      // If string found, call func with string as its argument on object tied to func
      n.notify(<string>);
}

Я думаю, это тоже может быть быстрее, просто потому, что вы избегаете любых возможных накладных расходов с помощью boost :: function wrapper.

...