Continue to update holders.

This commit is contained in:
mutouyun 2023-09-03 13:43:32 +08:00
parent c8010e3a51
commit a50bc78678
9 changed files with 320 additions and 24 deletions

View File

@ -11,7 +11,7 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include "libimp/construct.h" #include "libimp/uninitialized.h"
#include "libimp/detect_plat.h" #include "libimp/detect_plat.h"
#include "libimp/aligned.h" #include "libimp/aligned.h"

View File

@ -13,7 +13,7 @@
#include <cstddef> // std::nullptr_t #include <cstddef> // std::nullptr_t
#include "libimp/def.h" #include "libimp/def.h"
#include "libimp/construct.h" #include "libimp/uninitialized.h"
#include "libimp/generic.h" #include "libimp/generic.h"
#include "libimp/byte.h" #include "libimp/byte.h"

View File

@ -21,6 +21,12 @@ LIBIMP_NAMESPACE_BEG_
template <typename...> template <typename...>
using void_t = void; using void_t = void;
/**
* \brief A type-list for generic programming.
*/
template <typename...>
struct types {};
/** /**
* \brief To indicate that the contained object should be constructed in-place. * \brief To indicate that the contained object should be constructed in-place.
* \see https://en.cppreference.com/w/cpp/utility/in_place * \see https://en.cppreference.com/w/cpp/utility/in_place

View File

@ -10,7 +10,7 @@
#include <type_traits> #include <type_traits>
#include <cstdint> #include <cstdint>
#include "libimp/construct.h" #include "libimp/uninitialized.h"
#include "libimp/def.h" #include "libimp/def.h"
LIBIMP_NAMESPACE_BEG_ LIBIMP_NAMESPACE_BEG_

View File

@ -1,7 +1,7 @@
/** /**
* \file libimp/construct.h * \file libimp/uninitialized.h
* \author mutouyun (orz@orzz.org) * \author mutouyun (orz@orzz.org)
* \brief Construct an object from a memory buffer * \brief Uninitialized memory algorithms.
* \date 2022-02-27 * \date 2022-02-27
*/ */
#pragma once #pragma once
@ -14,6 +14,7 @@
#include "libimp/def.h" #include "libimp/def.h"
#include "libimp/detect_plat.h" #include "libimp/detect_plat.h"
#include "libimp/horrible_cast.h"
LIBIMP_NAMESPACE_BEG_ LIBIMP_NAMESPACE_BEG_
@ -67,4 +68,42 @@ void *destroy(T (*p)[N]) noexcept {
return p; return p;
} }
/**
* \brief Destroys a range of objects.
* \see https://en.cppreference.com/w/cpp/memory/destroy
*/
template <typename ForwardIt>
void destroy(ForwardIt first, ForwardIt last) noexcept {
#if defined(LIBIMP_CPP_17)
std::destroy(first, last);
#else
for (; first != last; ++first) {
destroy(std::addressof(*first));
}
#endif
}
/**
* \brief Constructs objects by default-initialization
* in an uninitialized area of memory, defined by a start and a count.
* \see https://en.cppreference.com/w/cpp/memory/uninitialized_default_construct_n
*/
template <typename ForwardIt, typename Size>
ForwardIt uninitialized_default_construct_n(ForwardIt first, Size n) {
#if defined(LIBIMP_CPP_17)
return std::uninitialized_default_construct_n(first, n);
#else
using T = typename std::iterator_traits<ForwardIt>::value_type;
ForwardIt current = first;
LIBIMP_TRY {
for (; n > 0; (void) ++current, --n)
::new (horrible_cast<void *>(std::addressof(*current))) T;
return current;
} LIBIMP_CATCH(...) {
destroy(first, current);
LIBIMP_THROW(, first);
}
#endif
}
LIBIMP_NAMESPACE_END_ LIBIMP_NAMESPACE_END_

View File

@ -10,7 +10,7 @@
#include <array> #include <array>
#include "libimp/export.h" #include "libimp/export.h"
#include "libimp/construct.h" #include "libimp/uninitialized.h"
#include "libimp/byte.h" #include "libimp/byte.h"
#include "libpmr/def.h" #include "libpmr/def.h"

View File

@ -14,28 +14,33 @@
#include <cstddef> #include <cstddef>
#include "libimp/export.h" #include "libimp/export.h"
#include "libimp/construct.h" #include "libimp/uninitialized.h"
#include "libimp/byte.h" #include "libimp/byte.h"
#include "libimp/generic.h" #include "libimp/generic.h"
#include "libpmr/def.h" #include "libpmr/def.h"
#include "libpmr/allocator.h"
LIBPMR_NAMESPACE_BEG_ LIBPMR_NAMESPACE_BEG_
class LIBIMP_EXPORT allocator;
/** /**
* \class holder_base * \class holder_base
* \brief Data holder base class. * \brief Data holder base class.
*/ */
class holder_base { class holder_base {
// non-copyable
holder_base(holder_base const &) = delete;
holder_base &operator=(holder_base const &) = delete;
public: public:
holder_base() noexcept = default;
virtual ~holder_base() noexcept = default; virtual ~holder_base() noexcept = default;
virtual bool valid() const noexcept = 0; virtual bool valid() const noexcept = 0;
virtual std::type_info const &type() const noexcept = 0; virtual std::type_info const &type() const noexcept = 0;
virtual std::size_t sizeof_type() const noexcept = 0; virtual std::size_t sizeof_type() const noexcept = 0;
virtual std::size_t sizeof_heap() const noexcept = 0;
virtual std::size_t count() const noexcept = 0; virtual std::size_t count() const noexcept = 0;
virtual std::size_t size () const noexcept = 0;
virtual void *get() noexcept = 0; virtual void *get() noexcept = 0;
virtual void const *get() const noexcept = 0; virtual void const *get() const noexcept = 0;
@ -55,8 +60,8 @@ public:
bool valid() const noexcept override { return false; } bool valid() const noexcept override { return false; }
std::type_info const &type() const noexcept override { return typeid(nullptr);} std::type_info const &type() const noexcept override { return typeid(nullptr);}
std::size_t sizeof_type() const noexcept override { return 0; } std::size_t sizeof_type() const noexcept override { return 0; }
std::size_t sizeof_heap() const noexcept override { return 0; }
std::size_t count() const noexcept override { return 0; } std::size_t count() const noexcept override { return 0; }
std::size_t size () const noexcept override { return 0; }
void *get() noexcept override { return nullptr; } void *get() noexcept override { return nullptr; }
void const *get() const noexcept override { return nullptr; } void const *get() const noexcept override { return nullptr; }
void move_to(allocator const &, void *) noexcept override {} void move_to(allocator const &, void *) noexcept override {}
@ -81,7 +86,7 @@ public:
holder() = default; // uninitialized holder() = default; // uninitialized
template <typename... A> template <typename... A>
holder(::LIBIMP::in_place_t, A &&...args) { holder(allocator const &, A &&...args) {
::LIBIMP::construct<Value>(value_storage_.data(), std::forward<A>(args)...); ::LIBIMP::construct<Value>(value_storage_.data(), std::forward<A>(args)...);
} }
@ -97,12 +102,12 @@ public:
return sizeof(Value); return sizeof(Value);
} }
std::size_t count() const noexcept override { std::size_t sizeof_heap() const noexcept override {
return 1; return 0;
} }
std::size_t size() const noexcept override { std::size_t count() const noexcept override {
return value_storage_.size(); return 1;
} }
void *get() noexcept override { void *get() noexcept override {
@ -113,16 +118,16 @@ public:
return value_storage_.data(); return value_storage_.data();
} }
void move_to(allocator const &, void *p) noexcept override { void move_to(allocator const &alloc, void *p) noexcept override {
::LIBIMP::construct<holder>(p, ::LIBIMP::in_place, std::move(*static_cast<Value *>(get()))); ::LIBIMP::construct<holder>(p, alloc, std::move(*static_cast<Value *>(get())));
} }
void copy_to(allocator const &, void *p) const noexcept(false) override { void copy_to(allocator const &alloc, void *p) const noexcept(false) override {
::LIBIMP::construct<holder>(p, ::LIBIMP::in_place, *static_cast<Value *>(get())); ::LIBIMP::construct<holder>(p, alloc, *static_cast<Value const *>(get()));
} }
void destroy(allocator const &) noexcept override { void destroy(allocator const &) noexcept override {
::LIBIMP::destroy<Value>(value_storage_.data()); ::LIBIMP::destroy(static_cast<Value *>(get()));
} }
}; };
@ -134,10 +139,177 @@ public:
template <typename Value> template <typename Value>
class holder<Value, false> : public holder_base { class holder<Value, false> : public holder_base {
Value *value_ptr_ = nullptr; Value *value_ptr_;
public: public:
holder() = default; // uninitialized holder() noexcept : value_ptr_(nullptr) {}
template <typename... A>
holder(allocator const &alloc, A &&...args) {
void *p = alloc.allocate(sizeof(Value), alignof(Value));
if (p == nullptr) return;
value_ptr_ = ::LIBIMP::construct<Value>(p, std::forward<A>(args)...);
}
bool valid() const noexcept override {
return value_ptr_ != nullptr;
}
std::type_info const &type() const noexcept override {
return typeid(Value);
}
std::size_t sizeof_type() const noexcept override {
return sizeof(Value);
}
std::size_t sizeof_heap() const noexcept override {
return sizeof(Value);
}
std::size_t count() const noexcept override {
return 1;
}
void *get() noexcept override {
return value_ptr_;
}
void const *get() const noexcept override {
return value_ptr_;
}
void move_to(allocator const &alloc, void *p) noexcept override {
if (value_ptr_ == nullptr) {
::LIBIMP::construct<holder>(p);
return;
}
::LIBIMP::construct<holder>(p, alloc, std::move(*value_ptr_));
}
void copy_to(allocator const &alloc, void *p) const noexcept(false) override {
if (value_ptr_ == nullptr) {
::LIBIMP::construct<holder>(p);
return;
}
::LIBIMP::construct<holder>(p, alloc, *value_ptr_);
}
void destroy(allocator const &alloc) noexcept override {
alloc.deallocate(::LIBIMP::destroy(value_ptr_), sizeof(Value), alignof(Value));
}
};
namespace detail {
struct holder_info {
std::type_info const *type;
std::size_t sizeof_type;
std::size_t count;
void (*copy)(allocator const &, void const *s, void *d);
};
template <typename Value, typename Construct>
void *holder_construct(allocator const &alloc, std::size_t n, Construct &&c) {
void *p = alloc.allocate(n * sizeof(Value));
if (p == nullptr) return nullptr;
LIBIMP_TRY {
std::forward<Construct>(c)(static_cast<Value *>(p), n);
return p;
} LIBIMP_CATCH(...) {
alloc.deallocate(p, n * sizeof(Value));
LIBIMP_THROW(, nullptr);
}
}
} // namespace detail
/**
* \class template <> holder<void, true>
* \brief A holder implementation of some data objects that stores type information on stack memory.
*/
template <>
class holder<void, true> : public holder_base {
detail::holder_info info_;
void *value_ptr_;
public:
holder() noexcept
: info_{&typeid(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 <typename Value,
std::enable_if_t<alignof(Value) <= alignof(std::max_align_t), bool> = true>
holder(allocator const &alloc, ::LIBIMP::types<Value>, std::size_t n) : holder() {
value_ptr_ = detail::holder_construct<Value>(alloc, n,
::LIBIMP::uninitialized_default_construct_n<Value *, std::size_t>);
if (value_ptr_ == nullptr) return;
info_.type = &typeid(Value);
info_.sizeof_type = sizeof(Value);
info_.count = n;
info_.copy = [](allocator const &alloc, void const *s, void *d) {
auto const &src = *static_cast<holder const *>(s);
auto & dst = *::LIBIMP::construct<holder>(d);
if (!src.valid()) return;
dst.value_ptr_ = detail::holder_construct<Value>(alloc, src.count(), [s = src.get()](Value *d, std::size_t n) {
std::uninitialized_copy_n(static_cast<Value const *>(s), n, d);
});
if (dst.value_ptr_ == nullptr) return;
dst.info_ = src.info_;
};
}
bool valid() const noexcept override {
return value_ptr_ != nullptr;
}
std::type_info const &type() const noexcept override {
return *info_.type;
}
std::size_t sizeof_type() const noexcept override {
return info_.sizeof_type;
}
std::size_t sizeof_heap() const noexcept override {
return info_.sizeof_type * info_.count;
}
std::size_t count() const noexcept override {
return info_.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<holder>(p);
if (value_ptr_ == nullptr) {
return;
}
std::swap(value_ptr_, des->value_ptr_);
std::swap(info_, des->info_);
}
void copy_to(allocator const &alloc, void *p) const noexcept(false) override {
auto *des = ::LIBIMP::construct<holder>(p);
if (value_ptr_ == nullptr) {
return;
}
info_.copy(alloc, this, des);
}
void destroy(allocator const &alloc) noexcept override {
alloc.deallocate(::LIBIMP::destroy(value_ptr_), info_.sizeof_type * info_.count);
}
}; };
LIBPMR_NAMESPACE_END_ LIBPMR_NAMESPACE_END_

View File

@ -5,7 +5,7 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "libimp/construct.h" #include "libimp/uninitialized.h"
#include "libimp/pimpl.h" #include "libimp/pimpl.h"
#include "libimp/countof.h" #include "libimp/countof.h"
#include "libimp/dataof.h" #include "libimp/dataof.h"

View File

@ -1,7 +1,86 @@
#include <type_traits>
#include <utility>
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "libpmr/small_storage.h" #include "libpmr/small_storage.h"
TEST(small_storage, holder_construct) {
pmr::holder_null();
pmr::holder<int, true>();
pmr::holder<int, false>();
pmr::holder<void, true>();
SUCCEED();
}
TEST(small_storage, holder_copy_move_construct) {
EXPECT_FALSE(std::is_copy_constructible<pmr::holder_null>::value);
EXPECT_FALSE((std::is_copy_constructible<pmr::holder<int, true>>::value));
EXPECT_FALSE((std::is_copy_constructible<pmr::holder<int, false>>::value));
EXPECT_FALSE((std::is_copy_constructible<pmr::holder<void, true>>::value));
EXPECT_FALSE(std::is_copy_assignable<pmr::holder_null>::value);
EXPECT_FALSE((std::is_copy_assignable<pmr::holder<int, true>>::value));
EXPECT_FALSE((std::is_copy_assignable<pmr::holder<int, false>>::value));
EXPECT_FALSE((std::is_copy_assignable<pmr::holder<void, true>>::value));
EXPECT_FALSE(std::is_move_constructible<pmr::holder_null>::value);
EXPECT_FALSE((std::is_move_constructible<pmr::holder<int, true>>::value));
EXPECT_FALSE((std::is_move_constructible<pmr::holder<int, false>>::value));
EXPECT_FALSE((std::is_move_constructible<pmr::holder<void, true>>::value));
EXPECT_FALSE(std::is_move_assignable<pmr::holder_null>::value);
EXPECT_FALSE((std::is_move_assignable<pmr::holder<int, true>>::value));
EXPECT_FALSE((std::is_move_assignable<pmr::holder<int, false>>::value));
EXPECT_FALSE((std::is_move_assignable<pmr::holder<void, true>>::value));
}
TEST(small_storage, holder_copy_move) {
struct foo {
int i;
foo(int i) : i(i) {}
foo(foo const &rhs) : i(rhs.i) {}
foo(foo &&rhs) : i(std::exchange(rhs.i, 0)) {}
};
pmr::allocator alc;
pmr::holder<foo, true> h1(alc, 1);
pmr::holder<foo, true> h2, h3; // uninitialized
h1.copy_to(alc, &h2);
EXPECT_EQ(static_cast<foo *>(h1.get())->i, 1);
EXPECT_EQ(static_cast<foo *>(h2.get())->i, 1);
h1.move_to(alc, &h3);
EXPECT_EQ(static_cast<foo *>(h1.get())->i, 0);
EXPECT_EQ(static_cast<foo *>(h3.get())->i, 1);
h1.destroy(alc);
h2.destroy(alc);
h3.destroy(alc);
pmr::holder<foo, false> h4(alc, 1);
pmr::holder<foo, false> h5, h6; // uninitialized
h4.copy_to(alc, &h5);
EXPECT_EQ(static_cast<foo *>(h4.get())->i, 1);
EXPECT_EQ(static_cast<foo *>(h5.get())->i, 1);
h4.move_to(alc, &h6);
EXPECT_EQ(static_cast<foo *>(h4.get())->i, 0);
EXPECT_EQ(static_cast<foo *>(h6.get())->i, 1);
h4.destroy(alc);
h5.destroy(alc);
h6.destroy(alc);
pmr::holder<void, true> h7(alc, ::LIBIMP::types<int>{}, 10);
pmr::holder<void, true> h8, h9; // uninitialized
h7.copy_to(alc, &h8);
EXPECT_EQ(h7.count(), 10);
EXPECT_EQ(h8.count(), 10);
h7.move_to(alc, &h9);
EXPECT_EQ(h7.count(), 0);
EXPECT_EQ(h9.count(), 10);
h7.destroy(alc);
h8.destroy(alc);
h9.destroy(alc);
}
TEST(small_storage, construct) { TEST(small_storage, construct) {
} }