Commit 74bf4289 authored by Kirk Shoop's avatar Kirk Shoop Committed by Facebook Github Bot

port properties from unifex

Summary: ported the properties header from unifex and added some simple compile tests

Reviewed By: ericniebler

Differential Revision: D15144375

fbshipit-source-id: 580f8c8977abd070333779898f3ac47510932f87
parent 4f1d8454
...@@ -122,12 +122,15 @@ PUSHMI_PP_IGNORE_CXX2A_COMPAT_BEGIN ...@@ -122,12 +122,15 @@ PUSHMI_PP_IGNORE_CXX2A_COMPAT_BEGIN
#define PUSHMI_PP_EVAL2(X, ...) PUSHMI_PP_VA_ARGS_EXPANDER2(X(__VA_ARGS__)) #define PUSHMI_PP_EVAL2(X, ...) PUSHMI_PP_VA_ARGS_EXPANDER2(X(__VA_ARGS__))
#define PUSHMI_PP_EXPAND_(...) __VA_ARGS__ #define PUSHMI_PP_EXPAND_(...) __VA_ARGS__
#define PUSHMI_PP_EXPAND(...) PUSHMI_PP_VA_ARGS_EXPANDER_INNER(PUSHMI_PP_EXPAND_(__VA_ARGS__)) #define PUSHMI_PP_EXPAND(...) \
PUSHMI_PP_VA_ARGS_EXPANDER_INNER(PUSHMI_PP_EXPAND_(__VA_ARGS__))
#define PUSHMI_PP_EXPAND2_(...) __VA_ARGS__ #define PUSHMI_PP_EXPAND2_(...) __VA_ARGS__
#define PUSHMI_PP_EXPAND2(...) PUSHMI_PP_VA_ARGS_EXPANDER2_INNER(PUSHMI_PP_EXPAND2_(__VA_ARGS__)) #define PUSHMI_PP_EXPAND2(...) \
PUSHMI_PP_VA_ARGS_EXPANDER2_INNER(PUSHMI_PP_EXPAND2_(__VA_ARGS__))
#define PUSHMI_PP_CHECK(...) PUSHMI_PP_CHECK_(__VA_ARGS__, 0,) #define PUSHMI_PP_CHECK(...) PUSHMI_PP_CHECK_(__VA_ARGS__, 0, )
#define PUSHMI_PP_CHECK_(...) PUSHMI_PP_VA_ARGS_EXPANDER_INNER(PUSHMI_PP_CHECK_N(__VA_ARGS__)) #define PUSHMI_PP_CHECK_(...) \
PUSHMI_PP_VA_ARGS_EXPANDER_INNER(PUSHMI_PP_CHECK_N(__VA_ARGS__))
#define PUSHMI_PP_CHECK_N(x, n, ...) n #define PUSHMI_PP_CHECK_N(x, n, ...) n
#define PUSHMI_PP_PROBE(x) x, 1, #define PUSHMI_PP_PROBE(x) x, 1,
#endif #endif
...@@ -562,7 +565,8 @@ struct Not { ...@@ -562,7 +565,8 @@ struct Not {
template <class T, class U> template <class T, class U>
struct And { struct And {
explicit constexpr operator bool() const noexcept { explicit constexpr operator bool() const noexcept {
return static_cast<bool>(std::conditional_t<static_cast<bool>(T{}), U, std::false_type>{}); return static_cast<bool>(
std::conditional_t<static_cast<bool>(T{}), U, std::false_type>{});
} }
constexpr auto operator!() const noexcept { constexpr auto operator!() const noexcept {
return Not<And>{}; return Not<And>{};
...@@ -580,7 +584,8 @@ struct And { ...@@ -580,7 +584,8 @@ struct And {
template <class T, class U> template <class T, class U>
struct Or { struct Or {
explicit constexpr operator bool() const noexcept { explicit constexpr operator bool() const noexcept {
return static_cast<bool>(std::conditional_t<static_cast<bool>(T{}), std::true_type, U>{}); return static_cast<bool>(
std::conditional_t<static_cast<bool>(T{}), std::true_type, U>{});
} }
constexpr auto operator!() const noexcept { constexpr auto operator!() const noexcept {
return Not<Or>{}; return Not<Or>{};
......
...@@ -34,20 +34,27 @@ namespace pushmi { ...@@ -34,20 +34,27 @@ namespace pushmi {
/* using override */ using folly::is_nothrow_invocable_r; /* using override */ using folly::is_nothrow_invocable_r;
PUSHMI_CONCEPT_DEF( PUSHMI_CONCEPT_DEF(
template (class F, class... Args) template(class F, class... Args) //
(concept Invocable)(F, Args...), (concept Invocable)(F, Args...), //
requires(F&& f) ( requires(F&& f)( //
pushmi::invoke((F &&) f, std::declval<Args>()...) pushmi::invoke((F &&) f, std::declval<Args>()...) //
) ) //
); );
PUSHMI_CONCEPT_DEF( PUSHMI_CONCEPT_DEF(
template (class F, class... Args) template(class F, class Ret, class... Args) //
(concept NothrowInvocable)(F, Args...), (concept _InvocableR)(F, Ret, Args...), //
requires(F&& f) ( Invocable<F, Args...>&& ConvertibleTo<invoke_result_t<F, Args...>, Ret> //
requires_<noexcept(pushmi::invoke((F &&) f, std::declval<Args>()...))> );
PUSHMI_CONCEPT_DEF(
template(class F, class... Args) //
(concept NothrowInvocable)(F, Args...), //
requires(F&& f)( //
requires_<
noexcept(pushmi::invoke((F &&) f, std::declval<Args>()...))> //
) && ) &&
Invocable<F, Args...> Invocable<F, Args...> //
); );
// //
...@@ -68,11 +75,11 @@ using deduced_type_t = invoke_result_t<construct_deduced<T>, AN...>; ...@@ -68,11 +75,11 @@ using deduced_type_t = invoke_result_t<construct_deduced<T>, AN...>;
// inspired by Ovrld - shown in a presentation by Nicolai Josuttis // inspired by Ovrld - shown in a presentation by Nicolai Josuttis
#if __cpp_variadic_using >= 201611 && __cpp_concepts #if __cpp_variadic_using >= 201611 && __cpp_concepts
template <SemiMovable... Fns> template <SemiMovable... Fns>
requires sizeof...(Fns) > 0 requires sizeof...(Fns) > 0 //
struct overload_fn : Fns... { struct overload_fn : Fns... {
constexpr overload_fn() = default; constexpr overload_fn() = default;
constexpr explicit overload_fn(Fns... fns) requires sizeof...(Fns) == 1 constexpr explicit overload_fn(Fns... fns) //
: Fns(std::move(fns))... {} requires sizeof...(Fns) == 1 : Fns(std::move(fns))... {}
constexpr overload_fn(Fns... fns) requires sizeof...(Fns) > 1 constexpr overload_fn(Fns... fns) requires sizeof...(Fns) > 1
: Fns(std::move(fns))... {} : Fns(std::move(fns))... {}
using Fns::operator()...; using Fns::operator()...;
...@@ -82,12 +89,11 @@ template <PUSHMI_TYPE_CONSTRAINT(SemiMovable)... Fns> ...@@ -82,12 +89,11 @@ template <PUSHMI_TYPE_CONSTRAINT(SemiMovable)... Fns>
#if __cpp_concepts #if __cpp_concepts
requires sizeof...(Fns) > 0 requires sizeof...(Fns) > 0
#endif #endif
struct overload_fn; struct overload_fn;
template <class Fn> template <class Fn>
struct overload_fn<Fn> : Fn { struct overload_fn<Fn> : Fn {
constexpr overload_fn() = default; constexpr overload_fn() = default;
constexpr explicit overload_fn(Fn fn) constexpr explicit overload_fn(Fn fn) : Fn(std::move(fn)) {}
: Fn(std::move(fn)) {}
constexpr overload_fn(overload_fn&&) = default; constexpr overload_fn(overload_fn&&) = default;
constexpr overload_fn& operator=(overload_fn&&) = default; constexpr overload_fn& operator=(overload_fn&&) = default;
constexpr overload_fn(const overload_fn&) = default; constexpr overload_fn(const overload_fn&) = default;
...@@ -110,11 +116,12 @@ struct overload_fn<Fn, Fns...> : Fn, overload_fn<Fns...> { ...@@ -110,11 +116,12 @@ struct overload_fn<Fn, Fns...> : Fn, overload_fn<Fns...> {
#else #else
template <class Fn, class... Fns> template <class Fn, class... Fns>
struct overload_fn<Fn, Fns...> { struct overload_fn<Fn, Fns...> {
private: private:
std::pair<Fn, overload_fn<Fns...>> fns_; std::pair<Fn, overload_fn<Fns...>> fns_;
template <bool B> template <bool B>
using _which_t = std::conditional_t<B, Fn, overload_fn<Fns...>>; using _which_t = std::conditional_t<B, Fn, overload_fn<Fns...>>;
public:
public:
constexpr overload_fn() = default; constexpr overload_fn() = default;
constexpr overload_fn(Fn fn, Fns... fns) constexpr overload_fn(Fn fn, Fns... fns)
: fns_{std::move(fn), overload_fn<Fns...>{std::move(fns)...}} {} : fns_{std::move(fn), overload_fn<Fns...>{std::move(fns)...}} {}
...@@ -122,18 +129,22 @@ public: ...@@ -122,18 +129,22 @@ public:
constexpr overload_fn& operator=(overload_fn&&) = default; constexpr overload_fn& operator=(overload_fn&&) = default;
constexpr overload_fn(const overload_fn&) = default; constexpr overload_fn(const overload_fn&) = default;
constexpr overload_fn& operator=(const overload_fn&) = default; constexpr overload_fn& operator=(const overload_fn&) = default;
PUSHMI_TEMPLATE (class... Args) PUSHMI_TEMPLATE(class... Args) //
(requires lazy::Invocable<Fn&, Args...> || (requires lazy::Invocable<Fn&, Args...> ||
lazy::Invocable<overload_fn<Fns...>&, Args...>) lazy::Invocable<overload_fn<Fns...>&, Args...>) //
decltype(auto) operator()(Args &&... args) noexcept(noexcept( decltype(auto)
std::declval<_which_t<Invocable<Fn&, Args...>>&>()(std::declval<Args>()...))) { operator()(Args&&... args) noexcept(
noexcept(std::declval<_which_t<Invocable<Fn&, Args...>>&>()(
std::declval<Args>()...))) {
return std::get<!Invocable<Fn&, Args...>>(fns_)((Args &&) args...); return std::get<!Invocable<Fn&, Args...>>(fns_)((Args &&) args...);
} }
PUSHMI_TEMPLATE (class... Args) PUSHMI_TEMPLATE(class... Args) //
(requires lazy::Invocable<const Fn&, Args...> || (requires lazy::Invocable<const Fn&, Args...> ||
lazy::Invocable<const overload_fn<Fns...>&, Args...>) lazy::Invocable<const overload_fn<Fns...>&, Args...>) //
decltype(auto) operator()(Args &&... args) const noexcept(noexcept( decltype(auto)
std::declval<const _which_t<Invocable<const Fn&, Args...>>&>()(std::declval<Args>()...))) { operator()(Args&&... args) const noexcept(noexcept(
std::declval<const _which_t<Invocable<const Fn&, Args...>>&>()(
std::declval<Args>()...))) {
return std::get<!Invocable<const Fn&, Args...>>(fns_)((Args &&) args...); return std::get<!Invocable<const Fn&, Args...>>(fns_)((Args &&) args...);
} }
}; };
......
...@@ -23,33 +23,33 @@ namespace folly { ...@@ -23,33 +23,33 @@ namespace folly {
namespace pushmi { namespace pushmi {
PUSHMI_CONCEPT_DEF( PUSHMI_CONCEPT_DEF(
template (class T) template(class T) //
concept Object, concept Object,
requires (T* p) ( requires(T* p) ( //
*p, *p, implicitly_convertible_to<const volatile void*>(p))//
implicitly_convertible_to<const volatile void*>(p) );
)
);
PUSHMI_CONCEPT_DEF( PUSHMI_CONCEPT_DEF(
template (class T, class... Args) template(class T, class... Args) //
(concept Constructible)(T, Args...), (concept Constructible)(T, Args...),
PUSHMI_PP_IS_CONSTRUCTIBLE(T, Args...) PUSHMI_PP_IS_CONSTRUCTIBLE(T, Args...));
);
PUSHMI_CONCEPT_DEF( PUSHMI_CONCEPT_DEF(
template (class From, class To) template(class From, class To) //
concept ExplicitlyConvertibleTo,
requires(From (&f)()) ( //
static_cast<To>(f()))
);
PUSHMI_CONCEPT_DEF(
template(class From, class To) //
concept ConvertibleTo, concept ConvertibleTo,
requires (From (&f)()) ( ExplicitlyConvertibleTo<From, To>&& std::is_convertible<From, To>::value);
static_cast<To>(f())
) && std::is_convertible<From, To>::value
);
PUSHMI_CONCEPT_DEF( PUSHMI_CONCEPT_DEF(
template (class T) template(class T) //
concept SemiMovable, concept SemiMovable,
Object<T> && Constructible<T, T> && ConvertibleTo<T, T> Object<T>&& Constructible<T, T>&& ConvertibleTo<T, T>);
);
} // namespace pushmi } // namespace pushmi
} // namespace folly } // namespace folly
...@@ -17,28 +17,10 @@ ...@@ -17,28 +17,10 @@
#include <exception> #include <exception>
#include <folly/experimental/pushmi/traits.h>
namespace folly { namespace folly {
namespace pushmi { namespace pushmi {
namespace detail {
// inherit: a class that inherits from a bunch of bases
template <class... Ts>
struct inherit : Ts... {
inherit() = default;
constexpr inherit(Ts... ts)
: Ts((Ts&&) ts)...
{}
};
template <class T>
struct inherit<T> : T {
inherit() = default;
explicit constexpr inherit(T t)
: T((T&&) t)
{}
};
template <>
struct inherit<>
{};
} // namespace detail
namespace awaitable_senders { namespace awaitable_senders {
struct sender_adl_hook { struct sender_adl_hook {
......
This diff is collapsed.
/*
* Copyright 2019-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/experimental/pushmi/tag_invoke.h>
#include <folly/experimental/pushmi/detail/if_constexpr.h>
using namespace folly::pushmi;
#include <folly/portability/GMock.h>
#include <folly/portability/GTest.h>
using namespace testing;
namespace {
struct executor_tag {};
template <class... Cpos>
class any_ex : private any_tag_invoke_base<any_ex<Cpos...>, Cpos...>,
public executor_tag {
using base_t = any_tag_invoke_base<any_ex<Cpos...>, Cpos...>;
friend base_t;
public:
any_ex() = default;
using base_t::base_t;
using base_t::operator bool;
};
enum struct foo_kind {
small = -1,
unspecified,
big,
};
// A CPO for querying what kind of foo an executor reports.
namespace __foo {
template <foo_kind K>
using __kind = std::integral_constant<foo_kind, K>;
template <foo_kind K>
struct __enum;
struct __cpo;
PUSHMI_TEMPLATE(class K)
(requires //
ConvertibleTo<K, foo_kind>) //
struct __cpo_impl : typed_tag_function<K() const> {
PUSHMI_TEMPLATE(
class Ex,
class CPO = __cpo,
class Default = __enum<foo_kind::unspecified>)
(requires //
DerivedFrom<Ex, executor_tag>) //
static constexpr auto __foo(PUSHMI_CEXP_MAYBE_UNUSED Ex const& ex) {
PUSHMI_IF_CONSTEXPR_RETURN((TagInvocableR<CPO, K, Ex const&>)( //
return tag_invoke(CPO{}, id(ex)); //
) else( //
return id(ex), Default{}; //
));
}
PUSHMI_TEMPLATE(class Ex)
(requires //
ConvertibleTo<decltype(__cpo_impl::__foo(std::declval<Ex const&>())), K>&&
DerivedFrom<Ex, executor_tag>) //
constexpr auto
operator()(Ex const& ex) const {
return __cpo_impl::__foo(ex);
}
};
template <foo_kind K>
struct __enum : __cpo_impl<__kind<K>>, __kind<K> {};
struct __cpo : __cpo_impl<foo_kind> {
static PUSHMI_INLINE_VAR constexpr __enum<foo_kind::unspecified> const
unspecified{};
static PUSHMI_INLINE_VAR constexpr __enum<foo_kind::big> const big{};
static PUSHMI_INLINE_VAR constexpr __enum<foo_kind::small> const small{};
static_assert(
unspecified != big && unspecified != small,
"avoid unused variable warnings and errors reported when maybe unused is applied.");
};
} // namespace __foo
PUSHMI_INLINE_VAR constexpr __foo::__cpo foo{};
struct destroyer_impl : typed_tag_function<void() &&> {
PUSHMI_TEMPLATE(class Ex)
(requires //
DerivedFrom<remove_cvref_t<Ex>, executor_tag> //
) //
constexpr auto
operator()(Ex&& ex) const {
return tag_invoke(*this, (Ex &&) ex);
}
};
PUSHMI_INLINE_VAR constexpr destroyer_impl destroyer{};
// Test an executor to see whether the kind of foo is known at compile-
// time.
PUSHMI_CONCEPT_DEF(
template(class Ex, foo_kind K) //
(concept _FooExecutor)(Ex, K), //
ConvertibleTo<
decltype(foo(std::declval<std::decay_t<Ex> const&>())),
__foo::__kind<K>>&& DerivedFrom<Ex, executor_tag>);
static_assert(
lazy::_FooExecutor<executor_tag, foo_kind::unspecified>,
"failed to use the concept");
static_assert(
_FooExecutor<executor_tag, foo_kind::unspecified>,
"failed to use the concept");
} // namespace
struct small_bar : executor_tag {
void baz() {}
private:
friend constexpr auto tag_invoke(PUSHMI_TAG_OF(foo), const small_bar&) noexcept {
return foo.small;
}
};
struct big_bar : executor_tag {
void baz() {}
private:
friend constexpr auto tag_invoke(PUSHMI_TAG_OF(foo), const big_bar&) noexcept {
return foo.big;
}
friend constexpr auto tag_invoke(PUSHMI_TAG_OF(destroyer), big_bar&&) noexcept {}
};
static_assert(TagFunction<PUSHMI_TAG_OF(foo)>, "foo must be a tag_function");
static_assert(
Same<PUSHMI_TAG_OF(foo(big_bar{})), PUSHMI_TAG_OF(foo.big)>,
"foo(big_bar) must be foo.big");
static_assert(
Same<PUSHMI_TAG_OF(foo(small_bar{})), PUSHMI_TAG_OF(foo.small)>,
"foo(small_bar) must be foo.small");
static_assert(
!InDomainOf<int&, PUSHMI_TAG_OF(foo)>,
"foo must not be applicable to int");
static_assert(
InDomainOf<executor_tag&, PUSHMI_TAG_OF(foo)>,
"foo must be applicable to executor_tag");
static_assert(
InDomainOf<big_bar&, PUSHMI_TAG_OF(foo)>,
"foo must be applicable to big_bar");
static_assert(
InDomainOf<small_bar&, PUSHMI_TAG_OF(foo)>,
"foo must be applicable to small_bar");
static_assert(
InDomainOf<any_ex<PUSHMI_TAG_OF(foo)>&, PUSHMI_TAG_OF(foo)>,
"foo must be applicable to any_ex<foo>");
static_assert(
InDomainOf<any_ex<>&, PUSHMI_TAG_OF(foo)>,
"foo must be applicable to any_ex<>");
static_assert(
_FooExecutor<any_ex<>, foo_kind::unspecified>,
"any_ex<> must be foo of kind unspecified at compile-time");
static_assert(
_FooExecutor<executor_tag, foo_kind::unspecified>,
"executor_tag must be foo of kind unspecified at compile-time");
static_assert(
_FooExecutor<big_bar, foo_kind::big>,
"big_bar must be foo of kind big at compile-time");
static_assert(
_FooExecutor<small_bar, foo_kind::small>,
"small_bar must be foo of kind small at compile-time");
static_assert(
Constructible<any_ex<PUSHMI_TAG_OF(foo)>, small_bar>,
"any_ex<PUSHMI_TAG_OF(foo)> must be constructible from small_bar");
static_assert(
Constructible<any_ex<>, small_bar>,
"any_ex<> must be constructible from small_bar");
static_assert(
Constructible<any_ex<PUSHMI_TAG_OF(foo)>, big_bar>,
"any_ex<PUSHMI_TAG_OF(foo)> must be constructible from big_bar");
static_assert(
Constructible<any_ex<>, big_bar>,
"any_ex<> must be constructible from big_bar");
static_assert(
!Constructible<any_ex<PUSHMI_TAG_OF(foo)>, int>,
"any_ex<PUSHMI_TAG_OF(foo)> must not be constructible from int");
static_assert(
Invocable<PUSHMI_TAG_OF(foo), any_ex<PUSHMI_TAG_OF(foo)>>,
"foo must be invocable with any_ex<PUSHMI_TAG_OF(foo)>");
static_assert(
TagInvocable<PUSHMI_TAG_OF(foo), any_ex<PUSHMI_TAG_OF(foo)>>,
"foo must be pinvocable with any_ex<PUSHMI_TAG_OF(foo)>");
TEST(TagInvokeTest, TagInvokeFooOnTypeErasedSmallBar) {
auto af0 = any_ex<PUSHMI_TAG_OF(foo)>{};
EXPECT_THAT((!!af0), Eq(false));
af0 = any_ex<PUSHMI_TAG_OF(foo)>{small_bar{}};
EXPECT_THAT((!!af0), Eq(true));
EXPECT_THAT((foo(af0)), Eq(foo.small));
EXPECT_THAT((!!af0), Eq(true));
}
TEST(TagInvokeTest, TagInvokeFooOnTypeErasedBigBar) {
auto af0 = any_ex<PUSHMI_TAG_OF(foo)>{big_bar{}};
EXPECT_THAT((foo(af0)), Eq(foo.big));
}
TEST(TagInvokeTest, TagInvokeDestroyerOnTypeErasedBigBa) {
auto af0 = any_ex<PUSHMI_TAG_OF(foo), PUSHMI_TAG_OF(destroyer)>{big_bar{}};
EXPECT_THAT((foo(af0)), Eq(foo.big));
destroyer(std::move(af0));
EXPECT_THAT((!!af0), Eq(false));
}
...@@ -190,6 +190,14 @@ constexpr bool is_v = is_<T, C>::value; ...@@ -190,6 +190,14 @@ constexpr bool is_v = is_<T, C>::value;
template <bool B, class T = void> template <bool B, class T = void>
using requires_ = std::enable_if_t<B, T>; using requires_ = std::enable_if_t<B, T>;
template <class T, class Self, class Derived = Self>
PUSHMI_PP_CONSTRAINED_USING(
static_cast<bool>(
not DerivedFrom<remove_cvref_t<T>, remove_cvref_t<Derived>> &&
not Same<remove_cvref_t<T>, remove_cvref_t<Self>>),
not_self_t =,
T);
template <bool> template <bool>
struct Enable_ {}; struct Enable_ {};
template <> template <>
...@@ -218,6 +226,21 @@ using identity_t = typename Enable_<sizeof...(Ts) == 1u>:: ...@@ -218,6 +226,21 @@ using identity_t = typename Enable_<sizeof...(Ts) == 1u>::
template<class...Ts> template<class...Ts>
using identity_or_void_t = typename Enable_<sizeof...(Ts) <= 1u>:: using identity_or_void_t = typename Enable_<sizeof...(Ts) <= 1u>::
template _type<FrontOrVoid_<Ts...>>::type; template _type<FrontOrVoid_<Ts...>>::type;
// inherit: a class that inherits from a bunch of bases
template <class... Ts>
struct inherit : Ts... {
inherit() = default;
constexpr inherit(Ts... ts) : Ts((Ts &&) ts)... {}
};
template <class T>
struct inherit<T> : T {
inherit() = default;
explicit constexpr inherit(T t) : T((T &&) t) {}
};
template <>
struct inherit<> {};
} // namespace detail } // namespace detail
} // namespace pushmi } // namespace pushmi
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment