Search code examples
c++c++11std-functionstdbind

Is it possible to accept the output of `std::bind` directly as a value, without conversion to std::function?


This answer states that std::bind returns an object by value, and this comment implies that assigning to std::function will cause a heap allocation to store the value returned by std::bind.

Is there a way to avoid this heap allocation and pass the return value of std::bind to another function directly by value?

If so, what would the method signature replace std::function with?


To be more explicit, I have a function like the following.

void runThisFunction(std::function<void()> func);

Suppose there is a function foo with the following signature.

void foo(int a, int b);

Now, I would invoke runThisFunction as follows.

runThisFunction(std::bind(foo, 1, 2));

In this invocation, the output of std::bind is converted into an std::function, and dynamic memory allocation occurs as part of this process.

Is it possible to replace std::function with some other declaration that would would receive the output of std::bind directly by value, thereby avoiding the dynamic memory allocation?


Solution

  • Is it possible to replace std::function with some other declaration that would would receive the output of std::bind directly by value, thereby avoiding the dynamic memory allocation?

    Yes it is; but the type returned by std::bind is unspecified, so you will need to use a template to capture the type;

    template <typename F>
    void runThisFunction(F func);
    

    On the memory allocation...

    In this invocation, the output of std::bind is converted into an std::function, and dynamic memory allocation occurs as part of this process.

    Dynamic memory may be used (but not always), it depends on the size of the functor being bound into the std::function and the quality of implementation.

    Further, the C++ spec has this §20.12.12.2.1/11;

    [Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable objects, for example, where f is an object holding only a pointer or reference to an object and a member function pointer. — end note ]

    I would not be too concerned about the memory allocation even if there is one. Unless the code is performance critical and you have measured it as such, the indirection required should not be a problem.

    Bear in mind that for your case, the foo being bound in the bind is a pointer and it is likely that there would be no dynamic memory allocation anyway.


    I started looking at this because I measured cache misses on the conversion due to unexpected slowness detected through instrumentation

    So you have some measured concerns about the performance... there are alternatives to using std::bind paired with std::function. std::bind is useful general purpose binder, but that doesn't mean it will also be performant enough - make your own. A custom functor could be more performant. A lambda based implementation would also be good to look at. Don't forget either that the function foo can be used with std::function as well and then you forgo the functor/binder completely (caveat, the signatures need to match).


    A side note on how "small" the object needs to be before the "small object" optimisations mentioned in the quote above kick in seems to vary between the library implementations quiet a bit.

    Here on coliru (libstdc++), the size of the argument for std::function needs to be 16 bytes or less, on MSVC, the limit is 32 bytes (both of these look to be 32-bit platforms). With a clang++ (libc++) 64-bit compile, this limit is 24 bytes... It really is up to the implementation how much space they will allow for before new allocations need to be made.

    I'm not sure how critical the performance is, but calculating this limit for your targets could also be done and then optimisations applied such that the arguments for the std::function are kept below these limits; e.g. using a pointer or reference (also std::ref) to a struct for arguments (but care must be taken that these are not left dangling).