Skip to content

Instantly share code, notes, and snippets.

@PurityLake
Last active May 9, 2023 20:12
Show Gist options
  • Save PurityLake/2ebd94d90038571356d2e1c61ae8da5c to your computer and use it in GitHub Desktop.
Save PurityLake/2ebd94d90038571356d2e1c61ae8da5c to your computer and use it in GitHub Desktop.
A sample of using Empty Base Object Optimisation using a custom simplified unique_ptr implementation
#pragma once
#include <iostream>
// T1 should be a stateless class that has only one member function
// T2 can be any noexcept constructible class
template <class T1, class T2>
// We inherit from T1 to provide the functionality of T1 without
// incurring any additional memory
class SimpleCompressedPair : T1
{
public:
T2 _t2;
explicit SimpleCompressedPair(T2 val) : _t2(val) { }
// This class can be cast to an object of T1 as it inherits
// from it
constexpr T1& getFirst() noexcept {
return *this;
}
// This class can be cast to an object of T1 as it inherits
// from it
constexpr const T1& getFirst() const noexcept {
return *this;
}
};
// This is used to free the memory in out unique_ptr
template<typename T>
struct DefaultDeleter
{
void operator()(T* ptr) { delete ptr; }
};
template<typename T, class Deleter = DefaultDeleter<T>>
class uniquer_ptr
{
public:
constexpr uniquer_ptr() noexcept : MyPair(nullptr) { }
constexpr uniquer_ptr(T* ptr) noexcept : MyPair(ptr) { }
uniquer_ptr(const uniquer_ptr&) = delete;
~uniquer_ptr() noexcept
{
if (MyPair._t2) {
MyPair.getFirst()(MyPair._t2);
std::cout << "HERE" << std::endl;
}
}
T* operator->() const noexcept { return MyPair._t2; }
T& operator*() const noexcept { return *MyPair._t2; }
private:
// Using this we not only store the memory required to hold the T*
SimpleCompressedPair<Deleter, T*> MyPair;
};
template<typename T, class Deleter = DefaultDeleter<T>>
class uniquer_ptr_but_fatter
{
public:
constexpr uniquer_ptr_but_fatter() noexcept : ptr(nullptr) { }
constexpr uniquer_ptr_but_fatter(T* ptr) noexcept : ptr(ptr), d() { }
constexpr uniquer_ptr_but_fatter(T* ptr, Deleter d) noexcept : ptr(ptr), d(d) { }
constexpr uniquer_ptr_but_fatter(T* ptr, Deleter&& d) noexcept : ptr(ptr), d(std::move(d)) { }
uniquer_ptr_but_fatter(const uniquer_ptr_but_fatter&) = delete;
~uniquer_ptr_but_fatter() noexcept
{
if (ptr) {
d(ptr);
}
}
T* operator->() const noexcept { return ptr; }
T& operator*() const noexcept { return *ptr; }
private:
// using this d is of size 1
// t is of size 8
// total of 9 bytes but is 8 byte aligned so becomes 16 bytes
Deleter d;
T* ptr;
};
int main(void) {
uniquer_ptr<int> ptr(new int(3));
std::cout << sizeof(ptr) << std::endl; // 8
uniquer_ptr_but_fatter<int> fatptr(new int(3));
std::cout << sizeof(fatptr) << std::endl; //// 16
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment