mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 16:56:45 +08:00
add: [pmr] allocator & ut
This commit is contained in:
parent
cc17980b97
commit
1fd5b3825e
@ -7,41 +7,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <array>
|
||||
|
||||
#include "libimp/export.h"
|
||||
#include "libimp/construct.h"
|
||||
#include "libimp/byte.h"
|
||||
|
||||
#include "libpmr/def.h"
|
||||
#include "libpmr/memory_resource.h"
|
||||
|
||||
LIBPMR_NAMESPACE_BEG_
|
||||
namespace detail {
|
||||
|
||||
/// @brief Helper trait for allocator.
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct has_allocate : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_allocate<T,
|
||||
typename std::enable_if<std::is_convertible<
|
||||
decltype(std::declval<T &>().allocate(std::declval<std::size_t>())), void *
|
||||
>::value>::type> : std::true_type {};
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct has_deallocate : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_deallocate<T,
|
||||
decltype(std::declval<T &>().deallocate(std::declval<void *>(),
|
||||
std::declval<std::size_t>()))
|
||||
> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
using is_memory_resource =
|
||||
typename std::enable_if<has_allocate <T>::value &&
|
||||
has_deallocate<T>::value>::type;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief An allocator which exhibits different allocation behavior
|
||||
@ -57,6 +32,78 @@ using is_memory_resource =
|
||||
*/
|
||||
class LIBIMP_EXPORT allocator {
|
||||
|
||||
class holder_base {
|
||||
public:
|
||||
virtual ~holder_base() noexcept = default;
|
||||
virtual void *alloc(std::size_t) = 0;
|
||||
virtual void free (void *, std::size_t) = 0;
|
||||
virtual bool valid() const noexcept = 0;
|
||||
};
|
||||
|
||||
class holder_null : public holder_base {
|
||||
public:
|
||||
void *alloc(std::size_t) override { return nullptr; }
|
||||
void free (void *, std::size_t) override {}
|
||||
bool valid() const noexcept override { return false; }
|
||||
};
|
||||
|
||||
template <typename MemRes>
|
||||
class holder_memory_resource : public holder_base {
|
||||
MemRes *p_mem_res_;
|
||||
|
||||
public:
|
||||
holder_memory_resource(MemRes *p_mr) noexcept
|
||||
: p_mem_res_(p_mr) {}
|
||||
|
||||
void *alloc(std::size_t s) override {
|
||||
return p_mem_res_->allocate(s);
|
||||
}
|
||||
|
||||
void free(void *p, std::size_t s) override {
|
||||
p_mem_res_->deallocate(p, s);
|
||||
}
|
||||
|
||||
bool valid() const noexcept override {
|
||||
return p_mem_res_ != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class holder_memory_resource<void> : public holder_null {
|
||||
void *p_dummy_;
|
||||
};
|
||||
|
||||
using void_holder_type = holder_memory_resource<void>;
|
||||
alignas(void_holder_type) std::array<::LIBIMP_::byte, sizeof(void_holder_type)> holder_;
|
||||
|
||||
holder_base & get_holder() noexcept;
|
||||
holder_base const &get_holder() const noexcept;
|
||||
|
||||
public:
|
||||
allocator() noexcept;
|
||||
~allocator() noexcept;
|
||||
|
||||
allocator(allocator const &other) noexcept = default;
|
||||
allocator &operator=(allocator const &other) & noexcept = default;
|
||||
|
||||
allocator(allocator &&other) noexcept;
|
||||
allocator &operator=(allocator &&other) & noexcept;
|
||||
|
||||
/// @brief Constructs a allocator from a memory resource pointer
|
||||
/// @remark The lifetime of the pointer must be longer than that of allocator.
|
||||
template <typename T, typename = is_memory_resource<T>>
|
||||
allocator(T *p_mr) : allocator() {
|
||||
if (p_mr == nullptr) return;
|
||||
::LIBIMP_::construct<holder_memory_resource<T>>(holder_.data(), p_mr);
|
||||
}
|
||||
|
||||
void swap(allocator &other) noexcept;
|
||||
bool valid() const noexcept;
|
||||
explicit operator bool() const noexcept;
|
||||
|
||||
/// @brief Allocate/deallocate memory.
|
||||
void *alloc(std::size_t s);
|
||||
void free (void *p, std::size_t s);
|
||||
};
|
||||
|
||||
LIBPMR_NAMESPACE_END_
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <cstddef> // std::size_t, std::max_align_t
|
||||
|
||||
#include "libimp/export.h"
|
||||
@ -13,14 +14,42 @@
|
||||
|
||||
LIBPMR_NAMESPACE_BEG_
|
||||
|
||||
/// @brief Helper trait for memory resource.
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct has_allocate : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_allocate<T,
|
||||
typename std::enable_if<std::is_convertible<
|
||||
decltype(std::declval<T &>().allocate(std::declval<std::size_t>())), void *
|
||||
>::value>::type> : std::true_type {};
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct has_deallocate : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_deallocate<T,
|
||||
decltype(std::declval<T &>().deallocate(std::declval<void *>(),
|
||||
std::declval<std::size_t>()))
|
||||
> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
using is_memory_resource =
|
||||
typename std::enable_if<has_allocate <T>::value &&
|
||||
has_deallocate<T>::value>::type;
|
||||
|
||||
/**
|
||||
* @brief A memory resource that uses the
|
||||
* global operator new and operator delete to allocate memory.
|
||||
* standard memory allocation and deallocation interface to allocate memory.
|
||||
*
|
||||
* @see https://en.cppreference.com/w/cpp/memory/new_delete_resource
|
||||
*/
|
||||
class LIBIMP_EXPORT new_delete_resource {
|
||||
public:
|
||||
/// @brief Returns a pointer to a new_delete_resource.
|
||||
static new_delete_resource *get() noexcept;
|
||||
|
||||
/// @brief Allocates storage with a size of at least bytes bytes, aligned to the specified alignment.
|
||||
/// @remark Returns nullptr if storage of the requested size and alignment cannot be obtained.
|
||||
/// @see https://en.cppreference.com/w/cpp/memory/memory_resource/do_allocate
|
||||
|
||||
58
src/libpmr/allocator.cpp
Normal file
58
src/libpmr/allocator.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
|
||||
#include <algorithm> // std::swap
|
||||
|
||||
#include "libpmr/allocator.h"
|
||||
|
||||
LIBPMR_NAMESPACE_BEG_
|
||||
|
||||
allocator::holder_base &allocator::get_holder() noexcept {
|
||||
return *::LIBIMP_::byte_cast<holder_base>(holder_.data());
|
||||
}
|
||||
|
||||
allocator::holder_base const &allocator::get_holder() const noexcept {
|
||||
return *::LIBIMP_::byte_cast<holder_base const>(holder_.data());
|
||||
}
|
||||
|
||||
allocator::allocator() noexcept {
|
||||
::LIBIMP_::construct<holder_null>(holder_.data());
|
||||
}
|
||||
|
||||
allocator::~allocator() noexcept {
|
||||
::LIBIMP_::destroy(&get_holder());
|
||||
}
|
||||
|
||||
allocator::allocator(allocator &&other) noexcept
|
||||
: allocator(other) /*copy*/ {
|
||||
::LIBIMP_::construct<holder_null>(other.holder_.data());
|
||||
}
|
||||
|
||||
allocator &allocator::operator=(allocator &&other) & noexcept {
|
||||
if (this == &other) return *this;
|
||||
this->holder_ = other.holder_;
|
||||
::LIBIMP_::construct<holder_null>(other.holder_.data());
|
||||
return *this;
|
||||
}
|
||||
|
||||
void allocator::swap(allocator &other) noexcept {
|
||||
std::swap(this->holder_, other.holder_);
|
||||
}
|
||||
|
||||
bool allocator::valid() const noexcept {
|
||||
return get_holder().valid();
|
||||
}
|
||||
|
||||
allocator::operator bool() const noexcept {
|
||||
return valid();
|
||||
}
|
||||
|
||||
void *allocator::alloc(std::size_t s) {
|
||||
if (!valid()) return nullptr;
|
||||
return get_holder().alloc(s);
|
||||
}
|
||||
|
||||
void allocator::free(void *p, std::size_t s) {
|
||||
if (!valid()) return;
|
||||
get_holder().free(p, s);
|
||||
}
|
||||
|
||||
LIBPMR_NAMESPACE_END_
|
||||
@ -27,6 +27,16 @@ bool verify_args(::LIBIMP_::log::gripper &log, std::size_t bytes, std::size_t al
|
||||
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to a new_delete_resource.
|
||||
*
|
||||
* @return new_delete_resource*
|
||||
*/
|
||||
new_delete_resource *new_delete_resource::get() noexcept {
|
||||
static new_delete_resource mem_res;
|
||||
return &mem_res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocates storage with a size of at least bytes bytes, aligned to the specified alignment.
|
||||
* @remark Alignment shall be a power of two.
|
||||
|
||||
@ -1,32 +1,78 @@
|
||||
|
||||
#include <vector>
|
||||
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
||||
#include <memory_resource>
|
||||
#endif
|
||||
#include <utility>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "libpmr/allocator.h"
|
||||
|
||||
TEST(allocator, detail) {
|
||||
EXPECT_FALSE(pmr::detail::has_allocate<void>::value);
|
||||
EXPECT_FALSE(pmr::detail::has_allocate<int>::value);
|
||||
EXPECT_FALSE(pmr::detail::has_allocate<std::vector<int>>::value);
|
||||
EXPECT_TRUE (pmr::detail::has_allocate<std::allocator<int>>::value);
|
||||
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
||||
EXPECT_TRUE (pmr::detail::has_allocate<std::pmr::memory_resource>::value);
|
||||
EXPECT_TRUE (pmr::detail::has_allocate<std::pmr::polymorphic_allocator<int>>::value);
|
||||
#endif
|
||||
|
||||
EXPECT_FALSE(pmr::detail::has_deallocate<void>::value);
|
||||
EXPECT_FALSE(pmr::detail::has_deallocate<int>::value);
|
||||
EXPECT_FALSE(pmr::detail::has_deallocate<std::vector<int>>::value);
|
||||
EXPECT_FALSE(pmr::detail::has_deallocate<std::allocator<int>>::value);
|
||||
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
||||
EXPECT_TRUE (pmr::detail::has_deallocate<std::pmr::memory_resource>::value);
|
||||
EXPECT_FALSE(pmr::detail::has_deallocate<std::pmr::polymorphic_allocator<int>>::value);
|
||||
#endif
|
||||
TEST(allocator, construct) {
|
||||
pmr::allocator alc;
|
||||
EXPECT_FALSE(alc.valid());
|
||||
EXPECT_FALSE(alc);
|
||||
}
|
||||
|
||||
TEST(allocator, construct) {
|
||||
TEST(allocator, construct_with_memory_resource) {
|
||||
pmr::new_delete_resource mem_res;
|
||||
pmr::allocator alc {&mem_res};
|
||||
EXPECT_TRUE(alc.valid());
|
||||
EXPECT_TRUE(alc);
|
||||
|
||||
auto p = alc.alloc(128);
|
||||
EXPECT_NE(p, nullptr);
|
||||
EXPECT_NO_THROW(alc.free(p, 128));
|
||||
}
|
||||
|
||||
TEST(allocator, construct_copy_move) {
|
||||
pmr::new_delete_resource mem_res;
|
||||
|
||||
pmr::allocator alc1 {&mem_res}, alc2;
|
||||
EXPECT_TRUE (alc1.valid());
|
||||
EXPECT_TRUE (alc1);
|
||||
EXPECT_FALSE(alc2.valid());
|
||||
EXPECT_FALSE(alc2);
|
||||
|
||||
pmr::allocator alc3 {alc1}, alc4{alc2}, alc5 {std::move(alc1)};
|
||||
EXPECT_TRUE (alc3.valid());
|
||||
EXPECT_TRUE (alc3);
|
||||
EXPECT_FALSE(alc4.valid());
|
||||
EXPECT_FALSE(alc4);
|
||||
EXPECT_TRUE (alc5.valid());
|
||||
EXPECT_TRUE (alc5);
|
||||
EXPECT_FALSE(alc1.valid());
|
||||
EXPECT_FALSE(alc1);
|
||||
}
|
||||
|
||||
TEST(allocator, swap) {
|
||||
pmr::new_delete_resource mem_res;
|
||||
|
||||
pmr::allocator alc1 {&mem_res}, alc2;
|
||||
EXPECT_TRUE (alc1.valid());
|
||||
EXPECT_TRUE (alc1);
|
||||
EXPECT_FALSE(alc2.valid());
|
||||
EXPECT_FALSE(alc2);
|
||||
|
||||
alc1.swap(alc2);
|
||||
EXPECT_FALSE(alc1.valid());
|
||||
EXPECT_FALSE(alc1);
|
||||
EXPECT_TRUE (alc2.valid());
|
||||
EXPECT_TRUE (alc2);
|
||||
}
|
||||
|
||||
TEST(allocator, invalid_alloc_free) {
|
||||
pmr::new_delete_resource mem_res;
|
||||
|
||||
pmr::allocator alc1 {&mem_res}, alc2;
|
||||
EXPECT_EQ(alc1.alloc(0), nullptr);
|
||||
EXPECT_NO_THROW(alc1.free(nullptr, 128));
|
||||
EXPECT_NO_THROW(alc1.free(nullptr, 0));
|
||||
EXPECT_NO_THROW(alc1.free(&alc1, 0));
|
||||
|
||||
EXPECT_EQ(alc2.alloc(0), nullptr);
|
||||
EXPECT_NO_THROW(alc2.free(nullptr, 128));
|
||||
EXPECT_NO_THROW(alc2.free(nullptr, 0));
|
||||
EXPECT_NO_THROW(alc2.free(&alc1, 0));
|
||||
|
||||
EXPECT_EQ(alc2.alloc(1024), nullptr);
|
||||
EXPECT_NO_THROW(alc2.free(&alc1, sizeof(alc1)));
|
||||
}
|
||||
@ -1,5 +1,8 @@
|
||||
|
||||
#include <utility>
|
||||
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
||||
#include <memory_resource>
|
||||
#endif
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
@ -21,20 +24,40 @@ void *test_mr(T &&mr, std::size_t bytes, std::size_t alignment) {
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(memory_resource, traits) {
|
||||
EXPECT_FALSE(pmr::has_allocate<void>::value);
|
||||
EXPECT_FALSE(pmr::has_allocate<int>::value);
|
||||
EXPECT_FALSE(pmr::has_allocate<std::vector<int>>::value);
|
||||
EXPECT_TRUE (pmr::has_allocate<std::allocator<int>>::value);
|
||||
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
||||
EXPECT_TRUE (pmr::has_allocate<std::pmr::memory_resource>::value);
|
||||
EXPECT_TRUE (pmr::has_allocate<std::pmr::polymorphic_allocator<int>>::value);
|
||||
#endif
|
||||
|
||||
EXPECT_FALSE(pmr::has_deallocate<void>::value);
|
||||
EXPECT_FALSE(pmr::has_deallocate<int>::value);
|
||||
EXPECT_FALSE(pmr::has_deallocate<std::vector<int>>::value);
|
||||
EXPECT_FALSE(pmr::has_deallocate<std::allocator<int>>::value);
|
||||
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
|
||||
EXPECT_TRUE (pmr::has_deallocate<std::pmr::memory_resource>::value);
|
||||
EXPECT_FALSE(pmr::has_deallocate<std::pmr::polymorphic_allocator<int>>::value);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(memory_resource, new_delete_resource) {
|
||||
pmr::new_delete_resource m_res;
|
||||
pmr::new_delete_resource mem_res;
|
||||
|
||||
EXPECT_EQ(test_mr(m_res, 0, 0), nullptr);
|
||||
EXPECT_EQ(test_mr(m_res, 0, 1), nullptr);
|
||||
EXPECT_EQ(test_mr(m_res, 0, 2), nullptr);
|
||||
EXPECT_EQ(test_mr(m_res, 0, 3), nullptr);
|
||||
EXPECT_EQ(test_mr(m_res, 0, 8), nullptr);
|
||||
EXPECT_EQ(test_mr(m_res, 0, 64), nullptr);
|
||||
EXPECT_EQ(test_mr(mem_res, 0, 0), nullptr);
|
||||
EXPECT_EQ(test_mr(mem_res, 0, 1), nullptr);
|
||||
EXPECT_EQ(test_mr(mem_res, 0, 2), nullptr);
|
||||
EXPECT_EQ(test_mr(mem_res, 0, 3), nullptr);
|
||||
EXPECT_EQ(test_mr(mem_res, 0, 8), nullptr);
|
||||
EXPECT_EQ(test_mr(mem_res, 0, 64), nullptr);
|
||||
|
||||
EXPECT_EQ(test_mr(m_res, 1, 0), nullptr);
|
||||
EXPECT_NE(test_mr(m_res, 1, 1), nullptr);
|
||||
EXPECT_NE(test_mr(m_res, 1, 2), nullptr);
|
||||
EXPECT_EQ(test_mr(m_res, 1, 3), nullptr);
|
||||
EXPECT_NE(test_mr(m_res, 1, 8), nullptr);
|
||||
EXPECT_NE(test_mr(m_res, 1, 64), nullptr);
|
||||
EXPECT_EQ(test_mr(mem_res, 1, 0), nullptr);
|
||||
EXPECT_NE(test_mr(mem_res, 1, 1), nullptr);
|
||||
EXPECT_NE(test_mr(mem_res, 1, 2), nullptr);
|
||||
EXPECT_EQ(test_mr(mem_res, 1, 3), nullptr);
|
||||
EXPECT_NE(test_mr(mem_res, 1, 8), nullptr);
|
||||
EXPECT_NE(test_mr(mem_res, 1, 64), nullptr);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user