Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I create a std::function with a custom allocator?

To save some code lets say I have a custom allocator named MyAlloc which I have successfully used with a std::vector<int> as follows:

std::vector<int,MyAlloc<int>> vec;

now I want to save a lambda in a std::function using the custom allocator, how do I do it?

My failed attempt:

int i[100];
std::function<void(int)> f(MyAlloc<void/*what to put here?*/>{},[i](int in){
    //...
});

Update: allocators in std::function have been deprecated

like image 431
odinthenerd Avatar asked Jan 13 '14 14:01

odinthenerd


People also ask

What is a custom allocator C++?

Allocators handle all the requests for allocation and deallocation of memory for a given container. The C++ Standard Library provides general-purpose allocators that are used by default, however, custom allocators may also be supplied by the programmer.

What is a std :: allocator?

std::allocator is the default memory allocator for the standard library containers, and you can substitute your own allocators. This allows you to control how the standard containers allocate memory.

What are the member function associated with std :: allocator ()?

Member functions associated with std::allocator() : address: It is used for obtaining the address of an object although it is removed in C++20. construct: It is used to construct an object.It is also removed in C++20. destroy: It is used to destruct an object in allocated storage.It is also removed in C++20.


2 Answers

According to the standard, you need to give a tag type as the first argument to indicate that you want to use a custom allocator:

std::function<void(int)> f(std::allocator_arg, MyAlloc<char>{}, [i](int in){
    //...
});

As pointed out by @Casey and @Potatoswatter in the comments, the template argument type given to the allocator does not matter, as long as it's an object type. So char is fine here.

Update for C++17: It turns out that the allocator support for std::function has a number of fundamental issues, which lead to it being deprecated in C++17. If you nonetheless insist on using it, be sure to carefully check your implementation before doing so. GCC's standard library never implemented those functions, but even if your standard library does, it might not behave the way that you expect.

like image 120
ComicSansMS Avatar answered Sep 30 '22 02:09

ComicSansMS


I realize this was answered properly, but even after reading this article and the replies I struggled a bit getting the syntax correct trying to overload an allocator for std::function that cross compiles on X64, PS4, and Xbox One in VS2012.

If it's not clear to the reader you will need to declare an allocator class per Casey's comment. Although this is fairly obvious if you read all the replies, what wasn't clear was the way these allocators are passed to the object which isn't like most of the STL allocators I've used before which take an allocator type (not instance) as part of the type specification.

For std::function an instantiated allocator is supplied to the constructor of your std::function object, which is what ComicSansMS is showing above.

For using this with a member function instead of the lambda code shown in this sample, it gets a bit tricky:

   #include <functional>

    MyAllocType g_myAlloc; // declared somewhere and globally instantiated to persist

    // sample of a member function that takes an int parameter
    class MyClassType
    {
    public:
        void TestFunction( int param )
        {
        }
    };

    MyClassType MyClass; // instantiated object

    // example without allocator
    // note the pointer to the class type that must precede function parameters since 
    // we are using a method. Also std::mem_fn is require to compile in VS2012 :/
    std::function<void(MyClassType*, int)> f( std::mem_fn( &MyClassType::TestFunction ) );

    // usage of function needs an instantiated object (perhaps there is a way around this?)
    f( &MyClass, 10 );

    // example with allocator
    std::function<void(MyClassType*, int)> f(std::allocator_arg, g_myAlloc, std::mem_fn( &MyClassType::TestFunction ) );

    // usage of function is the same as above and needs an instantiated object 
    f( &MyClass, 10 );

    //or a non member function, which is much cleaner looking
    void NonMemberFunction( int param )
    {
    }

    std::function<void(int)> f(std::allocator_arg, g_myAlloc, NonMemberFunction);

Hope this helps people, it took me longer than I'd like to admit to get this working, and as much as I use this site I figured I'd leave a comment here if for no one other than myself on how to use it. :)

2 final questions to those that are smarter than myself:

Q: Is there a way to include the allocator as part of the type?

Q: Is there a way to use a member function without an instance of an object?

To update this, if you decide to pass one of these std::function objects around as a parameter to some other function, I found I needed to use std::function::assign, or else the assignment results in a shallow copy. This can be a problem if you are trying to pass it along to an object with a longer lifecycle than the original.

Example:

std::function<void(MyClassType*, int)> f(std::allocator_arg, g_myAlloc, std::mem_fn( &MyClassType::TestFunction ) );

void FunctionTakeParam( std::function<void(MyClassType*, int)> &FunctionIn )
{
    // this results in a reallocation using your allocator
    std::function<void(MyClassType*, int)> MyLocalFunction.assign( std::allocator_arg, g_myAlloc, FunctionIn ); 

    // the below results in a shallow copy which will likely cause bad things
    //std::function<void(MyClassType*, int)> MyLocalFunction( std::allocator_arg, g_myAlloc, FunctionIn ); 

    ...
}
like image 20
jeffdev Avatar answered Sep 30 '22 02:09

jeffdev