Add ArenaAllocator::Ptr
This commit is contained in:
@@ -302,38 +302,87 @@ public:
|
||||
new_size * sizeof(T), alignof(T)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Smart pointer for arena-allocated objects with non-trivial
|
||||
* destructors.
|
||||
*
|
||||
* ArenaAllocator::Ptr calls the destructor but does not free memory (assumes
|
||||
* arena allocation). This provides RAII semantics for objects that need
|
||||
* cleanup without the overhead of individual memory deallocation.
|
||||
*
|
||||
* @tparam T The type of object being managed
|
||||
*/
|
||||
template <typename T> struct Ptr {
|
||||
Ptr() noexcept : ptr_(nullptr) {}
|
||||
|
||||
explicit Ptr(T *ptr) noexcept : ptr_(ptr) {}
|
||||
|
||||
Ptr(const Ptr &) = delete;
|
||||
Ptr &operator=(const Ptr &) = delete;
|
||||
|
||||
Ptr(Ptr &&other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; }
|
||||
|
||||
Ptr &operator=(Ptr &&other) noexcept {
|
||||
if (this != &other) {
|
||||
reset();
|
||||
ptr_ = other.ptr_;
|
||||
other.ptr_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Ptr() { reset(); }
|
||||
|
||||
T *operator->() const noexcept { return ptr_; }
|
||||
T &operator*() const noexcept { return *ptr_; }
|
||||
|
||||
T *get() const noexcept { return ptr_; }
|
||||
|
||||
explicit operator bool() const noexcept { return ptr_ != nullptr; }
|
||||
|
||||
T *release() noexcept {
|
||||
T *result = ptr_;
|
||||
ptr_ = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
void reset(T *new_ptr = nullptr) noexcept {
|
||||
if (ptr_) {
|
||||
ptr_->~T();
|
||||
}
|
||||
ptr_ = new_ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T *ptr_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Construct an object of type T in the arena using placement new.
|
||||
*
|
||||
* This is a convenience method that combines allocation with in-place
|
||||
* construction. It properly handles alignment requirements for type T.
|
||||
* This method returns different types based on whether T is trivially
|
||||
* destructible:
|
||||
* - For trivially destructible types: returns T* (raw pointer)
|
||||
* - For non-trivially destructible types: returns ArenaAllocator::Ptr<T>
|
||||
* (smart pointer that calls destructor)
|
||||
*
|
||||
* @tparam T The type of object to construct (must be trivially destructible)
|
||||
* @tparam T The type of object to construct
|
||||
* @tparam Args Types of constructor arguments
|
||||
* @param args Arguments to forward to T's constructor
|
||||
* @return Pointer to the constructed object
|
||||
* @return T* for trivially destructible types, ArenaAllocator::Ptr<T>
|
||||
* otherwise
|
||||
* @note Prints error to stderr and calls std::abort() if memory allocation
|
||||
* fails
|
||||
*
|
||||
* ## Type Requirements:
|
||||
* T must be trivially destructible (std::is_trivially_destructible_v<T>).
|
||||
* This prevents subtle bugs since destructors are never called for objects
|
||||
* constructed in the arena.
|
||||
*
|
||||
*
|
||||
* ## Note:
|
||||
* Objects constructed this way cannot be individually destroyed.
|
||||
* Their destructors will NOT be called automatically - hence the requirement
|
||||
* for trivially destructible types.
|
||||
*/
|
||||
template <typename T, typename... Args> T *construct(Args &&...args) {
|
||||
static_assert(
|
||||
std::is_trivially_destructible_v<T>,
|
||||
"ArenaAllocator::construct requires trivially destructible types. "
|
||||
"Objects constructed in the arena will not have their destructors "
|
||||
"called.");
|
||||
template <typename T, typename... Args> auto construct(Args &&...args) {
|
||||
void *ptr = allocate_raw(sizeof(T), alignof(T));
|
||||
return new (ptr) T(std::forward<Args>(args)...);
|
||||
T *obj = new (ptr) T(std::forward<Args>(args)...);
|
||||
|
||||
if constexpr (std::is_trivially_destructible_v<T>) {
|
||||
return obj;
|
||||
} else {
|
||||
return Ptr<T>(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user