Skip to content

Commit

Permalink
Add macro to simplify member function overloading by const and rlefer…
Browse files Browse the repository at this point in the history
…ence qualifiers

Reviewed By: yfeldblum

Differential Revision: D63159473

fbshipit-source-id: 55439ac4d08d16f6c0ddaeb223f703130c8af942
  • Loading branch information
TJ Yin authored and facebook-github-bot committed Sep 25, 2024
1 parent fb77fc1 commit a2513f6
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 4 deletions.
85 changes: 85 additions & 0 deletions folly/Utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -844,4 +844,89 @@ struct invocable_to_fn {
}
};
inline constexpr invocable_to_fn invocable_to{};

#define FOLLY_DETAIL_FORWARD_BODY(...) \
noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) { \
return __VA_ARGS__; \
}

/// FOLLY_FOR_EACH_THIS_OVERLOAD_IN_CLASS_BODY_DELEGATE
///
/// Helper macro to add 4 delegated, qualifier-overloaded methods to a class
///
/// Example:
///
/// template <typename T>
/// class optional {
/// public:
/// bool has_value() const;
///
/// T& value() & {
/// if (!has_value()) { throw std::bad_optional_access(); }
/// return m_value;
/// }
///
/// const T& value() const& {
/// if (!has_value()) { throw std::bad_optional_access(); }
/// return m_value;
/// }
///
/// T&& value() && {
/// if (!has_value()) { throw std::bad_optional_access(); }
/// return std::move(m_value);
/// }
///
/// const T&& value() const&& {
/// if (!has_value()) { throw std::bad_optional_access(); }
/// return std::move(m_value);
/// }
/// };
///
/// This is equivalent to
///
/// template <typename T>
/// class optional {
/// template <typename Self>
/// decltype(auto) value_impl(Self&& self) {
/// if (!self.has_value()) {
/// throw std::bad_optional_access();
/// }
/// return std::forward<Self>(self).m_value;
/// }
/// // ...
///
/// public:
/// bool has_value() const;
///
/// FOLLY_FOR_EACH_THIS_OVERLOAD_IN_CLASS_BODY_DELEGATE(value,
/// value_impl);
/// };
///
/// Note: This can be migrated to C++23's deducing this:
/// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html
///
// clang-format off
#define FOLLY_FOR_EACH_THIS_OVERLOAD_IN_CLASS_BODY_DELEGATE(MEMBER, DELEGATE) \
template <class... Args> \
[[maybe_unused]] FOLLY_ERASE_HACK_GCC \
constexpr auto MEMBER(Args&&... args) & FOLLY_DETAIL_FORWARD_BODY( \
::folly::remove_cvref_t<decltype(*this)>::DELEGATE( \
*this, static_cast<Args&&>(args)...)) \
template <class... Args> \
[[maybe_unused]] FOLLY_ERASE_HACK_GCC \
constexpr auto MEMBER(Args&&... args) const& FOLLY_DETAIL_FORWARD_BODY( \
::folly::remove_cvref_t<decltype(*this)>::DELEGATE( \
*this, static_cast<Args&&>(args)...)) \
template <class... Args> \
[[maybe_unused]] FOLLY_ERASE_HACK_GCC \
constexpr auto MEMBER(Args&&... args) && FOLLY_DETAIL_FORWARD_BODY( \
::folly::remove_cvref_t<decltype(*this)>::DELEGATE( \
std::move(*this), static_cast<Args&&>(args)...)) \
template <class... Args> \
[[maybe_unused]] FOLLY_ERASE_HACK_GCC \
constexpr auto MEMBER(Args&&... args) const&& FOLLY_DETAIL_FORWARD_BODY( \
::folly::remove_cvref_t<decltype(*this)>::DELEGATE( \
std::move(*this), static_cast<Args&&>(args)...)) \
/* enforce semicolon after macro */ static_assert(true)
// clang-format on
} // namespace folly
4 changes: 0 additions & 4 deletions folly/functional/Invoke.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@
#include <folly/lang/CustomizationPoint.h>

