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 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