alphashooter
alphashooter

Reputation: 314

Tricky callback realization (C++)

I have been trying to think out a lightweight class which would delegate static/member functions, functors, closures, etc. In my project, it is necessary to pass such the entities as function arguments, and in addition, this is used in some other cases.

The first time, I decided to use templates and inheritance and got something like this:

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

Actually, this method solves my problem but it works a bit slow. I tried to avoid virtual functions and empirically discovered that the code below works as well (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;
};

Obviously, this implementation works faster. Such the method is not safe, but I do not know the standard very well, so I do not know the compiler requirements for lambda expressions. So my question is: Is it magic? Or would it work on the most of compilers?

Upvotes: 0

Views: 158

Answers (1)

Matthieu M.
Matthieu M.

Reputation: 299740

Your code has an issue of lifetime: you have to think of the scope in which objects are valid (and after which they are dead).

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

    _invoker = &invoker;
}

invoker captures state, therefore cannot be transformed into a pointer to function. If _invoker were typed as Function the compiler would probably warn you.

The remark is the same for the second overload.

So, if it works, it's accidental.

Upvotes: 1

Related Questions