A lightweight fixed-size memory pool for C++ with RAII-based object lifetime management.
Designed for performance-critical and low-level code where dynamic allocations must be minimized or fully avoided.
- Header-only
- Fixed-capacity pool (no heap allocations after construction)
- Strong RAII ownership model (
PoolHandle<T>) - Free-list reuse (O(1) allocation / deallocation)
- Correct alignment handling (supports over-aligned types)
- Strong exception safety for object construction
- Optional compile-time thread safety (mutex-based)
- Optional user-defined error callback
- C++20 constraints (
std::destructible)
- C++20 or newer
- No external dependencies
#include "MemOx/object_pool.hpp"
struct Foo {
int a, b;
Foo(int x, int y) : a(x), b(y) {}
};
int main() {
ObjectPool<Foo> pool(128);
auto obj = pool.emplace(1, 2);
obj->a = 42;
// Object is automatically destroyed
// and returned to the pool when leaving scope
}using LogFunction = void (*)(const std::string&);
explicit ObjectPool(size_t capacity, LogFunction log = nullptr);Creates a pool capable of holding up to capacity objects of type T.
log — optional callback used for internal diagnostics and debugging.
auto obj = pool.emplace(args...);- Constructs
Tin-place inside the pool - Returns a
PoolHandle<T>with unique ownership - The object is automatically destroyed and returned to the pool when the handle goes out of scope
- Strong exception guarantee:
- If
T's constructor throws, the slot is returned to the pool - No leaks, the pool remains usable
- If
- If the pool is exhausted, an error is reported (exception or user-defined error callback)
// Non-copyable, non-movable
ObjectPool(const ObjectPool&) = delete;
ObjectPool& operator=(const ObjectPool&) = delete;
ObjectPool(ObjectPool&&) = delete;
ObjectPool& operator=(ObjectPool&&) = delete;ObjectPool is neither copyable nor movable.
- This guarantees:
- a single owner of the underlying memory block
- stable object addresses for the entire lifetime of the pool
- no accidental duplication or transfer of pool ownership
size_t size() const noexcept;
size_t capacity() const noexcept;size()— current number of live objectscapacity()— maximum pool capacity
RAII wrapper representing unique ownership of an object inside the pool.
- Move-only
- Automatically destroys the object on scope exit
- Returns memory back to the pool
- Safe to reset explicitly
T* get() const noexcept;
T& operator*() const;
T* operator->() const;
explicit operator bool() const;
void reset() noexcept;~ObjectPool() noexcept
{
#ifndef NDEBUG
assert(used_count_.load(std::memory_order_acquire) == 0 &&
"ObjectPool destroyed with live objects");
#endif
::operator delete(pool_memory_, std::align_val_t{kSlotAlign});
}The pool must outlive all objects allocated from it.
In debug builds, destroying an ObjectPool while it still owns live objects triggers an assertion failure, helping detect lifetime and ownership bugs early.
In release builds, the pool releases its internal memory without additional checks.
- Strong exception safety guarantee for
emplace() - If a constructor throws:
- Slot is returned to the free-list
size()counter is rolled back- No memory leaks
- Destructors of
Tmust not throw
- Not thread-safe
#define OxiMemPool_ThreadSafe
#include "MemOx/object_pool.hpp"- All public pool operations are protected by a single
std::mutex - Safe for concurrent
emplace()and object destruction - Object construction and destruction occur outside the mutex
- Slight performance overhead compared to single-threaded mode
- Not lock-free
- Errors are reported via
std::runtime_errorexceptions
#define OxiMemPool_ErrCallback
#include "MemOx/object_pool.hpp"Callback signature:
void(const char* message, size_t errorCode)Usage:
void MyErrorHandler(const char* msg, size_t code) {
// custom logging, abort, etc.
}
int main() {
ObjectPool<int> pool(10);
pool.set_error_callback(&MyErrorHandler);
}- If a callback is provided, it is invoked instead of throwing
- Typical error cases:
- Pool exhausted
- Pool constructed with zero capacity
| Macro | Values | Description |
|---|---|---|
| OxiMemPool_ThreadSafe | 0 / 1 | Enables mutex-based thread safety |
| OxiMemPool_ErrCallback | 0 / 1 | Enables user-defined error callback support |
- Pool size is fixed at construction time
- No automatic growth
- Objects are not zero-initialized
- No bounds checking in release builds
- Not lock-free (even in thread-safe mode)
- Pool destruction must be externally synchronized in multithreaded code
- Game engines
- ECS / component storage
- Custom allocators
- Embedded and real-time systems
- Low-level infrastructure
- Performance-critical subsystems
This project is licensed under the MIT License — see LICENSE.
@0x1mer
https://github.com/0x1mer