From 6e26184a04326fa125d28910d1b099857e9026d1 Mon Sep 17 00:00:00 2001 From: EnderIce2 Date: Fri, 9 May 2025 07:34:28 +0000 Subject: [PATCH] test(kernel/std): :test_tube: add tests for std::shared_ptr --- Kernel/include_std/memory | 105 ++++++++++++++++-- Kernel/tests/stl/shared_ptr.cpp | 187 ++++++++++++++++++++++++++++++++ 2 files changed, 283 insertions(+), 9 deletions(-) create mode 100644 Kernel/tests/stl/shared_ptr.cpp diff --git a/Kernel/include_std/memory b/Kernel/include_std/memory index e1c8f428..d81bfc8a 100644 --- a/Kernel/include_std/memory +++ b/Kernel/include_std/memory @@ -632,6 +632,7 @@ namespace std { long ref_count; virtual void destroy() = 0; + virtual void *get_ptr() const = 0; control_block_base() : ref_count(1) {} virtual ~control_block_base() = default; }; @@ -642,6 +643,7 @@ namespace std Y *ptr; Deleter deleter; void destroy() override { deleter(ptr); } + void *get_ptr() const override { return ptr; } control_block_impl(Y *p, Deleter d) : ptr(p), deleter(d) {} ~control_block_impl() override {} }; @@ -650,6 +652,7 @@ namespace std { T *ptr; void destroy() override { delete ptr; } + void *get_ptr() const override { return ptr; } control_block_default(T *p) : ptr(p) {} ~control_block_default() override {} }; @@ -661,6 +664,7 @@ namespace std Deleter deleter; Alloc alloc; void destroy() override { deleter(ptr); } + void *get_ptr() const override { return ptr; } control_block_alloc(Y *p, Deleter d, Alloc a) : ptr(p), deleter(d), alloc(a) {} ~control_block_alloc() override {} @@ -854,7 +858,7 @@ namespace std r.cb = tmp; } - element_type *get() const noexcept { return cb ? static_cast(cb)->ptr : nullptr; } + element_type *get() const noexcept { return cb ? static_cast(cb->get_ptr()) : nullptr; } T &operator*() const noexcept { return *get(); } T *operator->() const noexcept { return get(); } element_type &operator[](std::ptrdiff_t idx) const { return get()[idx]; } @@ -890,25 +894,108 @@ namespace std shared_ptr make_shared_for_overwrite(std::size_t N) { return shared_ptr(new T[N]); } template - shared_ptr allocate_shared(const Alloc &alloc, Args &&...args); + shared_ptr allocate_shared(const Alloc &alloc, Args &&...args) + { + using AllocTraits = std::allocator_traits; + using U = typename std::remove_extent::type; + Alloc a = alloc; + U *ptr = AllocTraits::allocate(a, 1); + try + { + AllocTraits::construct(a, ptr, std::forward(args)...); + return shared_ptr(ptr, [a](U *p) mutable + { + AllocTraits::destroy(a, p); + AllocTraits::deallocate(a, p, 1); }, a); + } + catch (...) + { + AllocTraits::deallocate(a, ptr, 1); + throw; + } + } template - shared_ptr allocate_shared(const Alloc &alloc, std::size_t N); + shared_ptr allocate_shared(const Alloc &alloc, std::size_t N) + { + using AllocTraits = std::allocator_traits; + using U = typename std::remove_extent::type; + Alloc a = alloc; + U *ptr = AllocTraits::allocate(a, N); + try + { + for (std::size_t i = 0; i < N; ++i) + AllocTraits::construct(a, ptr + i); + return shared_ptr(ptr, [a, N](U *p) mutable + { + for (std::size_t i = 0; i < N; ++i) + AllocTraits::destroy(a, p + i); + AllocTraits::deallocate(a, p, N); }, a); + } + catch (...) + { + AllocTraits::deallocate(a, ptr, N); + throw; + } + } template - shared_ptr allocate_shared(const Alloc &alloc); + shared_ptr allocate_shared(const Alloc &alloc) + { + return allocate_shared(alloc); + } template - shared_ptr allocate_shared(const Alloc &alloc, std::size_t N, const std::remove_extent_t &u); + shared_ptr allocate_shared(const Alloc &alloc, std::size_t N, const std::remove_extent_t &u) + { + using AllocTraits = std::allocator_traits; + using U = typename std::remove_extent::type; + Alloc a = alloc; + U *ptr = AllocTraits::allocate(a, N); + try + { + for (std::size_t i = 0; i < N; ++i) + AllocTraits::construct(a, ptr + i, u); + return shared_ptr(ptr, [a, N](U *p) mutable + { + for (std::size_t i = 0; i < N; ++i) + AllocTraits::destroy(a, p + i); + AllocTraits::deallocate(a, p, N); }, a); + } + catch (...) + { + AllocTraits::deallocate(a, ptr, N); + throw; + } + } template - shared_ptr allocate_shared(const Alloc &alloc, const std::remove_extent_t &u); + shared_ptr allocate_shared(const Alloc &alloc, const std::remove_extent_t &u) + { + return allocate_shared(alloc, 1, u); + } template - shared_ptr allocate_shared_for_overwrite(const Alloc &alloc); + shared_ptr allocate_shared_for_overwrite(const Alloc &alloc) + { + using AllocTraits = std::allocator_traits; + using U = typename std::remove_extent::type; + Alloc a = alloc; + U *ptr = AllocTraits::allocate(a, 1); + return shared_ptr(ptr, [a](U *p) mutable + { AllocTraits::deallocate(a, p, 1); }, a); + } template - shared_ptr allocate_shared_for_overwrite(const Alloc &alloc, std::size_t N); + shared_ptr allocate_shared_for_overwrite(const Alloc &alloc, std::size_t N) + { + using AllocTraits = std::allocator_traits; + using U = typename std::remove_extent::type; + Alloc a = alloc; + U *ptr = AllocTraits::allocate(a, N); + return shared_ptr(ptr, [a, N](U *p) mutable + { AllocTraits::deallocate(a, p, N); }, a); + } template std::shared_ptr static_pointer_cast(const std::shared_ptr &r) noexcept @@ -971,7 +1058,7 @@ namespace std } template - Deleter *get_deleter(const std::shared_ptr &p) noexcept { return p.get_deleter(); } + Deleter *get_deleter(const std::shared_ptr &p) noexcept { return p.cb ? &p.cb->deleter : nullptr; } template bool operator==(const std::shared_ptr &lhs, const std::shared_ptr &rhs) noexcept { return lhs.get() == rhs.get(); } diff --git a/Kernel/tests/stl/shared_ptr.cpp b/Kernel/tests/stl/shared_ptr.cpp new file mode 100644 index 00000000..c68023ed --- /dev/null +++ b/Kernel/tests/stl/shared_ptr.cpp @@ -0,0 +1,187 @@ +/* + This file is part of Fennix Kernel. + + Fennix Kernel is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + Fennix Kernel is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Fennix Kernel. If not, see . +*/ + +#ifdef DEBUG + +#include +#include +#include + +struct Base +{ + virtual ~Base() = default; + virtual int id() const { return 1; } +}; +struct Derived : Base +{ + int id() const override { return 2; } +}; + +struct Deleter +{ + bool *flag; + Deleter(bool *f) : flag(f) {} + void operator()(int *p) const + { + *flag = true; + delete p; + } +}; + +void test_shared_ptr_bitset() +{ + debug("std::shared_ptr ..."); + + /* 1. constructor default */ + + std::shared_ptr p1; + assert(!p1); + assert(p1.use_count() == 0); + + /* 2. constructor from raw ptr, get(), *, use_count(), operator bool */ + + auto p2 = std::shared_ptr(new int(5)); + assert(p2); + assert(p2.use_count() == 1); + assert(*p2 == 5); + assert(p2.get() && *p2 == 5); + + /* 3. copy/move ctor */ + + auto p3 = p2; + assert(p2.use_count() == 2 && p3.use_count() == 2); + auto p4 = std::move(p3); + assert(p4.use_count() == 2 && p3.use_count() == 0); + + /* 4. copy/move assign */ + + std::shared_ptr p5; + p5 = p4; + assert(p5.use_count() == 3); + std::shared_ptr p6; + p6 = std::move(p5); + assert(p6.use_count() == 3 && p5.use_count() == 0); + + /* 5. reset() */ + + p6.reset(); + assert(!p6 && p6.use_count() == 0); + p4.reset(new int(10)); + assert(*p4 == 10); + + /* 6. swap() member and std::swap */ + + p4.swap(p2); + assert(*p2 == 10 && *p4 == 5); + std::swap(p2, p4); + assert(*p4 == 10 && *p2 == 5); + + /* 7. observers: ->, *, get() already checked */ + + struct S + { + int x; + }; + auto ps = std::make_shared(); + ps->x = 7; + assert((*ps).x == 7 && ps.get()->x == 7); + + /* 8. use_count(), operator bool already checked */ + + /* 9. owner_before() */ + + auto pa = std::make_shared(1); + auto pb = std::make_shared(1); + assert(pa.owner_before(pb) || pb.owner_before(pa)); + + /* 10. make_shared, allocate_shared */ + + auto pm = std::make_shared(42); + assert(pm && *pm == 42); + std::allocator allo; + auto pa2 = std::allocate_shared(allo, 99); + assert(*pa2 == 99); + + /* 11. static_pointer_cast, dynamic_pointer_cast, const_pointer_cast, reinterpret_pointer_cast */ + + debug("skipping static_pointer_cast, dynamic_pointer_cast, const_pointer_cast, reinterpret_pointer_cast"); + + // auto pd = std::make_shared(); + // std::shared_ptr pb2 = std::static_pointer_cast(pd); + // assert(pb2->id() == 2); + + // std::shared_ptr base = std::make_shared(); + // auto dyn = std::dynamic_pointer_cast(base); + // assert(dyn && dyn->id() == 2); + // std::shared_ptr base2 = std::make_shared(); + // auto dyn2 = std::dynamic_pointer_cast(base2); + // assert(!dyn2); + + // auto pc = std::make_shared(123); + // auto pnc = std::const_pointer_cast(pc); + // assert(*pnc == 123); + // *pnc = 124; + // assert(*pnc == 124); + + // auto pint = std::make_shared(65); + // auto pchar = std::reinterpret_pointer_cast(pint); + // assert(pchar.get() == reinterpret_cast(pint.get())); + + /* 12. comparison operators ==, !=, <, <=, >, >= */ + + debug("skipping comparison operators ==, !=, <, <=, >, >="); + + // auto c1 = std::make_shared(1); + // auto c2 = std::make_shared(1); + // auto c3 = c1; + // assert(c1 == c3); + // assert(c1 != c2); + // assert((c2 < c1) == (c2.get() < c1.get())); + // assert(c1 <= c3); + // assert(c1 >= c3); + + /* 13. operator<< */ + + debug("skipping operator<<"); + + // std::ostringstream oss; + // std::shared_ptr pnull; + // oss << pnull; + // assert(oss.str() == \"0\"); + + /* 14. get_deleter */ + + debug("skipping get_deleter"); + + // bool flag = false; + // std::shared_ptr pd1(new int(7), Deleter(&flag)); + // auto d = std::get_deleter(pd1); + // assert(d && d->flag == &flag); + // pd1.reset(); + // assert(flag); + + /* 15. std::hash */ + + debug("skipping std::hash"); + + // std::hash> h; + // assert(h(c1) == std::hash()(c1.get())); + + debug("std::shared_ptr OK"); +} + +#endif // DEBUG