diff --git a/BUILD.bazel b/BUILD.bazel index edc60e0b..246183dc 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -860,6 +860,10 @@ cc_library( hdrs = ["include/fixed_containers/sub_struct_view.hpp"], includes = ["include"], deps = [ + ":fixed_map", + ":memory", + ":out", + ":reflection", ], copts = ["-std=c++20"], ) @@ -1610,6 +1614,7 @@ cc_test( name = "sub_struct_view_test", srcs = ["test/sub_struct_view_test.cpp"], deps = [ + ":out", ":sub_struct_view", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", diff --git a/include/fixed_containers/sub_struct_view.hpp b/include/fixed_containers/sub_struct_view.hpp index e0d19150..bfbd263f 100644 --- a/include/fixed_containers/sub_struct_view.hpp +++ b/include/fixed_containers/sub_struct_view.hpp @@ -1,6 +1,83 @@ #pragma once +#include "fixed_containers/fixed_map.hpp" +#include "fixed_containers/memory.hpp" +#include "fixed_containers/out.hpp" +#include "fixed_containers/reflection.hpp" + +#include +#include +#include + namespace fixed_containers::sub_struct_view { +struct FieldProperties +{ + std::ptrdiff_t offset{}; + bool is_pointer{}; + + constexpr bool operator==(const FieldProperties&) const = default; +}; + +template +using FieldPropertiesMap = + FixedMap()>; +template +auto extract_field_properties_of(const S& instance = {}) +{ + FieldPropertiesMap field_properties_map{}; + reflection::for_each_field( + instance, + [&](const std::string_view& name, F& field) + { + const std::byte* base_pointer = memory::addressof_as_const_byte_ptr(instance); + const std::byte* field_pointer = memory::addressof_as_const_byte_ptr(field); + const bool is_pointer = std::is_pointer_v; + field_properties_map.try_emplace( + name, std::distance(base_pointer, field_pointer), is_pointer); + }); + + return field_properties_map; } + +template +void sub_struct_view_of(std::byte* base_super_struct_pointer, + const SuperProperties& super_struct_field_properties, + std::byte* base_sub_struct_pointer, + const SubProperties& sub_struct_field_properties) +{ + for (const auto& [name, field_properties] : sub_struct_field_properties) + { + if (!field_properties.is_pointer) + { + continue; + } + + const std::ptrdiff_t super_struct_offset = super_struct_field_properties.at(name).offset; + std::byte* super_struct_field_ptr = + std::next(base_super_struct_pointer, super_struct_offset); + std::byte* sub_struct_field_ptr = + std::next(base_sub_struct_pointer, field_properties.offset); + + *reinterpret_cast(sub_struct_field_ptr) = + reinterpret_cast(super_struct_field_ptr); + } +} + +template +void sub_struct_view_of(Super& super_struct, + const SuperProperties& super_struct_field_properties, + out out_sub_struct, + const SubProperties& sub_struct_field_properties) +{ + std::byte* base_super_struct_pointer = memory::addressof_as_mutable_byte_ptr(super_struct); + std::byte* base_sub_struct_pointer = memory::addressof_as_mutable_byte_ptr(*out_sub_struct); + + return sub_struct_view_of(base_super_struct_pointer, + super_struct_field_properties, + base_sub_struct_pointer, + sub_struct_field_properties); +} + +} // namespace fixed_containers::sub_struct_view diff --git a/test/sub_struct_view_test.cpp b/test/sub_struct_view_test.cpp index 4f4b7b0d..0d22204c 100644 --- a/test/sub_struct_view_test.cpp +++ b/test/sub_struct_view_test.cpp @@ -1,14 +1,79 @@ #if defined(__clang__) && __clang_major__ >= 15 -// #include "fixed_containers/sub_struct_view.hpp" +#include "fixed_containers/sub_struct_view.hpp" + +#include "fixed_containers/out.hpp" #include namespace fixed_containers::sub_struct_view { -TEST(SubStructView, Placeholder) +namespace +{ +struct FlatSuperStruct1 { - // + bool ignore1_dont_forget_alignment{}; + double retain1{}; + float ignore2{}; + float retain2{}; + int ignore3{}; +}; + +struct FlatSubStruct1 +{ + const double* retain1; + const float* retain2; +}; + +} // namespace + +TEST(SubStructView, ExtractFieldPropertiesOf) +{ + { + const FlatSuperStruct1 instance{}; + + auto field_properties = extract_field_properties_of(instance); + + EXPECT_EQ(5, field_properties.size()); + EXPECT_EQ((FieldProperties{.offset = 0, .is_pointer = false}), + field_properties.at("ignore1_dont_forget_alignment")); + EXPECT_EQ((FieldProperties{.offset = 8, .is_pointer = false}), + field_properties.at("retain1")); + EXPECT_EQ((FieldProperties{.offset = 16, .is_pointer = false}), + field_properties.at("ignore2")); + EXPECT_EQ((FieldProperties{.offset = 20, .is_pointer = false}), + field_properties.at("retain2")); + EXPECT_EQ((FieldProperties{.offset = 24, .is_pointer = false}), + field_properties.at("ignore3")); + } + { + const FlatSubStruct1 instance{}; + + auto field_properties = extract_field_properties_of(instance); + + EXPECT_EQ(2, field_properties.size()); + EXPECT_EQ((FieldProperties{.offset = 0, .is_pointer = true}), + field_properties.at("retain1")); + EXPECT_EQ((FieldProperties{.offset = 8, .is_pointer = true}), + field_properties.at("retain2")); + } +} + +TEST(SubStructView, SubStructViewOf) +{ + FlatSuperStruct1 flat_super_struct_1{}; + FlatSubStruct1 flat_sub_struct_1{}; + + auto super_struct_field_properties = extract_field_properties_of(flat_super_struct_1); + auto sub_struct_field_properties = extract_field_properties_of(flat_sub_struct_1); + + sub_struct_view::sub_struct_view_of(flat_super_struct_1, + super_struct_field_properties, + out{flat_sub_struct_1}, + sub_struct_field_properties); + + 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 fixed_containers::sub_struct_view