Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions universal/include/userver/utils/forward_like.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,35 @@
/// @brief @copybrief utils::ForwardLike

#include <type_traits>
#include <utility>

USERVER_NAMESPACE_BEGIN

namespace utils {

// Analogue of std::forward_like from c++23.
template <typename TOwner, typename TMember>
decltype(auto) ForwardLike(TMember& member) {
if constexpr (std::is_lvalue_reference_v<TOwner> || std::is_lvalue_reference_v<TMember>) {
return member;
} else {
return std::move(member);
}
}
namespace impl {

template <typename T, typename U>
struct ForwardLikeHelper;

template <typename T, typename U>
struct ForwardLikeHelper<T&, U&> : std::type_identity<U&> {};

template <typename T, typename U>
struct ForwardLikeHelper<const T&, U&> : std::type_identity<const U&> {};

template <typename T, typename U>
struct ForwardLikeHelper<T&&, U&> : std::type_identity<U&&> {};

template <typename T, typename U>
struct ForwardLikeHelper<const T&&, U&> : std::type_identity<const U&&> {};

} // namespace impl

// Analogue of std::forward_like from c++23.
template <typename TOwner, typename TMember>
decltype(auto) ForwardLike(const TMember& member) {
return member;
constexpr auto&& ForwardLike(TMember&& member) noexcept {
using RType = impl::ForwardLikeHelper<TOwner&&, TMember&>::type;
return static_cast<RType>(member);
}

} // namespace utils
Expand Down
35 changes: 27 additions & 8 deletions universal/include/userver/utils/struct_subsets.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,34 @@ constexpr auto IsDefinedAndAggregate(Args...) -> bool {
return false;
}

template <typename Superset, typename Field, typename T>
auto&& GetSupersetField(T &t, InternalTag) noexcept {
if constexpr (std::is_reference_v<Field>) {
return std::forward<Field>(t);
} else {
return utils::ForwardLike<Superset>(t);
}
}

template <typename T>
struct SubsetField : std::type_identity<T> {};

template <typename T>
struct SubsetFieldRef : std::type_identity<const T&> {};

template <typename T>
struct SubsetFieldRef<T&&> : std::type_identity<T&&> {};

} // namespace utils::impl

USERVER_NAMESPACE_END

/// @cond

// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define USERVER_IMPL_STRUCT_MAP(r, data, elem) \
USERVER_NAMESPACE::utils::ForwardLike<OtherDeps, decltype(other.elem)>(other.elem),
#define USERVER_IMPL_STRUCT_MAP(r, data, elem) \
USERVER_NAMESPACE::utils::impl::GetSupersetField<OtherDeps, decltype(other.elem)>( \
other.elem, USERVER_NAMESPACE::utils::impl::InternalTag{}),

// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define USERVER_IMPL_MAKE_FROM_SUPERSET(Self, ...) \
Expand All @@ -56,14 +75,14 @@ USERVER_NAMESPACE_END
}

// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define USERVER_IMPL_STRUCT_SUBSET_MAP(r, data, elem) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
decltype(data::elem) elem;
#define USERVER_IMPL_STRUCT_SUBSET_MAP(r, data, elem) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
USERVER_NAMESPACE::utils::impl::SubsetField<decltype(data::elem)>::type elem;

// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define USERVER_IMPL_STRUCT_SUBSET_REF_MAP(r, data, elem) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
std::add_const_t<decltype(data::elem)>& elem;
#define USERVER_IMPL_STRUCT_SUBSET_REF_MAP(r, data, elem) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
USERVER_NAMESPACE::utils::impl::SubsetFieldRef<decltype(data::elem)>::type elem;

/// @endcond

Expand Down
171 changes: 171 additions & 0 deletions universal/src/utils/forward_like_test.cpp
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test has been run on std::forward_like from gcc and clang.

Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#include <userver/utils/forward_like.hpp>

#include <type_traits>
#include <utility>

USERVER_NAMESPACE_BEGIN

namespace {

template <typename T, typename U>
using ForwardLikeResult = decltype(utils::ForwardLike<T>(std::declval<U>()));

static_assert(std::is_same_v<ForwardLikeResult<int, int>, int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int, const int>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int, volatile int>, volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int, const volatile int>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int, int&>, int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int, const int&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int, volatile int&>, volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int, const volatile int&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int, int&&>, int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int, const int&&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int, volatile int&&>, volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int, const volatile int&&>, const volatile int&&>);

static_assert(std::is_same_v<ForwardLikeResult<const int, int>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int, const int>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int, volatile int>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int, const volatile int>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int, int&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int, const int&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int, volatile int&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int, const volatile int&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int, int&&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int, const int&&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int, volatile int&&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int, const volatile int&&>, const volatile int&&>);

static_assert(std::is_same_v<ForwardLikeResult<volatile int, int>, int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int, const int>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int, volatile int>, volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int, const volatile int>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int, int&>, int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int, const int&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int, volatile int&>, volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int, const volatile int&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int, int&&>, int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int, const int&&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int, volatile int&&>, volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int, const volatile int&&>, const volatile int&&>);

static_assert(std::is_same_v<ForwardLikeResult<const volatile int, int>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int, const int>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int, volatile int>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int, const volatile int>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int, int&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int, const int&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int, volatile int&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int, const volatile int&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int, int&&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int, const int&&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int, volatile int&&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int, const volatile int&&>, const volatile int&&>);

static_assert(std::is_same_v<ForwardLikeResult<int&, int>, int&>);
static_assert(std::is_same_v<ForwardLikeResult<int&, const int>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<int&, volatile int>, volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<int&, const volatile int>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<int&, int&>, int&>);
static_assert(std::is_same_v<ForwardLikeResult<int&, const int&>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<int&, volatile int&>, volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<int&, const volatile int&>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<int&, int&&>, int&>);
static_assert(std::is_same_v<ForwardLikeResult<int&, const int&&>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<int&, volatile int&&>, volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<int&, const volatile int&&>, const volatile int&>);

static_assert(std::is_same_v<ForwardLikeResult<const int&, int>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&, const int>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&, volatile int>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&, const volatile int>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&, int&>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&, const int&>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&, volatile int&>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&, const volatile int&>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&, int&&>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&, const int&&>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&, volatile int&&>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&, const volatile int&&>, const volatile int&>);

static_assert(std::is_same_v<ForwardLikeResult<volatile int&, int>, int&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&, const int>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&, volatile int>, volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&, const volatile int>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&, int&>, int&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&, const int&>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&, volatile int&>, volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&, const volatile int&>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&, int&&>, int&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&, const int&&>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&, volatile int&&>, volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&, const volatile int&&>, const volatile int&>);

static_assert(std::is_same_v<ForwardLikeResult<const volatile int&, int>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&, const int>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&, volatile int>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&, const volatile int>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&, int&>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&, const int&>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&, volatile int&>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&, const volatile int&>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&, int&&>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&, const int&&>, const int&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&, volatile int&&>, const volatile int&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&, const volatile int&&>, const volatile int&>);

static_assert(std::is_same_v<ForwardLikeResult<int&&, int>, int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int&&, const int>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int&&, volatile int>, volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int&&, const volatile int>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int&&, int&>, int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int&&, const int&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int&&, volatile int&>, volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int&&, const volatile int&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int&&, int&&>, int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int&&, const int&&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int&&, volatile int&&>, volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<int&&, const volatile int&&>, const volatile int&&>);

static_assert(std::is_same_v<ForwardLikeResult<const int&&, int>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&&, const int>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&&, volatile int>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&&, const volatile int>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&&, int&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&&, const int&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&&, volatile int&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&&, const volatile int&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&&, int&&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&&, const int&&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&&, volatile int&&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const int&&, const volatile int&&>, const volatile int&&>);

static_assert(std::is_same_v<ForwardLikeResult<volatile int&&, int>, int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&&, const int>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&&, volatile int>, volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&&, const volatile int>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&&, int&>, int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&&, const int&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&&, volatile int&>, volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&&, const volatile int&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&&, int&&>, int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&&, const int&&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&&, volatile int&&>, volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<volatile int&&, const volatile int&&>, const volatile int&&>);

static_assert(std::is_same_v<ForwardLikeResult<const volatile int&&, int>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&&, const int>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&&, volatile int>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&&, const volatile int>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&&, int&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&&, const int&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&&, volatile int&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&&, const volatile int&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&&, int&&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&&, const int&&>, const int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&&, volatile int&&>, const volatile int&&>);
static_assert(std::is_same_v<ForwardLikeResult<const volatile int&&, const volatile int&&>, const volatile int&&>);

} // namespace

USERVER_NAMESPACE_END
Loading
Loading