Add field set util to library
This commit is contained in:
parent
945b44e0e4
commit
816c5db9f5
3 changed files with 188 additions and 0 deletions
|
@ -1,2 +1,3 @@
|
||||||
export module larra.library;
|
export module larra.library;
|
||||||
export import larra.library.jid;
|
export import larra.library.jid;
|
||||||
|
export import larra.library.utils;
|
||||||
|
|
99
src/lib/utils.cpp
Normal file
99
src/lib/utils.cpp
Normal 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
88
tests/set.cpp
Normal 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
|
Loading…
Reference in a new issue