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 mmap
ing 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?
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:
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.
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?