Skip to content

Commit

Permalink
Introduce sub_struct_view::sub_struct_view_of()
Browse files Browse the repository at this point in the history
  • Loading branch information
alexkaratarakis committed Jul 14, 2024
1 parent 901a413 commit 9a5dd89
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 3 deletions.
5 changes: 5 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
)
Expand Down Expand Up @@ -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",
Expand Down
77 changes: 77 additions & 0 deletions include/fixed_containers/sub_struct_view.hpp
Original file line number Diff line number Diff line change
@@ -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 <cstddef>
#include <iterator>
#include <string_view>

namespace fixed_containers::sub_struct_view
{
struct FieldProperties
{
std::ptrdiff_t offset{};
bool is_pointer{};

constexpr bool operator==(const FieldProperties&) const = default;
};

template <typename S>
using FieldPropertiesMap =
FixedMap<std::string_view, FieldProperties, reflection::field_count_of<S>()>;

template <typename S>
auto extract_field_properties_of(const S& instance = {})
{
FieldPropertiesMap<S> field_properties_map{};
reflection::for_each_field(
instance,
[&]<typename F>(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<F>;
field_properties_map.try_emplace(
name, std::distance(base_pointer, field_pointer), is_pointer);
});

return field_properties_map;
}

template <typename SuperProperties, typename SubProperties>
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<std::uintptr_t*>(sub_struct_field_ptr) =
reinterpret_cast<std::uintptr_t>(super_struct_field_ptr);
}
}

template <typename Super, typename SuperProperties, typename Sub, typename SubProperties>
void sub_struct_view_of(Super& super_struct,
const SuperProperties& super_struct_field_properties,
out<Sub> 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
71 changes: 68 additions & 3 deletions test/sub_struct_view_test.cpp
Original file line number Diff line number Diff line change
@@ -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 <gtest/gtest.h>

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

Expand Down

0 comments on commit 9a5dd89

Please sign in to comment.