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,9 +3,27 @@
namespace larra::xmpp::utils {
namespace impl {
template <typename T, typename 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
// struct SomeStruct {
// std::string field1;
@ -34,9 +52,10 @@ struct FieldsDescription {
*/
template <std::size_t I, typename Self, typename Value>
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)>;
return std::is_constructible_v<impl::GetTypeT<decltype(Get<I>(tuple))>, decltype(value)>;
};
return false;
}())
@ -62,7 +81,8 @@ struct FieldsDescription {
*/
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)>
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 {[&] {
@ -75,7 +95,7 @@ struct FieldsDescription {
}()...};
});
};
};
}; // namespace larra::xmpp::utils
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
*
* \param I field index for object T
* \param T Type for return and pfr reflection
* \param self old object
* \param value new value for field
* \return T with new field value and values from self
*/
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)>;
template <std::size_t I,
typename T = void,
typename Self,
typename Value,
typename...,
typename Type = std::conditional_t<std::same_as<T, void>, std::decay_t<Self>, T>,
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) -> T {
return [&](auto... is) -> Type {
return {[&] -> decltype(auto) {
if constexpr(I == *is) {
return std::forward<Value>(value);
} 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
*
* \param FieldName field name for object T
* \param T Type for return and pfr reflection
* \param self old object
* \param value new value for field
* \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<[] {
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();
}()>(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();
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

View file

@ -84,4 +84,28 @@ TEST(NonPfrSet, NonCopyable) {
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