diff --git a/include/libimp/codecvt.h b/include/libimp/codecvt.h index 1107a0a..09931dd 100644 --- a/include/libimp/codecvt.h +++ b/include/libimp/codecvt.h @@ -1,7 +1,7 @@ /** * \file libimp/codecvt.h * \author mutouyun (orz@orzz.org) - * \brief Character set conversion interface + * \brief Character set conversion interface. * \date 2022-08-07 */ #pragma once diff --git a/include/libimp/countof.h b/include/libimp/countof.h index 9298f8d..72df251 100644 --- a/include/libimp/countof.h +++ b/include/libimp/countof.h @@ -1,7 +1,7 @@ /** * \file libimp/countof.h * \author mutouyun (orz@orzz.org) - * \brief Returns the size of the given range + * \brief Returns the size of the given range. * \date 2022-03-01 */ #pragma once diff --git a/include/libimp/dataof.h b/include/libimp/dataof.h index a49bd36..de19f62 100644 --- a/include/libimp/dataof.h +++ b/include/libimp/dataof.h @@ -1,7 +1,7 @@ /** * \file libimp/dataof.h * \author mutouyun (orz@orzz.org) - * \brief Returns the data pointer of the given range + * \brief Returns the data pointer of the given range. * \date 2023-05-27 */ #pragma once diff --git a/include/libimp/export.h b/include/libimp/export.h index 6ba509c..f12d452 100644 --- a/include/libimp/export.h +++ b/include/libimp/export.h @@ -1,7 +1,7 @@ /** * \file libimp/export.h * \author mutouyun (orz@orzz.org) - * \brief Define the symbol export interfaces + * \brief Define the symbol export interfaces. * \date 2022-02-27 */ #pragma once diff --git a/include/libimp/pimpl.h b/include/libimp/pimpl.h index d0b0b2b..990d0de 100644 --- a/include/libimp/pimpl.h +++ b/include/libimp/pimpl.h @@ -1,7 +1,7 @@ /** * \file libimp/pimpl.h * \author mutouyun (orz@orzz.org) - * \brief Pointer To Implementation (pImpl) idiom + * \brief Pointer To Implementation (pImpl) idiom. * \date 2022-02-27 */ #pragma once diff --git a/include/libimp/result.h b/include/libimp/result.h index 3960f88..a859b9f 100644 --- a/include/libimp/result.h +++ b/include/libimp/result.h @@ -1,7 +1,7 @@ /** * \file libimp/result.h * \author mutouyun (orz@orzz.org) - * \brief Define the return value type with a status code + * \brief Define the return value type with a status code. * \date 2022-04-17 */ #pragma once diff --git a/include/libimp/scope_exit.h b/include/libimp/scope_exit.h new file mode 100644 index 0000000..66e341f --- /dev/null +++ b/include/libimp/scope_exit.h @@ -0,0 +1,78 @@ +/** + * \file libimp/scope_exit.h + * \author mutouyun (orz@orzz.org) + * \brief Execute guard function when the enclosing scope exits. + * \date 2022-03-01 + */ +#pragma once + +#include // std::forward, std::move +#include // std::function +#include + +#include "libimp/def.h" + +LIBIMP_NAMESPACE_BEG_ + +template > +class scope_exit { + F destructor_; + mutable bool released_; + +public: + template + explicit scope_exit(G &&destructor) noexcept + : destructor_(std::forward(destructor)) + , released_ (false) {} + + scope_exit(scope_exit &&other) noexcept + : destructor_(std::move(other.destructor_)) + , released_ (std::exchange(other.released_, true)) /*release rhs*/ {} + + scope_exit &operator=(scope_exit &&other) noexcept { + destructor_ = std::move(other.destructor_); + released_ = std::exchange(other.released_, true); + return *this; + } + + ~scope_exit() noexcept { + if (!released_) destructor_(); + } + + void release() const noexcept { + released_ = true; + } + + void do_exit() noexcept { + if (released_) return; + destructor_(); + released_ = true; + } + + void swap(scope_exit &other) noexcept { + std::swap(destructor_, other.destructor_); + std::swap(released_ , other.released_); + } +}; + +/// \brief Creates a scope_exit object. +template +auto make_scope_exit(F &&destructor) noexcept { + return scope_exit>(std::forward(destructor)); +} + +namespace detail { + +struct scope_exit_helper { + template + auto operator=(F &&f) const noexcept { + return make_scope_exit(std::forward(f)); + } +}; + +} // namespace detail + +#define LIBIMP_SCOPE_EXIT($val) \ + auto $val = ::LIBIMP::detail::scope_exit_helper{} + +LIBIMP_NAMESPACE_END_ diff --git a/include/libimp/span.h b/include/libimp/span.h index c05f79b..fe94284 100644 --- a/include/libimp/span.h +++ b/include/libimp/span.h @@ -1,7 +1,7 @@ /** * \file libimp/span.h * \author mutouyun (orz@orzz.org) - * \brief Describes an object that can refer to a contiguous sequence of objects + * \brief Describes an object that can refer to a contiguous sequence of objects. * \date 2022-10-16 */ #pragma once diff --git a/include/libimp/system.h b/include/libimp/system.h index aed8d7c..3517ed7 100644 --- a/include/libimp/system.h +++ b/include/libimp/system.h @@ -1,7 +1,7 @@ /** * \file libimp/system.h * \author mutouyun (orz@orzz.org) - * \brief Isolation and encapsulation of system APIs + * \brief Isolation and encapsulation of system APIs. * \date 2022-08-07 */ #pragma once diff --git a/include/libimp/underlyof.h b/include/libimp/underlyof.h index edef44f..0fa4ace 100644 --- a/include/libimp/underlyof.h +++ b/include/libimp/underlyof.h @@ -1,7 +1,7 @@ /** * \file libimp/underlyof.h * \author mutouyun (orz@orzz.org) - * \brief Returns the underlying type of the given enum + * \brief Returns the underlying type of the given enum. * \date 2022-03-01 */ #pragma once diff --git a/include/libpmr/small_storage.h b/include/libpmr/small_storage.h index 068bab9..9406a3c 100644 --- a/include/libpmr/small_storage.h +++ b/include/libpmr/small_storage.h @@ -223,6 +223,39 @@ void *holder_construct(allocator const &alloc, std::size_t n, Construct &&c) { } } +class holder_info_ptr { + allocator const &alloc_; + holder_info **pptr_; + +public: + holder_info_ptr(allocator const &alloc, holder_info *(&ptr)) noexcept + : alloc_(alloc), pptr_(&ptr) { + *pptr_ = static_cast(alloc_.allocate(sizeof(holder_info), alignof(holder_info))); + } + + ~holder_info_ptr() noexcept { + if (pptr_ == nullptr) return; + alloc_.deallocate(*pptr_, sizeof(holder_info), alignof(holder_info)); + *pptr_ = nullptr; + } + + explicit operator bool() const noexcept { + return (pptr_ != nullptr) && (*pptr_ != nullptr); + } + + holder_info *operator->() const noexcept { + return *pptr_; + } + + holder_info &operator*() noexcept { + return **pptr_; + } + + void release() noexcept { + pptr_ = nullptr; + } +}; + } // namespace detail /** @@ -246,7 +279,7 @@ public: std::enable_if_t = true> holder(allocator const &alloc, ::LIBIMP::types, std::size_t n) : holder() { value_ptr_ = detail::holder_construct(alloc, n, - ::LIBIMP::uninitialized_default_construct_n); + ::LIBIMP::uninitialized_default_construct_n); if (value_ptr_ == nullptr) return; info_.type = &typeid(Value); info_.sizeof_type = sizeof(Value); @@ -271,6 +304,7 @@ public: } std::type_info const &type() const noexcept override { + if (!valid()) return typeid(nullptr); return *info_.type; } @@ -314,4 +348,104 @@ public: } }; +/** + * \class template <> holder + * \brief A holder implementation of some data objects that stores type information on heap memory. + */ +template <> +class holder : public holder_base { + + detail::holder_info *info_ptr_; + void *value_ptr_; + +public: + holder() noexcept + : info_ptr_ (nullptr) + , value_ptr_(nullptr) {} + + /// \brief Constructs a holder with type of `Value`. + /// alignof(Value) must be less than or equal to alignof(std::max_align_t). + template = true> + holder(allocator const &alloc, ::LIBIMP::types, std::size_t n) : holder() { + detail::holder_info_ptr info_p{alloc, info_ptr_}; + if (!info_p) return; + value_ptr_ = detail::holder_construct(alloc, n, + ::LIBIMP::uninitialized_default_construct_n); + if (value_ptr_ == nullptr) return; + info_p->type = &typeid(Value); + info_p->sizeof_type = sizeof(Value); + info_p->count = n; + info_p->copy = [](allocator const &alloc, void const *s, void *d) { + auto const &src = *static_cast(s); + auto & dst = *::LIBIMP::construct(d); + if (!src.valid()) return; + detail::holder_info_ptr info_p{alloc, dst.info_ptr_}; + if (!info_p) return; + dst.value_ptr_ = detail::holder_construct(alloc, src.count(), [s = src.get()](Value *d, std::size_t n) { + std::uninitialized_copy_n(static_cast(s), n, d); + }); + if (dst.value_ptr_ == nullptr) return; + *info_p = *src.info_ptr_; + info_p.release(); + }; + info_p->dest = [](void *p, std::size_t n) noexcept { + ::LIBIMP::destroy_n(static_cast(p), n); + }; + info_p.release(); + } + + bool valid() const noexcept override { + return (value_ptr_ != nullptr) && (info_ptr_ != nullptr); + } + + std::type_info const &type() const noexcept override { + if (!valid()) return typeid(nullptr); + return *info_ptr_->type; + } + + std::size_t sizeof_type() const noexcept override { + if (!valid()) return 0; + return info_ptr_->sizeof_type; + } + + std::size_t sizeof_heap() const noexcept override { + if (!valid()) return 0; + return info_ptr_->sizeof_type * info_ptr_->count + sizeof(detail::holder_info); + } + + std::size_t count() const noexcept override { + if (!valid()) return 0; + return info_ptr_->count; + } + + void *get() noexcept override { + return value_ptr_; + } + + void const *get() const noexcept override { + return value_ptr_; + } + + void move_to(allocator const &, void *p) noexcept override { + auto *des = ::LIBIMP::construct(p); + if (!valid()) return; + std::swap(value_ptr_, des->value_ptr_); + std::swap(info_ptr_ , des->info_ptr_); + } + + void copy_to(allocator const &alloc, void *p) const noexcept(false) override { + auto *des = ::LIBIMP::construct(p); + if (!valid()) return; + info_ptr_->copy(alloc, this, des); + } + + void destroy(allocator const &alloc) noexcept override { + if (!valid()) return; + info_ptr_->dest (value_ptr_, count()); + alloc.deallocate(value_ptr_, sizeof_heap()); + alloc.deallocate(info_ptr_ , sizeof(detail::holder_info), alignof(detail::holder_info)); + } +}; + LIBPMR_NAMESPACE_END_ diff --git a/test/pmr/test_pmr_small_storage.cpp b/test/pmr/test_pmr_small_storage.cpp index 6b5e1d9..6300f64 100644 --- a/test/pmr/test_pmr_small_storage.cpp +++ b/test/pmr/test_pmr_small_storage.cpp @@ -11,6 +11,7 @@ TEST(small_storage, holder_construct) { pmr::holder(); pmr::holder(); pmr::holder(); + pmr::holder(); SUCCEED(); } @@ -19,21 +20,25 @@ TEST(small_storage, holder_copy_move_construct) { EXPECT_FALSE((std::is_copy_constructible>::value)); EXPECT_FALSE((std::is_copy_constructible>::value)); EXPECT_FALSE((std::is_copy_constructible>::value)); + EXPECT_FALSE((std::is_copy_constructible>::value)); EXPECT_FALSE(std::is_copy_assignable::value); EXPECT_FALSE((std::is_copy_assignable>::value)); EXPECT_FALSE((std::is_copy_assignable>::value)); EXPECT_FALSE((std::is_copy_assignable>::value)); + EXPECT_FALSE((std::is_copy_assignable>::value)); EXPECT_FALSE(std::is_move_constructible::value); EXPECT_FALSE((std::is_move_constructible>::value)); EXPECT_FALSE((std::is_move_constructible>::value)); EXPECT_FALSE((std::is_move_constructible>::value)); + EXPECT_FALSE((std::is_move_constructible>::value)); EXPECT_FALSE(std::is_move_assignable::value); EXPECT_FALSE((std::is_move_assignable>::value)); EXPECT_FALSE((std::is_move_assignable>::value)); EXPECT_FALSE((std::is_move_assignable>::value)); + EXPECT_FALSE((std::is_move_assignable>::value)); } TEST(small_storage, holder_copy_move) { @@ -46,7 +51,7 @@ TEST(small_storage, holder_copy_move) { pmr::allocator alc; pmr::holder h1(alc, 1); - pmr::holder h2, h3; // uninitialized + pmr::holder h2, h3; h1.copy_to(alc, &h2); EXPECT_EQ(static_cast(h1.get())->i, 1); EXPECT_EQ(static_cast(h2.get())->i, 1); @@ -58,7 +63,7 @@ TEST(small_storage, holder_copy_move) { h3.destroy(alc); pmr::holder h4(alc, 1); - pmr::holder h5, h6; // uninitialized + pmr::holder h5, h6; h4.copy_to(alc, &h5); EXPECT_EQ(static_cast(h4.get())->i, 1); EXPECT_EQ(static_cast(h5.get())->i, 1); @@ -70,7 +75,7 @@ TEST(small_storage, holder_copy_move) { h6.destroy(alc); pmr::holder h7(alc, ::LIBIMP::types{}, 10); - pmr::holder h8, h9; // uninitialized + pmr::holder h8, h9; h7.copy_to(alc, &h8); EXPECT_EQ(h7.count(), 10); EXPECT_EQ(h8.count(), 10); @@ -80,6 +85,18 @@ TEST(small_storage, holder_copy_move) { h7.destroy(alc); h8.destroy(alc); h9.destroy(alc); + + pmr::holder h10(alc, ::LIBIMP::types{}, 10); + pmr::holder h11, h12; + h10.copy_to(alc, &h11); + EXPECT_EQ(h10.count(), 10); + EXPECT_EQ(h11.count(), 10); + h10.move_to(alc, &h12); + EXPECT_EQ(h10.count(), 0); + EXPECT_EQ(h12.count(), 10); + h10.destroy(alc); + h11.destroy(alc); + h12.destroy(alc); } TEST(small_storage, construct) {