Add field set util to library

This commit is contained in:
sha512sum 2024-08-27 17:12:21 +00:00
parent 945b44e0e4
commit 816c5db9f5
3 changed files with 188 additions and 0 deletions

View file

@ -1,2 +1,3 @@
export module larra.library;
export import larra.library.jid;
export import larra.library.utils;

99
src/lib/utils.cpp Normal file
View file

@ -0,0 +1,99 @@
module;
#include <boost/pfr.hpp>
export module larra.library.utils;
import std;
import utempl;
namespace larra::xmpp::utils {
template <typename T, typename... Fs>
struct FieldDescription {
utempl::Tuple<Fs...> tuple;
template <typename R>
constexpr auto GetType(R(T::* ptr)) -> R;
template <std::size_t I, typename Self, typename Value>
constexpr auto With(Self&& self, Value&& value) const
requires([] {
if constexpr(I < sizeof...(Fs)) {
return std::is_constructible_v<decltype(GetType(Get<I>(tuple))), decltype(value)>;
};
return false;
}())
{
return [&](auto... is) -> T {
return {[&] -> decltype(auto) {
if constexpr(*is == I) {
return std::forward<Value>(value);
} else {
return std::forward_like<Self>(self.*Get<*is>(this->tuple));
};
}()...};
} | utempl::kSeq<sizeof...(Fs)>;
};
template <typename Self, typename Value, typename Type>
constexpr auto With(Type(T::* ptr), Self&& self, Value&& value) const
requires std::is_constructible_v<Type, decltype(value)>
{
return utempl::Unpack(this->tuple, [&](auto... fs) -> T {
return {[&] {
if constexpr(std::is_same_v<decltype(fs), decltype(ptr)>) {
if(fs == ptr) {
return std::forward<Value>(value);
};
};
return std::forward_like<Self>(self.*fs);
}()...};
});
};
};
template <typename T, typename... Fs>
consteval auto CreateFieldsDescriptionHelper(Fs&&... fs) -> FieldDescription<T, std::decay_t<Fs>...> {
return {.tuple = {std::forward<Fs>(fs)...}};
};
export template <typename T, typename... Rs>
consteval auto CreateFieldsDescription(Rs(T::*... ptr)) {
return [&](auto... is) {
return CreateFieldsDescriptionHelper<T>(ptr...);
} | utempl::kSeq<sizeof...(Rs)>;
};
export struct FieldSetHelper {
template <std::size_t I, typename Self, typename Value>
static constexpr auto With(Self&& self, Value&& value)
requires([] {
if constexpr(I < boost::pfr::tuple_size_v<std::decay_t<Self>>) {
return std::is_constructible_v<std::decay_t<decltype(boost::pfr::get<I>(std::declval<Self&>()))>, decltype(value)>;
};
return false;
}())
{
using T = std::decay_t<Self>;
return [&](auto... is) -> T {
return {[&] -> decltype(auto) {
if constexpr(I == *is) {
return std::forward<Value>(value);
} else {
return std::forward_like<Self>(boost::pfr::get<*is>(self));
};
}()...};
} | utempl::kSeq<boost::pfr::tuple_size_v<T>>;
};
template <utempl::ConstexprString FieldName, typename Self, typename Value>
static constexpr auto With(Self&& self, Value&& value) -> decltype(With<[] {
auto names = boost::pfr::names_as_array<std::decay_t<Self>>();
return std::ranges::find(names, static_cast<std::string_view>(FieldName)) - names.begin();
}()>(std::forward<Self>(self), std::forward<Value>(value))) {
constexpr auto names = boost::pfr::names_as_array<std::decay_t<Self>>();
constexpr auto id = std::ranges::find(names, static_cast<std::string_view>(FieldName)) - names.begin();
return With<id>(std::forward<Self>(self), std::forward<Value>(value));
};
};
} // namespace larra::xmpp::utils

88
tests/set.cpp Normal file
View file

@ -0,0 +1,88 @@
module;
#include <gtest/gtest.h>
export module tests.set;
import larra.library.utils;
namespace larra::xmpp {
struct SomeStruct {
std::string field1;
float field2 = 1.f;
template <typename Self>
auto Field1(this Self&& self, std::string field1) -> decltype(auto) {
return utils::FieldSetHelper::With<0>(std::forward<Self>(self), std::move(field1));
}
template <typename Self>
auto Field2(this Self&& self, float field2) -> decltype(auto) {
return utils::FieldSetHelper::With<"field2">(std::forward<Self>(self), std::move(field2));
}
};
TEST(Set, Basic) {
auto obj = SomeStruct{}.Field1("Hello").Field2(.0f);
EXPECT_EQ(obj.field1, "Hello");
EXPECT_EQ(obj.field2, .0f);
}
struct SomeStruct2 {
SomeStruct2(const SomeStruct2&) = delete;
SomeStruct2() = default;
SomeStruct2(SomeStruct2&&) = default;
};
struct SomeStruct3 {
SomeStruct2 field1;
SomeStruct2 field2;
template <typename Self>
auto Field1(this Self&& self, SomeStruct2 field1) -> decltype(auto) {
return utils::FieldSetHelper::With<0>(std::forward<Self>(self), std::move(field1));
}
template <typename Self>
auto Field2(this Self&& self, SomeStruct2 field2) -> decltype(auto) {
return utils::FieldSetHelper::With<"field2">(std::forward<Self>(self), std::move(field2));
}
};
TEST(Set, NonCopyable) {
auto obj = SomeStruct3{}.Field1({}).Field2({});
}
struct SomeStruct4 {
std::string field1;
float field2 = 1.f;
static constexpr auto kUtils = utils::CreateFieldsDescription(&SomeStruct4::field1, &SomeStruct4::field2);
template <typename Self>
auto Field1(this Self&& self, std::string field1) -> decltype(auto) {
return kUtils.With<0>(std::forward<Self>(self), std::move(field1));
}
template <typename Self>
auto Field2(this Self&& self, float field2) -> decltype(auto) {
return kUtils.With(&SomeStruct4::field2, std::forward<Self>(self), std::move(field2));
}
};
struct SomeStruct5 {
SomeStruct2 field1;
SomeStruct2 field2;
static constexpr auto kUtils = utils::CreateFieldsDescription(&SomeStruct5::field1, &SomeStruct5::field2);
template <typename Self>
auto Field1(this Self&& self, SomeStruct2 field1) -> decltype(auto) {
return kUtils.With<0>(std::forward<Self>(self), std::move(field1));
}
template <typename Self>
auto Field2(this Self&& self, SomeStruct2 field2) -> decltype(auto) {
return kUtils.With(&SomeStruct5::field2, std::forward<Self>(self), std::move(field2));
}
};
TEST(NonPfrSet, Basic) {
auto obj = SomeStruct4{}.Field1("Hello").Field2(.0f);
EXPECT_EQ(obj.field1, "Hello");
EXPECT_EQ(obj.field2, .0f);
}
TEST(NonPfrSet, NonCopyable) {
auto obj = SomeStruct5{}.Field1({}).Field2({});
}
} // namespace larra::xmpp