Header only implementation of the unique pointer in modern C++
Created
October 11, 2020 12:28
-
-
Save hnrck/055b0abef908860170f62257afbb3ec9 to your computer and use it in GitHub Desktop.
Header only implementation of the unique pointer in modern C++
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
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) |
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
#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); | |
} |
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
#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