Skip to content

Commit

Permalink
Allow string literals in record dimension and reflection using Boost.…
Browse files Browse the repository at this point in the history
…Describe
  • Loading branch information
bernhardmgruber committed May 10, 2023
1 parent 9d0d3a7 commit 51bf100
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 1 deletion.
83 changes: 83 additions & 0 deletions include/llama/Core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
#include <iostream>
#include <string>
#include <type_traits>
#if __has_include(<boost/describe/members.hpp>)
# include <boost/describe/members.hpp>
#endif

namespace llama
{
Expand Down Expand Up @@ -53,6 +56,86 @@ namespace llama
{
};

#if __cpp_nontype_template_args >= 201911L
namespace internal
{
// N includes the char to store the null-terminator
template<std::size_t N>
struct StringLiteral
{
constexpr StringLiteral(const char* str)
{
std::copy(str, str + N, data);
}

char data[N];
};

template<std::size_t N>
StringLiteral(const char (&str)[N]) -> StringLiteral<N>;

template<StringLiteral Name>
struct StringTag
{
};
} // namespace internal

inline namespace literals
{
/// Literal operator for converting a string literal "abc"_Name to a StringTag<"Name">.
template<internal::StringLiteral Name>
auto operator"" _Name()
{
return internal::StringTag<Name>{};
}
} // namespace literals

/// Alternative to \ref Field. Use with string literals, e.g. NamedField<"x", float>. Access at the \ref View
/// requires to use "x"_Name then.
template<internal::StringLiteral Tag, typename Type>
using NamedField = Field<internal::StringTag<Tag>, Type>;

# if __has_include(<boost/describe/members.hpp>)
namespace internal
{
template<typename T>
auto reflectToRecordDim();

template<class C, typename T>
auto memberPointerPointeeType(T C::*) -> T;

constexpr auto constexpr_strlen(const char* s)
{
const char* end = s;
while(*end != 0)
end++;
return end - s;
}

template<typename Member>
using MakeFieldFromMemberDescriptor = NamedField<
StringLiteral<constexpr_strlen(Member::name) + 1>(Member::name),
decltype(reflectToRecordDim<decltype(memberPointerPointeeType(Member::pointer))>())>;

template<typename T>
auto reflectToRecordDim()
{
if constexpr(boost::describe::has_describe_members<T>::value)
{
using MemberList = boost::describe::describe_members<T, boost::describe::mod_public>;
return mp_rename<mp_transform<MakeFieldFromMemberDescriptor, MemberList>, llama::Record>{};
}
else
return T{};
}
} // namespace internal

/// Reflects the given type T using Boost.Describe and creates a record dimension for it.
template<typename T>
using ReflectToRecordDim = decltype(internal::reflectToRecordDim<T>());
# endif
#endif

template<typename T>
struct NrAndOffset
{
Expand Down
69 changes: 68 additions & 1 deletion tests/recorddimension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,77 @@ TEST_CASE("recorddim.recurring_tags")
llama::Field<tag::Y, llama::Record<llama::Field<tag::X, float>>>,
llama::Field<tag::Z, llama::Record<llama::Field<tag::X, llama::Record<llama::Field<tag::X, float>>>>>>;


auto view = llama::allocView(llama::mapping::AoS{llama::ArrayExtents{1}, RecordDim{}});
view(0)(tag::X{}) = 42;
view(0)(tag::Y{}, tag::X{}) = 42;
view(0)(tag::Z{}, tag::X{}, tag::X{}) = 42;
}
#endif

#if __cpp_nontype_template_args >= 201911L
TEST_CASE("recorddim.NamedFields")
{
using RecordDim = llama::Record<
llama::NamedField<"x", int>,
llama::NamedField<"y", int>,
llama::NamedField<"pos", Vec2F>,
llama::Field<tag::Vel, llama::Record<llama::Field<tag::X, float>, llama::NamedField<"y", float>>>>;

auto view = llama::allocView(llama::mapping::AoS{llama::ArrayExtents{1}, RecordDim{}});

using namespace llama::literals;
view(0)("x"_Name) = 1;
view(0)("y"_Name) = 2;
view(0)("pos"_Name, tag::X{}) = 3;
view(0)("pos"_Name, tag::Y{}) = 4;
view(0)(tag::Vel{}, tag::X{}) = 5;
view(0)(tag::Vel{}, "y"_Name) = 6;

CHECK(view(0)(llama::RecordCoord<0>{}) == 1);
CHECK(view(0)(llama::RecordCoord<1>{}) == 2);
CHECK(view(0)(llama::RecordCoord<2, 0>{}) == 3);
CHECK(view(0)(llama::RecordCoord<2, 1>{}) == 4);
CHECK(view(0)(llama::RecordCoord<3, 0>{}) == 5);
CHECK(view(0)(llama::RecordCoord<3, 1>{}) == 6);
}
#endif

#if __has_include(<boost/describe/members.hpp>)
# include <boost/describe/class.hpp>
namespace
{
struct MyVel
{
int x;
int y;
};
BOOST_DESCRIBE_STRUCT(MyVel, (), (x, y));

struct MyStruct
{
int a;
int b;
MyVel pos;
};
BOOST_DESCRIBE_STRUCT(MyStruct, (), (a, b, pos));
} // namespace


TEST_CASE("recorddim.Boost.Describe")
{
using RecordDim = llama::ReflectToRecordDim<MyStruct>;
auto view = llama::allocView(llama::mapping::AoS{llama::ArrayExtents{1}, RecordDim{}});

using namespace llama::literals;

view(0)("a"_Name) = 1;
view(0)("b"_Name) = 2;
view(0)("pos"_Name, "x"_Name) = 3;
view(0)("pos"_Name, "y"_Name) = 4;

CHECK(view(0)(llama::RecordCoord<0>{}) == 1);
CHECK(view(0)(llama::RecordCoord<1>{}) == 2);
CHECK(view(0)(llama::RecordCoord<2, 0>{}) == 3);
CHECK(view(0)(llama::RecordCoord<2, 1>{}) == 4);
}
#endif

0 comments on commit 51bf100

Please sign in to comment.