Skip to content

Commit

Permalink
folly concepts facebook#1 (facebook#2249)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: facebook#2249

We have a need for some common concepts that are used in many places.

This is a proposal, please let me know what you think.

Differential Revision: D59396081
  • Loading branch information
DenisYaroshevskiy authored and facebook-github-bot committed Jul 18, 2024
1 parent f223c71 commit 602ba5b
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 2 deletions.
6 changes: 6 additions & 0 deletions folly/Portability.h
Original file line number Diff line number Diff line change
Expand Up @@ -606,3 +606,9 @@ constexpr auto kCpplibVer = 0;
#else
#define FOLLY_CONSTEVAL constexpr
#endif

#if FOLLY_CPLUSPLUS >= 202002L && __has_include(<concepts>) && defined(__cpp_concepts)
#define FOLLY_HAS_CONCEPTS 1
#else
#define FOLLY_HAS_CONCEPTS 0
#endif
33 changes: 33 additions & 0 deletions folly/Traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@

#include <folly/Portability.h>

#if FOLLY_HAS_CONCEPTS
#include <concepts>
#endif

namespace folly {

#if defined(__cpp_lib_type_identity) && __cpp_lib_type_identity >= 201806L
Expand Down Expand Up @@ -168,6 +172,8 @@ namespace detail {
/// Note that this only works with type template parameters. It does not work
/// with non-type template parameters, template template parameters, or alias
/// templates.
///
/// NOTE: there is also `instantiation_of` concept
template <template <typename...> class, typename>
inline constexpr bool is_instantiation_of_v = false;
template <template <typename...> class C, typename... T>
Expand All @@ -186,6 +192,18 @@ struct is_similar_instantiation

} // namespace detail

#if FOLLY_HAS_CONCEPTS

/**
* Concept to restrict a parameter to instantiation of a given template.
* NOTE: this only works with type template parameters.
*/
template <typename T, template <typename...> class Templ>
concept instantiation_of =
detail::is_instantiation_of_v<Templ, std::remove_cvref_t<T>>;

#endif

/// member_pointer_traits
///
/// For a member-pointer, reveals its constituent member-type and object-type.
Expand Down Expand Up @@ -1329,4 +1347,19 @@ template <typename T, typename... Dependencies>
using enable_std_hash_helper =
enable_hasher_helper<T, std::hash, Dependencies...>;

#if FOLLY_HAS_CONCEPTS

/**
* Concept to restrict perfect forwarding to a specific type.
*
* Example:
* ```
* void foo(folly::forwarded<std::vector<int>> auto&& vec);
* ```
*/
template <typename Fwd, typename To>
concept forwarded = std::same_as<std::remove_cvref_t<Fwd>, To>;

#endif

} // namespace folly
8 changes: 6 additions & 2 deletions folly/test/PortabilityTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@

#include <folly/portability/GTest.h>

#if FOLLY_HAS_CONCEPTS
#include <concepts>
#endif

class Base {
public:
virtual ~Base() {}
Expand Down Expand Up @@ -52,6 +56,6 @@ TEST(Portability, Final) {
EXPECT_EQ(3, fooDerived(p.get()));
}

#if __has_include(<range>)
static_assert(std::ranges::random_access_range<std::vector<int>>);
#if FOLLY_HAS_CONCEPTS
static_assert(std::same_as<int, decltype(3)>);
#endif
35 changes: 35 additions & 0 deletions folly/test/TraitsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,21 @@ TEST(Traits, isInstantiationOf) {
EXPECT_FALSE((detail::is_instantiation_of<A, B>::value));
}

TEST(Traits, InstantiationOf) {
#if FOLLY_HAS_CONCEPTS
static_assert(folly::instantiation_of<A<int>, A>);
static_assert(folly::instantiation_of<A<int>&, A>);
static_assert(!folly::instantiation_of<A<int>&, std::vector>);

auto example = [](folly::instantiation_of<std::vector> auto&&) {};

static_assert(std::invocable<decltype(example), std::vector<int>&&>);
static_assert(std::invocable<decltype(example), std::vector<int>&>);
static_assert(std::invocable<decltype(example), const std::vector<int>&>);
static_assert(std::invocable<decltype(example), std::vector<int>>);
#endif
}

TEST(Traits, isSimilarInstantiationV) {
EXPECT_TRUE((detail::is_similar_instantiation_v<A<int>, A<long>>));
EXPECT_FALSE((detail::is_similar_instantiation_v<A<int>, tag_t<int>>));
Expand Down Expand Up @@ -760,3 +775,23 @@ TEST(Traits, value_list) {
folly::value_list_element_type_t<1, vtag_t<7u, 8, '9'>>>));
EXPECT_EQ(8, (folly::value_list_element_v<1, vtag_t<7u, 8, '9'>>));
}

TEST(Traits, Forwarded) {
#if FOLLY_HAS_CONCEPTS
static_assert(folly::forwarded<std::vector<int>, std::vector<int>>);
static_assert(folly::forwarded<std::vector<int>&, std::vector<int>>);
static_assert(folly::forwarded<const std::vector<int>&, std::vector<int>>);
static_assert(folly::forwarded<std::vector<int>&&, std::vector<int>>);

constexpr auto forwardedExample =
[](folly::forwarded<std::vector<int>> auto&&) {};

static_assert(std::invocable<decltype(forwardedExample), std::vector<int>>);
static_assert(
std::invocable<decltype(forwardedExample), const std::vector<int>&>);
static_assert(std::invocable<decltype(forwardedExample), std::vector<int>&&>);

static_assert(
!std::invocable<decltype(forwardedExample), std::vector<char>&&>);
#endif
}

0 comments on commit 602ba5b

Please sign in to comment.