From 44858c0202a300eaea4f0c3686d678854d5e8402 Mon Sep 17 00:00:00 2001 From: Vladislav Date: Thu, 24 Dec 2020 22:26:28 -0800 Subject: [PATCH 1/6] Finished allocator --- CMakeLists.txt | 4 +- apps/Allocator/CMakeLists.txt | 4 + apps/Allocator/src/main_Allocator.cpp | 56 +++++++ apps/CMakeLists.txt | 5 +- apps/HelloWorld/CMakeLists.txt | 2 +- apps/IpFilter/CMakeLists.txt | 2 +- cmake/project_target.cmake | 7 +- library/Allocator/CMakeLists.txt | 3 + .../Allocator/include/Allocator/Allocator.h | 137 ++++++++++++++++++ library/Allocator/include/Allocator/List.h | 86 +++++++++++ library/Allocator/src/Allocator.cpp | 1 + library/Allocator/src/List.cpp | 1 + library/Allocator/test/CMakeLists.txt | 1 + library/Allocator/test/src/test_Allocator.cpp | 62 ++++++++ library/CMakeLists.txt | 3 +- scripts/deploy.sh | 8 +- 16 files changed, 368 insertions(+), 14 deletions(-) create mode 100644 apps/Allocator/CMakeLists.txt create mode 100644 apps/Allocator/src/main_Allocator.cpp create mode 100644 library/Allocator/CMakeLists.txt create mode 100644 library/Allocator/include/Allocator/Allocator.h create mode 100644 library/Allocator/include/Allocator/List.h create mode 100644 library/Allocator/src/Allocator.cpp create mode 100644 library/Allocator/src/List.cpp create mode 100644 library/Allocator/test/CMakeLists.txt create mode 100644 library/Allocator/test/src/test_Allocator.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 598c869..515040d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,9 +15,9 @@ option(ENABLE_TESTING "Build small (unit) tests" ON) # ================ Project ======================== # Project name and a few useful settings -project(ip_filter +project(allocator VERSION 0.0.$ENV{TRAVIS_BUILD_NUMBER} - DESCRIPTION "OTUS c++ homeworks: ip_filter" + DESCRIPTION "OTUS c++ homeworks: allocator" LANGUAGES CXX ) diff --git a/apps/Allocator/CMakeLists.txt b/apps/Allocator/CMakeLists.txt new file mode 100644 index 0000000..24c08e4 --- /dev/null +++ b/apps/Allocator/CMakeLists.txt @@ -0,0 +1,4 @@ +add_and_install_project_app(allocator + DEPEND + lib_Allocator +) \ No newline at end of file diff --git a/apps/Allocator/src/main_Allocator.cpp b/apps/Allocator/src/main_Allocator.cpp new file mode 100644 index 0000000..4c0e125 --- /dev/null +++ b/apps/Allocator/src/main_Allocator.cpp @@ -0,0 +1,56 @@ +#include +#include + +#include +#include + + +int main() { + size_t factorial = 1; + + // map with std::allocator + std::map std_m; + for (size_t i = 0; i < 10; ++i) { + std_m[i] = factorial; + factorial *= (i+1); + } + for (auto it = std_m.begin(); it != std_m.end(); ++it) { + std::cout << it->first << " " << it->second << std::endl; + } + + // map with custom allocator + factorial = 1; + std::map, + Allocator::Allocator, 10>> m; + for (size_t i = 0; i < 10; ++i) { + m[i] = factorial; + factorial *= (i+1); + } + for (auto it = m.begin(); it != m.end(); ++it) { + std::cout << it->first << " " << it->second << std::endl; + } + + // custom container with std::allocator + Allocator::List std_l; + for (int i = 0; i < 10; ++i) { + std_l.push_back(i); + } + for (auto it = std_l.begin(); it != std_l.end(); ++it) { + std::cout << *it << " "; + } + std::cout << std::endl; + + // custom container with custom allocator + Allocator::List> l; + for (int i = 0; i < 10; ++i) { + l.push_back(i); + } + for (auto it = l.begin(); it != l.end(); ++it) { + std::cout << *it << " "; + } + std::cout << std::endl; + + + + return 0; +} diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 6404fab..6b32c20 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,2 +1,3 @@ -# add_subdirectory(HelloWorld) -add_subdirectory(IpFilter) \ No newline at end of file +add_subdirectory(HelloWorld) +add_subdirectory(IpFilter) +add_subdirectory(Allocator) \ No newline at end of file diff --git a/apps/HelloWorld/CMakeLists.txt b/apps/HelloWorld/CMakeLists.txt index f2b2a63..e09d08c 100644 --- a/apps/HelloWorld/CMakeLists.txt +++ b/apps/HelloWorld/CMakeLists.txt @@ -1,4 +1,4 @@ add_and_install_project_app(helloworld DEPEND - HelloWorld + lib_HelloWorld ) \ No newline at end of file diff --git a/apps/IpFilter/CMakeLists.txt b/apps/IpFilter/CMakeLists.txt index 28a3f08..26eaed2 100644 --- a/apps/IpFilter/CMakeLists.txt +++ b/apps/IpFilter/CMakeLists.txt @@ -1,4 +1,4 @@ add_and_install_project_app(ip_filter DEPEND - IpFilter + lib_IpFilter ) \ No newline at end of file diff --git a/cmake/project_target.cmake b/cmake/project_target.cmake index 1fb0e00..d2ecc16 100644 --- a/cmake/project_target.cmake +++ b/cmake/project_target.cmake @@ -1,6 +1,7 @@ function(add_project_library) get_filename_component(lib_name "${CMAKE_CURRENT_SOURCE_DIR}" NAME) - set(lib_name lib${lib_name}) + set(lib_name lib_${lib_name}) + glob_target_files(${lib_name} TARGET_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) @@ -50,7 +51,7 @@ function(add_project_test) ) target_link_libraries(${test_name} - lib${target_name} + lib_${target_name} ${Boost_LIBRARIES} ) @@ -78,7 +79,7 @@ function(add_and_install_project_app app_name) set(app_lib_dependency "") foreach(link_lib ${add_project_app_DEPEND}) - LIST(APPEND app_lib_dependency lib${link_lib}) + LIST(APPEND app_lib_dependency ${link_lib}) endforeach() target_link_libraries(${app_name} diff --git a/library/Allocator/CMakeLists.txt b/library/Allocator/CMakeLists.txt new file mode 100644 index 0000000..a8d262f --- /dev/null +++ b/library/Allocator/CMakeLists.txt @@ -0,0 +1,3 @@ +add_project_library() + +add_subdirectory(test) \ No newline at end of file diff --git a/library/Allocator/include/Allocator/Allocator.h b/library/Allocator/include/Allocator/Allocator.h new file mode 100644 index 0000000..bf4db9f --- /dev/null +++ b/library/Allocator/include/Allocator/Allocator.h @@ -0,0 +1,137 @@ +#pragma once + +#include +#include +#include + +namespace Allocator { + +//-------------------------------------- +#pragma mark Allocator declaration +//-------------------------------------- + +template +class Allocator{ +public: + using value_type = T; + using pointer = T*; + Allocator(); + + template + struct rebind; + + pointer allocate(std::size_t n = 1); + void deallocate(pointer p, std::size_t n); + template + void construct(U *p, Args&&... args); + void destroy(pointer p); + + ~Allocator(); + + bool operator==(const Allocator& other); + bool operator!=(const Allocator& other); + +private: + T* FindAvailableChunk(std::size_t n); + + const size_t max_allocatable_objects; + bool *m_free_chunks = nullptr; + T *m_area = nullptr; +}; + +//-------------------------------------- +#pragma mark Implementation +//-------------------------------------- + +template +Allocator::Allocator(): max_allocatable_objects(N) { + m_area = static_cast (::operator new (N*sizeof(T))); + if (nullptr == m_area) { + throw std::bad_alloc(); + } + m_free_chunks = new bool[max_allocatable_objects]; + if (m_free_chunks == nullptr) { + throw std::bad_alloc(); + } + for (size_t i = 0; i < max_allocatable_objects; ++i) { + m_free_chunks[i] = true; + } +} + +template +template +struct Allocator::rebind{ + using other = Allocator; +}; + +template +T* Allocator::allocate(std::size_t n) { + if (n > max_allocatable_objects) + throw std::bad_alloc{}; + auto p = FindAvailableChunk(n); + return p; +} + +template +void Allocator::deallocate(T *p, std::size_t n) { + int pos = (p-m_area); + if (pos < 0 || pos + n > max_allocatable_objects) + throw std::bad_alloc{}; + while (n >0) { + m_free_chunks[pos] = false; + ++pos; + --n; + } +} + +template +template +void Allocator::construct(U *p, Args&&... args) { + new(p) U(std::forward(args)...); +} + +template +void Allocator::destroy(T *p) { + p->~T(); +} + +template +Allocator::~Allocator() { + ::operator delete(m_area); +} + +template +bool Allocator::operator==(const Allocator& other) { + return true; +} + +template +bool Allocator::operator!=(const Allocator& other) { + return !((*this)==other); +} + +template +T* Allocator::FindAvailableChunk(std::size_t n) { + size_t idx = 0; + bool stop = false; + while (!stop && idx + n < max_allocatable_objects) { + while (!m_free_chunks[idx] && idx + n < max_allocatable_objects) { + ++idx; + } + bool s = true; + for (int i = 0; i < n; ++i) { + s &= m_free_chunks[idx+i]; + } + stop |= s; + } + if (idx+n > max_allocatable_objects) + throw std::bad_alloc{}; + else{ + for (int i = 0; i < n; ++i){ + m_free_chunks[idx+i] = false; + } + return m_area + idx; + } +} + +} // namespace Allocator \ No newline at end of file diff --git a/library/Allocator/include/Allocator/List.h b/library/Allocator/include/Allocator/List.h new file mode 100644 index 0000000..4d36280 --- /dev/null +++ b/library/Allocator/include/Allocator/List.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +namespace Allocator { + +//------------------------------------------------------------ +#pragma mark List Declaration +//------------------------------------------------------------ + +template > +class List +{ +private: + struct Node { + T data = T(); + Node *next = nullptr; + }; + + using NodeAllocator = typename Allocator::template rebind::other; + +public: + struct iterator + { + Node* m_ptr; + + iterator(Node* ptr) : m_ptr(ptr) {} + + iterator operator++() { + m_ptr = m_ptr->next; + + return *this; } + T& operator*() { return m_ptr->data; } + bool operator==(const iterator& rhs) { return m_ptr == rhs.m_ptr; } + bool operator!=(const iterator& rhs) { return m_ptr != rhs.m_ptr; } + }; + + List(); + + auto begin() -> iterator { return iterator(m_dummy_head->next); } + auto end() -> iterator { return iterator(m_tail->next); } + auto begin() const -> const iterator { return iterator(m_dummy_head->next); } + auto end() const -> const iterator { return iterator(m_tail->next); } + void push_back(const T &t); + ~List(); + +private: + Node *m_dummy_head = nullptr, *m_tail = nullptr; + NodeAllocator m_node_allocator; +}; + +//------------------------------------------------------------ +#pragma mark List Implementation +//------------------------------------------------------------ + +template +List::List(): m_dummy_head() { + m_dummy_head = m_node_allocator.allocate(1); + m_node_allocator.construct(m_dummy_head, Node{}); + m_tail = m_dummy_head; +} + +template +void List::push_back(const T &t) { + Node *node = m_node_allocator.allocate(1); + m_node_allocator.construct(node, Node{t, nullptr}); + m_tail->next = node; + m_tail = node; +} + +template +List::~List() +{ + auto node = m_dummy_head->next; + while (node != nullptr) + { + auto next_node = node->next; + m_node_allocator.deallocate(node, 1); + m_node_allocator.destroy(node); + node = next_node; + } + m_node_allocator.deallocate(m_dummy_head, 1); + m_node_allocator.destroy(m_dummy_head); +} + +} // namespace slist \ No newline at end of file diff --git a/library/Allocator/src/Allocator.cpp b/library/Allocator/src/Allocator.cpp new file mode 100644 index 0000000..1687097 --- /dev/null +++ b/library/Allocator/src/Allocator.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/library/Allocator/src/List.cpp b/library/Allocator/src/List.cpp new file mode 100644 index 0000000..1a10f5f --- /dev/null +++ b/library/Allocator/src/List.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/library/Allocator/test/CMakeLists.txt b/library/Allocator/test/CMakeLists.txt new file mode 100644 index 0000000..b6826b6 --- /dev/null +++ b/library/Allocator/test/CMakeLists.txt @@ -0,0 +1 @@ +add_project_test() \ No newline at end of file diff --git a/library/Allocator/test/src/test_Allocator.cpp b/library/Allocator/test/src/test_Allocator.cpp new file mode 100644 index 0000000..1d2b7c8 --- /dev/null +++ b/library/Allocator/test/src/test_Allocator.cpp @@ -0,0 +1,62 @@ +#define BOOST_TEST_MODULE Allocator_test_module + +#include +#include +#include +#include +#include + +BOOST_AUTO_TEST_SUITE(Allocator_test_suite) + +BOOST_AUTO_TEST_CASE(Allocator_stdVectorInt) +{ + constexpr size_t N = 16; + std::vector> vec; + for (size_t i = 0; i < N/2; ++i) { + vec.push_back(42); + } + BOOST_CHECK(std::all_of(vec.begin(), vec.end(), + [](int a){ return a == 42;})); +} + +BOOST_AUTO_TEST_CASE(Allocator_stdMapInt) +{ + constexpr size_t N = 10; + std::map, + Allocator::Allocator, N>> mm; + for (size_t i = 0; i < N; ++i) { + mm[i] = i+42; + } + for (int i = 0; i < N; ++i) { + BOOST_CHECK(mm.at(i) == (i+42)); + } +} + +BOOST_AUTO_TEST_CASE(List_stdAllocator) +{ + Allocator::List l; + constexpr int N = 10; + for (int i = 0; i < N; ++i) { + l.push_back(i); + } + int i = 0; + for (auto it = l.begin(); it != l.end(); ++it, ++i) { + BOOST_CHECK(*it == i); + } +} + +BOOST_AUTO_TEST_CASE(List_CustomAllocator) +{ + constexpr size_t N = 9; + Allocator::List> l; + for (int i = 0; i < N; ++i) { + l.push_back(i); + } + int i = 0; + for (auto it = l.begin(); it != l.end(); ++it, ++i) { + BOOST_CHECK(*it == i); + } +} + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 6404fab..1c56ec0 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -1,2 +1,3 @@ # add_subdirectory(HelloWorld) -add_subdirectory(IpFilter) \ No newline at end of file +# add_subdirectory(IpFilter) +add_subdirectory(Allocator) \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh index fbb59cc..1cc0392 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -1,11 +1,11 @@ cmake . # build lib -cmake --build . --target libIpFilter +cmake --build . --target lib_Allocator # build test for lib -cmake --build . --target test_IpFilter +cmake --build . --target test_Allocator # run test -ctest test_IpFilter +ctest test_Allocator # build and install app -cmake --build . --target ip_filter +cmake --build . --target allocator # generate deb package cpack From 85864a3dff0b80f8e88ffef85fe4e27ea802bafa Mon Sep 17 00:00:00 2001 From: Vladislav Date: Thu, 24 Dec 2020 22:37:43 -0800 Subject: [PATCH 2/6] test for Travis: it cannot generate allocator deb --- library/CMakeLists.txt | 2 +- scripts/deploy.sh | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 1c56ec0..d55efa8 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -1,3 +1,3 @@ # add_subdirectory(HelloWorld) -# add_subdirectory(IpFilter) +add_subdirectory(IpFilter) add_subdirectory(Allocator) \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 1cc0392..f3feaed 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -1,11 +1,11 @@ cmake . # build lib -cmake --build . --target lib_Allocator +cmake --build . --target lib_IpFilter # build test for lib -cmake --build . --target test_Allocator +cmake --build . --target test_IpFilter # run test -ctest test_Allocator +ctest test_IpFilter # build and install app -cmake --build . --target allocator +cmake --build . --target ip_filter # generate deb package cpack From 5f06e340e613ca17bf27793ed5ca5a7f0d0dc41d Mon Sep 17 00:00:00 2001 From: Vladislav Date: Thu, 24 Dec 2020 22:40:52 -0800 Subject: [PATCH 3/6] some fixes --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 515040d..bde7278 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ option(ENABLE_TESTING "Build small (unit) tests" ON) # ================ Project ======================== # Project name and a few useful settings -project(allocator +project(ip_filter VERSION 0.0.$ENV{TRAVIS_BUILD_NUMBER} DESCRIPTION "OTUS c++ homeworks: allocator" LANGUAGES CXX From c9ab9b81a7b3bee88a77f118f87cb3fd87630686 Mon Sep 17 00:00:00 2001 From: Vladislav Date: Thu, 24 Dec 2020 22:54:08 -0800 Subject: [PATCH 4/6] let see --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 241562b..01a2438 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,4 +27,7 @@ deploy: key: $BINTRAY_API_KEY on: all_branches: true + +after_failure: + - cat /home/travis/build/DwarKapex/otus-cppdeveloper/_CPack_Packages/Linux/DEB/PreinstallOutput.log \ No newline at end of file From c189a69fa67e45b121073b6f86920089714bdb5c Mon Sep 17 00:00:00 2001 From: Vladislav Date: Thu, 24 Dec 2020 22:57:54 -0800 Subject: [PATCH 5/6] Revert changes --- .travis.yml | 3 --- CMakeLists.txt | 2 +- apps/CMakeLists.txt | 4 ++-- library/CMakeLists.txt | 2 +- scripts/deploy.sh | 8 ++++---- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 01a2438..241562b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,4 @@ deploy: key: $BINTRAY_API_KEY on: all_branches: true - -after_failure: - - cat /home/travis/build/DwarKapex/otus-cppdeveloper/_CPack_Packages/Linux/DEB/PreinstallOutput.log \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index bde7278..515040d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ option(ENABLE_TESTING "Build small (unit) tests" ON) # ================ Project ======================== # Project name and a few useful settings -project(ip_filter +project(allocator VERSION 0.0.$ENV{TRAVIS_BUILD_NUMBER} DESCRIPTION "OTUS c++ homeworks: allocator" LANGUAGES CXX diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 6b32c20..1c56ec0 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,3 +1,3 @@ -add_subdirectory(HelloWorld) -add_subdirectory(IpFilter) +# add_subdirectory(HelloWorld) +# add_subdirectory(IpFilter) add_subdirectory(Allocator) \ No newline at end of file diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index d55efa8..1c56ec0 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -1,3 +1,3 @@ # add_subdirectory(HelloWorld) -add_subdirectory(IpFilter) +# add_subdirectory(IpFilter) add_subdirectory(Allocator) \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh index f3feaed..1cc0392 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -1,11 +1,11 @@ cmake . # build lib -cmake --build . --target lib_IpFilter +cmake --build . --target lib_Allocator # build test for lib -cmake --build . --target test_IpFilter +cmake --build . --target test_Allocator # run test -ctest test_IpFilter +ctest test_Allocator # build and install app -cmake --build . --target ip_filter +cmake --build . --target allocator # generate deb package cpack From 92ab57092039c061cb1039f3e590b76999a05126 Mon Sep 17 00:00:00 2001 From: Vladislav Date: Sat, 26 Dec 2020 08:36:56 -0800 Subject: [PATCH 6/6] Addressed Alex's comments --- .../Allocator/include/Allocator/Allocator.h | 27 ++++++++++--------- library/Allocator/include/Allocator/List.h | 8 +++--- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/library/Allocator/include/Allocator/Allocator.h b/library/Allocator/include/Allocator/Allocator.h index bf4db9f..696d179 100644 --- a/library/Allocator/include/Allocator/Allocator.h +++ b/library/Allocator/include/Allocator/Allocator.h @@ -3,6 +3,7 @@ #include #include #include +#include namespace Allocator { @@ -44,19 +45,7 @@ class Allocator{ //-------------------------------------- template -Allocator::Allocator(): max_allocatable_objects(N) { - m_area = static_cast (::operator new (N*sizeof(T))); - if (nullptr == m_area) { - throw std::bad_alloc(); - } - m_free_chunks = new bool[max_allocatable_objects]; - if (m_free_chunks == nullptr) { - throw std::bad_alloc(); - } - for (size_t i = 0; i < max_allocatable_objects; ++i) { - m_free_chunks[i] = true; - } -} +Allocator::Allocator(): max_allocatable_objects(N) {} template template @@ -66,6 +55,17 @@ struct Allocator::rebind{ template T* Allocator::allocate(std::size_t n) { + if (nullptr == m_area) { + m_area = static_cast (::operator new (max_allocatable_objects*sizeof(T))); + if (nullptr == m_area) { + throw std::bad_alloc(); + } + m_free_chunks = new bool[max_allocatable_objects]; + if (m_free_chunks == nullptr) { + throw std::bad_alloc(); + } + std::fill_n(m_free_chunks, max_allocatable_objects, true); + } if (n > max_allocatable_objects) throw std::bad_alloc{}; auto p = FindAvailableChunk(n); @@ -98,6 +98,7 @@ void Allocator::destroy(T *p) { template Allocator::~Allocator() { ::operator delete(m_area); + delete [] m_free_chunks; } template diff --git a/library/Allocator/include/Allocator/List.h b/library/Allocator/include/Allocator/List.h index 4d36280..6c445cd 100644 --- a/library/Allocator/include/Allocator/List.h +++ b/library/Allocator/include/Allocator/List.h @@ -11,9 +11,11 @@ namespace Allocator { template > class List { +public: + using value_type = typename std::allocator_traits::value_type; private: struct Node { - T data = T(); + value_type data = value_type(); Node *next = nullptr; }; @@ -41,7 +43,7 @@ class List auto end() -> iterator { return iterator(m_tail->next); } auto begin() const -> const iterator { return iterator(m_dummy_head->next); } auto end() const -> const iterator { return iterator(m_tail->next); } - void push_back(const T &t); + void push_back(const value_type &t); ~List(); private: @@ -61,7 +63,7 @@ List::List(): m_dummy_head() { } template -void List::push_back(const T &t) { +void List::push_back(const value_type &t) { Node *node = m_node_allocator.allocate(1); m_node_allocator.construct(node, Node{t, nullptr}); m_tail->next = node;