forked from OSchip/llvm-project
[libcxx] P0604, invoke_result and is_invocable
Summary: Introduce a new form of `result_of` without function type encoding. Rename and split `is_callable/is_nothrow_callable` into `is_invocable/is_nothrow_invocable/is_invocable_r/is_nothrow_invocable_r` (and associated types accordingly) Change function type encoding of previous `is_callable/is_nothrow_callable` traits to conventional template type parameter lists. Reviewers: EricWF, mclow.lists, bebuch Reviewed By: EricWF, bebuch Subscribers: lichray, bebuch, cfe-commits Differential Revision: https://reviews.llvm.org/D38831 llvm-svn: 320509
This commit is contained in:
parent
81a4a02cbc
commit
bcde6e715e
|
@ -137,13 +137,11 @@ namespace std
|
|||
template <class Base, class Derived> struct is_base_of;
|
||||
template <class From, class To> struct is_convertible;
|
||||
|
||||
template <class, class R = void> struct is_callable; // not defined
|
||||
template <class Fn, class... ArgTypes, class R>
|
||||
struct is_callable<Fn(ArgTypes...), R>;
|
||||
template <class Fn, class... ArgTypes> struct is_invocable;
|
||||
template <class R, class Fn, class... ArgTypes> struct is_invocable_r;
|
||||
|
||||
template <class, class R = void> struct is_nothrow_callable; // not defined
|
||||
template <class Fn, class... ArgTypes, class R>
|
||||
struct is_nothrow_callable<Fn(ArgTypes...), R>;
|
||||
template <class Fn, class... ArgTypes> struct is_nothrow_invocable;
|
||||
template <class R, class Fn, class... ArgTypes> struct is_nothrow_invocable_r;
|
||||
|
||||
// Alignment properties and transformations:
|
||||
template <class T> struct alignment_of;
|
||||
|
@ -157,6 +155,7 @@ namespace std
|
|||
template <class T> struct underlying_type;
|
||||
template <class> class result_of; // undefined
|
||||
template <class Fn, class... ArgTypes> class result_of<Fn(ArgTypes...)>;
|
||||
template <class Fn, class... ArgTypes> struct invoke_result; // C++17
|
||||
|
||||
// const-volatile modifications:
|
||||
template <class T>
|
||||
|
@ -215,8 +214,10 @@ namespace std
|
|||
using common_type_t = typename common_type<T...>::type; // C++14
|
||||
template <class T>
|
||||
using underlying_type_t = typename underlying_type<T>::type; // C++14
|
||||
template <class F, class... ArgTypes>
|
||||
using result_of_t = typename result_of<F(ArgTypes...)>::type; // C++14
|
||||
template <class T>
|
||||
using result_of_t = typename result_of<T>::type; // C++14
|
||||
template <class Fn, class... ArgTypes>
|
||||
using invoke_result_t = typename invoke_result<Fn, ArgTypes...>::type; // C++17
|
||||
|
||||
template <class...>
|
||||
using void_t = void; // C++17
|
||||
|
@ -370,10 +371,14 @@ namespace std
|
|||
= is_base_of<Base, Derived>::value; // C++17
|
||||
template <class From, class To> constexpr bool is_convertible_v
|
||||
= is_convertible<From, To>::value; // C++17
|
||||
template <class T, class R = void> constexpr bool is_callable_v
|
||||
= is_callable<T, R>::value; // C++17
|
||||
template <class T, class R = void> constexpr bool is_nothrow_callable_v
|
||||
= is_nothrow_callable<T, R>::value; // C++17
|
||||
template <class Fn, class... ArgTypes> constexpr bool is_invocable_v
|
||||
= is_invocable<Fn, ArgTypes...>::value; // C++17
|
||||
template <class R, class Fn, class... ArgTypes> constexpr bool is_invocable_r_v
|
||||
= is_invocable_r<R, Fn, ArgTypes...>::value; // C++17
|
||||
template <class Fn, class... ArgTypes> constexpr bool is_nothrow_invocable_v
|
||||
= is_nothrow_invocable<Fn, ArgTypes...>::value; // C++17
|
||||
template <class R, class Fn, class... ArgTypes> constexpr bool is_nothrow_invocable_r_v
|
||||
= is_nothrow_invocable_r<R, Fn, ArgTypes...>::value; // C++17
|
||||
|
||||
// [meta.logical], logical operator traits:
|
||||
template<class... B> struct conjunction; // C++17
|
||||
|
@ -4401,6 +4406,13 @@ using __nothrow_invokable_r =
|
|||
_Ret, _Fp, _Args...
|
||||
>;
|
||||
|
||||
template <class _Fp, class ..._Args>
|
||||
using __nothrow_invokable =
|
||||
__nothrow_invokable_r_imp<
|
||||
__invokable<_Fp, _Args...>::value,
|
||||
true, void, _Fp, _Args...
|
||||
>;
|
||||
|
||||
template <class _Fp, class ..._Args>
|
||||
struct __invoke_of
|
||||
: public enable_if<
|
||||
|
@ -4423,30 +4435,48 @@ template <class _Tp> using result_of_t = typename result_of<_Tp>::type;
|
|||
|
||||
#if _LIBCPP_STD_VER > 14
|
||||
|
||||
// is_callable
|
||||
// invoke_result
|
||||
|
||||
template <class _Fn, class _Ret = void>
|
||||
struct _LIBCPP_TEMPLATE_VIS is_callable;
|
||||
template <class _Fn, class... _Args>
|
||||
struct _LIBCPP_TEMPLATE_VIS invoke_result
|
||||
: __invoke_of<_Fn, _Args...>
|
||||
{
|
||||
};
|
||||
|
||||
template <class _Fn, class ..._Args, class _Ret>
|
||||
struct _LIBCPP_TEMPLATE_VIS is_callable<_Fn(_Args...), _Ret>
|
||||
template <class _Fn, class... _Args>
|
||||
using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;
|
||||
|
||||
// is_invocable
|
||||
|
||||
template <class _Fn, class ..._Args>
|
||||
struct _LIBCPP_TEMPLATE_VIS is_invocable
|
||||
: integral_constant<bool, __invokable<_Fn, _Args...>::value> {};
|
||||
|
||||
template <class _Ret, class _Fn, class ..._Args>
|
||||
struct _LIBCPP_TEMPLATE_VIS is_invocable_r
|
||||
: integral_constant<bool, __invokable_r<_Ret, _Fn, _Args...>::value> {};
|
||||
|
||||
template <class _Fn, class _Ret = void>
|
||||
constexpr bool is_callable_v = is_callable<_Fn, _Ret>::value;
|
||||
template <class _Fn, class ..._Args>
|
||||
constexpr bool is_invocable_v = is_invocable<_Fn, _Args...>::value;
|
||||
|
||||
template <class _Ret, class _Fn, class ..._Args>
|
||||
constexpr bool is_invocable_r_v = is_invocable_r<_Ret, _Fn, _Args...>::value;
|
||||
|
||||
// is_nothrow_callable
|
||||
|
||||
template <class _Fn, class _Ret = void>
|
||||
struct _LIBCPP_TEMPLATE_VIS is_nothrow_callable;
|
||||
template <class _Fn, class ..._Args>
|
||||
struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable
|
||||
: integral_constant<bool, __nothrow_invokable<_Fn, _Args...>::value> {};
|
||||
|
||||
template <class _Fn, class ..._Args, class _Ret>
|
||||
struct _LIBCPP_TEMPLATE_VIS is_nothrow_callable<_Fn(_Args...), _Ret>
|
||||
: integral_constant<bool, __nothrow_invokable_r<_Ret, _Fn, _Args...>::value>
|
||||
{};
|
||||
template <class _Ret, class _Fn, class ..._Args>
|
||||
struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable_r
|
||||
: integral_constant<bool, __nothrow_invokable_r<_Ret, _Fn, _Args...>::value> {};
|
||||
|
||||
template <class _Fn, class _Ret = void>
|
||||
constexpr bool is_nothrow_callable_v = is_nothrow_callable<_Fn, _Ret>::value;
|
||||
template <class _Fn, class ..._Args>
|
||||
constexpr bool is_nothrow_invocable_v = is_nothrow_invocable<_Fn, _Args...>::value;
|
||||
|
||||
template <class _Ret, class _Fn, class ..._Args>
|
||||
constexpr bool is_nothrow_invocable_r_v = is_nothrow_invocable_r<_Ret, _Fn, _Args...>::value;
|
||||
|
||||
#endif // _LIBCPP_STD_VER > 14
|
||||
|
||||
|
|
|
@ -582,7 +582,7 @@ struct __variant {
|
|||
private:
|
||||
template <class _Visitor, class... _Values>
|
||||
static constexpr void __std_visit_exhaustive_visitor_check() {
|
||||
static_assert(is_callable_v<_Visitor(_Values...)>,
|
||||
static_assert(is_invocable_v<_Visitor, _Values...>,
|
||||
"`std::visit` requires the visitor to be exhaustive.");
|
||||
}
|
||||
|
||||
|
|
|
@ -438,26 +438,26 @@ void throws_in_constructor_test()
|
|||
void call_operator_sfinae_test() {
|
||||
{ // wrong number of arguments
|
||||
using T = decltype(std::not_fn(returns_true));
|
||||
static_assert(std::is_callable<T()>::value, ""); // callable only with no args
|
||||
static_assert(!std::is_callable<T(bool)>::value, "");
|
||||
static_assert(std::is_invocable<T>::value, ""); // callable only with no args
|
||||
static_assert(!std::is_invocable<T, bool>::value, "");
|
||||
}
|
||||
{ // violates const correctness (member function pointer)
|
||||
using T = decltype(std::not_fn(&MemFunCallable::return_value_nc));
|
||||
static_assert(std::is_callable<T(MemFunCallable&)>::value, "");
|
||||
static_assert(!std::is_callable<T(const MemFunCallable&)>::value, "");
|
||||
static_assert(std::is_invocable<T, MemFunCallable&>::value, "");
|
||||
static_assert(!std::is_invocable<T, const MemFunCallable&>::value, "");
|
||||
}
|
||||
{ // violates const correctness (call object)
|
||||
using Obj = CopyCallable<bool>;
|
||||
using NCT = decltype(std::not_fn(Obj{true}));
|
||||
using CT = const NCT;
|
||||
static_assert(std::is_callable<NCT()>::value, "");
|
||||
static_assert(!std::is_callable<CT()>::value, "");
|
||||
static_assert(std::is_invocable<NCT>::value, "");
|
||||
static_assert(!std::is_invocable<CT>::value, "");
|
||||
}
|
||||
{ // returns bad type with no operator!
|
||||
auto fn = [](auto x) { return x; };
|
||||
using T = decltype(std::not_fn(fn));
|
||||
static_assert(std::is_callable<T(bool)>::value, "");
|
||||
static_assert(!std::is_callable<T(std::string)>::value, "");
|
||||
static_assert(std::is_invocable<T, bool>::value, "");
|
||||
static_assert(!std::is_invocable<T, std::string>::value, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ int main()
|
|||
static_assert(!std::is_copy_assignable<H>::value, "");
|
||||
static_assert(!std::is_move_assignable<H>::value, "");
|
||||
#if TEST_STD_VER > 14
|
||||
static_assert(!std::is_callable<H(X&)>::value, "");
|
||||
static_assert(!std::is_callable<H(X const&)>::value, "");
|
||||
static_assert(!std::is_invocable<H, X&>::value, "");
|
||||
static_assert(!std::is_invocable<H, X const&>::value, "");
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,160 +0,0 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11, c++14
|
||||
|
||||
// type_traits
|
||||
|
||||
// is_callable
|
||||
|
||||
// Most testing of is_callable is done within the [meta.trans.other] result_of
|
||||
// tests.
|
||||
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
struct Tag {};
|
||||
struct DerFromTag : Tag {};
|
||||
|
||||
struct Implicit {
|
||||
Implicit(int) {}
|
||||
};
|
||||
|
||||
struct Explicit {
|
||||
explicit Explicit(int) {}
|
||||
};
|
||||
|
||||
struct NotCallableWithInt {
|
||||
int operator()(int) = delete;
|
||||
int operator()(Tag) { return 42; }
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
using Fn = int(Tag::*)(int);
|
||||
using RFn = int(Tag::*)(int) &&;
|
||||
// INVOKE bullet 1, 2 and 3
|
||||
{
|
||||
// Bullet 1
|
||||
static_assert(std::is_callable<Fn(Tag&, int)>::value, "");
|
||||
static_assert(std::is_callable<Fn(DerFromTag&, int)>::value, "");
|
||||
static_assert(std::is_callable<RFn(Tag&&, int)>::value, "");
|
||||
static_assert(!std::is_callable<RFn(Tag&, int)>::value, "");
|
||||
static_assert(!std::is_callable<Fn(Tag&)>::value, "");
|
||||
static_assert(!std::is_callable<Fn(Tag const&, int)>::value, "");
|
||||
}
|
||||
{
|
||||
// Bullet 2
|
||||
using T = std::reference_wrapper<Tag>;
|
||||
using DT = std::reference_wrapper<DerFromTag>;
|
||||
using CT = std::reference_wrapper<const Tag>;
|
||||
static_assert(std::is_callable<Fn(T&, int)>::value, "");
|
||||
static_assert(std::is_callable<Fn(DT&, int)>::value, "");
|
||||
static_assert(std::is_callable<Fn(const T&, int)>::value, "");
|
||||
static_assert(std::is_callable<Fn(T&&, int)>::value, "");
|
||||
static_assert(!std::is_callable<Fn(CT&, int)>::value, "");
|
||||
static_assert(!std::is_callable<RFn(T, int)>::value, "");
|
||||
}
|
||||
{
|
||||
// Bullet 3
|
||||
using T = Tag*;
|
||||
using DT = DerFromTag*;
|
||||
using CT = const Tag*;
|
||||
using ST = std::unique_ptr<Tag>;
|
||||
static_assert(std::is_callable<Fn(T&, int)>::value, "");
|
||||
static_assert(std::is_callable<Fn(DT&, int)>::value, "");
|
||||
static_assert(std::is_callable<Fn(const T&, int)>::value, "");
|
||||
static_assert(std::is_callable<Fn(T&&, int)>::value, "");
|
||||
static_assert(std::is_callable<Fn(ST, int)>::value, "");
|
||||
static_assert(!std::is_callable<Fn(CT&, int)>::value, "");
|
||||
static_assert(!std::is_callable<RFn(T, int)>::value, "");
|
||||
}
|
||||
}
|
||||
{
|
||||
// Bullets 4, 5 and 6
|
||||
using Fn = int (Tag::*);
|
||||
static_assert(!std::is_callable<Fn()>::value, "");
|
||||
{
|
||||
// Bullet 4
|
||||
static_assert(std::is_callable<Fn(Tag&)>::value, "");
|
||||
static_assert(std::is_callable<Fn(DerFromTag&)>::value, "");
|
||||
static_assert(std::is_callable<Fn(Tag&&)>::value, "");
|
||||
static_assert(std::is_callable<Fn(Tag const&)>::value, "");
|
||||
}
|
||||
{
|
||||
// Bullet 5
|
||||
using T = std::reference_wrapper<Tag>;
|
||||
using DT = std::reference_wrapper<DerFromTag>;
|
||||
using CT = std::reference_wrapper<const Tag>;
|
||||
static_assert(std::is_callable<Fn(T&)>::value, "");
|
||||
static_assert(std::is_callable<Fn(DT&)>::value, "");
|
||||
static_assert(std::is_callable<Fn(const T&)>::value, "");
|
||||
static_assert(std::is_callable<Fn(T&&)>::value, "");
|
||||
static_assert(std::is_callable<Fn(CT&)>::value, "");
|
||||
}
|
||||
{
|
||||
// Bullet 6
|
||||
using T = Tag*;
|
||||
using DT = DerFromTag*;
|
||||
using CT = const Tag*;
|
||||
using ST = std::unique_ptr<Tag>;
|
||||
static_assert(std::is_callable<Fn(T&)>::value, "");
|
||||
static_assert(std::is_callable<Fn(DT&)>::value, "");
|
||||
static_assert(std::is_callable<Fn(const T&)>::value, "");
|
||||
static_assert(std::is_callable<Fn(T&&)>::value, "");
|
||||
static_assert(std::is_callable<Fn(ST)>::value, "");
|
||||
static_assert(std::is_callable<Fn(CT&)>::value, "");
|
||||
}
|
||||
}
|
||||
{
|
||||
// INVOKE bullet 7
|
||||
{
|
||||
// Function pointer
|
||||
using Fp = void(*)(Tag&, int);
|
||||
static_assert(std::is_callable<Fp(Tag&, int)>::value, "");
|
||||
static_assert(std::is_callable<Fp(DerFromTag&, int)>::value, "");
|
||||
static_assert(!std::is_callable<Fp(const Tag&, int)>::value, "");
|
||||
static_assert(!std::is_callable<Fp()>::value, "");
|
||||
static_assert(!std::is_callable<Fp(Tag&)>::value, "");
|
||||
}
|
||||
{
|
||||
// Function reference
|
||||
using Fp = void(&)(Tag&, int);
|
||||
static_assert(std::is_callable<Fp(Tag&, int)>::value, "");
|
||||
static_assert(std::is_callable<Fp(DerFromTag&, int)>::value, "");
|
||||
static_assert(!std::is_callable<Fp(const Tag&, int)>::value, "");
|
||||
static_assert(!std::is_callable<Fp()>::value, "");
|
||||
static_assert(!std::is_callable<Fp(Tag&)>::value, "");
|
||||
}
|
||||
{
|
||||
// Function object
|
||||
using Fn = NotCallableWithInt;
|
||||
static_assert(std::is_callable<Fn(Tag)>::value, "");
|
||||
static_assert(!std::is_callable<Fn(int)>::value, "");
|
||||
}
|
||||
}
|
||||
{
|
||||
// Check that the conversion to the return type is properly checked
|
||||
using Fn = int(*)();
|
||||
static_assert(std::is_callable<Fn(), Implicit>::value, "");
|
||||
static_assert(std::is_callable<Fn(), double>::value, "");
|
||||
static_assert(std::is_callable<Fn(), const volatile void>::value, "");
|
||||
static_assert(!std::is_callable<Fn(), Explicit>::value, "");
|
||||
}
|
||||
{
|
||||
// Check for is_callable_v
|
||||
using Fn = void(*)();
|
||||
static_assert(std::is_callable_v<Fn()>, "");
|
||||
static_assert(!std::is_callable_v<Fn(int)>, "");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11, c++14
|
||||
|
||||
// type_traits
|
||||
|
||||
// is_invocable
|
||||
|
||||
// Most testing of is_invocable is done within the [meta.trans.other] result_of
|
||||
// tests.
|
||||
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
struct Tag {};
|
||||
struct DerFromTag : Tag {};
|
||||
|
||||
struct Implicit {
|
||||
Implicit(int) {}
|
||||
};
|
||||
|
||||
struct Explicit {
|
||||
explicit Explicit(int) {}
|
||||
};
|
||||
|
||||
struct NotCallableWithInt {
|
||||
int operator()(int) = delete;
|
||||
int operator()(Tag) { return 42; }
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
using Fn = int(Tag::*)(int);
|
||||
using RFn = int(Tag::*)(int) &&;
|
||||
// INVOKE bullet 1, 2 and 3
|
||||
{
|
||||
// Bullet 1
|
||||
static_assert(std::is_invocable<Fn, Tag&, int>::value, "");
|
||||
static_assert(std::is_invocable<Fn, DerFromTag&, int>::value, "");
|
||||
static_assert(std::is_invocable<RFn, Tag&&, int>::value, "");
|
||||
static_assert(!std::is_invocable<RFn, Tag&, int>::value, "");
|
||||
static_assert(!std::is_invocable<Fn, Tag&>::value, "");
|
||||
static_assert(!std::is_invocable<Fn, Tag const&, int>::value, "");
|
||||
}
|
||||
{
|
||||
// Bullet 2
|
||||
using T = std::reference_wrapper<Tag>;
|
||||
using DT = std::reference_wrapper<DerFromTag>;
|
||||
using CT = std::reference_wrapper<const Tag>;
|
||||
static_assert(std::is_invocable<Fn, T&, int>::value, "");
|
||||
static_assert(std::is_invocable<Fn, DT&, int>::value, "");
|
||||
static_assert(std::is_invocable<Fn, const T&, int>::value, "");
|
||||
static_assert(std::is_invocable<Fn, T&&, int>::value, "");
|
||||
static_assert(!std::is_invocable<Fn, CT&, int>::value, "");
|
||||
static_assert(!std::is_invocable<RFn, T, int>::value, "");
|
||||
}
|
||||
{
|
||||
// Bullet 3
|
||||
using T = Tag*;
|
||||
using DT = DerFromTag*;
|
||||
using CT = const Tag*;
|
||||
using ST = std::unique_ptr<Tag>;
|
||||
static_assert(std::is_invocable<Fn, T&, int>::value, "");
|
||||
static_assert(std::is_invocable<Fn, DT&, int>::value, "");
|
||||
static_assert(std::is_invocable<Fn, const T&, int>::value, "");
|
||||
static_assert(std::is_invocable<Fn, T&&, int>::value, "");
|
||||
static_assert(std::is_invocable<Fn, ST, int>::value, "");
|
||||
static_assert(!std::is_invocable<Fn, CT&, int>::value, "");
|
||||
static_assert(!std::is_invocable<RFn, T, int>::value, "");
|
||||
}
|
||||
}
|
||||
{
|
||||
// Bullets 4, 5 and 6
|
||||
using Fn = int (Tag::*);
|
||||
static_assert(!std::is_invocable<Fn>::value, "");
|
||||
{
|
||||
// Bullet 4
|
||||
static_assert(std::is_invocable<Fn, Tag&>::value, "");
|
||||
static_assert(std::is_invocable<Fn, DerFromTag&>::value, "");
|
||||
static_assert(std::is_invocable<Fn, Tag&&>::value, "");
|
||||
static_assert(std::is_invocable<Fn, Tag const&>::value, "");
|
||||
}
|
||||
{
|
||||
// Bullet 5
|
||||
using T = std::reference_wrapper<Tag>;
|
||||
using DT = std::reference_wrapper<DerFromTag>;
|
||||
using CT = std::reference_wrapper<const Tag>;
|
||||
static_assert(std::is_invocable<Fn, T&>::value, "");
|
||||
static_assert(std::is_invocable<Fn, DT&>::value, "");
|
||||
static_assert(std::is_invocable<Fn, const T&>::value, "");
|
||||
static_assert(std::is_invocable<Fn, T&&>::value, "");
|
||||
static_assert(std::is_invocable<Fn, CT&>::value, "");
|
||||
}
|
||||
{
|
||||
// Bullet 6
|
||||
using T = Tag*;
|
||||
using DT = DerFromTag*;
|
||||
using CT = const Tag*;
|
||||
using ST = std::unique_ptr<Tag>;
|
||||
static_assert(std::is_invocable<Fn, T&>::value, "");
|
||||
static_assert(std::is_invocable<Fn, DT&>::value, "");
|
||||
static_assert(std::is_invocable<Fn, const T&>::value, "");
|
||||
static_assert(std::is_invocable<Fn, T&&>::value, "");
|
||||
static_assert(std::is_invocable<Fn, ST>::value, "");
|
||||
static_assert(std::is_invocable<Fn, CT&>::value, "");
|
||||
}
|
||||
}
|
||||
{
|
||||
// INVOKE bullet 7
|
||||
{
|
||||
// Function pointer
|
||||
using Fp = void(*)(Tag&, int);
|
||||
static_assert(std::is_invocable<Fp, Tag&, int>::value, "");
|
||||
static_assert(std::is_invocable<Fp, DerFromTag&, int>::value, "");
|
||||
static_assert(!std::is_invocable<Fp, const Tag&, int>::value, "");
|
||||
static_assert(!std::is_invocable<Fp>::value, "");
|
||||
static_assert(!std::is_invocable<Fp, Tag&>::value, "");
|
||||
}
|
||||
{
|
||||
// Function reference
|
||||
using Fp = void(&)(Tag&, int);
|
||||
static_assert(std::is_invocable<Fp, Tag&, int>::value, "");
|
||||
static_assert(std::is_invocable<Fp, DerFromTag&, int>::value, "");
|
||||
static_assert(!std::is_invocable<Fp, const Tag&, int>::value, "");
|
||||
static_assert(!std::is_invocable<Fp>::value, "");
|
||||
static_assert(!std::is_invocable<Fp, Tag&>::value, "");
|
||||
}
|
||||
{
|
||||
// Function object
|
||||
using Fn = NotCallableWithInt;
|
||||
static_assert(std::is_invocable<Fn, Tag>::value, "");
|
||||
static_assert(!std::is_invocable<Fn, int>::value, "");
|
||||
}
|
||||
}
|
||||
{
|
||||
// Check that the conversion to the return type is properly checked
|
||||
using Fn = int(*)();
|
||||
static_assert(std::is_invocable_r<Implicit, Fn>::value, "");
|
||||
static_assert(std::is_invocable_r<double, Fn>::value, "");
|
||||
static_assert(std::is_invocable_r<const volatile void, Fn>::value, "");
|
||||
static_assert(!std::is_invocable_r<Explicit, Fn>::value, "");
|
||||
}
|
||||
{
|
||||
// Check for is_invocable_v
|
||||
using Fn = void(*)();
|
||||
static_assert(std::is_invocable_v<Fn>, "");
|
||||
static_assert(!std::is_invocable_v<Fn, int>, "");
|
||||
}
|
||||
{
|
||||
// Check for is_invocable_r_v
|
||||
using Fn = void(*)();
|
||||
static_assert(std::is_invocable_r_v<void, Fn>, "");
|
||||
static_assert(!std::is_invocable_r_v<int, Fn>, "");
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11, c++14
|
||||
|
||||
// type_traits
|
||||
|
||||
// is_nothrow_callable
|
||||
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
struct Tag {};
|
||||
|
||||
struct Implicit {
|
||||
Implicit(int) noexcept {}
|
||||
};
|
||||
|
||||
struct ThrowsImplicit {
|
||||
ThrowsImplicit(int) {}
|
||||
};
|
||||
|
||||
struct Explicit {
|
||||
explicit Explicit(int) noexcept {}
|
||||
};
|
||||
|
||||
template <bool IsNoexcept, class Ret, class ...Args>
|
||||
struct CallObject {
|
||||
Ret operator()(Args&&...) const noexcept(IsNoexcept);
|
||||
};
|
||||
|
||||
template <class Fn>
|
||||
constexpr bool throws_callable() {
|
||||
return std::is_callable<Fn>::value &&
|
||||
!std::is_nothrow_callable<Fn>::value;
|
||||
}
|
||||
|
||||
template <class Fn, class Ret>
|
||||
constexpr bool throws_callable() {
|
||||
return std::is_callable<Fn, Ret>::value &&
|
||||
!std::is_nothrow_callable<Fn, Ret>::value;
|
||||
}
|
||||
|
||||
// FIXME(EricWF) Don't test the where noexcept is *not* part of the type system
|
||||
// once implementations have caught up.
|
||||
void test_noexcept_function_pointers()
|
||||
{
|
||||
struct Dummy { void foo() noexcept {} static void bar() noexcept {} };
|
||||
#if !defined(__cpp_noexcept_function_type)
|
||||
{
|
||||
// Check that PMF's and function pointers *work*. is_nothrow_callable will always
|
||||
// return false because 'noexcept' is not part of the function type.
|
||||
static_assert(throws_callable<decltype(&Dummy::foo)(Dummy&)>(), "");
|
||||
static_assert(throws_callable<decltype(&Dummy::bar)()>(), "");
|
||||
}
|
||||
#else
|
||||
{
|
||||
// Check that PMF's and function pointers actually work and that
|
||||
// is_nothrow_callable returns true for noexcept PMF's and function
|
||||
// pointers.
|
||||
static_assert(std::is_nothrow_callable<decltype(&Dummy::foo)(Dummy&)>::value, "");
|
||||
static_assert(std::is_nothrow_callable<decltype(&Dummy::bar)()>::value, "");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
// Check that the conversion to the return type is properly checked
|
||||
using Fn = CallObject<true, int>;
|
||||
static_assert(std::is_nothrow_callable<Fn(), Implicit>::value, "");
|
||||
static_assert(std::is_nothrow_callable<Fn(), double>::value, "");
|
||||
static_assert(std::is_nothrow_callable<Fn(), const volatile void>::value, "");
|
||||
static_assert(throws_callable<Fn(), ThrowsImplicit>(), "");
|
||||
static_assert(!std::is_nothrow_callable<Fn(), Explicit>(), "");
|
||||
}
|
||||
{
|
||||
// Check that the conversion to the parameters is properly checked
|
||||
using Fn = CallObject<true, void, const Implicit&, const ThrowsImplicit&>;
|
||||
static_assert(std::is_nothrow_callable<Fn(Implicit&, ThrowsImplicit&)>::value, "");
|
||||
static_assert(std::is_nothrow_callable<Fn(int, ThrowsImplicit&)>::value, "");
|
||||
static_assert(throws_callable<Fn(int, int)>(), "");
|
||||
static_assert(!std::is_nothrow_callable<Fn()>::value, "");
|
||||
}
|
||||
{
|
||||
// Check that the noexcept-ness of function objects is checked.
|
||||
using Fn = CallObject<true, void>;
|
||||
using Fn2 = CallObject<false, void>;
|
||||
static_assert(std::is_nothrow_callable<Fn()>::value, "");
|
||||
static_assert(throws_callable<Fn2()>(), "");
|
||||
}
|
||||
{
|
||||
// Check that PMD derefs are noexcept
|
||||
using Fn = int (Tag::*);
|
||||
static_assert(std::is_nothrow_callable<Fn(Tag&)>::value, "");
|
||||
static_assert(std::is_nothrow_callable<Fn(Tag&), Implicit>::value, "");
|
||||
static_assert(throws_callable<Fn(Tag&), ThrowsImplicit>(), "");
|
||||
}
|
||||
{
|
||||
// Check for is_nothrow_callable_v
|
||||
using Fn = CallObject<true, int>;
|
||||
static_assert(std::is_nothrow_callable_v<Fn()>, "");
|
||||
static_assert(!std::is_nothrow_callable_v<Fn(int)>, "");
|
||||
}
|
||||
test_noexcept_function_pointers();
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11, c++14
|
||||
|
||||
// type_traits
|
||||
|
||||
// is_nothrow_invocable
|
||||
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
struct Tag {};
|
||||
|
||||
struct Implicit {
|
||||
Implicit(int) noexcept {}
|
||||
};
|
||||
|
||||
struct ThrowsImplicit {
|
||||
ThrowsImplicit(int) {}
|
||||
};
|
||||
|
||||
struct Explicit {
|
||||
explicit Explicit(int) noexcept {}
|
||||
};
|
||||
|
||||
template <bool IsNoexcept, class Ret, class ...Args>
|
||||
struct CallObject {
|
||||
Ret operator()(Args&&...) const noexcept(IsNoexcept);
|
||||
};
|
||||
|
||||
template <class Fn, class ...Args>
|
||||
constexpr bool throws_invocable() {
|
||||
return std::is_invocable<Fn, Args...>::value &&
|
||||
!std::is_nothrow_invocable<Fn, Args...>::value;
|
||||
}
|
||||
|
||||
template <class Ret, class Fn, class ...Args>
|
||||
constexpr bool throws_invocable_r() {
|
||||
return std::is_invocable_r<Ret, Fn, Args...>::value &&
|
||||
!std::is_nothrow_invocable_r<Ret, Fn, Args...>::value;
|
||||
}
|
||||
|
||||
// FIXME(EricWF) Don't test the where noexcept is *not* part of the type system
|
||||
// once implementations have caught up.
|
||||
void test_noexcept_function_pointers()
|
||||
{
|
||||
struct Dummy { void foo() noexcept {} static void bar() noexcept {} };
|
||||
#if !defined(__cpp_noexcept_function_type)
|
||||
{
|
||||
// Check that PMF's and function pointers *work*. is_nothrow_invocable will always
|
||||
// return false because 'noexcept' is not part of the function type.
|
||||
static_assert(throws_invocable<decltype(&Dummy::foo), Dummy&>(), "");
|
||||
static_assert(throws_invocable<decltype(&Dummy::bar)>(), "");
|
||||
}
|
||||
#else
|
||||
{
|
||||
// Check that PMF's and function pointers actually work and that
|
||||
// is_nothrow_invocable returns true for noexcept PMF's and function
|
||||
// pointers.
|
||||
static_assert(std::is_nothrow_invocable<decltype(&Dummy::foo), Dummy&>::value, "");
|
||||
static_assert(std::is_nothrow_invocable<decltype(&Dummy::bar)>::value, "");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
// Check that the conversion to the return type is properly checked
|
||||
using Fn = CallObject<true, int>;
|
||||
static_assert(std::is_nothrow_invocable_r<Implicit, Fn>::value, "");
|
||||
static_assert(std::is_nothrow_invocable_r<double, Fn>::value, "");
|
||||
static_assert(std::is_nothrow_invocable_r<const volatile void, Fn>::value, "");
|
||||
static_assert(throws_invocable_r<ThrowsImplicit, Fn>(), "");
|
||||
static_assert(!std::is_nothrow_invocable<Fn(), Explicit>(), "");
|
||||
}
|
||||
{
|
||||
// Check that the conversion to the parameters is properly checked
|
||||
using Fn = CallObject<true, void, const Implicit&, const ThrowsImplicit&>;
|
||||
static_assert(std::is_nothrow_invocable<Fn, Implicit&, ThrowsImplicit&>::value, "");
|
||||
static_assert(std::is_nothrow_invocable<Fn, int, ThrowsImplicit&>::value, "");
|
||||
static_assert(throws_invocable<Fn, int, int>(), "");
|
||||
static_assert(!std::is_nothrow_invocable<Fn>::value, "");
|
||||
}
|
||||
{
|
||||
// Check that the noexcept-ness of function objects is checked.
|
||||
using Fn = CallObject<true, void>;
|
||||
using Fn2 = CallObject<false, void>;
|
||||
static_assert(std::is_nothrow_invocable<Fn>::value, "");
|
||||
static_assert(throws_invocable<Fn2>(), "");
|
||||
}
|
||||
{
|
||||
// Check that PMD derefs are noexcept
|
||||
using Fn = int (Tag::*);
|
||||
static_assert(std::is_nothrow_invocable<Fn, Tag&>::value, "");
|
||||
static_assert(std::is_nothrow_invocable_r<Implicit, Fn, Tag&>::value, "");
|
||||
static_assert(throws_invocable_r<ThrowsImplicit, Fn, Tag&>(), "");
|
||||
}
|
||||
{
|
||||
// Check for is_nothrow_invocable_v
|
||||
using Fn = CallObject<true, int>;
|
||||
static_assert(std::is_nothrow_invocable_v<Fn>, "");
|
||||
static_assert(!std::is_nothrow_invocable_v<Fn, int>, "");
|
||||
}
|
||||
{
|
||||
// Check for is_nothrow_invocable_r_v
|
||||
using Fn = CallObject<true, int>;
|
||||
static_assert(std::is_nothrow_invocable_r_v<void, Fn>, "");
|
||||
static_assert(!std::is_nothrow_invocable_r_v<int, Fn, int>, "");
|
||||
}
|
||||
test_noexcept_function_pointers();
|
||||
}
|
|
@ -42,16 +42,46 @@ struct HasType : std::false_type {};
|
|||
template <class T>
|
||||
struct HasType<T, typename Voider<typename T::type>::type> : std::true_type {};
|
||||
|
||||
#if TEST_STD_VER > 14
|
||||
template <typename T, typename U>
|
||||
struct test_invoke_result;
|
||||
|
||||
template <typename Fn, typename ...Args, typename Ret>
|
||||
struct test_invoke_result<Fn(Args...), Ret>
|
||||
{
|
||||
static void call()
|
||||
{
|
||||
static_assert(std::is_invocable<Fn, Args...>::value, "");
|
||||
static_assert(std::is_invocable_r<Ret, Fn, Args...>::value, "");
|
||||
static_assert((std::is_same<typename std::invoke_result<Fn, Args...>::type, Ret>::value), "");
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <class T, class U>
|
||||
void test_result_of()
|
||||
{
|
||||
#if TEST_STD_VER > 14
|
||||
static_assert(std::is_callable<T>::value, "");
|
||||
static_assert(std::is_callable<T, U>::value, "");
|
||||
#endif
|
||||
static_assert((std::is_same<typename std::result_of<T>::type, U>::value), "");
|
||||
#if TEST_STD_VER > 14
|
||||
test_invoke_result<T, U>::call();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if TEST_STD_VER > 14
|
||||
template <typename T>
|
||||
struct test_invoke_no_result;
|
||||
|
||||
template <typename Fn, typename ...Args>
|
||||
struct test_invoke_no_result<Fn(Args...)>
|
||||
{
|
||||
static void call()
|
||||
{
|
||||
static_assert(std::is_invocable<Fn, Args...>::value == false, "");
|
||||
static_assert((!HasType<std::invoke_result<Fn, Args...> >::value), "");
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
void test_no_result()
|
||||
{
|
||||
|
@ -59,7 +89,7 @@ void test_no_result()
|
|||
static_assert((!HasType<std::result_of<T> >::value), "");
|
||||
#endif
|
||||
#if TEST_STD_VER > 14
|
||||
static_assert(std::is_callable<T>::value == false, "");
|
||||
test_invoke_no_result<T>::call();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,23 @@ struct wat
|
|||
struct F {};
|
||||
struct FD : public F {};
|
||||
|
||||
#if TEST_STD_VER > 14
|
||||
template <typename T, typename U>
|
||||
struct test_invoke_result;
|
||||
|
||||
template <typename Fn, typename ...Args, typename Ret>
|
||||
struct test_invoke_result<Fn(Args...), Ret>
|
||||
{
|
||||
static void call()
|
||||
{
|
||||
static_assert(std::is_invocable<Fn, Args...>::value, "");
|
||||
static_assert(std::is_invocable_r<Ret, Fn, Args...>::value, "");
|
||||
static_assert((std::is_same<typename std::invoke_result<Fn, Args...>::type, Ret>::value), "");
|
||||
static_assert((std::is_same<std::invoke_result_t<Fn, Args...>, Ret>::value), "");
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <class T, class U>
|
||||
void test_result_of_imp()
|
||||
{
|
||||
|
@ -35,8 +52,7 @@ void test_result_of_imp()
|
|||
static_assert((std::is_same<std::result_of_t<T>, U>::value), "");
|
||||
#endif
|
||||
#if TEST_STD_VER > 14
|
||||
static_assert(std::is_callable<T>::value, "");
|
||||
static_assert(std::is_callable<T, U>::value, "");
|
||||
test_invoke_result<T, U>::call();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue