1

Looking at some static-allocation replacements for std::function, ones that don't involve any std libraries.
Soon to realize that some only support stateless lambdas (any reason for that? ) .
E.g., vl seems to work fine with [&], while etl does not - I get garbage values.

My test code:

template<typename Func, typename... Args>
class Callback
{
public:
    Func dFunc;
    etl::delegate<void(Func v, int& ret )> dLambda;
    vl::Func<void(Func func, int& ret)> dLambda1;

    Callback(Func func, Args&...args) :
        dFunc(func),
        dLambda([&args...](Func v, int& ret) { ret = v(args...); }),
        dLambda1([&args...](Func v, int& ret) { ret = v(args...); })
    {}

    void run(int& ret)
    {
        dLambda(dFunc, ret );

        //auto test = dLambda;
        //CHECK_EQUAL(true, is_stateless<decltype(test)>::value);

    }
    void run1(int & ret)
    {
        dLambda1(dFunc, ret);

        //auto test1 = dLambda1;
        //CHECK_EQUAL(true, is_stateless<decltype(test1)>::value);
    }
};

I tested them both for is_stateless stateless test , both said they are not! (??) Furthermore the test itself 'fixed' a non working replacement (??)

I would like to know what is added to the ones that do support [&] ? What should I look for?

2
  • "I tested them both for is_stateless stateless test , both said they are not! (??)" You're not asking about the content of the functor; you're asking about the functor type itself. Which is not a lambda. dLambda1 may contain a lambda, but its type is not a lambda, let alone "stateless". Commented Aug 28, 2022 at 5:11
  • If I understand correctly I can't extract the values from lambda, so this is the only thing I came up with... Commented Aug 28, 2022 at 5:20

1 Answer 1

3

ETL documents that etl::delegate doesn't own the lambda at all, see https://www.etlcpp.com/delegate.html. It only stores a pointer to the passed object. It doesn't store the lambda at all. See also the code at https://github.com/ETLCPP/etl/blob/master/include/etl/private/delegate_cpp11.h#L117.

Contrary to what the linked documentation seems to imply, it is always undefined behavior to pass the constructor of etl::delegate a lambda expression directly as argument, no matter whether it is stateless or not. Because etl::delegate stores a pointer to the passed object, when operator() is called it will try to call a non-static member function on an out-of-lifetime object.

The undefined behavior is just less likely to cause unintended behavior of the compiled program if the lambda is stateless and so the compiled member function called on the invalid pointer doesn't actually have to access/dereference the pointer.

You must store the lambda somewhere outside the etl::delegate.

I am not sure why the author didn't add a lambda-to-function-pointer conversion for non-capturing lambdas and/or disallowed rvalue arguments to the constructor. The way it is written now it is extremely easy to misuse.


vl::Func seems to implement a pattern similar to std::function and does own the passed callable. That also means it uses new to store the callable in dynamic memory, but no matching delete as far as I can tell.

Therefore it is not a static-allocation replacement. It does use dynamic allocation.

(Sorry for my previous assertion that it is leaking the object. I completely misread the code. I don't see any defect in the implementation anymore as far as the superficial look I had at the code.)


It is not possible to implement what std::function does in generality without dynamic allocation. A static allocation replacement for std::function which also owns the callable must have some parameter limiting the maximum size of a callable object it can store or something similar because the callable would need to be embedded directly into the storage of the std::function-replacement object.

From the examples above you can see that problem. Both of them don't have such a parameter. So one of the replacements isn't owning and the other isn't statically allocating, they can't be doing both.


To test whether the lambda is stateless you need to apply is_stateless to the type of the lambda expression, e.g.

auto lambda = [&args...](Func v, int& ret) { ret = v(args...); };

CHECK_EQUAL(true, is_stateless<decltype(lambda)>::value);

You are applying is_stateless to a type that is not a lambda at all.

Sign up to request clarification or add additional context in comments.

5 Comments

Could I be confusing and github.com/khoih-prog/functional-vlpp is using heap allocation, I see a new and delete in code, but not other std library..?
Thanks so much! So if I limit the number of allocations, could I use vl as static? can you provide small fix?
@schantischul There is no small fix. It requires a completely different design than either of the two replacements you suggested.
@schantischul I made a serious mistake while looking at the vl::Func code. It does not leak anything and I don't see (superficially) any defects in the implementation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.