#define FOLLY_DETAIL_FORWARD_REF(a) static_cast<decltype(a)&&>(a)
#define FOLLY_DETAIL_FORWARD_BODY(e) \
noexcept(noexcept(e))->decltype(e) { \
return e; \
}

/**
* include or backport:
Expand Down
76 changes: 76 additions & 0 deletions folly/test/UtilityTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,3 +463,79 @@ static_assert(is_nx_conv_v<of<int, 1, 1, 1>&&, int>);
static_assert(is_nx_conv_v<of<int, 1, 1, 1> const&&, int>);

} // namespace folly::detail::invocable_to_test

namespace folly::detail::method_overload_delegation_test {

class MethodOverloadDelegation {
private:
int value_ = 0;

template <class Self>
static auto&& testForwardImpl(Self&& self) {
return std::forward<Self>(self).value_;
}

template <class Self, class T>
static void testNoexceptImpl(Self&&, T) noexcept(T::value) {}

template <class Self>
static constexpr int testConstexprImpl(Self&&) {
return 42;
}

template <class Self, class T>
static constexpr std::enable_if_t<T::value> testSfinaeImpl(Self&&, T) {}

public:
FOLLY_FOR_EACH_THIS_OVERLOAD_IN_CLASS_BODY_DELEGATE(
testForward, testForwardImpl);
FOLLY_FOR_EACH_THIS_OVERLOAD_IN_CLASS_BODY_DELEGATE(
testConstexpr, testConstexprImpl);
FOLLY_FOR_EACH_THIS_OVERLOAD_IN_CLASS_BODY_DELEGATE(
testNoexcept, testNoexceptImpl);
FOLLY_FOR_EACH_THIS_OVERLOAD_IN_CLASS_BODY_DELEGATE(
testSfinae, testSfinaeImpl);
};

template <class Self, class T>
constexpr bool testForward =
std::is_same_v<decltype(std::declval<Self>().testForward()), T>;

static_assert(testForward<MethodOverloadDelegation&, int&>);
static_assert(testForward<MethodOverloadDelegation&&, int&&>);
static_assert(testForward<const MethodOverloadDelegation&, const int&>);
static_assert(testForward<const MethodOverloadDelegation&&, const int&&>);

template <class Self>
constexpr bool testConstexpr =
static_cast<Self>(std::array<std::remove_cvref_t<Self>, 1>{}[0])
.testConstexpr() == 42;

static_assert(testConstexpr<MethodOverloadDelegation&>);
static_assert(testConstexpr<MethodOverloadDelegation&&>);
static_assert(testConstexpr<const MethodOverloadDelegation&>);
static_assert(testConstexpr<const MethodOverloadDelegation&&>);

template <class Self>
constexpr bool testNoexcept =
noexcept(std::declval<Self>().testNoexcept(std::true_type{})) &&
!noexcept(std::declval<Self>().testNoexcept(std::false_type{}));

static_assert(testNoexcept<MethodOverloadDelegation&>);
static_assert(testNoexcept<MethodOverloadDelegation&&>);
static_assert(testNoexcept<const MethodOverloadDelegation&>);
static_assert(testNoexcept<const MethodOverloadDelegation&&>);

template <class Self, class T>
concept testSfinae = requires { std::declval<Self>().testSfinae(T{}); };

static_assert(testSfinae<MethodOverloadDelegation&, std::true_type>);
static_assert(testSfinae<const MethodOverloadDelegation&, std::true_type>);
static_assert(testSfinae<MethodOverloadDelegation&&, std::true_type>);
static_assert(testSfinae<const MethodOverloadDelegation&&, std::true_type>);
static_assert(!testSfinae<MethodOverloadDelegation&, int>);
static_assert(!testSfinae<const MethodOverloadDelegation&, int>);
static_assert(!testSfinae<MethodOverloadDelegation&&, int>);
static_assert(!testSfinae<const MethodOverloadDelegation&&, int>);

} // namespace folly::detail::method_overload_delegation_test

0 comments on commit a2513f6

Please sign in to comment.