diff --git a/include/yk/allocator/concepts.hpp b/include/yk/allocator/concepts.hpp new file mode 100644 index 0000000..73593cd --- /dev/null +++ b/include/yk/allocator/concepts.hpp @@ -0,0 +1,23 @@ +#ifndef YK_ALLOCATOR_CONCEPTS_HPP +#define YK_ALLOCATOR_CONCEPTS_HPP + +#include +#include + +namespace yk { + +namespace xo { // exposition-only + +// C++26 +// https://en.cppreference.com/w/cpp/named_req/Allocator +template +concept simple_allocator = requires(Alloc alloc, std::size_t n) { + { *alloc.allocate(n) } -> std::same_as; + { alloc.deallocate(alloc.allocate(n), n) }; +} && std::copy_constructible && std::equality_comparable; + +} // namespace xo + +} // namespace yk + +#endif diff --git a/include/yk/allocator/default_init_allocator.hpp b/include/yk/allocator/default_init_allocator.hpp new file mode 100644 index 0000000..2722d7d --- /dev/null +++ b/include/yk/allocator/default_init_allocator.hpp @@ -0,0 +1,41 @@ +#ifndef YK_ALLOCATOR_DEFAULT_INIT_ALLOCATOR_HPP +#define YK_ALLOCATOR_DEFAULT_INIT_ALLOCATOR_HPP + +#include "yk/allocator/concepts.hpp" + +#include +#include +#include +#include + +namespace yk { + +// http://stackoverflow.com/a/21028912/273767 + +template > +class default_init_allocator : public A { + static_assert(xo::simple_allocator); + + using traits = std::allocator_traits; + +public: + template + struct rebind { + using other = default_init_allocator>; + }; + + using A::A; + + template + constexpr void construct(U* ptr) noexcept(std::is_nothrow_default_constructible_v) { + ::new (static_cast(ptr)) U; + } + template + constexpr void construct(U* ptr, Args&&... args) noexcept(noexcept(traits::construct(static_cast(*this), ptr, std::forward(args)...))) { + traits::construct(static_cast(*this), ptr, std::forward(args)...); + } +}; + +} // namespace yk + +#endif diff --git a/test/test.cpp b/test/test.cpp index 66b1703..90fcdc8 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,3 +1,4 @@ +#include "yk/allocator/default_init_allocator.hpp" #include "yk/util/forward_like.hpp" #include "yk/util/pack_indexing.hpp" #include "yk/util/reverse.hpp" @@ -11,7 +12,10 @@ #include +#include +#include #include +#include #include #include #include @@ -127,4 +131,28 @@ BOOST_AUTO_TEST_CASE(ToSubrange) { BOOST_TEST((std::ranges::equal(v, yk::to_subrange(rng)))); } +BOOST_AUTO_TEST_CASE(Allocator) { + { + unsigned char uninitialized_storage[sizeof(std::uint32_t)] = {0xde, 0xad, 0xbe, 0xef}; + alignas(std::uint32_t) unsigned char storage[sizeof(std::uint32_t)]; + std::ranges::copy(uninitialized_storage, storage); + + std::allocator alloc; + std::allocator_traits>::construct(alloc, reinterpret_cast(storage)); + + BOOST_TEST(!std::ranges::equal(uninitialized_storage, storage)); + BOOST_TEST(std::ranges::equal(std::vector{0, 0, 0, 0}, storage)); + } + { + unsigned char uninitialized_storage[sizeof(std::uint32_t)] = {0xde, 0xad, 0xbe, 0xef}; + alignas(std::uint32_t) unsigned char storage[sizeof(std::uint32_t)]; + std::ranges::copy(uninitialized_storage, storage); + + yk::default_init_allocator alloc; + alloc.construct(storage); + + BOOST_TEST(std::ranges::equal(uninitialized_storage, storage)); + } +} + BOOST_AUTO_TEST_SUITE_END() // yk_util