Here is my scenario:
There is a set of Instance
objects, the number of the objects being determined at run time.
For each of those instances, a custom heap is created. (Yes, I refer to ::HeapCreate()
et. all win32 functions).
struct Instance
{
HANDLE heap;
};
Now, there is some Instance
specific State object which has access (a pointer) to the Instance. Its a 1:1 relationship. 1 State per Instance.
struct State
{
Instance * inst;
};
In that State, I want to use STL containers like std::vector
and alike, but I want those to use an allocator, which uses the heap created by the instance.
Reading up on the history of std::allocator
from the times of C++98 up to C++17 and newer, some old requirements have been dropped, but my take away is, that it is not possible to write such an allocator for my above shown use case. Given that the Allocator would be of the same type each time, but stateful (as in instances using different heap HANDLE
s).
So here my question(s):
- Can I/ should I even try to use the STL allocator stuff for this use case or better just roll my select few own container classes instead?
- I stumbled across that
std::pmr
C++17 namespace and wonder if there is stuff in there I am not yet acquainted with, which would help me with my use case.
Your answers will be very valuable if you show the "idiomatic" way how to solve this problem in modern (c++17 and newer) C++.
Here, my incomplete and current "work in progress". I will fill in more over time, based on answers and comments and my own progress.
#include <Windows.h>
struct custom_heap_allocator
{
HANDLE heap;
// No default constructor
custom_heap_allocator() = delete;
// an instance is tied to a heap handle.
explicit custom_heap_allocator(HANDLE h)
: heap{ h }
{
}
// can I get away with this....
// copy constructor for same type.
custom_heap_allocator(const custom_heap_allocator& other)
: heap{ other.heap }
{
}
//... or do I need something like this? Or is it somehow covered by rebind?
//template<class U>
//custom_heap_allocator(const custom_heap_allocator<U>& other)
// : heap{ other.heap }
//{
//}
template<class U>
struct rebind {
typedef custom_heap_allocator other;
};
};
template <class T, class U>
constexpr bool operator== (const custom_heap_allocator& a1, const custom_heap_allocator& a2) noexcept
{
return a1.heap == a2.heap;
}
template <class T, class U>
constexpr bool operator!= (const custom_heap_allocator& a1, const custom_heap_allocator& a2) noexcept
{
return a1.heap != a2.heap;
}
Note: shutting down an Instance
will simply destroy the heap (no need to call destructors for stuff still on the heap, as those are just simple data and not system resources etc. And all dynamic stuff they hold are also from the same heap instance).