Хитрая реализация обратного вызова (C ++)

0

Я пытаюсь придумать легкий класс, который бы делегировал функции static/member, функторы, замыкания и т.д. В моем проекте необходимо передать такие сущности, как аргументы функции, и, кроме того, это используется в некоторых других случаев.

В первый раз я решил использовать шаблоны и наследование и получил что-то вроде этого:

template<class ReturnType, class ...Args>
class callback {
private:

    //
    struct abstract_invoker {
        virtual ReturnType invoke(Args...) = 0;
    };

    //
    template<class Function>
    class sf_invoker : public abstract_invoker {
    public:
        sf_invoker(Function function) : _function(function) { };

        ReturnType invoke(Args ...args) override {
            return _function(args...);
        }

    private:
        Function _function;
    };

    //
    template<class Class>
    class mf_invoker : public abstract_invoker {
        using Function = ReturnType (Class::*)(Args...);

    public:
        mf_invoker(Class& target, Function function) : _target(target), _function(function) { };

        ReturnType invoke(Args ...args) override {
            return (_target.*_function)(args...);
        }

    private:
        Class&      _target;
        Function    _function;
    };

// --------------------------------------
public:
    template<class Function>
    callback(Function function) {
        _invoker = new sf_invoker<Function>(function);
    }
    template<class Class>
    callback(Class& object, ReturnType(Class::*function)(Args...)) {
        _invoker = new mf_invoker<Class>(object, function);
    }
    ~callback() {
        delete _invoker;
    }

    ReturnType operator() (Args ...args) {
        return _invoker->invoke(args...);
    }

private:
    abstract_invoker* _invoker;
};

На самом деле, этот метод решает мою проблему, но он работает немного медленно. Я пытался избежать виртуальных функций и эмпирически обнаружил, что приведенный ниже код работает (VS 2014):

template<class ReturnType, class ...Args>
class callback {
    using Function = ReturnType (*)(Args...);

public:
    template<class _Function>
    callback(_Function function) {
        auto invoker = [function] (Args ...args) -> ReturnType {
            return function(args...);
        };

        _invoker = &invoker;
    }
    template<class _Class>
    callback(_Class& object, ReturnType (_Class::*function)(Args...)) {
        auto invoker = [function, &object] (Args ...args) -> ReturnType {
            return (object.*function)(args...);
        };

        _invoker = &invoker;
    }

    ReturnType operator()(Args ...args) {
        return (*(Function*) _invoker)(args...);
    }

private:
    void* _invoker;
};

Очевидно, что эта реализация работает быстрее. Такой метод небезопасен, но я не очень хорошо знаю этот стандарт, поэтому я не знаю требований к компилятору для лямбда-выражений. Поэтому мой вопрос: это волшебство? Или он будет работать на большинстве компиляторов?

  • 2
    Это неопределенное поведение во всем.
  • 0
    Почему бы вам просто не использовать std :: function?
Показать ещё 10 комментариев
Теги:
c++11
lambda
closures

1 ответ

1
Лучший ответ

В вашем коде есть проблема жизни: вам нужно подумать о области действия объектов (и после этого они мертвы).

template<class Func>
callback(Func function) {
    auto invoker = [function] (Args ...args) -> ReturnType {
        return function(args...);
    };

    _invoker = &invoker;
}

invoker захватывает состояние, поэтому не может быть преобразован в указатель на функцию. Если _invoker был введен как Function компилятор, вероятно, предупредил бы вас.

Замечание одинаково для второй перегрузки.

Итак, если это работает, это случайно.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню