Search code examples
c++stlallocator

Is it possible to bundle "runtime" allocator information with an allocator in C++?


When we supply an allocator for std::vector in C++, note that we supply an allocator class. A class cannot be created at runtime.

However, what if I need my allocator to depends on runtime information? For example, we might need to determine how to allocate from the particular user input. Or, if we want to create a vector by using virtual memory from mmaping a file, but the file is not known at compile time, then we also need this. Surely we can use global variables for this, but surely that's not a good way.

Is it possible to store such runtime information in the use of std::vector?

The question is simple enough. However, after looking around, I see no answers at all. Apparently there are no elegant ways to do this?


Solution

  • Yes, it's possible. All containers in the standard library satisfy the allocator-aware requirement. In simple terms, this means that the container has to store an allocator as a subobject, and handle this object correctly when the container is copied, moved, etc. There are two approaches to implementing stateful allocators:

    Stateful Static Allocators

    template <typename T>
    struct mmap_allocator { /* ... */ };
    
    mmap_allocator<data> create_mmap_allocator(std::string_view file) { /* ... */ }
    
    auto create_vec() {
        std::vector<data, mmap_allocator<data>> result(create_mmap_allocator());
        /* ... */
        return result;
    }
    

    mmap_allocator can store state, such as which file was opened, a pointer to the mapped memory, etc. An obvious downside is that this std::vector is now distinct from a regular std::vector, since it uses a custom allocator.

    Polymorphic Allocators

    If you want even more customization, such as using a totally different type of allocator in the same vector type, this becomes annoying. It's also annoying that std::vector<T> and std::vector<T, mmap_allocator<T>> are incompatible. To solve this, there are polymorphic allocators:

    struct mmap_resource : std::pmr::memory_resource { /* ... */ };
    
    mmap_resource create_mmap_resource(std::string_view file) {
        /* ... */
    }
    
    auto use_vec() {
        // TODO: the allocator doesn't own the memory resource, so
        //       we might need to bundle the vector and the resource in
        //       a struct, etc.
        auto resource = create_mmap_resource(...);
        std::pmr::polymorphic_allocator alloc = &resource;
     
        std::pmr::vector<data> result(alloc);
        /* ... */
    }
    

    Polymorphic allocators are wrappers for a polymorphic class std::pmr::memory_resource, which you can customize.

    Also see: polymorphic_allocator: when and why should I use it?