forked from OSchip/llvm-project
[libc++] Add the __bind_back and __compose helpers
Those are going to be used to implement range adaptors, see D107098 for details. Differential Revision: https://reviews.llvm.org/D107785
This commit is contained in:
parent
683147ff11
commit
89a7bdb1f3
|
@ -130,10 +130,12 @@ set(files
|
|||
__functional_base
|
||||
__functional/binary_function.h
|
||||
__functional/binary_negate.h
|
||||
__functional/bind_back.h
|
||||
__functional/bind_front.h
|
||||
__functional/bind.h
|
||||
__functional/binder1st.h
|
||||
__functional/binder2nd.h
|
||||
__functional/compose.h
|
||||
__functional/default_searcher.h
|
||||
__functional/function.h
|
||||
__functional/hash.h
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___FUNCTIONAL_BIND_BACK_H
|
||||
#define _LIBCPP___FUNCTIONAL_BIND_BACK_H
|
||||
|
||||
#include <__config>
|
||||
#include <__functional/invoke.h>
|
||||
#include <__functional/perfect_forward.h>
|
||||
#include <__utility/forward.h>
|
||||
#include <__utility/integer_sequence.h>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if _LIBCPP_STD_VER > 17
|
||||
|
||||
template <size_t _NBound, class = make_index_sequence<_NBound>>
|
||||
struct __bind_back_op;
|
||||
|
||||
template <size_t _NBound, size_t ..._Ip>
|
||||
struct __bind_back_op<_NBound, index_sequence<_Ip...>> {
|
||||
template <class _Fn, class _Bound, class ..._Args>
|
||||
_LIBCPP_HIDE_FROM_ABI
|
||||
constexpr auto operator()(_Fn&& __f, _Bound&& __bound, _Args&& ...__args) const
|
||||
noexcept(noexcept(_VSTD::invoke(_VSTD::forward<_Fn>(__f), _VSTD::forward<_Args>(__args)..., _VSTD::get<_Ip>(_VSTD::forward<_Bound>(__bound))...)))
|
||||
-> decltype( _VSTD::invoke(_VSTD::forward<_Fn>(__f), _VSTD::forward<_Args>(__args)..., _VSTD::get<_Ip>(_VSTD::forward<_Bound>(__bound))...))
|
||||
{ return _VSTD::invoke(_VSTD::forward<_Fn>(__f), _VSTD::forward<_Args>(__args)..., _VSTD::get<_Ip>(_VSTD::forward<_Bound>(__bound))...); }
|
||||
};
|
||||
|
||||
template <class _Fn, class _BoundArgs>
|
||||
struct __bind_back_t : __perfect_forward<__bind_back_op<tuple_size_v<_BoundArgs>>, _Fn, _BoundArgs> {
|
||||
using __perfect_forward<__bind_back_op<tuple_size_v<_BoundArgs>>, _Fn, _BoundArgs>::__perfect_forward;
|
||||
};
|
||||
|
||||
template <class _Fn, class ..._Args, class = _EnableIf<
|
||||
_And<
|
||||
is_constructible<decay_t<_Fn>, _Fn>,
|
||||
is_move_constructible<decay_t<_Fn>>,
|
||||
is_constructible<decay_t<_Args>, _Args>...,
|
||||
is_move_constructible<decay_t<_Args>>...
|
||||
>::value
|
||||
>>
|
||||
_LIBCPP_HIDE_FROM_ABI
|
||||
constexpr auto __bind_back(_Fn&& __f, _Args&&... __args)
|
||||
noexcept(noexcept(__bind_back_t<decay_t<_Fn>, tuple<decay_t<_Args>...>>(_VSTD::forward<_Fn>(__f), _VSTD::forward_as_tuple(_VSTD::forward<_Args>(__args)...))))
|
||||
-> decltype( __bind_back_t<decay_t<_Fn>, tuple<decay_t<_Args>...>>(_VSTD::forward<_Fn>(__f), _VSTD::forward_as_tuple(_VSTD::forward<_Args>(__args)...)))
|
||||
{ return __bind_back_t<decay_t<_Fn>, tuple<decay_t<_Args>...>>(_VSTD::forward<_Fn>(__f), _VSTD::forward_as_tuple(_VSTD::forward<_Args>(__args)...)); }
|
||||
|
||||
#endif // _LIBCPP_STD_VER > 17
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___FUNCTIONAL_BIND_BACK_H
|
|
@ -0,0 +1,52 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___FUNCTIONAL_COMPOSE_H
|
||||
#define _LIBCPP___FUNCTIONAL_COMPOSE_H
|
||||
|
||||
#include <__config>
|
||||
#include <__functional/invoke.h>
|
||||
#include <__functional/perfect_forward.h>
|
||||
#include <__utility/forward.h>
|
||||
#include <type_traits>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if _LIBCPP_STD_VER > 17
|
||||
|
||||
struct __compose_op {
|
||||
template<class _Fn1, class _Fn2, class ..._Args>
|
||||
_LIBCPP_HIDE_FROM_ABI
|
||||
constexpr auto operator()(_Fn1&& __f1, _Fn2&& __f2, _Args&&... __args) const
|
||||
noexcept(noexcept(_VSTD::invoke(_VSTD::forward<_Fn1>(__f1), _VSTD::invoke(_VSTD::forward<_Fn2>(__f2), _VSTD::forward<_Args>(__args)...))))
|
||||
-> decltype( _VSTD::invoke(_VSTD::forward<_Fn1>(__f1), _VSTD::invoke(_VSTD::forward<_Fn2>(__f2), _VSTD::forward<_Args>(__args)...)))
|
||||
{ return _VSTD::invoke(_VSTD::forward<_Fn1>(__f1), _VSTD::invoke(_VSTD::forward<_Fn2>(__f2), _VSTD::forward<_Args>(__args)...)); }
|
||||
};
|
||||
|
||||
template <class _Fn1, class _Fn2>
|
||||
struct __compose_t : __perfect_forward<__compose_op, _Fn1, _Fn2> {
|
||||
using __perfect_forward<__compose_op, _Fn1, _Fn2>::__perfect_forward;
|
||||
};
|
||||
|
||||
template <class _Fn1, class _Fn2>
|
||||
_LIBCPP_HIDE_FROM_ABI
|
||||
constexpr auto __compose(_Fn1&& __f1, _Fn2&& __f2)
|
||||
noexcept(noexcept(__compose_t<decay_t<_Fn1>, decay_t<_Fn2>>(_VSTD::forward<_Fn1>(__f1), _VSTD::forward<_Fn2>(__f2))))
|
||||
-> decltype( __compose_t<decay_t<_Fn1>, decay_t<_Fn2>>(_VSTD::forward<_Fn1>(__f1), _VSTD::forward<_Fn2>(__f2)))
|
||||
{ return __compose_t<decay_t<_Fn1>, decay_t<_Fn2>>(_VSTD::forward<_Fn1>(__f1), _VSTD::forward<_Fn2>(__f2)); }
|
||||
|
||||
#endif // _LIBCPP_STD_VER > 17
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___FUNCTIONAL_COMPOSE_H
|
|
@ -492,10 +492,12 @@ POLICY: For non-variadic implementations, the number of arguments is limited
|
|||
#include <__debug>
|
||||
#include <__functional/binary_function.h> // TODO: deprecate
|
||||
#include <__functional/binary_negate.h>
|
||||
#include <__functional/bind_back.h>
|
||||
#include <__functional/bind_front.h>
|
||||
#include <__functional/bind.h>
|
||||
#include <__functional/binder1st.h>
|
||||
#include <__functional/binder2nd.h>
|
||||
#include <__functional/compose.h>
|
||||
#include <__functional/default_searcher.h>
|
||||
#include <__functional/function.h>
|
||||
#include <__functional/hash.h>
|
||||
|
|
|
@ -444,15 +444,17 @@ module std [system] {
|
|||
module binary_function { private header "__functional/binary_function.h" }
|
||||
module binary_negate { private header "__functional/binary_negate.h" }
|
||||
module bind { private header "__functional/bind.h" }
|
||||
module bind_back { private header "__functional/bind_back.h" }
|
||||
module bind_front { private header "__functional/bind_front.h" }
|
||||
module binder1st { private header "__functional/binder1st.h" }
|
||||
module binder2nd { private header "__functional/binder2nd.h" }
|
||||
module compose { private header "__functional/compose.h" }
|
||||
module default_searcher { private header "__functional/default_searcher.h" }
|
||||
module function { private header "__functional/function.h" }
|
||||
module hash { private header "__functional/hash.h" }
|
||||
module identity { private header "__functional/identity.h" }
|
||||
module is_transparent { private header "__functional/is_transparent.h" }
|
||||
module invoke { private header "__functional/invoke.h" }
|
||||
module is_transparent { private header "__functional/is_transparent.h" }
|
||||
module mem_fn { private header "__functional/mem_fn.h" }
|
||||
module mem_fun_ref { private header "__functional/mem_fun_ref.h" }
|
||||
module not_fn { private header "__functional/not_fn.h" }
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: modules-build
|
||||
|
||||
// WARNING: This test was generated by 'generate_private_header_tests.py'
|
||||
// and should not be edited manually.
|
||||
|
||||
// expected-error@*:* {{use of private header from outside its module: '__functional/bind_back.h'}}
|
||||
#include <__functional/bind_back.h>
|
|
@ -0,0 +1,16 @@
|
|||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: modules-build
|
||||
|
||||
// WARNING: This test was generated by 'generate_private_header_tests.py'
|
||||
// and should not be edited manually.
|
||||
|
||||
// expected-error@*:* {{use of private header from outside its module: '__functional/compose.h'}}
|
||||
#include <__functional/compose.h>
|
|
@ -0,0 +1,416 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
|
||||
// functional
|
||||
|
||||
// template <class F, class... Args>
|
||||
// constexpr unspecified __bind_back(F&&, Args&&...);
|
||||
|
||||
// This isn't part of the standard, however we use it internally and there is a
|
||||
// chance that it will be added to the standard, so we implement those tests
|
||||
// as-if it were part of the spec.
|
||||
|
||||
#include <functional>
|
||||
#include <cassert>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "callable_types.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
struct CopyMoveInfo {
|
||||
enum { none, copy, move } copy_kind;
|
||||
|
||||
constexpr CopyMoveInfo() : copy_kind(none) {}
|
||||
constexpr CopyMoveInfo(CopyMoveInfo const&) : copy_kind(copy) {}
|
||||
constexpr CopyMoveInfo(CopyMoveInfo&&) : copy_kind(move) {}
|
||||
};
|
||||
|
||||
template <class ...Args>
|
||||
struct is_bind_backable {
|
||||
template <class ...LocalArgs>
|
||||
static auto test(int)
|
||||
-> decltype((void)std::__bind_back(std::declval<LocalArgs>()...), std::true_type());
|
||||
|
||||
template <class...>
|
||||
static std::false_type test(...);
|
||||
|
||||
static constexpr bool value = decltype(test<Args...>(0))::value;
|
||||
};
|
||||
|
||||
struct NotCopyMove {
|
||||
NotCopyMove() = delete;
|
||||
NotCopyMove(const NotCopyMove&) = delete;
|
||||
NotCopyMove(NotCopyMove&&) = delete;
|
||||
template <class ...Args>
|
||||
void operator()(Args&& ...) const { }
|
||||
};
|
||||
|
||||
struct NonConstCopyConstructible {
|
||||
explicit NonConstCopyConstructible() {}
|
||||
NonConstCopyConstructible(NonConstCopyConstructible&) {}
|
||||
};
|
||||
|
||||
struct MoveConstructible {
|
||||
explicit MoveConstructible() {}
|
||||
MoveConstructible(MoveConstructible&&) {}
|
||||
};
|
||||
|
||||
struct MakeTuple {
|
||||
template <class ...Args>
|
||||
constexpr auto operator()(Args&& ...args) const {
|
||||
return std::make_tuple(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <int X>
|
||||
struct Elem {
|
||||
template <int Y>
|
||||
constexpr bool operator==(Elem<Y> const&) const
|
||||
{ return X == Y; }
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
// Bind arguments, call without arguments
|
||||
{
|
||||
{
|
||||
auto f = std::__bind_back(MakeTuple{});
|
||||
assert(f() == std::make_tuple());
|
||||
}
|
||||
{
|
||||
auto f = std::__bind_back(MakeTuple{}, Elem<1>{});
|
||||
assert(f() == std::make_tuple(Elem<1>{}));
|
||||
}
|
||||
{
|
||||
auto f = std::__bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{});
|
||||
assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{}));
|
||||
}
|
||||
{
|
||||
auto f = std::__bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{});
|
||||
assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}));
|
||||
}
|
||||
}
|
||||
|
||||
// Bind no arguments, call with arguments
|
||||
{
|
||||
{
|
||||
auto f = std::__bind_back(MakeTuple{});
|
||||
assert(f(Elem<1>{}) == std::make_tuple(Elem<1>{}));
|
||||
}
|
||||
{
|
||||
auto f = std::__bind_back(MakeTuple{});
|
||||
assert(f(Elem<1>{}, Elem<2>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}));
|
||||
}
|
||||
{
|
||||
auto f = std::__bind_back(MakeTuple{});
|
||||
assert(f(Elem<1>{}, Elem<2>{}, Elem<3>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}));
|
||||
}
|
||||
}
|
||||
|
||||
// Bind arguments, call with arguments
|
||||
{
|
||||
{
|
||||
auto f = std::__bind_back(MakeTuple{}, Elem<1>{});
|
||||
assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{}));
|
||||
}
|
||||
{
|
||||
auto f = std::__bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{});
|
||||
assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{}, Elem<2>{}));
|
||||
}
|
||||
{
|
||||
auto f = std::__bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{});
|
||||
assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{}, Elem<2>{}, Elem<3>{}));
|
||||
}
|
||||
|
||||
{
|
||||
auto f = std::__bind_back(MakeTuple{}, Elem<1>{});
|
||||
assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{}));
|
||||
}
|
||||
{
|
||||
auto f = std::__bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{});
|
||||
assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{}, Elem<2>{}));
|
||||
}
|
||||
{
|
||||
auto f = std::__bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{});
|
||||
assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{}, Elem<2>{}, Elem<3>{}));
|
||||
}
|
||||
}
|
||||
|
||||
// Basic tests with fundamental types
|
||||
{
|
||||
int n = 2;
|
||||
int m = 1;
|
||||
auto add = [](int x, int y) { return x + y; };
|
||||
auto addN = [](int a, int b, int c, int d, int e, int f) {
|
||||
return a + b + c + d + e + f;
|
||||
};
|
||||
|
||||
auto a = std::__bind_back(add, m, n);
|
||||
assert(a() == 3);
|
||||
|
||||
auto b = std::__bind_back(addN, m, n, m, m, m, m);
|
||||
assert(b() == 7);
|
||||
|
||||
auto c = std::__bind_back(addN, n, m);
|
||||
assert(c(1, 1, 1, 1) == 7);
|
||||
|
||||
auto f = std::__bind_back(add, n);
|
||||
assert(f(3) == 5);
|
||||
|
||||
auto g = std::__bind_back(add, n, 1);
|
||||
assert(g() == 3);
|
||||
|
||||
auto h = std::__bind_back(addN, 1, 1, 1);
|
||||
assert(h(2, 2, 2) == 9);
|
||||
}
|
||||
|
||||
// Make sure we don't treat std::reference_wrapper specially.
|
||||
{
|
||||
auto sub = [](std::reference_wrapper<int> a, std::reference_wrapper<int> b) {
|
||||
return a.get() - b.get();
|
||||
};
|
||||
int i = 1, j = 2;
|
||||
auto f = std::__bind_back(sub, std::ref(i));
|
||||
assert(f(std::ref(j)) == 2 - 1);
|
||||
}
|
||||
|
||||
// Make sure we can call a function that's a pointer to a member function.
|
||||
{
|
||||
struct MemberFunction {
|
||||
constexpr bool foo(int, int) { return true; }
|
||||
};
|
||||
MemberFunction value;
|
||||
auto fn = std::__bind_back(&MemberFunction::foo, 0, 0);
|
||||
assert(fn(value));
|
||||
}
|
||||
|
||||
// Make sure that we copy the bound arguments into the unspecified-type.
|
||||
{
|
||||
auto add = [](int x, int y) { return x + y; };
|
||||
int n = 2;
|
||||
auto i = std::__bind_back(add, n, 1);
|
||||
n = 100;
|
||||
assert(i() == 3);
|
||||
}
|
||||
|
||||
// Make sure we pass the bound arguments to the function object
|
||||
// with the right value category.
|
||||
{
|
||||
{
|
||||
auto wasCopied = [](CopyMoveInfo info) {
|
||||
return info.copy_kind == CopyMoveInfo::copy;
|
||||
};
|
||||
CopyMoveInfo info;
|
||||
auto copied = std::__bind_back(wasCopied, info);
|
||||
assert(copied());
|
||||
}
|
||||
|
||||
{
|
||||
auto wasMoved = [](CopyMoveInfo info) {
|
||||
return info.copy_kind == CopyMoveInfo::move;
|
||||
};
|
||||
CopyMoveInfo info;
|
||||
auto moved = std::__bind_back(wasMoved, info);
|
||||
assert(std::move(moved)());
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we call the correctly cv-ref qualified operator() based on the
|
||||
// value category of the __bind_back unspecified-type.
|
||||
{
|
||||
struct F {
|
||||
constexpr int operator()() & { return 1; }
|
||||
constexpr int operator()() const& { return 2; }
|
||||
constexpr int operator()() && { return 3; }
|
||||
constexpr int operator()() const&& { return 4; }
|
||||
};
|
||||
auto x = std::__bind_back(F{});
|
||||
using X = decltype(x);
|
||||
assert(static_cast<X&>(x)() == 1);
|
||||
assert(static_cast<X const&>(x)() == 2);
|
||||
assert(static_cast<X&&>(x)() == 3);
|
||||
assert(static_cast<X const&&>(x)() == 4);
|
||||
}
|
||||
|
||||
// Make sure the __bind_back unspecified-type is NOT invocable when the call would select a
|
||||
// differently-qualified operator().
|
||||
//
|
||||
// For example, if the call to `operator()() &` is ill-formed, the call to the unspecified-type
|
||||
// should be ill-formed and not fall back to the `operator()() const&` overload.
|
||||
{
|
||||
// Make sure we delete the & overload when the underlying call isn't valid
|
||||
{
|
||||
struct F {
|
||||
void operator()() & = delete;
|
||||
void operator()() const&;
|
||||
void operator()() &&;
|
||||
void operator()() const&&;
|
||||
};
|
||||
using X = decltype(std::__bind_back(F{}));
|
||||
static_assert(!std::is_invocable_v<X&>);
|
||||
static_assert( std::is_invocable_v<X const&>);
|
||||
static_assert( std::is_invocable_v<X>);
|
||||
static_assert( std::is_invocable_v<X const>);
|
||||
}
|
||||
|
||||
// There's no way to make sure we delete the const& overload when the underlying call isn't valid,
|
||||
// so we can't check this one.
|
||||
|
||||
// Make sure we delete the && overload when the underlying call isn't valid
|
||||
{
|
||||
struct F {
|
||||
void operator()() &;
|
||||
void operator()() const&;
|
||||
void operator()() && = delete;
|
||||
void operator()() const&&;
|
||||
};
|
||||
using X = decltype(std::__bind_back(F{}));
|
||||
static_assert( std::is_invocable_v<X&>);
|
||||
static_assert( std::is_invocable_v<X const&>);
|
||||
static_assert(!std::is_invocable_v<X>);
|
||||
static_assert( std::is_invocable_v<X const>);
|
||||
}
|
||||
|
||||
// Make sure we delete the const&& overload when the underlying call isn't valid
|
||||
{
|
||||
struct F {
|
||||
void operator()() &;
|
||||
void operator()() const&;
|
||||
void operator()() &&;
|
||||
void operator()() const&& = delete;
|
||||
};
|
||||
using X = decltype(std::__bind_back(F{}));
|
||||
static_assert( std::is_invocable_v<X&>);
|
||||
static_assert( std::is_invocable_v<X const&>);
|
||||
static_assert( std::is_invocable_v<X>);
|
||||
static_assert(!std::is_invocable_v<X const>);
|
||||
}
|
||||
}
|
||||
|
||||
// Some examples by Tim Song
|
||||
{
|
||||
{
|
||||
struct T { };
|
||||
struct F {
|
||||
void operator()(T&&) const &;
|
||||
void operator()(T&&) && = delete;
|
||||
};
|
||||
using X = decltype(std::__bind_back(F{}));
|
||||
static_assert(!std::is_invocable_v<X, T>);
|
||||
}
|
||||
|
||||
{
|
||||
struct T { };
|
||||
struct F {
|
||||
void operator()(T const&) const;
|
||||
void operator()(T&&) const = delete;
|
||||
};
|
||||
using X = decltype(std::__bind_back(F{}, T{}));
|
||||
static_assert(!std::is_invocable_v<X>);
|
||||
}
|
||||
}
|
||||
|
||||
// Test properties of the constructor of the unspecified-type returned by __bind_back.
|
||||
{
|
||||
{
|
||||
MoveOnlyCallable value(true);
|
||||
auto ret = std::__bind_back(std::move(value), 1);
|
||||
assert(ret());
|
||||
assert(ret(1, 2, 3));
|
||||
|
||||
auto ret1 = std::move(ret);
|
||||
assert(!ret());
|
||||
assert(ret1());
|
||||
assert(ret1(1, 2, 3));
|
||||
|
||||
using RetT = decltype(ret);
|
||||
static_assert( std::is_move_constructible<RetT>::value);
|
||||
static_assert(!std::is_copy_constructible<RetT>::value);
|
||||
static_assert(!std::is_move_assignable<RetT>::value);
|
||||
static_assert(!std::is_copy_assignable<RetT>::value);
|
||||
}
|
||||
{
|
||||
CopyCallable value(true);
|
||||
auto ret = std::__bind_back(value, 1);
|
||||
assert(ret());
|
||||
assert(ret(1, 2, 3));
|
||||
|
||||
auto ret1 = std::move(ret);
|
||||
assert(ret1());
|
||||
assert(ret1(1, 2, 3));
|
||||
|
||||
auto ret2 = std::__bind_back(std::move(value), 1);
|
||||
assert(!ret());
|
||||
assert(ret2());
|
||||
assert(ret2(1, 2, 3));
|
||||
|
||||
using RetT = decltype(ret);
|
||||
static_assert( std::is_move_constructible<RetT>::value);
|
||||
static_assert( std::is_copy_constructible<RetT>::value);
|
||||
static_assert(!std::is_move_assignable<RetT>::value);
|
||||
static_assert(!std::is_copy_assignable<RetT>::value);
|
||||
}
|
||||
{
|
||||
CopyAssignableWrapper value(true);
|
||||
using RetT = decltype(std::__bind_back(value, 1));
|
||||
|
||||
static_assert(std::is_move_constructible<RetT>::value);
|
||||
static_assert(std::is_copy_constructible<RetT>::value);
|
||||
static_assert(std::is_move_assignable<RetT>::value);
|
||||
static_assert(std::is_copy_assignable<RetT>::value);
|
||||
}
|
||||
{
|
||||
MoveAssignableWrapper value(true);
|
||||
using RetT = decltype(std::__bind_back(std::move(value), 1));
|
||||
|
||||
static_assert( std::is_move_constructible<RetT>::value);
|
||||
static_assert(!std::is_copy_constructible<RetT>::value);
|
||||
static_assert( std::is_move_assignable<RetT>::value);
|
||||
static_assert(!std::is_copy_assignable<RetT>::value);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure __bind_back is SFINAE friendly
|
||||
{
|
||||
static_assert(!std::is_constructible_v<NotCopyMove, NotCopyMove&>);
|
||||
static_assert(!std::is_move_constructible_v<NotCopyMove>);
|
||||
static_assert(!is_bind_backable<NotCopyMove>::value);
|
||||
static_assert(!is_bind_backable<NotCopyMove&>::value);
|
||||
|
||||
auto takeAnything = [](auto&& ...) { };
|
||||
static_assert(!std::is_constructible_v<MoveConstructible, MoveConstructible&>);
|
||||
static_assert( std::is_move_constructible_v<MoveConstructible>);
|
||||
static_assert( is_bind_backable<decltype(takeAnything), MoveConstructible>::value);
|
||||
static_assert(!is_bind_backable<decltype(takeAnything), MoveConstructible&>::value);
|
||||
|
||||
static_assert( std::is_constructible_v<NonConstCopyConstructible, NonConstCopyConstructible&>);
|
||||
static_assert(!std::is_move_constructible_v<NonConstCopyConstructible>);
|
||||
static_assert(!is_bind_backable<decltype(takeAnything), NonConstCopyConstructible&>::value);
|
||||
static_assert(!is_bind_backable<decltype(takeAnything), NonConstCopyConstructible>::value);
|
||||
}
|
||||
|
||||
// Make sure bind_back's unspecified type's operator() is SFINAE-friendly
|
||||
{
|
||||
using T = decltype(std::__bind_back(std::declval<int(*)(int, int)>(), 1));
|
||||
static_assert(!std::is_invocable<T>::value);
|
||||
static_assert( std::is_invocable<T, int>::value);
|
||||
static_assert(!std::is_invocable<T, void*>::value);
|
||||
static_assert(!std::is_invocable<T, int, int>::value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
|
||||
// template <class F1, class F2>
|
||||
// constexpr unspecified __compose(F1&&, F2&&);
|
||||
|
||||
#include <functional>
|
||||
#include <cassert>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
template <int X>
|
||||
struct Elem {
|
||||
template <int Y>
|
||||
constexpr bool operator==(Elem<Y> const&) const
|
||||
{ return X == Y; }
|
||||
};
|
||||
|
||||
struct F {
|
||||
template <class ...Args>
|
||||
constexpr auto operator()(Args&& ...args) const {
|
||||
return std::make_tuple(Elem<888>{}, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
struct G {
|
||||
template <class ...Args>
|
||||
constexpr auto operator()(Args&& ...args) const {
|
||||
return std::make_tuple(Elem<999>{}, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
F const f;
|
||||
G const g;
|
||||
|
||||
{
|
||||
auto c = std::__compose(f, g);
|
||||
assert(c() == f(g()));
|
||||
}
|
||||
{
|
||||
auto c = std::__compose(f, g);
|
||||
assert(c(Elem<0>{}) == f(g(Elem<0>{})));
|
||||
}
|
||||
{
|
||||
auto c = std::__compose(f, g);
|
||||
assert(c(Elem<0>{}, Elem<1>{}) == f(g(Elem<0>{}, Elem<1>{})));
|
||||
}
|
||||
{
|
||||
auto c = std::__compose(f, g);
|
||||
assert(c(Elem<0>{}, Elem<1>{}, Elem<2>{}) == f(g(Elem<0>{}, Elem<1>{}, Elem<2>{})));
|
||||
}
|
||||
|
||||
// Make sure we can call a function that's a pointer to a member function.
|
||||
{
|
||||
struct MemberFunction1 {
|
||||
constexpr Elem<0> foo() { return {}; }
|
||||
};
|
||||
struct MemberFunction2 {
|
||||
constexpr MemberFunction1 bar() { return {}; }
|
||||
};
|
||||
auto c = std::__compose(&MemberFunction1::foo, &MemberFunction2::bar);
|
||||
assert(c(MemberFunction2{}) == Elem<0>{});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -376,12 +376,6 @@ constexpr bool test() {
|
|||
|
||||
// Make sure bind_front is SFINAE friendly
|
||||
{
|
||||
using T = decltype(std::bind_front(std::declval<int(*)(int, int)>(), 1));
|
||||
static_assert(!std::is_invocable<T>::value);
|
||||
static_assert( std::is_invocable<T, int>::value);
|
||||
static_assert(!std::is_invocable<T, void*>::value);
|
||||
static_assert(!std::is_invocable<T, int, int>::value);
|
||||
|
||||
static_assert(!std::is_constructible_v<NotCopyMove, NotCopyMove&>);
|
||||
static_assert(!std::is_move_constructible_v<NotCopyMove>);
|
||||
static_assert(!is_bind_frontable<NotCopyMove>::value);
|
||||
|
@ -399,6 +393,15 @@ constexpr bool test() {
|
|||
static_assert(!is_bind_frontable<decltype(takeAnything), NonConstCopyConstructible>::value);
|
||||
}
|
||||
|
||||
// Make sure bind_front's unspecified type's operator() is SFINAE-friendly
|
||||
{
|
||||
using T = decltype(std::bind_front(std::declval<int(*)(int, int)>(), 1));
|
||||
static_assert(!std::is_invocable<T>::value);
|
||||
static_assert( std::is_invocable<T, int>::value);
|
||||
static_assert(!std::is_invocable<T, void*>::value);
|
||||
static_assert(!std::is_invocable<T, int, int>::value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue