Skip to content

Instantly share code, notes, and snippets.

@igutekunst
Created March 16, 2022 16:03
Show Gist options
  • Save igutekunst/2b4831cbb9884042be28b4a9064d2dc2 to your computer and use it in GitHub Desktop.
Save igutekunst/2b4831cbb9884042be28b4a9064d2dc2 to your computer and use it in GitHub Desktop.
Pool Allocator and Unique Pointer with Deleter
// 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