diff --git a/BUILD.bazel b/BUILD.bazel index d5fe28eb..e1e64ca3 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -778,9 +778,12 @@ cc_library( hdrs = ["include/fixed_containers/sub_struct_view.hpp"], includes = ["include"], deps = [ + ":assert_or_abort", ":fixed_map", + ":iterator_utils", ":memory", ":out", + ":random_access_iterator", ":reflection", ], copts = ["-std=c++20"], diff --git a/include/fixed_containers/sub_struct_view.hpp b/include/fixed_containers/sub_struct_view.hpp index bfbd263f..073437af 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,142 @@ 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 i) + { + assert_or_abort(i < accessing_info.size); + SubStruct instance{}; + std::byte* base_of_ith_entry = + std::next(accessing_info.base_array_super_struct_ptr, + static_cast(i * 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; } + + 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), + .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); + } + + const_reference at(const std::size_t i) const + { + return create_view_at_offset(accessing_info_, i); + } + + constexpr const_iterator begin() noexcept { return cbegin(); } + constexpr const_iterator begin() const noexcept { return cbegin(); } + constexpr const_iterator cbegin() const noexcept { return create_const_iterator(0); } + + constexpr const_iterator end() noexcept { return cend(); } + constexpr const_iterator end() const noexcept { return cend(); } + constexpr const_iterator cend() const noexcept + { + return create_const_iterator(accessing_info_.size); + } + +private: + 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 34a816ee..b03e98d6 100644 --- a/test/sub_struct_view_test.cpp +++ b/test/sub_struct_view_test.cpp @@ -6,6 +6,8 @@ #include +#include + namespace fixed_containers::sub_struct_view { namespace @@ -75,6 +77,71 @@ 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 retain1{}; + float ignore2{}; +}; + +struct PointXZ +{ + const double* z{}; + const double* x{}; +}; + +struct FlatSubStruct2 +{ + ContiguousRangeSubStructView retain1{}; +}; + +} // namespace + +TEST(ContiguousRangeSubStructView, OperatorAt) +{ + FlatSuperStruct2 flat_super_struct_2{}; + FlatSubStruct2 flat_sub_struct_2{}; + + flat_sub_struct_2.retain1 = flat_super_struct_2.retain1; + + ASSERT_EQ(flat_sub_struct_2.retain1.at(0).x, &flat_super_struct_2.retain1.at(0).x); + ASSERT_EQ(flat_sub_struct_2.retain1.at(0).z, &flat_super_struct_2.retain1.at(0).z); + + ASSERT_EQ(flat_sub_struct_2.retain1.at(1).x, &flat_super_struct_2.retain1.at(1).x); + ASSERT_EQ(flat_sub_struct_2.retain1.at(1).z, &flat_super_struct_2.retain1.at(1).z); + + ASSERT_EQ(flat_sub_struct_2.retain1.at(2).x, &flat_super_struct_2.retain1.at(2).x); + ASSERT_EQ(flat_sub_struct_2.retain1.at(2).z, &flat_super_struct_2.retain1.at(2).z); + + ASSERT_DEATH(flat_sub_struct_2.retain1.at(3), ""); +} + +TEST(ContiguousRangeSubStructView, Iteration) +{ + FlatSuperStruct2 flat_super_struct_2{}; + FlatSubStruct2 flat_sub_struct_2{}; + + flat_sub_struct_2.retain1 = flat_super_struct_2.retain1; + + std::size_t counter = 0; + for (auto&& sub_struct : flat_sub_struct_2.retain1) + { + ASSERT_EQ(sub_struct.x, &flat_super_struct_2.retain1.at(counter).x); + ASSERT_EQ(sub_struct.z, &flat_super_struct_2.retain1.at(counter).z); + counter++; + } +} + } // namespace fixed_containers::sub_struct_view #endif