[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:
Zhihao Yuan 2017-12-12 18:42:04 +00:00
parent 81a4a02cbc
commit bcde6e715e
10 changed files with 408 additions and 320 deletions

View File

@ -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

View File

@ -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.");
}

View File

@ -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, "");
}
}

View File

@ -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
}

View File

@ -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)>, "");
}
}

View File

@ -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>, "");
}
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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
}

View File

@ -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
}