Comment by kjksf

Comment by kjksf 6 months ago

2 replies

I don't know fancy C++ so I don't understand your point about perfect forwarding.

But I do know the code I write and you're wrong about performance of Func0 and Func1. Those are 2 machine words and all it takes to construct them or copy them is to set those 2 fields.

There's just no way to make it faster than that, both at runtime or at compile time.

The whole point of this implementation was giving up fancy features of std::function in exchange for code that is small, fast (both runtime and at compilation time) and one that I 100% understand in a way I'll never understand std::function.

cherryteastain 6 months ago

In this function

    void Call(T arg) const {
        if (fn) {
            fn(userData, arg);
        }
    }
Say you pass something like an std::vector<double> of size 1 million into Call. It'll first copy the std::vector<double> at the point you invoke Call, even if you never call fn. Then, if fn is not nullptr, you'll then copy the same vector once more to invoke fn. If you change Call instead to

    void Call(T&& arg) const {
        if (fn) {
            fn(userData, std::forward<T>(arg));
        }
    } 
the copy will not happen at the point Call is invoked. Additionally, if arg is an rvalue, fn will be called by moving instead of copying. Makes a big difference for something like

    std::vector<double> foo();
    void bar(Func1<std::vector<double>> f) {
        auto v = foo();
        f(std::move(v));
    }
OskarS 6 months ago

> But I do know the code I write and you're wrong about performance of Func0 and Func1. Those are 2 machine words and all it takes to construct them or copy them is to set those 2 fields.

You also have to heap allocate your userData, which is something std::function<> avoids (in all standard implementations) if it’s small enough (this is why the sizeof() of std::function is larger than 16 bytes, so that it can optionally store the data inline, similar to the small string optimization). The cost of that heap allocation is not insignificant.

If I were doing this, I might just go the full C route and just use function pointers and an extra ”userData” argument. This seems like an awkward ”middle ground” between C and C++.