Update field set helpers
This commit is contained in:
parent
84abbe8178
commit
5c5d1a37d5
2 changed files with 101 additions and 28 deletions
|
@ -3,8 +3,26 @@
|
|||
|
||||
namespace larra::xmpp::utils {
|
||||
|
||||
namespace impl {
|
||||
|
||||
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
|
||||
// struct SomeStruct {
|
||||
|
@ -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;
|
||||
}())
|
||||
|
@ -61,8 +80,9 @@ struct FieldsDescription {
|
|||
* \return T with new field value and values from self
|
||||
*/
|
||||
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)>
|
||||
constexpr auto With(Type(T::*ptr), Self&& self, Value&& value) const
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue