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
......@@ -34,20 +34,27 @@ namespace pushmi {
/* using override */ using folly::is_nothrow_invocable_r;
PUSHMI_CONCEPT_DEF(
template (class F, class... Args)
(concept Invocable)(F, Args...),
requires(F&& f) (
pushmi::invoke((F &&) f, std::declval<Args>()...)
)
template(class F, class... Args) //
(concept Invocable)(F, Args...), //
requires(F&& f)( //
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...>
template(class F, class Ret, class... Args) //
(concept _InvocableR)(F, Ret, Args...), //
Invocable<F, Args...>&& ConvertibleTo<invoke_result_t<F, Args...>, Ret> //
);
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...> //
);
//
......@@ -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
#if __cpp_variadic_using >= 201611 && __cpp_concepts
template <SemiMovable... Fns>
requires sizeof...(Fns) > 0
struct overload_fn : Fns... {
requires sizeof...(Fns) > 0 //
struct overload_fn : Fns... {
constexpr overload_fn() = default;
constexpr explicit overload_fn(Fns... fns) requires sizeof...(Fns) == 1
: Fns(std::move(fns))... {}
constexpr explicit overload_fn(Fns... fns) //
requires sizeof...(Fns) == 1 : Fns(std::move(fns))... {}
constexpr overload_fn(Fns... fns) requires sizeof...(Fns) > 1
: Fns(std::move(fns))... {}
using Fns::operator()...;
......@@ -80,14 +87,13 @@ struct overload_fn : Fns... {
#else
template <PUSHMI_TYPE_CONSTRAINT(SemiMovable)... Fns>
#if __cpp_concepts
requires sizeof...(Fns) > 0
requires sizeof...(Fns) > 0
#endif
struct overload_fn;
struct overload_fn;
template <class Fn>
struct overload_fn<Fn> : Fn {
constexpr overload_fn() = default;
constexpr explicit overload_fn(Fn fn)
: Fn(std::move(fn)) {}
constexpr explicit overload_fn(Fn fn) : Fn(std::move(fn)) {}
constexpr overload_fn(overload_fn&&) = default;
constexpr overload_fn& operator=(overload_fn&&) = default;
constexpr overload_fn(const overload_fn&) = default;
......@@ -110,11 +116,12 @@ struct overload_fn<Fn, Fns...> : Fn, overload_fn<Fns...> {
#else
template <class Fn, class... Fns>
struct overload_fn<Fn, Fns...> {
private:
private:
std::pair<Fn, overload_fn<Fns...>> fns_;
template <bool B>
using _which_t = std::conditional_t<B, Fn, overload_fn<Fns...>>;
public:
public:
constexpr overload_fn() = default;
constexpr overload_fn(Fn fn, Fns... fns)
: fns_{std::move(fn), overload_fn<Fns...>{std::move(fns)...}} {}
......@@ -122,18 +129,22 @@ public:
constexpr overload_fn& operator=(overload_fn&&) = default;
constexpr overload_fn(const overload_fn&) = default;
constexpr overload_fn& operator=(const overload_fn&) = default;
PUSHMI_TEMPLATE (class... Args)
(requires lazy::Invocable<Fn&, Args...> ||
lazy::Invocable<overload_fn<Fns...>&, Args...>)
decltype(auto) operator()(Args &&... args) noexcept(noexcept(
std::declval<_which_t<Invocable<Fn&, Args...>>&>()(std::declval<Args>()...))) {
PUSHMI_TEMPLATE(class... Args) //
(requires lazy::Invocable<Fn&, Args...> ||
lazy::Invocable<overload_fn<Fns...>&, Args...>) //
decltype(auto)
operator()(Args&&... args) noexcept(
noexcept(std::declval<_which_t<Invocable<Fn&, Args...>>&>()(
std::declval<Args>()...))) {
return std::get<!Invocable<Fn&, Args...>>(fns_)((Args &&) args...);
}
PUSHMI_TEMPLATE (class... Args)
(requires lazy::Invocable<const Fn&, Args...> ||
lazy::Invocable<const overload_fn<Fns...>&, Args...>)
decltype(auto) operator()(Args &&... args) const noexcept(noexcept(
std::declval<const _which_t<Invocable<const Fn&, Args...>>&>()(std::declval<Args>()...))) {
PUSHMI_TEMPLATE(class... Args) //
(requires lazy::Invocable<const Fn&, Args...> ||
lazy::Invocable<const overload_fn<Fns...>&, Args...>) //
decltype(auto)
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...);
}
};
......
......@@ -23,33 +23,33 @@ namespace folly {
namespace pushmi {
PUSHMI_CONCEPT_DEF(
template (class T)
concept Object,
requires (T* p) (
*p,
implicitly_convertible_to<const volatile void*>(p)
)
);
template(class T) //
concept Object,
requires(T* p) ( //
*p, implicitly_convertible_to<const volatile void*>(p))//
);
PUSHMI_CONCEPT_DEF(
template (class T, class... Args)
(concept Constructible)(T, Args...),
PUSHMI_PP_IS_CONSTRUCTIBLE(T, Args...)
);
template(class T, class... Args) //
(concept Constructible)(T, Args...),
PUSHMI_PP_IS_CONSTRUCTIBLE(T, Args...));
PUSHMI_CONCEPT_DEF(
template (class From, class To)
concept ConvertibleTo,
requires (From (&f)()) (
static_cast<To>(f())
) && std::is_convertible<From, To>::value
);
template(class From, class To) //
concept ExplicitlyConvertibleTo,
requires(From (&f)()) ( //
static_cast<To>(f()))
);
PUSHMI_CONCEPT_DEF(
template (class T)
concept SemiMovable,
Object<T> && Constructible<T, T> && ConvertibleTo<T, T>
);
template(class From, class To) //
concept ConvertibleTo,
ExplicitlyConvertibleTo<From, To>&& std::is_convertible<From, To>::value);
PUSHMI_CONCEPT_DEF(
template(class T) //
concept SemiMovable,
Object<T>&& Constructible<T, T>&& ConvertibleTo<T, T>);
} // namespace pushmi
} // namespace folly
......@@ -17,28 +17,10 @@
#include <exception>
#include <folly/experimental/pushmi/traits.h>
namespace folly {
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 {
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;
template <bool B, class T = void>
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>
struct Enable_ {};
template <>
......@@ -218,6 +226,21 @@ using identity_t = typename Enable_<sizeof...(Ts) == 1u>::
template<class...Ts>
using identity_or_void_t = typename Enable_<sizeof...(Ts) <= 1u>::
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 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