Update field set helpers

This commit is contained in:
sha512sum 2024-08-30 18:50:25 +00:00
parent 84abbe8178
commit 5c5d1a37d5
2 changed files with 101 additions and 28 deletions

View file

@ -3,8 +3,26 @@
namespace larra::xmpp::utils { namespace larra::xmpp::utils {
namespace impl {
template <typename T, typename R> template <typename T, typename R>
constexpr auto GetType(R(T::* ptr)) -> R; constexpr auto GetType(R(T::*ptr)) -> R;
template <typename T>
using GetTypeT = decltype(GetType(std::declval<T>()));
template <typename Self, typename Type, typename... Fs>
concept SetConcept =
(std::conditional_t<std::same_as<GetTypeT<Fs>, Type>,
std::true_type,
std::integral_constant<
bool,
std::is_reference_v<Self>
? std::copy_constructible<GetTypeT<Fs>>
: std::move_constructible<GetTypeT<Fs>>&& requires { std::declval<Self>().*std::declval<Fs>(); }>>::value &&
...);
} // namespace impl
// Fields description // Fields description
// struct SomeStruct { // struct SomeStruct {
@ -34,12 +52,13 @@ struct FieldsDescription {
*/ */
template <std::size_t I, typename Self, typename Value> template <std::size_t I, typename Self, typename Value>
constexpr auto With(Self&& self, Value&& value) const constexpr auto With(Self&& self, Value&& value) const
requires([] { requires(std::is_constructible_v<T, impl::GetTypeT<Fs>...> && impl::SetConcept<Self, std::decay_t<Value>, Fs...> &&
if constexpr(I < sizeof...(Fs)) { [] {
return std::is_constructible_v<decltype(GetType(Get<I>(tuple))), decltype(value)>; if constexpr(I < sizeof...(Fs)) {
}; return std::is_constructible_v<impl::GetTypeT<decltype(Get<I>(tuple))>, decltype(value)>;
return false; };
}()) return false;
}())
{ {
return [&](auto... is) -> T { return [&](auto... is) -> T {
@ -61,8 +80,9 @@ struct FieldsDescription {
* \return T with new field value and values from self * \return T with new field value and values from self
*/ */
template <typename Self, typename Value, typename Type> template <typename Self, typename Value, typename Type>
constexpr auto With(Type(T::* ptr), Self&& self, Value&& value) const constexpr auto With(Type(T::*ptr), Self&& self, Value&& value) const
requires std::is_constructible_v<Type, decltype(value)> requires(std::is_constructible_v<T, impl::GetTypeT<Fs>...> && std::is_constructible_v<Type, decltype(value)> &&
impl::SetConcept<Self, Type, Fs...>)
{ {
return utempl::Unpack(this->tuple, [&](auto... fs) -> T { return utempl::Unpack(this->tuple, [&](auto... fs) -> T {
return {[&] { return {[&] {
@ -75,7 +95,7 @@ struct FieldsDescription {
}()...}; }()...};
}); });
}; };
}; }; // namespace larra::xmpp::utils
namespace impl { namespace impl {
@ -118,47 +138,76 @@ struct FieldSetHelper {
/* Method accepting field index, self and new value for field and returns object with new field value /* Method accepting field index, self and new value for field and returns object with new field value
* *
* \param I field index for object T * \param I field index for object T
* \param T Type for return and pfr reflection
* \param self old object * \param self old object
* \param value new value for field * \param value new value for field
* \return T with new field value and values from self * \return T with new field value and values from self
*/ */
template <std::size_t I, typename Self, typename Value> template <std::size_t I,
static constexpr auto With(Self&& self, Value&& value) typename T = void,
requires([] { typename Self,
if constexpr(I < boost::pfr::tuple_size_v<std::decay_t<Self>>) { typename Value,
return std::is_constructible_v<std::decay_t<decltype(boost::pfr::get<I>(std::declval<Self&>()))>, decltype(value)>; typename...,
}; typename Type = std::conditional_t<std::same_as<T, void>, std::decay_t<Self>, T>,
return false; typename TT = decltype([] -> decltype(auto) {
}()) if constexpr(std::same_as<T, void>) {
return [] -> Self {
std::unreachable();
}();
} else {
return [] -> std::remove_reference_t<decltype(std::forward_like<Self>(std::declval<T>()))> {
std::unreachable();
}();
}
}())>
static constexpr auto With(Self&& self, Value&& value) -> Type
requires(
std::is_aggregate_v<Type> && (std::same_as<T, void> || requires { static_cast<T&>(self); }) &&
[] {
if constexpr(I < boost::pfr::tuple_size_v<Type>) {
return std::is_constructible_v<std::decay_t<decltype(boost::pfr::get<I>(std::declval<TT>()))>, decltype(value)>;
};
return false;
}() &&
[](auto... is) {
return ((*is == I ? true
: std::is_reference_v<Self> ? std::copy_constructible<decltype(boost::pfr::get<*is>(std::declval<TT>()))>
: std::move_constructible<decltype(boost::pfr::get<*is>(std::declval<TT>()))>) &&
...) &&
std::is_constructible_v<Type, decltype(boost::pfr::get<*is>(std::declval<TT>()))...>;
} | utempl::kSeq<boost::pfr::tuple_size_v<Type>>)
{ {
using T = std::decay_t<Self>; return [&](auto... is) -> Type {
return [&](auto... is) -> T {
return {[&] -> decltype(auto) { return {[&] -> decltype(auto) {
if constexpr(I == *is) { if constexpr(I == *is) {
return std::forward<Value>(value); return std::forward<Value>(value);
} else { } else {
return std::forward_like<Self>(boost::pfr::get<*is>(self)); return std::forward_like<Self>(boost::pfr::get<*is>(static_cast<TT&>(self)));
}; }
}()...}; }()...};
} | utempl::kSeq<boost::pfr::tuple_size_v<T>>; } | utempl::kSeq<boost::pfr::tuple_size_v<TT>>;
}; };
// clang-format off
/* Method accepting field name, self and new value for field and returns object with new field value /* Method accepting field name, self and new value for field and returns object with new field value
* *
* \param FieldName field name for object T * \param FieldName field name for object T
* \param T Type for return and pfr reflection
* \param self old object * \param self old object
* \param value new value for field * \param value new value for field
* \return T with new field value and values from self * \return T with new field value and values from self
*/ */
template <utempl::ConstexprString FieldName, typename Self, typename Value> template <utempl::ConstexprString FieldName, typename T = void, typename Self, typename Value>
static constexpr auto With(Self&& self, Value&& value) -> decltype(With<[] { static constexpr auto With(Self&& self, Value&& value) -> decltype(With<[] {
auto names = boost::pfr::names_as_array<std::decay_t<Self>>(); constexpr auto names = boost::pfr::names_as_array<std::conditional_t<std::same_as<T, void>, std::decay_t<Self>, T>>();
return std::ranges::find(names, static_cast<std::string_view>(FieldName)) - names.begin(); 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>>(); T>(std::forward<Self>(self), std::forward<Value>(value))) {
constexpr auto names = boost::pfr::names_as_array<std::conditional_t<std::same_as<T, void>, std::decay_t<Self>, T>>();
constexpr auto id = std::ranges::find(names, static_cast<std::string_view>(FieldName)) - names.begin(); 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)); return With<id, T>(std::forward<Self>(self), std::forward<Value>(value));
}; };
// clang-format on
}; };
} // namespace larra::xmpp::utils } // namespace larra::xmpp::utils

View file

@ -84,4 +84,28 @@ TEST(NonPfrSet, NonCopyable) {
auto obj = SomeStruct5{}.Field1({}).Field2({}); auto obj = SomeStruct5{}.Field1({}).Field2({});
} }
struct SomeStruct6 {
std::string field1;
std::string field2;
template <typename Self>
auto Field1(this Self&& self, std::string field1) -> std::decay_t<Self> {
return utils::FieldSetHelper::With<"field1", SomeStruct6>(std::forward<Self>(self), std::move(field1));
}
template <typename Self>
auto Field2(this Self&& self, std::string field2) -> std::decay_t<Self> {
return utils::FieldSetHelper::With<"field2", SomeStruct6>(std::forward<Self>(self), std::move(field2));
}
};
struct SomeStruct7 : SomeStruct6 {
SomeStruct7(SomeStruct6&& obj) : SomeStruct6(std::move(obj)) {};
using SomeStruct6::SomeStruct6;
};
TEST(Set, Inheritance) {
const SomeStruct7 obj = SomeStruct7{}.Field1("Hello").Field2("World");
ASSERT_EQ(obj.field1, "Hello");
ASSERT_EQ(obj.field2, "World");
}
} // namespace larra::xmpp } // namespace larra::xmpp