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 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue