blob: efebb6026091fa8a58e10d3b243561f992b115b5 [file] [edit]
//===----------------------------------------------------------------------===//
//
// 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: std-at-least-c++26
// <functional>
// template<auto f, class... Args>
// constexpr unspecified bind_front(Args&&...);
#include <functional>
#include <cassert>
#include <concepts>
#include <tuple>
#include <type_traits>
#include <utility>
#include "test_macros.h"
#include "types.h"
constexpr void test_basic_bindings() {
{ // Bind arguments, call without arguments
{
auto f = std::bind_front<MakeTuple{}>();
assert(f() == std::make_tuple());
}
{
auto f = std::bind_front<MakeTuple{}>(Elem<1>{});
assert(f() == std::make_tuple(Elem<1>{}));
}
{
auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{});
assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{}));
}
{
auto f = std::bind_front<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_front<MakeTuple{}>();
assert(f(Elem<1>{}) == std::make_tuple(Elem<1>{}));
}
{
auto f = std::bind_front<MakeTuple{}>();
assert(f(Elem<1>{}, Elem<2>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}));
}
{
auto f = std::bind_front<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_front<MakeTuple{}>(Elem<1>{});
assert(f(Elem<10>{}) == std::make_tuple(Elem<1>{}, Elem<10>{}));
}
{
auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{});
assert(f(Elem<10>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<10>{}));
}
{
auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{}, Elem<3>{});
assert(f(Elem<10>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}, Elem<10>{}));
}
{
auto f = std::bind_front<MakeTuple{}>(Elem<1>{});
assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<1>{}, Elem<10>{}, Elem<11>{}));
}
{
auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{});
assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<10>{}, Elem<11>{}));
}
{
auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{}, Elem<3>{});
assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}, Elem<10>{}, Elem<11>{}));
}
{
auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{}, Elem<3>{});
assert(f(Elem<10>{}, Elem<11>{}, Elem<12>{}) ==
std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}, Elem<10>{}, Elem<11>{}, Elem<12>{}));
}
}
{ // Basic tests with fundamental types
const int n = 2;
const int m = 1;
int o = 0;
auto add = [](int x, int y) { return x + y; };
auto add6 = [](int a, int b, int c, int d, int e, int f) { return a + b + c + d + e + f; };
auto increment = [](int& x) { return ++x; };
auto a = std::bind_front<add>(m, n);
assert(a() == 3);
auto b = std::bind_front<add6>(m, n, m, m, m, m);
assert(b() == 7);
auto c = std::bind_front<add6>(n, m);
assert(c(1, 1, 1, 1) == 7);
auto f = std::bind_front<add>(n);
assert(f(3) == 5);
auto g = std::bind_front<add>(n, 1);
assert(g() == 3);
auto h = std::bind_front<add6>(1, 1, 1);
assert(h(2, 2, 2) == 9);
auto i = std::bind_front<increment>();
assert(i(o) == 1);
assert(o == 1);
auto j = std::bind_front<increment>(std::ref(o));
assert(j() == 2);
assert(o == 2);
}
}
constexpr void test_edge_cases() {
{ // 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;
int j = 2;
auto f = std::bind_front<sub>(std::ref(i));
assert(f(std::ref(j)) == -1);
}
{ // Make sure we can call a function that's a pointer to a member function.
struct MemberFunction {
constexpr int mul(int x, int y) { return x * y; }
};
MemberFunction value;
auto fn = std::bind_front<&MemberFunction::mul>(value, 2);
assert(fn(3) == 6);
}
{ // Make sure we can call a function that's a pointer to a member object.
struct MemberObject {
int obj;
};
MemberObject value{.obj = 3};
auto fn1 = std::bind_front<&MemberObject::obj>();
assert(fn1(value) == 3);
auto fn2 = std::bind_front<&MemberObject::obj>(value);
assert(fn2() == 3);
}
}
constexpr void test_passing_arguments() {
{ // Make sure that we copy the bound arguments into the unspecified-type.
int n = 2;
auto f = std::bind_front<[](int x, int y) { return x + y; }>(n, 1);
n = 100;
assert(f() == 3);
}
{ // Make sure we pass the bound arguments to the function object
// with the right value category.
{
auto was_copied = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::copy; };
CopyMoveInfo info;
auto f = std::bind_front<was_copied>(info);
assert(f());
}
{
auto was_moved = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::move; };
CopyMoveInfo info;
auto f = std::bind_front<was_moved>(info);
assert(std::move(f)());
}
}
}
constexpr void test_perfect_forwarding_call_wrapper() {
{ // Make sure we call the correctly cv-ref qualified operator()
// based on the value category of the bind_front<NTTP> unspecified-type.
struct X {
constexpr int operator()() & { return 1; }
constexpr int operator()() const& { return 2; }
constexpr int operator()() && { return 3; }
constexpr int operator()() const&& { return 4; }
};
auto f = std::bind_front<X{}>();
using F = decltype(f);
assert(static_cast<F&>(f)() == 2);
assert(static_cast<const F&>(f)() == 2);
assert(static_cast<F&&>(f)() == 2);
assert(static_cast<const F&&>(f)() == 2);
}
// Call to `bind_front<NTTP>` unspecified-type's operator() should always result in call to the const& overload of the underlying function object.
{
{ // Make sure unspecified-type is still callable when we delete the & overload.
struct X {
int operator()() & = delete;
int operator()() const&;
int operator()() &&;
int operator()() const&&;
};
using F = decltype(std::bind_front<X{}>());
static_assert(std::invocable<F&>);
static_assert(std::invocable<const F&>);
static_assert(std::invocable<F>);
static_assert(std::invocable<const F>);
}
{ // Make sure unspecified-type is not callable when we delete the const& overload.
struct X {
int operator()() &;
int operator()() const& = delete;
int operator()() &&;
int operator()() const&&;
};
using F = decltype(std::bind_front<X{}>());
static_assert(!std::invocable<F&>);
static_assert(!std::invocable<const F&>);
static_assert(!std::invocable<F>);
static_assert(!std::invocable<const F>);
}
{ // Make sure unspecified-type is still callable when we delete the && overload.
struct X {
int operator()() &;
int operator()() const&;
int operator()() && = delete;
int operator()() const&&;
};
using F = decltype(std::bind_front<X{}>());
static_assert(std::invocable<F&>);
static_assert(std::invocable<const F&>);
static_assert(std::invocable<F>);
static_assert(std::invocable<const F>);
}
{ // Make sure unspecified-type is still callable when we delete the const&& overload.
struct X {
int operator()() &;
int operator()() const&;
int operator()() &&;
int operator()() const&& = delete;
};
using F = decltype(std::bind_front<X{}>());
static_assert(std::invocable<F&>);
static_assert(std::invocable<const F&>);
static_assert(std::invocable<F>);
static_assert(std::invocable<const F>);
}
}
{ // Test perfect forwarding when various overloads are available
struct X {
constexpr Tag<0> operator()(int&, char) const { return {}; }
constexpr Tag<1> operator()(const int&, char) const { return {}; }
constexpr Tag<2> operator()(int&&, char) const { return {}; }
constexpr Tag<3> operator()(const int&&, char) const { return {}; }
};
auto f = std::bind_front<X{}>(0);
std::same_as<Tag<0>> auto _ = f('a');
std::same_as<Tag<1>> auto _ = std::as_const(f)('a');
std::same_as<Tag<2>> auto _ = std::move(f)('a');
std::same_as<Tag<3>> auto _ = std::move(std::as_const(f))('a');
}
{ // Test perfect forwarding
auto f = [](int& val) {
val = 5;
return 10;
};
auto bf = std::bind_front<f>();
int val = 0;
assert(bf(val) == 10);
assert(val == 5);
using BF = decltype(bf);
static_assert(std::invocable<BF, int&>);
static_assert(!std::invocable<BF, int>);
}
}
constexpr void test_return_type() {
{ // Test constructors and assignment operators
struct LeftShift {
constexpr unsigned int operator()(unsigned int x, unsigned int y) const { return x << y; }
};
auto power_of_2 = std::bind_front<LeftShift{}>(1);
assert(power_of_2(5) == 32U);
assert(power_of_2(4) == 16U);
auto moved = std::move(power_of_2);
assert(moved(6) == 64);
assert(moved(7) == 128);
auto copied = power_of_2;
assert(copied(3) == 8);
assert(copied(2) == 4);
moved = std::move(copied);
assert(copied(1) == 2);
assert(copied(0) == 1);
copied = moved;
assert(copied(8) == 256);
assert(copied(9) == 512);
}
{ // Make sure `bind_front<NTTP>` unspecified-type's operator() is SFINAE-friendly.
using F = decltype(std::bind_front<[](int x, int y) { return x / y; }>(1));
static_assert(!std::is_invocable<F>::value);
static_assert(std::is_invocable<F, int>::value);
static_assert(!std::is_invocable<F, void*>::value);
static_assert(!std::is_invocable<F, int, int>::value);
}
{ // Test noexceptness
auto always_noexcept = std::bind_front<MaybeNoexceptFn<true>{}>();
static_assert(noexcept(always_noexcept()));
auto never_noexcept = std::bind_front<MaybeNoexceptFn<false>{}>();
static_assert(!noexcept(never_noexcept()));
}
#ifndef TEST_HAS_NO_EXCEPTIONS
# ifndef __cpp_constexpr_exceptions
if !consteval
# endif
{ // Test exception propagation
auto throws = [](int x, int y) { throw x + y; };
auto f = std::bind_front<throws>(10);
static_assert(!noexcept(f(20)));
try {
f(20);
} catch (int z) {
assert(z == 30);
}
}
#endif // TEST_HAS_NO_EXCEPTIONS
{ // Test calling volatile wrapper -- we allow it as an extension
using Fn = decltype(std::bind_front<std::integral_constant<int, 0>{}>());
static_assert(std::invocable<volatile Fn&>);
static_assert(std::invocable<const volatile Fn&>);
static_assert(std::invocable<volatile Fn>);
static_assert(std::invocable<const volatile Fn>);
}
}
constexpr bool test() {
test_basic_bindings();
test_edge_cases();
test_passing_arguments();
test_perfect_forwarding_call_wrapper();
test_return_type();
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}