Skip to content

Instantly share code, notes, and snippets.

@hnrck
Created October 11, 2020 12:28
Show Gist options
  • Save hnrck/055b0abef908860170f62257afbb3ec9 to your computer and use it in GitHub Desktop.
Save hnrck/055b0abef908860170f62257afbb3ec9 to your computer and use it in GitHub Desktop.
Header only implementation of the unique pointer in modern C++

My unique pointer

Header only implementation of the unique pointer in modern C++

cmake_minimum_required(VERSION 3.17)
project(my_unique_pointer)
set(CMAKE_CXX_STANDARD 20)
include_directories(.)
add_executable(my_unique_pointer my_memory.h main.cpp)
#include <iostream>
#include <my_memory.h>
struct Foo {
int x;
};
struct FooD final {
static std::size_t counter;
FooD() {
++counter;
}
auto get_counter() const -> void {
std::cout << this << ".counter = " << counter << std::endl;
}
auto operator()(Foo *p_f) -> void {
std::cout << "Deleting " << p_f << std::endl;
delete p_f;
}
};
std::size_t FooD::counter = std::size_t{0};
struct toto;
using p_toto_t = toto *;
using p_const_toto_t = const toto *;
static auto toto_new(int x) -> p_toto_t;
static auto toto_del(p_toto_t p_toto) -> void;
static auto toto_get_x(p_const_toto_t p_toto) -> int;
auto main() -> int {
{
const auto mup = my::unique_ptr<std::string>{new std::string("Hello")};
std::cout << *mup << std::endl;
}
{
const auto mup = my::make_unique<std::string>("Hello");
std::cout << *mup << std::endl;
}
{
auto mup = my::make_unique<int>(1);
std::cout << mup.get() << ": " << *mup << std::endl;
auto p_int = new int(2);
std::cout << mup.get() << ": " << *mup << std::endl;
mup.reset(p_int);
std::cout << mup.get() << ": " << *mup << std::endl;
auto ptr = mup.release();
std::cout << mup.get() << std::endl;
std::cout << ptr << ": " << *ptr << std::endl;
free(ptr);
}
{
std::cout << "FooD counter: " << FooD::counter << std::endl;
auto foo_d = FooD{};
std::cout << "FooD counter: " << FooD::counter << std::endl;
auto mup = my::unique_ptr<Foo, FooD>(new Foo{1}, foo_d);
std::cout << "FooD counter: " << FooD::counter << std::endl;
auto mup_d = mup.get_deleter();
std::cout << "FooD counter: " << FooD::counter << std::endl;
std::cout << "mup deleter counter: ";
mup_d.get_counter();
}
{
auto ptr_1 = my::unique_ptr<int>(new int(42));
auto ptr_2 = my::unique_ptr<int>(new int(24));
std::cout << *ptr_1 << ", " << *ptr_2 << std::endl;
ptr_1.swap(ptr_2);
std::cout << *ptr_1 << ", " << *ptr_2 << std::endl;
}
{
const auto mup_foo = my::make_unique<Foo>(Foo{42});
std::cout << mup_foo->x << std::endl;
}
{
auto mup_d = my::unique_ptr<int, std::function<void(int *)>>(new int{42}, [](const int *ptr) {
std::cout << "destroying from a custom deleter...\n";
delete ptr;
});
std::cout << *mup_d << std::endl;
}
{
std::cout << "My unique pointer_type with PIMPL toto" << std::endl;
auto mup_toto = my::unique_ptr<toto, std::function<void(p_toto_t)>>(toto_new(11), toto_del);
const auto x = toto_get_x(mup_toto.get());
std::cout << "My unique pointer_type Toto get x: " << x << std::endl;
}
{
std::cout << "My unique pointer_type with array" << std::endl;
auto arr = my::make_unique<int[]>(std::size_t{5});
arr[0] = 1;
arr[2] = 2;
arr[4] = 3;
for (auto i = 0U; i < 5U; ++i) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
return (EXIT_SUCCESS);
}
struct toto {
int x;
};
auto toto_new(int x) -> p_toto_t {
auto p_toto = (p_toto_t) (calloc(1, sizeof(toto)));
std::cout << "Initializing toto" << std::endl;
if (p_toto != nullptr) {
p_toto->x = x;
}
return (p_toto);
}
auto toto_del(p_toto_t p_toto) -> void {
std::cout << "Destructing toto" << std::endl;
if (p_toto != nullptr) {
free(p_toto);
}
}
auto toto_get_x(p_const_toto_t p_toto) -> int {
std::cout << "Accessing toto" << std::endl;
if (p_toto == nullptr) {
throw std::runtime_error("Null pointer_type");
}
return (p_toto->x);
}
#ifndef MY_UNIQUE_POINTER_MY_MEMORY_H
#define MY_UNIQUE_POINTER_MY_MEMORY_H
#include <memory>
/// @namespace my
/// @brief My implementation of part of the standard library
namespace my {
/// @class Unique pointer
/// @brief Unique pointer over single value implementation
/// @tparam element_t the pointed element type
/// @tparam deleter_t the pointed element destructor
template<class element_t, class deleter_t = std::default_delete<element_t>> class unique_ptr final {
public:
using deleter_type = deleter_t; ///< @typedef deleter_t type
using element_type = element_t; ///< @typedef Pointed element type
using pointer_type = element_type *; ///< @typedef Pointer type
/// @fn Unique pointer constructor
/// @brief Construct an unique pointer
/// @param ptr The pointer to handle
/// @param deleter The pointed element deleter
constexpr explicit unique_ptr(pointer_type ptr = pointer_type{}, deleter_type deleter = deleter_type{}) :
m_pointer{std::move(ptr)},
m_deleter{std::move(deleter)} {}
/// @fn Unique pointer destructor
/// @brief Destruct the unique pointer and the pointer handled pointed element
~unique_ptr() {
m_deleter(m_pointer);
}
/// @fn Unique pointer copy constructor
/// @brief Deleted
unique_ptr(const unique_ptr &) = delete;
/// @fn Unique pointer copy assignment
/// @brief Deleted
auto operator=(const unique_ptr &) = delete;
/// @fn Unique pointer move constructor
/// @brief Default
unique_ptr(unique_ptr &&) noexcept = default;
/// @fn Unique pointer copy assignment
/// @brief Default
auto operator=(unique_ptr &&) noexcept -> unique_ptr & = default;
/// @fn release
/// @brief Release the handled pointer
/// @return The handled pointer
auto release() -> pointer_type {
pointer_type ptr = m_pointer;
m_pointer = pointer_type{};
return (ptr);
}
/// @fn Reset
/// @brief Destroy pointed element and reinitialize handled pointer
auto reset(pointer_type ptr = pointer_type{}) -> void {
m_deleter(m_pointer);
m_pointer = std::move(ptr);
}
/// @fn Swap
/// @brief Swap two unique pointers handled pointers
auto swap(unique_ptr &other) noexcept -> void {
const auto tmp_pointer = std::move(other.m_pointer);
other.m_pointer = std::move(m_pointer);
m_pointer = std::move(tmp_pointer);
}
/// @fn Get
/// @brief Get the handled pointer
/// @return The handled pointer
[[nodiscard]] auto get() const -> pointer_type {
return (m_pointer);
}
/// @fn Get deleter
/// @brief Get the element deleter
/// @return The deleter
[[nodiscard]] auto get_deleter() const -> deleter_type {
return (m_deleter);
}
/// @fn Bool operator
/// @brief Check if the handled pointer is null
/// @return False if the handled pointer is null, else true
explicit operator bool() const noexcept {
return (m_pointer != pointer_type{});
}
/// @fn Dereference operator
/// @brief Dereference the handled pointer
/// @return The pointed element
auto operator*() const noexcept -> element_type {
return (*m_pointer);
}
/// @fn Access operator
/// @brief Access field from pointed element
/// @return The accessed field
auto operator->() const noexcept -> pointer_type {
return (m_pointer);
}
/// @fn Equal operator
/// @brief Check if pointers pointed element are equal
/// @return True if equal, else false
auto operator==(pointer_type ptr) -> bool {
if (m_pointer == pointer_type{} and ptr == pointer_type{}) {
return (true);
} else if (m_pointer == pointer_type{} xor ptr == pointer_type{}) {
return (false);
}
return (*m_pointer == *ptr);
}
private:
pointer_type m_pointer; ///< @var The handled pointer
deleter_type m_deleter; ///< @var The pointed element destructor
};
/// @class Unique pointer
/// @brief Unique pointer over array values implementation
/// @tparam element_t the pointed element type
/// @tparam deleter_t the pointed element destructor
template<class element_t, class deleter_t> class unique_ptr<element_t[], deleter_t> {
public:
using deleter_type = deleter_t; ///< @typedef deleter_t type
using element_type = element_t; ///< @typedef Pointed element type
using pointer_type = element_type *; ///< @typedef Pointer type
/// @fn Unique pointer constructor
/// @brief Construct an unique pointer
/// @param ptr The pointer to handle
/// @param deleter The pointed element deleter
constexpr explicit unique_ptr(pointer_type ptr = pointer_type{}, deleter_type deleter = deleter_t{}) :
m_pointer{std::move(ptr)},
m_deleter{std::move(deleter)} {}
/// @fn Unique pointer destructor
/// @brief Destruct the unique pointer and the pointer handled pointed element
~unique_ptr() {
m_deleter(m_pointer);
}
/// @fn Unique pointer copy constructor
/// @brief Deleted
unique_ptr(const unique_ptr &) = delete;
/// @fn Unique pointer copy assignment
/// @brief Deleted
auto operator=(const unique_ptr &) = delete;
/// @fn Unique pointer move constructor
/// @brief Default
unique_ptr(unique_ptr &&) noexcept = default;
/// @fn Unique pointer copy assignment
/// @brief Default
auto operator=(unique_ptr &&) noexcept -> unique_ptr & = default;
/// @fn release
/// @brief Release the handled pointer
/// @return The handled pointer
auto release() -> pointer_type {
pointer_type ptr = m_pointer;
m_pointer = pointer_type{};
return (ptr);
}
/// @fn Reset
/// @brief Destroy pointed element and reinitialize handled pointer
auto reset(pointer_type ptr = pointer_type{}) -> void {
m_deleter(m_pointer);
m_pointer = std::move(ptr);
}
/// @fn Swap
/// @brief Swap two unique pointers handled pointers
auto swap(unique_ptr &other) -> void {
const auto tmp_pointer = std::move(other.m_pointer);
other.m_pointer = std::move(m_pointer);
m_pointer = std::move(tmp_pointer);
}
/// @fn Get
/// @brief Get the handled pointer
/// @return The handled pointer
[[nodiscard]] auto get() const -> pointer_type {
return (m_pointer);
}
/// @fn Get deleter
/// @brief Get the element deleter
/// @return The deleter
[[nodiscard]] auto get_deleter() const -> deleter_type {
return (m_deleter);
}
/// @fn Bool operator
/// @brief Check if the handled pointer is null
/// @return False if the handled pointer is null, else true
explicit operator bool() const noexcept {
return (m_pointer != pointer_type{});
}
/// @fn Dereference operator
/// @brief Dereference the handled pointer
/// @return The pointed element
auto operator*() const noexcept -> element_type {
return (*m_pointer);
}
/// @fn Access operator
/// @brief Access field from pointed element
/// @return The accessed field
auto operator->() const noexcept -> pointer_type {
return (m_pointer);
}
/// @fn [] operator
/// @brief Access array cell
/// @return Reference to element at given index
auto operator[](size_t index) -> element_type & {
return (m_pointer[index]);
}
/// @fn [] operator
/// @brief Access array cell read only
/// @return Const reference to element at given index
auto operator[](size_t index) const -> const element_type & {
return (m_pointer[index]);
}
/// @fn Equal operator
/// @brief Check if pointers pointed element are equal
/// @return True if equal, else false
auto operator==(pointer_type ptr) -> bool {
if (m_pointer == pointer_type{} and ptr == pointer_type{}) {
return (true);
} else if (m_pointer == pointer_type{} xor ptr == pointer_type{}) {
return (false);
}
return (*m_pointer == *ptr);
}
private:
pointer_type m_pointer; ///< @var The handled pointer
deleter_type m_deleter; ///< @var The pointed element destructor
};
/// @fn Make unique pointer
/// @brief Create and return an unique pointer for a single value
/// @tparam element_t The pointed element type
/// @tparam var_args The element type constructor arguments types
/// @param args The element type constructor arguments
/// @return The created unique pointer
template<class element_t, class... var_args> inline auto make_unique(var_args &&... args) -> unique_ptr<element_t> {
return (unique_ptr<element_t>(new element_t(args...)));
}
/// @fn Make unique pointer
/// @brief Create and return an unique pointer for an array values
/// @tparam element_t The pointed element type
/// \param size The size of the array
/// @return The created unique pointer
template<class element_t> inline auto make_unique(std::size_t size) -> unique_ptr<element_t> {
using element_type = typename std::remove_extent<element_t>::type;
return (unique_ptr<element_t>(new element_type[size]()));
}
} // namespace my
#endif // MY_UNIQUE_POINTER_MY_MEMORY_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment