mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-07 01:06:45 +08:00
Update libpmr code
This commit is contained in:
parent
fc60c75ac4
commit
2f79716b1c
@ -10,6 +10,7 @@
|
|||||||
#include <new>
|
#include <new>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "libimp/byte.h"
|
#include "libimp/byte.h"
|
||||||
#include "libimp/export.h"
|
#include "libimp/export.h"
|
||||||
@ -37,7 +38,7 @@ union block {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief A fixed-length memory block central cache pool.
|
* \brief A fixed-length memory block central cache pool.
|
||||||
* \tparam BlockSize specifies the memory block size
|
* \tparam BlockT specifies the memory block type
|
||||||
*/
|
*/
|
||||||
template <typename BlockT, std::size_t BlockPoolExpansion>
|
template <typename BlockT, std::size_t BlockPoolExpansion>
|
||||||
class central_cache_pool {
|
class central_cache_pool {
|
||||||
@ -66,7 +67,7 @@ public:
|
|||||||
if (chunk == nullptr) {
|
if (chunk == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
for (std::size_t i = 0; i < block_pool_expansion - 1; ++i) {
|
for (std::size_t i = 0; i < BlockPoolExpansion - 1; ++i) {
|
||||||
(*chunk)[i].next = &(*chunk)[i + 1];
|
(*chunk)[i].next = &(*chunk)[i + 1];
|
||||||
}
|
}
|
||||||
chunk->back().next = nullptr;
|
chunk->back().next = nullptr;
|
||||||
@ -91,12 +92,103 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief A fixed-length memory block central cache pool with no default expansion size.
|
||||||
|
template <typename BlockT>
|
||||||
|
class central_cache_pool<BlockT, 0> {
|
||||||
|
|
||||||
|
/// \brief The block type, which should be a union of a pointer and a storage.
|
||||||
|
using block_t = BlockT;
|
||||||
|
/// \brief The node type, which is used to store the block pointer.
|
||||||
|
using node_t = typename ::LIBCONCUR::intrusive_stack<block_t *>::node;
|
||||||
|
|
||||||
|
/// \brief The central cache stack.
|
||||||
|
::LIBCONCUR::intrusive_stack<block_t *> cached_;
|
||||||
|
::LIBCONCUR::intrusive_stack<block_t *> aqueired_;
|
||||||
|
|
||||||
|
central_cache_pool() noexcept = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
block_t *aqueire() noexcept {
|
||||||
|
auto *n = cached_.pop();
|
||||||
|
if (n != nullptr) {
|
||||||
|
aqueired_.push(n);
|
||||||
|
return n->value;
|
||||||
|
}
|
||||||
|
// For pools with no default expansion size,
|
||||||
|
// the central cache pool is only buffered, not allocated.
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void release(block_t *p) noexcept {
|
||||||
|
if (p == nullptr) return;
|
||||||
|
auto *a = aqueired_.pop();
|
||||||
|
if (a == nullptr) {
|
||||||
|
a = central_cache_allocator().construct<node_t>();
|
||||||
|
if (a == nullptr) return;
|
||||||
|
}
|
||||||
|
a->value = p;
|
||||||
|
cached_.push(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Get the singleton instance.
|
||||||
|
static central_cache_pool &instance() noexcept {
|
||||||
|
static central_cache_pool pool;
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Fixed-length memory block pool.
|
* \brief Fixed-length memory block pool.
|
||||||
* \tparam BlockSize specifies the memory block size
|
* \tparam BlockSize specifies the memory block size
|
||||||
* \tparam BlockPoolExpansion specifies the default number of blocks to expand when the block pool is exhausted
|
* \tparam BlockPoolExpansion specifies the default number of blocks to expand when the block pool is exhausted
|
||||||
*/
|
*/
|
||||||
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
||||||
|
class block_pool;
|
||||||
|
|
||||||
|
/// \brief General-purpose block pool for any size of memory block.
|
||||||
|
/// \note This block pool can only be used to deallocate a group of memory blocks of unknown but consistent size,
|
||||||
|
/// and cannot be used for memory block allocation.
|
||||||
|
template <>
|
||||||
|
class block_pool<0, 0> {
|
||||||
|
|
||||||
|
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
||||||
|
friend class block_pool;
|
||||||
|
|
||||||
|
/// \brief The block type.
|
||||||
|
struct block_t {
|
||||||
|
block_t *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief The central cache pool type.
|
||||||
|
using central_cache_pool_t = central_cache_pool<block_t, 0>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr static std::size_t block_size = 0;
|
||||||
|
|
||||||
|
block_pool() noexcept : cursor_(central_cache_pool_t::instance().aqueire()) {}
|
||||||
|
~block_pool() noexcept {
|
||||||
|
central_cache_pool_t::instance().release(cursor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
block_pool(block_pool const &) = delete;
|
||||||
|
block_pool& operator=(block_pool const &) = delete;
|
||||||
|
|
||||||
|
block_pool(block_pool &&rhs) noexcept : cursor_(std::exchange(rhs.cursor_, nullptr)) {}
|
||||||
|
block_pool &operator=(block_pool &&) noexcept = delete;
|
||||||
|
|
||||||
|
void deallocate(void *p) noexcept {
|
||||||
|
if (p == nullptr) return;
|
||||||
|
block_t *b = static_cast<block_t *>(p);
|
||||||
|
b->next = cursor_;
|
||||||
|
cursor_ = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
block_t *cursor_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief A block pool for a block of memory of a specific size.
|
||||||
|
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
||||||
class block_pool {
|
class block_pool {
|
||||||
|
|
||||||
/// \brief The block type.
|
/// \brief The block type.
|
||||||
@ -120,6 +212,15 @@ public:
|
|||||||
block_pool(block_pool const &) = delete;
|
block_pool(block_pool const &) = delete;
|
||||||
block_pool& operator=(block_pool const &) = delete;
|
block_pool& operator=(block_pool const &) = delete;
|
||||||
|
|
||||||
|
block_pool(block_pool &&rhs) noexcept
|
||||||
|
: cursor_(std::exchange(rhs.cursor_, nullptr)) {}
|
||||||
|
block_pool &operator=(block_pool &&) noexcept = delete;
|
||||||
|
|
||||||
|
/// \brief Used to take all memory blocks from within a general-purpose block pool.
|
||||||
|
/// \note Of course, the actual memory blocks they manage must be the same size.
|
||||||
|
block_pool(block_pool<0, 0> &&rhs) noexcept
|
||||||
|
: cursor_(reinterpret_cast<block_t *>(std::exchange(rhs.cursor_, nullptr))) {}
|
||||||
|
|
||||||
void *allocate() noexcept {
|
void *allocate() noexcept {
|
||||||
if (cursor_ == nullptr) {
|
if (cursor_ == nullptr) {
|
||||||
cursor_ = expand();
|
cursor_ = expand();
|
||||||
|
|||||||
@ -6,6 +6,10 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "libimp/aligned.h"
|
||||||
|
|
||||||
#define LIBPMR pmr
|
#define LIBPMR pmr
|
||||||
#define LIBPMR_NAMESPACE_BEG_ namespace LIBPMR {
|
#define LIBPMR_NAMESPACE_BEG_ namespace LIBPMR {
|
||||||
#define LIBPMR_NAMESPACE_END_ }
|
#define LIBPMR_NAMESPACE_END_ }
|
||||||
@ -15,8 +19,8 @@ LIBPMR_NAMESPACE_BEG_
|
|||||||
/// \brief Constants.
|
/// \brief Constants.
|
||||||
|
|
||||||
enum : std::size_t {
|
enum : std::size_t {
|
||||||
block_pool_expansion = 64,
|
|
||||||
central_cache_default_size = 1024 * 1024, ///< 1MB
|
central_cache_default_size = 1024 * 1024, ///< 1MB
|
||||||
|
regular_head_size = ::LIBIMP::round_up(sizeof(std::size_t), alignof(std::max_align_t)),
|
||||||
};
|
};
|
||||||
|
|
||||||
LIBPMR_NAMESPACE_END_
|
LIBPMR_NAMESPACE_END_
|
||||||
|
|||||||
@ -7,9 +7,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "libimp/aligned.h"
|
#include "libimp/aligned.h"
|
||||||
#include "libimp/uninitialized.h"
|
#include "libimp/uninitialized.h"
|
||||||
|
#include "libimp/byte.h"
|
||||||
|
#include "libimp/detect_plat.h"
|
||||||
|
|
||||||
#include "libpmr/def.h"
|
#include "libpmr/def.h"
|
||||||
#include "libpmr/block_pool.h"
|
#include "libpmr/block_pool.h"
|
||||||
@ -27,34 +31,126 @@ constexpr inline std::size_t regular_level(std::size_t s) noexcept {
|
|||||||
|
|
||||||
constexpr inline std::size_t regular_sizeof(std::size_t s) noexcept {
|
constexpr inline std::size_t regular_sizeof(std::size_t s) noexcept {
|
||||||
switch (regular_level(s)) {
|
switch (regular_level(s)) {
|
||||||
case 0 : return ::LIBIMP::round_up<std::size_t>(s, 8);
|
case 0 : return std::max<std::size_t>(::LIBIMP::round_up<std::size_t>(s, 8), regular_head_size);
|
||||||
case 1 : return ::LIBIMP::round_up<std::size_t>(s, 128);
|
case 1 : return ::LIBIMP::round_up<std::size_t>(s, 128);
|
||||||
case 2 : return ::LIBIMP::round_up<std::size_t>(s, 1024);
|
case 2 : return ::LIBIMP::round_up<std::size_t>(s, 1024);
|
||||||
case 3 : return ::LIBIMP::round_up<std::size_t>(s, 8192);
|
case 3 : return ::LIBIMP::round_up<std::size_t>(s, 8192);
|
||||||
default: return 0;
|
default: return (std::numeric_limits<std::size_t>::max)();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr inline std::size_t regular_sizeof() noexcept {
|
constexpr inline std::size_t regular_sizeof() noexcept {
|
||||||
return regular_sizeof(sizeof(T));
|
return regular_sizeof(regular_head_size + sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
class block_pool_like {
|
||||||
|
public:
|
||||||
|
virtual ~block_pool_like() noexcept = default;
|
||||||
|
virtual void *allocate() noexcept = 0;
|
||||||
|
virtual void deallocate(void *p) noexcept = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::unordered_map<std::size_t, block_pool_like *> &get_block_pool_map() noexcept {
|
||||||
|
thread_local std::unordered_map<std::size_t, block_pool_like *> instances;
|
||||||
|
return instances;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
||||||
class block_pool_resource : public block_pool<BlockSize, BlockPoolExpansion> {
|
class block_pool_resource;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class block_pool_resource<0, 0> : public block_pool<0, 0>
|
||||||
|
, public block_pool_like {
|
||||||
|
|
||||||
|
void *allocate() noexcept override {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static block_pool_resource *get() noexcept {
|
void deallocate(void *p) noexcept override {
|
||||||
thread_local block_pool_resource instance;
|
block_pool<0, 0>::deallocate(p);
|
||||||
return &instance;
|
|
||||||
}
|
|
||||||
void *allocate(std::size_t /*bytes*/, std::size_t /*alignment*/ = alignof(std::max_align_t)) noexcept {
|
|
||||||
return block_pool<BlockSize, BlockPoolExpansion>::allocate();
|
|
||||||
}
|
|
||||||
void deallocate(void *p, std::size_t /*bytes*/, std::size_t /*alignment*/ = alignof(std::max_align_t)) noexcept {
|
|
||||||
block_pool<BlockSize, BlockPoolExpansion>::deallocate(p);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
||||||
|
class block_pool_resource : public block_pool<BlockSize, BlockPoolExpansion>
|
||||||
|
, public block_pool_like {
|
||||||
|
|
||||||
|
using base_t = block_pool<BlockSize, BlockPoolExpansion>;
|
||||||
|
|
||||||
|
void *allocate() noexcept override {
|
||||||
|
return base_t::allocate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(void *p) noexcept override {
|
||||||
|
base_t::deallocate(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static block_pool_resource *get() noexcept;
|
||||||
|
|
||||||
|
using base_t::base_t;
|
||||||
|
|
||||||
|
void *allocate(std::size_t /*bytes*/, std::size_t /*alignment*/ = alignof(std::max_align_t)) noexcept {
|
||||||
|
void *p = base_t::allocate();
|
||||||
|
p = ::LIBIMP::construct<std::size_t>(p, BlockSize);
|
||||||
|
return reinterpret_cast<::LIBIMP::byte *>(p) + regular_head_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(void *p, std::size_t /*bytes*/, std::size_t /*alignment*/ = alignof(std::max_align_t)) noexcept {
|
||||||
|
p = reinterpret_cast<::LIBIMP::byte *>(p) - regular_head_size;
|
||||||
|
auto r_size = *static_cast<std::size_t *>(p);
|
||||||
|
if (r_size <= BlockSize) {
|
||||||
|
base_t::deallocate(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &map = get_block_pool_map();
|
||||||
|
auto it = map.find(r_size);
|
||||||
|
if ((it == map.end()) || (it->second == nullptr)) LIBIMP_TRY {
|
||||||
|
// If the corresponding memory resource cannot be found,
|
||||||
|
// create a temporary general-purpose block pool to deallocate memory.
|
||||||
|
it = map.emplace(r_size, new block_pool_resource<0, 0>).first;
|
||||||
|
} LIBIMP_CATCH(...) {
|
||||||
|
// If the memory resource cannot be created,
|
||||||
|
// store the pointer directly to avoid leakage.
|
||||||
|
base_t::deallocate(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
it->second->deallocate(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
||||||
|
auto block_pool_resource<BlockSize, BlockPoolExpansion>::get() noexcept
|
||||||
|
-> block_pool_resource<BlockSize, BlockPoolExpansion> * {
|
||||||
|
auto &map = get_block_pool_map();
|
||||||
|
thread_local block_pool_resource *pi = nullptr;
|
||||||
|
if (pi != nullptr) {
|
||||||
|
return pi;
|
||||||
|
}
|
||||||
|
auto it = map.find(BlockSize);
|
||||||
|
if ((it != map.end()) && (it->second != nullptr)) {
|
||||||
|
auto *bp = static_cast <block_pool<0, 0> *>(
|
||||||
|
dynamic_cast<block_pool_resource<0, 0> *>(it->second));
|
||||||
|
if (bp == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
thread_local block_pool_resource instance(std::move(*bp));
|
||||||
|
delete static_cast<block_pool_resource<0, 0> *>(bp);
|
||||||
|
pi = &instance;
|
||||||
|
} else {
|
||||||
|
thread_local block_pool_resource instance;
|
||||||
|
pi = &instance;
|
||||||
|
}
|
||||||
|
LIBIMP_TRY {
|
||||||
|
map.emplace(BlockSize, pi);
|
||||||
|
} LIBIMP_CATCH(...) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return pi;
|
||||||
|
}
|
||||||
|
|
||||||
template <std::size_t N, std::size_t L = regular_level(N)>
|
template <std::size_t N, std::size_t L = regular_level(N)>
|
||||||
class regular_resource : public new_delete_resource {};
|
class regular_resource : public new_delete_resource {};
|
||||||
|
|
||||||
@ -71,16 +167,19 @@ template <std::size_t N>
|
|||||||
class regular_resource<N, 3> : public block_pool_resource<N, 64> {};
|
class regular_resource<N, 3> : public block_pool_resource<N, 64> {};
|
||||||
|
|
||||||
template <typename T, typename... A>
|
template <typename T, typename... A>
|
||||||
T *new_(A &&... args) noexcept {
|
T *new$(A &&... args) noexcept {
|
||||||
auto *mem_res = regular_resource<regular_sizeof<T>()>::get();
|
auto *mem_res = regular_resource<regular_sizeof<T>()>::get();
|
||||||
|
if (mem_res == nullptr) return nullptr;
|
||||||
return ::LIBIMP::construct<T>(mem_res->allocate(sizeof(T), alignof(T)), std::forward<A>(args)...);
|
return ::LIBIMP::construct<T>(mem_res->allocate(sizeof(T), alignof(T)), std::forward<A>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void delete_(T *p) noexcept {
|
void delete$(T *p) noexcept {
|
||||||
if (p == nullptr) return;
|
if (p == nullptr) return;
|
||||||
|
::LIBIMP::destroy(p);
|
||||||
auto *mem_res = regular_resource<regular_sizeof<T>()>::get();
|
auto *mem_res = regular_resource<regular_sizeof<T>()>::get();
|
||||||
mem_res->deallocate(::LIBIMP::destroy(p), sizeof(T), alignof(T));
|
if (mem_res == nullptr) return;
|
||||||
|
mem_res->deallocate(p, sizeof(T), alignof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
LIBPMR_NAMESPACE_END_
|
LIBPMR_NAMESPACE_END_
|
||||||
|
|||||||
@ -1,5 +1,97 @@
|
|||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
#include "libpmr/new.h"
|
#include "libpmr/new.h"
|
||||||
|
|
||||||
|
TEST(pmr_new, regular_sizeof) {
|
||||||
|
ASSERT_EQ(pmr::regular_sizeof<std::int8_t >(), pmr::regular_head_size + 8);
|
||||||
|
ASSERT_EQ(pmr::regular_sizeof<std::int16_t>(), pmr::regular_head_size + 8);
|
||||||
|
ASSERT_EQ(pmr::regular_sizeof<std::int32_t>(), pmr::regular_head_size + 8);
|
||||||
|
ASSERT_EQ(pmr::regular_sizeof<std::int64_t>(), pmr::regular_head_size + 8);
|
||||||
|
|
||||||
|
ASSERT_EQ((pmr::regular_sizeof<std::array<char, 10 >>()), ::LIBIMP::round_up<std::size_t>(pmr::regular_head_size + 10 , 8));
|
||||||
|
ASSERT_EQ((pmr::regular_sizeof<std::array<char, 100 >>()), ::LIBIMP::round_up<std::size_t>(pmr::regular_head_size + 100 , 8));
|
||||||
|
ASSERT_EQ((pmr::regular_sizeof<std::array<char, 1000 >>()), ::LIBIMP::round_up<std::size_t>(pmr::regular_head_size + 1000 , 128));
|
||||||
|
ASSERT_EQ((pmr::regular_sizeof<std::array<char, 10000 >>()), ::LIBIMP::round_up<std::size_t>(pmr::regular_head_size + 10000, 8192));
|
||||||
|
ASSERT_EQ((pmr::regular_sizeof<std::array<char, 100000>>()), (std::numeric_limits<std::size_t>::max)());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pmr_new, new$) {
|
||||||
|
auto p = pmr::new$<int>();
|
||||||
|
ASSERT_NE(p, nullptr);
|
||||||
|
*p = -1;
|
||||||
|
ASSERT_EQ(*p, -1);
|
||||||
|
pmr::delete$(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pmr_new, new$value) {
|
||||||
|
auto p = pmr::new$<int>((std::numeric_limits<int>::max)());
|
||||||
|
ASSERT_NE(p, nullptr);
|
||||||
|
ASSERT_EQ(*p, (std::numeric_limits<int>::max)());
|
||||||
|
pmr::delete$(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <std::size_t Pts, std::size_t N>
|
||||||
|
void test_new$array() {
|
||||||
|
std::array<void *, Pts> pts;
|
||||||
|
using T = std::array<char, N>;
|
||||||
|
for (int i = 0; i < pts.size(); ++i) {
|
||||||
|
auto p = pmr::new$<T>();
|
||||||
|
pts[i] = p;
|
||||||
|
std::memset(p, i, sizeof(T));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < pts.size(); ++i) {
|
||||||
|
T tmp;
|
||||||
|
std::memset(&tmp, i, sizeof(T));
|
||||||
|
ASSERT_EQ(std::memcmp(pts[i], &tmp, sizeof(T)), 0);
|
||||||
|
pmr::delete$(static_cast<T *>(pts[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(pmr_new, new$array) {
|
||||||
|
test_new$array<1000, 10>();
|
||||||
|
test_new$array<1000, 100>();
|
||||||
|
test_new$array<1000, 1000>();
|
||||||
|
test_new$array<1000, 10000>();
|
||||||
|
test_new$array<1000, 100000>();
|
||||||
|
// test_new$array<1000, 1000000>();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class Base {
|
||||||
|
public:
|
||||||
|
virtual ~Base() = default;
|
||||||
|
virtual int get() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Derived : public Base {
|
||||||
|
public:
|
||||||
|
Derived(int value) : value_(value) {}
|
||||||
|
int get() const override { return value_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(pmr_new, delete$poly) {
|
||||||
|
Base *p = pmr::new$<Derived>(-1);
|
||||||
|
ASSERT_NE(p, nullptr);
|
||||||
|
ASSERT_EQ(p->get(), -1);
|
||||||
|
pmr::delete$(p);
|
||||||
|
|
||||||
|
ASSERT_EQ(p, pmr::new$<Derived>((std::numeric_limits<int>::max)()));
|
||||||
|
ASSERT_EQ(p->get(), (std::numeric_limits<int>::max)());
|
||||||
|
pmr::delete$(p);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user