I have created a custom memory allocator. To use it with STL Containers, I've also created a wrapper so that it adheres to the std::allocator_traits
requirements.
#include <cstdint>
#include <memory>
#include <deque>
#include <queue>
class CustomAlloc
{
public:
CustomAlloc(const std::size_t& maxMem) noexcept
:
m_maxMem(maxMem),
m_usedMem(0)
{
}
CustomAlloc(CustomAlloc&& other) noexcept
:
m_maxMem(other.m_maxMem),
m_usedMem(other.m_usedMem)
{
}
CustomAlloc& operator=(CustomAlloc&& other) noexcept
{
m_maxMem = other.m_maxMem;
m_usedMem = other.m_usedMem;
return *this;
}
CustomAlloc(const CustomAlloc&) = delete;
CustomAlloc& operator=(CustomAlloc&) = delete;
[[nodiscard]] void* Allocate(const std::size_t& size)
{
if(m_usedMem size > m_maxMem)
{
throw std::bad_alloc();
}
void* ptr = std::malloc(size);
if(ptr == nullptr)
{
throw std::bad_alloc();
}
m_usedMem = size;
return ptr;
}
void Free(void* const ptr) const
{
return std::free(ptr);
}
const std::size_t& GetMaxMem() const noexcept
{
return m_maxMem;
}
private:
std::size_t m_maxMem;
std::size_t m_usedMem;
};
template<typename T, typename Alloc>
class STLAdaptor
{
public:
typedef T value_type;
STLAdaptor(Alloc& allocator) noexcept
:
m_allocator(allocator)
{
}
template<typename U>
STLAdaptor(const STLAdaptor<U, Alloc>& other) noexcept
:
m_allocator(other.m_allocator)
{}
[[nodiscard]] constexpr T* allocate(std::size_t n)
{
return reinterpret_cast<T*>
(m_allocator.Allocate(n * sizeof(T)));
}
constexpr void deallocate(T* p, [[maybe_unused]] std::size_t n)
{
m_allocator.Free(p);
}
std::size_t MaxAllocationSize() const
{
return m_allocator.GetMaxMem();
}
bool operator==(const STLAdaptor<T,Alloc>& rhs)
{
return m_allocator.GetStart() == rhs.m_allocator.GetStart();
}
bool operator!=(const STLAdaptor<T,Alloc>& rhs)
{
return !(*this == rhs);
}
Alloc& m_allocator;
};
template<typename T, typename Alloc>
using CustomDeque = std::deque<T, STLAdaptor<T,Alloc>>;
template<typename T, typename Alloc>
using CustomQueue = std::queue<T, CustomDeque<T, Alloc>>;
The issue I'm facing:
int main()
{
CustomAlloc customAlloc(3000000);
CustomDeque<int, CustomAlloc> customDeque(customAlloc);
CustomQueue<int, CustomAlloc> customQueue(STLAdaptor<int, CustomAlloc>{customAlloc});
return 0;
}
This works perfectly fine, but it's code-gore to have to pass in the STLAdaptor<int, CustomAlloc>{customAlloc}
where all other STL Containers can rely on the converting constructor for the STLAdaptor
.
What I'd like to do is:
int main()
{
CustomAlloc customAlloc(3000000);
CustomDeque<int, CustomAlloc> customDeque(customAlloc);
CustomQueue<int, CustomAlloc> customQueue(customAlloc);
return 0;
}
Where the customAlloc
is somehow passed to the underlying std::deque
.
CodePudding user response:
If you can't change CustomAlloc code as Ted Lyngmo suggested, the alternative is to define CustomQueue as a subclass of std::queue:
template<typename T, typename Alloc>
struct CustomQueue : std::queue<T, CustomDeque<T, Alloc>>
{
CustomQueue(Alloc& alloc) :std::queue<T, CustomDeque<T, Alloc>>(STLAdaptor<T, Alloc>(alloc)) {}
};
Now you can construct it by passing merely the allocator:
int main()
{
CustomAlloc customAlloc(3000000);
CustomDeque<int, CustomAlloc> customDeque(customAlloc);
CustomQueue<int, CustomAlloc> customQueue(customAlloc);
customQueue.push(5);
return 0;
}
This code was tested on VS 2019.