diff --git a/nano/core_test/utility.cpp b/nano/core_test/utility.cpp index de7a91a6..4ef4614c 100644 --- a/nano/core_test/utility.cpp +++ b/nano/core_test/utility.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -7,6 +8,33 @@ #include +TEST (optional_ptr, basic) +{ + struct valtype + { + int64_t x{ 1 }; + int64_t y{ 2 }; + int64_t z{ 3 }; + }; + + nano::optional_ptr opt; + ASSERT_FALSE (opt); + ASSERT_FALSE (opt.is_initialized ()); + + { + auto val = valtype{}; + opt = val; + ASSERT_LT (sizeof (opt), sizeof (val)); + std::unique_ptr uptr; + ASSERT_EQ (sizeof (opt), sizeof (uptr)); + } + ASSERT_TRUE (opt); + ASSERT_TRUE (opt.is_initialized ()); + ASSERT_EQ (opt->x, 1); + ASSERT_EQ (opt->y, 2); + ASSERT_EQ (opt->z, 3); +} + TEST (thread, worker) { std::atomic passed_sleep{ false }; diff --git a/nano/lib/CMakeLists.txt b/nano/lib/CMakeLists.txt index 5729a3a0..69e9afd2 100644 --- a/nano/lib/CMakeLists.txt +++ b/nano/lib/CMakeLists.txt @@ -41,6 +41,7 @@ add_library (nano_lib memory.cpp numbers.hpp numbers.cpp + optional_ptr.hpp rep_weights.hpp rep_weights.cpp rocksdbconfig.hpp diff --git a/nano/lib/optional_ptr.hpp b/nano/lib/optional_ptr.hpp new file mode 100644 index 00000000..7c7ab903 --- /dev/null +++ b/nano/lib/optional_ptr.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include + +#include +#include + +namespace nano +{ +/** + * A space efficient optional which does heap allocation when needed. + * This is an alternative to boost/std::optional when the value type is + * large and often not present. + * + * optional_ptr is similar to using std::unique_ptr as an optional, with the + * main difference being that it's copyable. + */ +template +class optional_ptr +{ + static_assert (sizeof (T) > alignof (std::max_align_t), "Use [std|boost]::optional"); + +public: + optional_ptr () = default; + + optional_ptr (T const & value) : + ptr (new T{ value }) + { + } + + optional_ptr (optional_ptr const & other) + { + if (other && other.ptr) + { + ptr = std::make_unique (*other.ptr); + } + } + + optional_ptr & operator= (optional_ptr const & other) + { + if (other && other.ptr) + { + ptr = std::make_unique (*other.ptr); + } + return *this; + } + + T & operator* () + { + return *ptr; + } + + T const & operator* () const + { + return *ptr; + } + + T * const operator-> () + { + return ptr.operator-> (); + } + + T const * const operator-> () const + { + return ptr.operator-> (); + } + + T const * const get () const + { + debug_assert (is_initialized ()); + return ptr.get (); + } + + T * const get () + { + debug_assert (is_initialized ()); + return ptr.get (); + } + + explicit operator bool () const + { + return static_cast (ptr); + } + + bool is_initialized () const + { + return static_cast (ptr); + } + +private: + std::unique_ptr ptr{ nullptr }; +}; +}