From 38dff62cff8f4108696f4e01c7bc099e32b802cc Mon Sep 17 00:00:00 2001 From: Vladislav Belov Date: Thu, 22 Oct 2026 23:22:30 +0200 Subject: [PATCH] Adds AllocatorAdapter written against C++20 --- source/ps/memory/AllocatorAdapter.h | 89 +++++++++++++++++++ .../ps/memory/tests/test_AllocatorAdapter.h | 82 +++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 source/ps/memory/AllocatorAdapter.h create mode 100644 source/ps/memory/tests/test_AllocatorAdapter.h diff --git a/source/ps/memory/AllocatorAdapter.h b/source/ps/memory/AllocatorAdapter.h new file mode 100644 index 0000000000..5d7652f1bb --- /dev/null +++ b/source/ps/memory/AllocatorAdapter.h @@ -0,0 +1,89 @@ +/* Copyright (C) 2026 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. 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 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#ifndef INCLUDED_PS_ALLOCATORADAPTER +#define INCLUDED_PS_ALLOCATORADAPTER + +#include +#include +#include + +namespace PS::Memory +{ + +/** + * Provides a way to use untyped allocators like LinearAllocator for + * containers. This allocator adapter must not outlive the target + * allocator. A client must not mix adapters with different + * allocators. + * + * It provides a basic STL allocator interface. It allows to use a stateful + * allocator: + * + * std::vector> + * + * @see ScopedLinearAllocator + */ +template +class AllocatorAdapter +{ +public: + using value_type = T; + + // We don't need runtime checks and per-element copies for containers + // when we have a unique allocator (means no mixing different allocators). + using propagate_on_container_copy_assignment = std::true_type; + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; + using is_always_equal = std::false_type; + + template + struct rebind + { + using other = AllocatorAdapter; + }; + + constexpr AllocatorAdapter(Allocator& allocator) noexcept : m_Allocator(std::addressof(allocator)) {} + template + constexpr AllocatorAdapter(const AllocatorAdapter& allocatorAdapter) noexcept : m_Allocator(allocatorAdapter.m_Allocator) {} + + [[nodiscard]] constexpr T* allocate(std::size_t n) + { + return static_cast(m_Allocator->allocate(n * sizeof(T), alignof(T))); + } + + constexpr void deallocate(T* ptr, std::size_t n) + { + m_Allocator->deallocate(static_cast(ptr), n * sizeof(T)); + } + + template + constexpr bool operator==(const AllocatorAdapter& other) const noexcept + { + return m_Allocator == other.m_Allocator; + } + +private: + template friend class AllocatorAdapter; + + // A pointer to make it moveable. + Allocator* m_Allocator{nullptr}; +}; + +} // namespace PS::Memory + +#endif // INCLUDED_PS_ALLOCATORADAPTER diff --git a/source/ps/memory/tests/test_AllocatorAdapter.h b/source/ps/memory/tests/test_AllocatorAdapter.h new file mode 100644 index 0000000000..ac5407c266 --- /dev/null +++ b/source/ps/memory/tests/test_AllocatorAdapter.h @@ -0,0 +1,82 @@ +/* Copyright (C) 2026 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. 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 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#include "lib/self_test.h" + +#include "ps/memory/AllocatorAdapter.h" +#include "ps/memory/LinearAllocator.h" + +#include +#include +#include +#include +#include +#include + +class TestAllocatorAdapter : public CxxTest::TestSuite +{ +public: + template + using Scoped = std::scoped_allocator_adaptor< + PS::Memory::AllocatorAdapter>; + + void test_move() + { + PS::Memory::LinearAllocator allocator{16, 1024}; + + std::vector> v1{ + PS::Memory::AllocatorAdapter{allocator}}; + v1.assign(10, 1); + + std::vector> v2{ + PS::Memory::AllocatorAdapter{allocator}}; + + const std::size_t allocatedSize{allocator.GetSize()}; + v2 = std::move(v1); + TS_ASSERT_EQUALS(allocatedSize, allocator.GetSize()); + } + + void test_swap() + { + PS::Memory::LinearAllocator allocator{16, 1024}; + + std::vector> v1{ + PS::Memory::AllocatorAdapter{allocator}}; + v1.assign(10, 1); + + std::vector> v2{ + PS::Memory::AllocatorAdapter{allocator}}; + + const std::size_t allocatedSize{allocator.GetSize()}; + std::swap(v1, v2); + TS_ASSERT_EQUALS(allocatedSize, allocator.GetSize()); + } + + void test_nested_containers() + { + PS::Memory::LinearAllocator allocator{16, 4096}; + + using Vector = std::vector>; + using Map = std::map, Scoped>>; + + Map complexContainer(Map::allocator_type{allocator}); + complexContainer[42].assign(10, 1); + TS_ASSERT_EQUALS(complexContainer.size(), 1); + TS_ASSERT_EQUALS(complexContainer[42].size(), 10); + TS_ASSERT_EQUALS(complexContainer[42][0], 1); + } +};