Created
March 16, 2022 16:03
-
-
Save igutekunst/2b4831cbb9884042be28b4a9064d2dc2 to your computer and use it in GitHub Desktop.
Pool Allocator and Unique Pointer with Deleter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// License: Do what you want, but if you use this code, you're on your own. | |
// Alternatively, you can use it with an MIT license, or BSD 3-clause license. | |
#include <iostream> | |
#include <stdio.h> | |
#include <vector> | |
#include <memory> | |
#include <queue> | |
#include <typeinfo> | |
#include <cxxabi.h> | |
#include <tuple> | |
#include <utility> | |
namespace gute { | |
template<typename T> | |
class unique_ptr { | |
using Deleter = void (*)(T *, void *context); | |
public: | |
unique_ptr(T *t, Deleter d = nullptr, void* context = nullptr) : t(t), deleter(d), context(context) {} | |
~unique_ptr() { | |
if (deleter) { | |
t->~T(); | |
deleter(t, context); | |
} else { | |
delete t; | |
} | |
} | |
unique_ptr(std::nullptr_t) : t(nullptr), deleter(nullptr), context(nullptr) {} | |
unique_ptr &operator=(std::nullptr_t) { | |
reset(); | |
return *this; | |
} | |
// Constructor/Assignment that allows move semantics | |
unique_ptr(unique_ptr &&moving) noexcept: t(nullptr), deleter(nullptr), context(nullptr) { | |
moving.swap(*this); | |
} | |
unique_ptr &operator=(unique_ptr &&moving) noexcept { | |
moving.swap(*this); | |
return *this; | |
} | |
// Constructor/Assignment for use with types derived from T | |
template<typename U> | |
unique_ptr(unique_ptr<U> &&moving) { | |
// TODO see if there is a way to no make deleter and context public | |
unique_ptr<T> tmp(moving.release(), | |
reinterpret_cast<Deleter> (moving.deleter), moving.context); | |
tmp.swap(*this); | |
moving.deleter = nullptr; | |
moving.context = nullptr; | |
} | |
template<typename U> | |
unique_ptr &operator=(unique_ptr<U> &&moving) { | |
unique_ptr<T> tmp(moving.release(), moving.deleter, moving.context); | |
swap(*moving); | |
moving.deleter = nullptr; | |
moving.context = nullptr; | |
return *this; | |
} | |
// Remove compiler generated copy semantics. | |
unique_ptr(unique_ptr const &) = delete; | |
unique_ptr &operator=(unique_ptr const &) = delete; | |
// Const correct access owned object | |
T *operator->() const { return t; } | |
T &operator*() const { return *t; } | |
// Access to smart pointer state | |
[[nodiscard]] T *get() const { return t; } | |
explicit operator bool() const { return t; } | |
// Modify object state | |
T *release() noexcept { | |
T *result = nullptr; | |
std::swap(result, t); | |
return result; | |
} | |
void swap(unique_ptr &src) noexcept { | |
std::swap(t, src.t); | |
std::swap(deleter, src.deleter); | |
std::swap(context, src.context); | |
} | |
void reset() { | |
T *tmp = release(); | |
delete tmp; | |
} | |
void* context; | |
Deleter deleter; | |
private: | |
T *t; | |
protected: | |
}; | |
}; | |
template <typename T> | |
class PoolAllocatorBase { | |
}; | |
template<typename T, size_t num_items> | |
class PoolAllocator: public PoolAllocatorBase<T> { | |
public: | |
PoolAllocator() { | |
for (size_t i = 0; i < num_items; i++) { | |
empty_buffers.push(i); | |
} | |
} | |
struct Wrapper { | |
PoolAllocator<T, num_items> *pool; | |
size_t index; | |
uint8_t buffer[sizeof(T)]; | |
}; | |
static void deallocate(T* item, void* context) { | |
printf("Deallocating "); | |
std::cout << type_name<T>(); | |
auto p = (uint8_t *) item; | |
auto wrapper = reinterpret_cast<Wrapper *> (p - offsetof(Wrapper, buffer)); | |
auto& pool = *wrapper->pool; | |
pool.empty_buffers.push(wrapper->index); | |
} | |
using unique_ptr = gute::unique_ptr<T>; | |
template<typename ... Args> | |
unique_ptr allocate(Args &&... args) { | |
auto empty_index = empty_buffers.front(); | |
empty_buffers.pop(); | |
auto w = (Wrapper *) (void *) &buffers[empty_index]; | |
w->pool = this; | |
w->index = empty_index; | |
return unique_ptr(new(&w->buffer) T(std::forward<Args>(args)...), deallocate, this); | |
} | |
protected: | |
std::queue<size_t> empty_buffers; | |
uint8_t buffers[num_items][sizeof(Wrapper)]; | |
}; | |
class Human { | |
public: | |
explicit Human(const char *name) : name(name) {} | |
~Human() { | |
printf("Deallocating %s\n", name); | |
} | |
const char *name; | |
}; | |
PoolAllocator<Human, 10> men; | |
using pool_type = PoolAllocator<Human, 10>; | |
using ptr_type = PoolAllocator<Human, 10>::unique_ptr; | |
void print_man(ptr_type man) { | |
printf("man: %s\n", man->name); | |
} | |
class BufferBase { | |
public: | |
virtual ~BufferBase() { | |
printf("Base Destructor\n") ; | |
} | |
virtual size_t get_size() { | |
return 0; | |
} | |
}; | |
template<size_t size> | |
class Buffer : public BufferBase { | |
public: | |
~Buffer() { | |
printf("~Buffer<%zu> \n", size) ; | |
} | |
size_t get_size() override { | |
return size; | |
} | |
uint8_t data[size]; | |
}; | |
PoolAllocator<Buffer<10>, 10> buffers; | |
PoolAllocator<Buffer<100>, 10> buffers_100; | |
PoolAllocator<Buffer<1000>, 10> buffers_1000; | |
template<size_t... I> | |
class MultiSizedBufferAllocator { | |
public: | |
using ptr = PoolAllocatorBase<BufferBase>*; | |
MultiSizedBufferAllocator() | |
//size_array {std::make_tuple(I, &(std::get<0>(allocators)))...} | |
{ | |
auto p = std::get<0>(allocators); | |
ptr j = reinterpret_cast<ptr> (&p); | |
p.allocate(); | |
printf("allocated\n"); | |
} | |
using size_tuple = std::tuple<size_t, ptr>; | |
using array = std::array<size_tuple, sizeof...(I)>; | |
template<size_t size> | |
void allocate() { | |
if (size < 10) { | |
std::get<0>(allocators).allocate(); | |
} | |
} | |
private: | |
std::tuple<PoolAllocator<Buffer<I>, 10>...> allocators = {PoolAllocator<Buffer<I>, 10>()...}; | |
std::array<size_t, sizeof...(I)> sizes = {{I...}}; | |
array size_array; | |
}; | |
class Packet { | |
public: | |
explicit Packet(gute::unique_ptr<BufferBase> buffer): buffer(std::move(buffer)) {} | |
void do_something() { | |
printf("Buffer size: %zu\n", buffer->get_size()); | |
} | |
private: | |
gute::unique_ptr<BufferBase> buffer; | |
}; | |
MultiSizedBufferAllocator<10, 100, 1000> multi_sized_buffer_allocator; | |
template<typename T> | |
void serialize(T&& t){ | |
printf("Serialize "); | |
std::cout << type_name<T>();; | |
} | |
template<> | |
void serialize<int>(int&& i) { | |
printf("Serialize special int\n"); | |
}; | |
PoolAllocatorBase<BufferBase> base; | |
int main() { | |
multi_sized_buffer_allocator.allocate<10>(); | |
{ | |
auto buffer = buffers.allocate(); | |
auto bigger_buffer = buffers_100.allocate(); | |
Packet p(std::move(buffer)); | |
Packet big_packet(std::move(bigger_buffer)); | |
p.do_something(); | |
} | |
auto pete = men.allocate("Pete"); | |
auto big_pete = men.allocate("Big Pete"); | |
std::vector<decltype(pete)> vector_of_men; | |
vector_of_men.push_back(std::move(big_pete)); | |
vector_of_men.push_back(std::move(pete)); | |
for (auto &&man: vector_of_men) { | |
print_man(std::move(man)); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment