Reimplement the allocator required for the container type with $new

This commit is contained in:
mutouyun 2025-02-13 15:28:14 +08:00 committed by 木头云
parent 4e70d6c60b
commit 10c0d14de6
8 changed files with 115 additions and 103 deletions

View File

@ -1,5 +1,5 @@
/** /**
* \file libipc/polymorphic_allocator * \file libipc/bytes_allocator.h
* \author mutouyun (orz@orzz.org) * \author mutouyun (orz@orzz.org)
* \brief A generic polymorphic memory allocator. * \brief A generic polymorphic memory allocator.
*/ */
@ -50,13 +50,13 @@ using is_memory_resource =
* \brief An allocator which exhibits different allocation behavior * \brief An allocator which exhibits different allocation behavior
* depending upon the memory resource from which it is constructed. * depending upon the memory resource from which it is constructed.
* *
* \note Unlike `std::pmr::polymorphic_allocator`, it does not * \note Unlike `std::pmr::container_allocator`, it does not
* rely on a specific inheritance relationship and only restricts * rely on a specific inheritance relationship and only restricts
* the interface behavior of the incoming memory resource object to * the interface behavior of the incoming memory resource object to
* conform to `std::pmr::memory_resource`. * conform to `std::pmr::memory_resource`.
* *
* \see https://en.cppreference.com/w/cpp/memory/memory_resource * \see https://en.cppreference.com/w/cpp/memory/memory_resource
* https://en.cppreference.com/w/cpp/memory/polymorphic_allocator * https://en.cppreference.com/w/cpp/memory/container_allocator
*/ */
class LIBIPC_EXPORT bytes_allocator { class LIBIPC_EXPORT bytes_allocator {
@ -159,88 +159,5 @@ public:
} }
}; };
/**
* \brief An allocator that can be used by all standard library containers,
* based on ipc::bytes_allocator.
*
* \see https://en.cppreference.com/w/cpp/memory/allocator
* https://en.cppreference.com/w/cpp/memory/polymorphic_allocator
*/
template <typename T>
class polymorphic_allocator {
template <typename U>
friend class polymorphic_allocator;
public:
// type definitions
typedef T value_type;
typedef value_type * pointer;
typedef const value_type *const_pointer;
typedef value_type & reference;
typedef const value_type &const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
private:
bytes_allocator alloc_;
public:
// the other type of std_allocator
template <typename U>
struct rebind {
using other = polymorphic_allocator<U>;
};
polymorphic_allocator() noexcept {}
template <typename P, is_memory_resource<P> = true>
polymorphic_allocator(P *p_mr) noexcept : alloc_(p_mr) {}
// construct by copying (do nothing)
polymorphic_allocator (polymorphic_allocator<T> const &) noexcept {}
polymorphic_allocator& operator=(polymorphic_allocator<T> const &) noexcept { return *this; }
// construct from a related allocator (do nothing)
template <typename U> polymorphic_allocator (polymorphic_allocator<U> const &) noexcept {}
template <typename U> polymorphic_allocator &operator=(polymorphic_allocator<U> const &) noexcept { return *this; }
polymorphic_allocator (polymorphic_allocator &&) noexcept = default;
polymorphic_allocator& operator=(polymorphic_allocator &&) noexcept = default;
constexpr size_type max_size(void) const noexcept {
return (std::numeric_limits<size_type>::max)() / sizeof(value_type);
}
pointer allocate(size_type count) noexcept {
if (count == 0) return nullptr;
if (count > this->max_size()) return nullptr;
return static_cast<pointer>(alloc_.allocate(count * sizeof(value_type), alignof(T)));
}
void deallocate(pointer p, size_type count) noexcept {
alloc_.deallocate(p, count * sizeof(value_type), alignof(T));
}
template <typename... P>
static void construct(pointer p, P && ... params) {
std::ignore = ipc::construct<T>(p, std::forward<P>(params)...);
}
static void destroy(pointer p) {
std::ignore = ipc::destroy(p);
}
};
template <typename T, typename U>
constexpr bool operator==(polymorphic_allocator<T> const &, polymorphic_allocator<U> const &) noexcept {
return true;
}
template <typename T, typename U>
constexpr bool operator!=(polymorphic_allocator<T> const &, polymorphic_allocator<U> const &) noexcept {
return false;
}
} // namespace mem } // namespace mem
} // namespace ipc } // namespace ipc

View File

@ -6,7 +6,7 @@
#pragma once #pragma once
#include "libipc/imp/export.h" #include "libipc/imp/export.h"
#include "libipc/mem/polymorphic_allocator.h" #include "libipc/mem/bytes_allocator.h"
namespace ipc { namespace ipc {
namespace mem { namespace mem {

View File

@ -0,0 +1,95 @@
/**
* \file libipc/container_allocator.h
* \author mutouyun (orz@orzz.org)
* \brief An allocator that can be used by all standard library containers.
*/
#pragma once
#include <cstddef>
#include <utility>
#include "libipc/imp/uninitialized.h"
#include "libipc/mem/new.h"
namespace ipc {
namespace mem {
/**
* \brief An allocator that can be used by all standard library containers.
*
* \see https://en.cppreference.com/w/cpp/memory/allocator
* https://en.cppreference.com/w/cpp/memory/polymorphic_allocator
*/
template <typename T>
class container_allocator {
template <typename U>
friend class container_allocator;
public:
// type definitions
typedef T value_type;
typedef value_type * pointer;
typedef const value_type *const_pointer;
typedef value_type & reference;
typedef const value_type &const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
// the other type of std_allocator
template <typename U>
struct rebind {
using other = container_allocator<U>;
};
container_allocator() noexcept {}
// construct by copying (do nothing)
container_allocator (container_allocator<T> const &) noexcept {}
container_allocator& operator=(container_allocator<T> const &) noexcept { return *this; }
// construct from a related allocator (do nothing)
template <typename U> container_allocator (container_allocator<U> const &) noexcept {}
template <typename U> container_allocator &operator=(container_allocator<U> const &) noexcept { return *this; }
container_allocator (container_allocator &&) noexcept = default;
container_allocator& operator=(container_allocator &&) noexcept = default;
constexpr size_type max_size() const noexcept {
return 1;
}
pointer allocate(size_type count) noexcept {
if (count == 0) return nullptr;
if (count > this->max_size()) return nullptr;
return mem::$new<value_type>();
}
void deallocate(pointer p, size_type count) noexcept {
if (count == 0) return;
if (count > this->max_size()) return;
mem::$delete(p);
}
template <typename... P>
static void construct(pointer p, P && ... params) {
std::ignore = ipc::construct<T>(p, std::forward<P>(params)...);
}
static void destroy(pointer p) {
std::ignore = ipc::destroy(p);
}
};
template <typename T, typename U>
constexpr bool operator==(container_allocator<T> const &, container_allocator<U> const &) noexcept {
return true;
}
template <typename T, typename U>
constexpr bool operator!=(container_allocator<T> const &, container_allocator<U> const &) noexcept {
return false;
}
} // namespace mem
} // namespace ipc

View File

@ -11,7 +11,7 @@
#include "libipc/imp/export.h" #include "libipc/imp/export.h"
#include "libipc/imp/span.h" #include "libipc/imp/span.h"
#include "libipc/imp/byte.h" #include "libipc/imp/byte.h"
#include "libipc/mem/polymorphic_allocator.h" #include "libipc/mem/bytes_allocator.h"
namespace ipc { namespace ipc {
namespace mem { namespace mem {

View File

@ -2,7 +2,7 @@
#include <algorithm> // std::swap #include <algorithm> // std::swap
#include "libipc/imp/log.h" #include "libipc/imp/log.h"
#include "libipc/mem/polymorphic_allocator.h" #include "libipc/mem/bytes_allocator.h"
#include "libipc/mem/memory_resource.h" #include "libipc/mem/memory_resource.h"
namespace ipc { namespace ipc {

View File

@ -6,7 +6,7 @@
#include "libipc/def.h" #include "libipc/def.h"
#include "libipc/imp/detect_plat.h" #include "libipc/imp/detect_plat.h"
#include "libipc/imp/byte.h" #include "libipc/imp/byte.h"
#include "libipc/mem/polymorphic_allocator.h" #include "libipc/mem/bytes_allocator.h"
#include "libipc/mem/memory_resource.h" #include "libipc/mem/memory_resource.h"
namespace ipc { namespace ipc {

View File

@ -6,7 +6,7 @@
#include "libipc/def.h" #include "libipc/def.h"
#include "libipc/imp/fmt.h" #include "libipc/imp/fmt.h"
#include "libipc/mem/polymorphic_allocator.h" #include "libipc/mem/container_allocator.h"
namespace ipc { namespace ipc {
@ -15,12 +15,12 @@ struct hash : public std::hash<T> {};
template <typename Key, typename T> template <typename Key, typename T>
using unordered_map = std::unordered_map< using unordered_map = std::unordered_map<
Key, T, ipc::hash<Key>, std::equal_to<Key>, ipc::mem::polymorphic_allocator<std::pair<Key const, T>> Key, T, ipc::hash<Key>, std::equal_to<Key>, ipc::mem::container_allocator<std::pair<Key const, T>>
>; >;
template <typename Key, typename T> template <typename Key, typename T>
using map = std::map< using map = std::map<
Key, T, std::less<Key>, ipc::mem::polymorphic_allocator<std::pair<Key const, T>> Key, T, std::less<Key>, ipc::mem::container_allocator<std::pair<Key const, T>>
>; >;
/// \brief Check string validity. /// \brief Check string validity.

View File

@ -8,15 +8,15 @@
# include <memory_resource> # include <memory_resource>
#endif #endif
#include "libipc/mem/polymorphic_allocator.h" #include "libipc/mem/bytes_allocator.h"
#include "libipc/mem/memory_resource.h" #include "libipc/mem/memory_resource.h"
TEST(polymorphic_allocator, ctor) { TEST(bytes_allocator, ctor) {
ipc::mem::bytes_allocator alc; ipc::mem::bytes_allocator alc;
SUCCEED(); SUCCEED();
} }
TEST(polymorphic_allocator, ctor_value_initialization) { TEST(bytes_allocator, ctor_value_initialization) {
ipc::mem::bytes_allocator alc{}; ipc::mem::bytes_allocator alc{};
auto p = alc.allocate(128); auto p = alc.allocate(128);
EXPECT_NE(p, nullptr); EXPECT_NE(p, nullptr);
@ -37,14 +37,14 @@ public:
} // namespace } // namespace
TEST(polymorphic_allocator, memory_resource_traits) { TEST(bytes_allocator, memory_resource_traits) {
EXPECT_FALSE(ipc::mem::has_allocate<void>::value); EXPECT_FALSE(ipc::mem::has_allocate<void>::value);
EXPECT_FALSE(ipc::mem::has_allocate<int>::value); EXPECT_FALSE(ipc::mem::has_allocate<int>::value);
EXPECT_FALSE(ipc::mem::has_allocate<std::vector<int>>::value); EXPECT_FALSE(ipc::mem::has_allocate<std::vector<int>>::value);
EXPECT_FALSE(ipc::mem::has_allocate<std::allocator<int>>::value); EXPECT_FALSE(ipc::mem::has_allocate<std::allocator<int>>::value);
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource) #if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
EXPECT_TRUE (ipc::mem::has_allocate<std::ipc::mem::memory_resource>::value); EXPECT_TRUE (ipc::mem::has_allocate<std::ipc::mem::memory_resource>::value);
EXPECT_TRUE (ipc::mem::has_allocate<std::ipc::mem::polymorphic_allocator<int>>::value); EXPECT_TRUE (ipc::mem::has_allocate<std::ipc::mem::container_allocator<int>>::value);
#endif #endif
EXPECT_FALSE(ipc::mem::has_deallocate<void>::value); EXPECT_FALSE(ipc::mem::has_deallocate<void>::value);
@ -53,11 +53,11 @@ TEST(polymorphic_allocator, memory_resource_traits) {
EXPECT_FALSE(ipc::mem::has_deallocate<std::allocator<int>>::value); EXPECT_FALSE(ipc::mem::has_deallocate<std::allocator<int>>::value);
#if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource) #if defined(LIBIMP_CPP_17) && defined(__cpp_lib_memory_resource)
EXPECT_TRUE (ipc::mem::has_deallocate<std::ipc::mem::memory_resource>::value); EXPECT_TRUE (ipc::mem::has_deallocate<std::ipc::mem::memory_resource>::value);
EXPECT_FALSE(ipc::mem::has_deallocate<std::ipc::mem::polymorphic_allocator<int>>::value); EXPECT_FALSE(ipc::mem::has_deallocate<std::ipc::mem::container_allocator<int>>::value);
#endif #endif
} }
TEST(polymorphic_allocator, ctor_copy_move) { TEST(bytes_allocator, ctor_copy_move) {
ipc::mem::new_delete_resource mem_res; ipc::mem::new_delete_resource mem_res;
dummy_resource dummy_res; dummy_resource dummy_res;
ipc::mem::bytes_allocator alc1{&mem_res}, alc2{&dummy_res}; ipc::mem::bytes_allocator alc1{&mem_res}, alc2{&dummy_res};
@ -79,7 +79,7 @@ TEST(polymorphic_allocator, ctor_copy_move) {
ASSERT_NO_THROW(alc5.deallocate(p, 128)); ASSERT_NO_THROW(alc5.deallocate(p, 128));
} }
TEST(polymorphic_allocator, swap) { TEST(bytes_allocator, swap) {
ipc::mem::new_delete_resource mem_res; ipc::mem::new_delete_resource mem_res;
dummy_resource dummy_res; dummy_resource dummy_res;
ipc::mem::bytes_allocator alc1{&mem_res}, alc2{&dummy_res}; ipc::mem::bytes_allocator alc1{&mem_res}, alc2{&dummy_res};
@ -90,7 +90,7 @@ TEST(polymorphic_allocator, swap) {
ASSERT_EQ(alc1.allocate(128), nullptr); ASSERT_EQ(alc1.allocate(128), nullptr);
} }
TEST(polymorphic_allocator, invalid_alloc_free) { TEST(bytes_allocator, invalid_alloc_free) {
ipc::mem::bytes_allocator alc1; ipc::mem::bytes_allocator alc1;
EXPECT_EQ(alc1.allocate(0), nullptr); EXPECT_EQ(alc1.allocate(0), nullptr);
EXPECT_NO_THROW(alc1.deallocate(nullptr, 128)); EXPECT_NO_THROW(alc1.deallocate(nullptr, 128));
@ -98,6 +98,6 @@ TEST(polymorphic_allocator, invalid_alloc_free) {
EXPECT_NO_THROW(alc1.deallocate(&alc1, 0)); EXPECT_NO_THROW(alc1.deallocate(&alc1, 0));
} }
TEST(polymorphic_allocator, sizeof) { TEST(bytes_allocator, sizeof) {
EXPECT_EQ(sizeof(ipc::mem::bytes_allocator), sizeof(void *) * 2); EXPECT_EQ(sizeof(ipc::mem::bytes_allocator), sizeof(void *) * 2);
} }