From 9f8390f4222da714e01aa94b400a7f5ac3146384 Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Sun, 30 Jun 2024 21:53:06 -0700 Subject: [PATCH] Introduce ContiguousRangeSubStructView --- BUILD.bazel | 7 +- include/fixed_containers/sub_struct_view.hpp | 147 +++++++++++++++++++ test/sub_struct_view_test.cpp | 123 ++++++++++++++++ 3 files changed, 276 insertions(+), 1 deletion(-) diff --git a/BUILD.bazel b/BUILD.bazel index 246183dc..9c471c56 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -858,11 +858,15 @@ cc_library( cc_library( name = "sub_struct_view", hdrs = ["include/fixed_containers/sub_struct_view.hpp"], - includes = ["include"], + includes = includes_config(), + strip_include_prefix = strip_include_prefix_config(), deps = [ + ":assert_or_abort", ":fixed_map", + ":iterator_utils", ":memory", ":out", + ":random_access_iterator", ":reflection", ], copts = ["-std=c++20"], @@ -1614,6 +1618,7 @@ cc_test( name = "sub_struct_view_test", srcs = ["test/sub_struct_view_test.cpp"], deps = [ + ":fixed_vector", ":out", ":sub_struct_view", "@com_google_googletest//:gtest", diff --git a/include/fixed_containers/sub_struct_view.hpp b/include/fixed_containers/sub_struct_view.hpp index bfbd263f..b529debf 100644 --- a/include/fixed_containers/sub_struct_view.hpp +++ b/include/fixed_containers/sub_struct_view.hpp @@ -1,8 +1,11 @@ #pragma once +#include "fixed_containers/assert_or_abort.hpp" #include "fixed_containers/fixed_map.hpp" +#include "fixed_containers/iterator_utils.hpp" #include "fixed_containers/memory.hpp" #include "fixed_containers/out.hpp" +#include "fixed_containers/random_access_iterator.hpp" #include "fixed_containers/reflection.hpp" #include @@ -80,4 +83,148 @@ void sub_struct_view_of(Super& super_struct, sub_struct_field_properties); } +template +class ContiguousRangeSubStructView +{ + struct AccessingInfo + { + FieldPropertiesMap sub_struct_field_properties{}; + FieldPropertiesMap super_struct_field_properties{}; + std::byte* base_array_super_struct_ptr{}; + std::size_t stride{}; + std::size_t size{}; + }; + + static SubStruct create_view_at_offset(const AccessingInfo& accessing_info, + const std::size_t index) + { + assert_or_abort(index < accessing_info.size); + SubStruct instance{}; + std::byte* base_of_ith_entry = + std::next(accessing_info.base_array_super_struct_ptr, + static_cast(index * accessing_info.stride)); + sub_struct_view::sub_struct_view_of(base_of_ith_entry, + accessing_info.super_struct_field_properties, + memory::addressof_as_mutable_byte_ptr(instance), + accessing_info.sub_struct_field_properties); + return instance; + } + + using ReferenceType = SubStruct; + + class ReferenceProvider + { + private: + const AccessingInfo* accessing_info_; + std::size_t current_index_; + + public: + constexpr ReferenceProvider() noexcept + : ReferenceProvider{nullptr, 0} + { + } + + constexpr ReferenceProvider(const AccessingInfo& accessing_info, + const std::size_t& current_index) noexcept + : accessing_info_{&accessing_info} + , current_index_{current_index} + { + } + + constexpr void advance(const std::size_t n) noexcept { current_index_ += n; } + constexpr void recede(const std::size_t n) noexcept { current_index_ -= n; } + + [[nodiscard]] constexpr ReferenceType get() const noexcept + { + return create_view_at_offset(*accessing_info_, current_index_); + } + + constexpr bool operator==(const ReferenceProvider& other) const noexcept + { + assert_or_abort(accessing_info_ == other.accessing_info_); + return current_index_ == other.current_index_; + } + constexpr auto operator<=>(const ReferenceProvider& other) const noexcept + { + assert_or_abort(accessing_info_ == other.accessing_info_); + return current_index_ <=> other.current_index_; + } + + constexpr std::ptrdiff_t operator-(const ReferenceProvider& other) const noexcept + { + assert_or_abort(accessing_info_ == other.accessing_info_); + return static_cast(current_index_ - other.current_index_); + } + }; + + using IteratorType = RandomAccessIterator; + +public: + using const_reference = ReferenceType; + using const_iterator = IteratorType; + +private: + AccessingInfo accessing_info_; + +public: + ContiguousRangeSubStructView() + : accessing_info_{} + { + } + + template + ContiguousRangeSubStructView(SuperStructContainer& super_struct_container) + : accessing_info_{ + .sub_struct_field_properties = extract_field_properties_of(), + .super_struct_field_properties = {}, + .base_array_super_struct_ptr = + memory::addressof_as_mutable_byte_ptr(*super_struct_container.data()), + .stride = {}, + .size = super_struct_container.size(), + } + + { + using SuperStruct = typename SuperStructContainer::value_type; + auto super_struct_field_properties_all = extract_field_properties_of(); + for (const auto& [name, _] : accessing_info_.sub_struct_field_properties) + { + accessing_info_.super_struct_field_properties[name] = + super_struct_field_properties_all.at(name); + } + + accessing_info_.stride = sizeof(SuperStruct); + } + + [[nodiscard]] const_reference at(const std::size_t index) const + { + return create_view_at_offset(accessing_info_, index); + } + + [[nodiscard]] std::size_t size() const { return accessing_info_.size; } + + constexpr const_iterator begin() noexcept { return cbegin(); } + [[nodiscard]] constexpr const_iterator begin() const noexcept { return cbegin(); } + [[nodiscard]] constexpr const_iterator cbegin() const noexcept + { + return create_const_iterator(0); + } + + constexpr const_iterator end() noexcept { return cend(); } + [[nodiscard]] constexpr const_iterator end() const noexcept { return cend(); } + [[nodiscard]] constexpr const_iterator cend() const noexcept + { + return create_const_iterator(accessing_info_.size); + } + +private: + [[nodiscard]] constexpr const_iterator create_const_iterator( + const std::size_t offset_from_start) const noexcept + { + return const_iterator{ReferenceProvider{accessing_info_, offset_from_start}}; + } +}; + } // namespace fixed_containers::sub_struct_view diff --git a/test/sub_struct_view_test.cpp b/test/sub_struct_view_test.cpp index 0d22204c..7221cdf1 100644 --- a/test/sub_struct_view_test.cpp +++ b/test/sub_struct_view_test.cpp @@ -2,10 +2,14 @@ #include "fixed_containers/sub_struct_view.hpp" +#include "fixed_containers/fixed_vector.hpp" #include "fixed_containers/out.hpp" #include +#include +#include + namespace fixed_containers::sub_struct_view { namespace @@ -75,6 +79,125 @@ TEST(SubStructView, SubStructViewOf) ASSERT_EQ(flat_sub_struct_1.retain1, &flat_super_struct_1.retain1); ASSERT_EQ(flat_sub_struct_1.retain2, &flat_super_struct_1.retain2); } + +namespace +{ +struct PointXYZ +{ + double x{}; + double y{}; + double z{}; +}; + +struct FlatSuperStruct2 +{ + int ignore1{}; + std::array retain_array_1{}; + FixedVector retain_vec_2{}; + float ignore2{}; +}; + +struct PointXZ +{ + const double* z{}; + const double* x{}; +}; + +struct FlatSubStruct2 +{ + ContiguousRangeSubStructView retain_array_1{}; + ContiguousRangeSubStructView retain_vec_2{}; +}; + +} // namespace + +TEST(ContiguousRangeSubStructView, OperatorAt) +{ + FlatSuperStruct2 flat_super_struct_2{}; + FlatSubStruct2 flat_sub_struct_2{}; + flat_super_struct_2.retain_vec_2.resize(3); + + flat_sub_struct_2.retain_array_1 = flat_super_struct_2.retain_array_1; + flat_sub_struct_2.retain_vec_2 = flat_super_struct_2.retain_vec_2; + + { + ASSERT_EQ(3, flat_sub_struct_2.retain_array_1.size()); + + ASSERT_EQ(flat_sub_struct_2.retain_array_1.at(0).x, + &flat_super_struct_2.retain_array_1.at(0).x); + ASSERT_EQ(flat_sub_struct_2.retain_array_1.at(0).z, + &flat_super_struct_2.retain_array_1.at(0).z); + + ASSERT_EQ(flat_sub_struct_2.retain_array_1.at(1).x, + &flat_super_struct_2.retain_array_1.at(1).x); + ASSERT_EQ(flat_sub_struct_2.retain_array_1.at(1).z, + &flat_super_struct_2.retain_array_1.at(1).z); + + ASSERT_EQ(flat_sub_struct_2.retain_array_1.at(2).x, + &flat_super_struct_2.retain_array_1.at(2).x); + ASSERT_EQ(flat_sub_struct_2.retain_array_1.at(2).z, + &flat_super_struct_2.retain_array_1.at(2).z); + + ASSERT_DEATH((void)flat_sub_struct_2.retain_array_1.at(3), ""); + } + + { + ASSERT_EQ(3, flat_sub_struct_2.retain_vec_2.size()); + + ASSERT_EQ(flat_sub_struct_2.retain_vec_2.at(0).x, + &flat_super_struct_2.retain_vec_2.at(0).x); + ASSERT_EQ(flat_sub_struct_2.retain_vec_2.at(0).z, + &flat_super_struct_2.retain_vec_2.at(0).z); + + ASSERT_EQ(flat_sub_struct_2.retain_vec_2.at(1).x, + &flat_super_struct_2.retain_vec_2.at(1).x); + ASSERT_EQ(flat_sub_struct_2.retain_vec_2.at(1).z, + &flat_super_struct_2.retain_vec_2.at(1).z); + + ASSERT_EQ(flat_sub_struct_2.retain_vec_2.at(2).x, + &flat_super_struct_2.retain_vec_2.at(2).x); + ASSERT_EQ(flat_sub_struct_2.retain_vec_2.at(2).z, + &flat_super_struct_2.retain_vec_2.at(2).z); + + ASSERT_DEATH((void)flat_sub_struct_2.retain_vec_2.at(3), ""); + } +} + +TEST(ContiguousRangeSubStructView, Iteration) +{ + FlatSuperStruct2 flat_super_struct_2{}; + FlatSubStruct2 flat_sub_struct_2{}; + flat_super_struct_2.retain_vec_2.resize(3); + + flat_sub_struct_2.retain_array_1 = flat_super_struct_2.retain_array_1; + flat_sub_struct_2.retain_vec_2 = flat_super_struct_2.retain_vec_2; + + { + ASSERT_EQ(3, flat_sub_struct_2.retain_array_1.size()); + + std::size_t counter = 0; + for (auto&& sub_struct : flat_sub_struct_2.retain_array_1) + { + ASSERT_EQ(sub_struct.x, &flat_super_struct_2.retain_array_1.at(counter).x); + ASSERT_EQ(sub_struct.z, &flat_super_struct_2.retain_array_1.at(counter).z); + counter++; + } + ASSERT_EQ(3, counter); + } + { + ASSERT_EQ(3, flat_sub_struct_2.retain_vec_2.size()); + + std::size_t counter = 0; + for (auto&& sub_struct : flat_sub_struct_2.retain_vec_2) + { + ASSERT_EQ(sub_struct.x, &flat_super_struct_2.retain_vec_2.at(counter).x); + ASSERT_EQ(sub_struct.z, &flat_super_struct_2.retain_vec_2.at(counter).z); + counter++; + } + ASSERT_EQ(3, counter); + } +} + } // namespace fixed_containers::sub_struct_view #endif