blob: 11c839b6932f6ebee27b14878969321877aa7058 [file] [log] [blame]
// { dg-do run { target c++26 } }
// { dg-require-effective-target hosted }
#include <functional>
#include <string_view>
#include <testsuite_hooks.h>
using std::copyable_function;
static_assert( !std::is_constructible_v<std::copyable_function<void()>,
std::copyable_function<void()&>> );
static_assert( !std::is_constructible_v<std::copyable_function<void()>,
std::copyable_function<void()&&>> );
static_assert( !std::is_constructible_v<std::copyable_function<void()&>,
std::copyable_function<void()&&>> );
static_assert( !std::is_constructible_v<std::copyable_function<void() const>,
std::copyable_function<void()>> );
using FuncType = int(int);
// Top level const qualifiers are ignored and decay is performed in parameters
// of function_types.
static_assert( std::is_same_v<std::copyable_function<void(int const)>,
std::copyable_function<void(int)>> );
static_assert( std::is_same_v<std::copyable_function<void(int[2])>,
std::copyable_function<void(int*)>>);
static_assert( std::is_same_v<std::copyable_function<void(int[])>,
std::copyable_function<void(int*)>>);
static_assert( std::is_same_v<std::copyable_function<void(int const[5])>,
std::copyable_function<void(int const*)>>);
static_assert( std::is_same_v<std::copyable_function<void(FuncType)>,
std::copyable_function<void(FuncType*)>>);
// Non-trivial args, guarantess that type is not passed by copy
struct CountedArg
{
CountedArg() = default;
CountedArg(const CountedArg& f) noexcept : counter(f.counter) { ++counter; }
CountedArg& operator=(CountedArg&&) = delete;
int counter = 0;
};
CountedArg const c;
// When copyable_function or move_only_function is constructed from other copyable_function,
// the compiler can avoid double indirection per C++26 [func.wrap.general] p2.
void
test01()
{
auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
std::copyable_function<int(CountedArg) const noexcept> c1(f);
using CF = std::copyable_function<int(CountedArg) const noexcept>;
VERIFY( c1(c) == 1 );
std::copyable_function<int(CountedArg) const> c2a(c1);
VERIFY( c2a(c) == 1 );
std::copyable_function<int(CountedArg) const> c2b(static_cast<CF>(c1));
VERIFY( c2b(c) == 1 );
std::move_only_function<int(CountedArg) const> m2a(c1);
VERIFY( m2a(c) == 1 );
std::move_only_function<int(CountedArg) const> m2b(static_cast<CF>(c1));
VERIFY( m2b(c) == 1 );
std::copyable_function<int(CountedArg)> c3a(c1);
VERIFY( c3a(c) == 1 );
std::copyable_function<int(CountedArg)> c3b(static_cast<CF>(c1));
VERIFY( c3b(c) == 1 );
std::move_only_function<int(CountedArg)> m3a(c1);
VERIFY( m3a(c) == 1 );
std::move_only_function<int(CountedArg)> m3b(static_cast<CF>(c1));
VERIFY( m3b(c) == 1 );
// Invokers internally uses Counted&& for non-trivial types,
// sinature remain compatible.
std::copyable_function<int(CountedArg&&)> c4a(c1);
VERIFY( c4a({}) == 0 );
std::copyable_function<int(CountedArg&&)> c4b(static_cast<CF>(c1));
VERIFY( c4b({}) == 0 );
std::move_only_function<int(CountedArg&&)> m4a(c1);
VERIFY( m4a({}) == 0 );
std::move_only_function<int(CountedArg&&)> m4b(static_cast<CF>(c1));
VERIFY( m4b({}) == 0 );
std::copyable_function<int(CountedArg&&)&> c5a(c1);
VERIFY( c5a({}) == 0 );
std::copyable_function<int(CountedArg&&)&&> c5b(static_cast<CF>(c1));
VERIFY( std::move(c5b)({}) == 0 );
std::move_only_function<int(CountedArg&&)&> m5a(c1);
VERIFY( m5a({}) == 0 );
std::move_only_function<int(CountedArg&&)&&> m5b(static_cast<CF>(c1));
VERIFY( std::move(m5b)({}) == 0 );
// Incompatible signatures
std::copyable_function<long(CountedArg) const noexcept> c6a(c1);
VERIFY( c6a(c) == 2 );
std::copyable_function<long(CountedArg) const noexcept> c6b(static_cast<CF>(c1));
VERIFY( c6b(c) == 2 );
std::move_only_function<long(CountedArg) const noexcept> m6a(c1);
VERIFY( m6a(c) == 2 );
std::move_only_function<long(CountedArg) const noexcept> m6b(static_cast<CF>(c1));
VERIFY( m6b(c) == 2 );
}
void
test02()
{
auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
std::copyable_function<int(CountedArg) const noexcept> c1(f);
using CF = std::copyable_function<int(CountedArg) const noexcept>;
VERIFY( c1(c) == 1 );
std::copyable_function<int(CountedArg) const> c2;
c2 = c1;
VERIFY( c2(c) == 1 );
c2 = static_cast<CF>(c1);
VERIFY( c2(c) == 1 );
std::move_only_function<int(CountedArg) const> m2;
m2 = c1;
VERIFY( m2(c) == 1 );
m2 = static_cast<CF>(c1);
VERIFY( m2(c) == 1 );
// Incompatible signatures
std::copyable_function<long(CountedArg) const noexcept> c3;
c3 = c1;
VERIFY( c3(c) == 2 );
c3 = static_cast<CF>(c1);
VERIFY( c3(c) == 2 );
std::move_only_function<long(CountedArg) const noexcept> m3;
m3 = c1;
VERIFY( m3(c) == 2 );
m3 = static_cast<CF>(c1);
VERIFY( m3(c) == 2 );
}
void
test03()
{
std::copyable_function<int(long) const noexcept> c1;
VERIFY( c1 == nullptr );
std::copyable_function<int(long) const> c2(c1);
VERIFY( c2 == nullptr );
c2 = c1;
VERIFY( c2 == nullptr );
c2 = std::move(c1);
VERIFY( c2 == nullptr );
std::copyable_function<bool(int) const> c3(std::move(c1));
VERIFY( c3 == nullptr );
c3 = c1;
VERIFY( c3 == nullptr );
c3 = std::move(c1);
VERIFY( c3 == nullptr );
// LWG4255 move_only_function constructor should recognize empty
// copyable_functions
std::move_only_function<int(long) const noexcept> m1(c1);
VERIFY( m1 == nullptr );
m1 = c1;
VERIFY( m1 == nullptr );
m1 = std::move(c1);
VERIFY( m1 == nullptr );
std::move_only_function<int(long) const> m2(c1);
VERIFY( m2 == nullptr );
m2 = c1;
VERIFY( m2 == nullptr );
m2 = std::move(c1);
VERIFY( m2 == nullptr );
std::move_only_function<bool(int) const> m3(std::move(c1));
VERIFY( m3 == nullptr );
m3 = c1;
VERIFY( m3 == nullptr );
m3 = std::move(c1);
VERIFY( m3 == nullptr );
}
void
test04()
{
struct F
{
int operator()(CountedArg const& arg) noexcept
{ return arg.counter; }
int operator()(CountedArg const& arg) const noexcept
{ return arg.counter + 1000; }
};
F f;
std::copyable_function<int(CountedArg) const> c1(f);
VERIFY( c1(c) == 1001 );
// Call const overload as std::copyable_function<int(CountedArg) const>
// inside td::copyable_function<int(CountedArg)> would do.
std::copyable_function<int(CountedArg)> c2(c1);
VERIFY( c2(c) == 1001 );
std::move_only_function<int(CountedArg)> m2(c1);
VERIFY( m2(c) == 1001 );
std::copyable_function<int(CountedArg)> m3(f);
VERIFY( m3(c) == 1 );
}
void
test05()
{
auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
std::copyable_function<int(CountedArg)> w1(f);
// copyable_function stores copyable_function due incompatibile signatures
std::copyable_function<int(CountedArg const&)> w2(std::move(w1));
// copy is made when passing to int(CountedArg)
VERIFY( w2(c) == 1 );
// wrapped 3 times
w1 = std::move(w2);
VERIFY( w1(c) == 2 );
// wrapped 4 times
w2 = std::move(w1);
VERIFY( w2(c) == 2 );
// wrapped 5 times
w1 = std::move(w2);
VERIFY( w1(c) == 3 );
}
void
test06()
{
// No special interoperability with std::function
auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
std::function<int(CountedArg)> f1(f);
std::copyable_function<int(CountedArg) const> c1(std::move(f1));
VERIFY( c1(c) == 2 );
std::copyable_function<int(CountedArg) const> c2(f);
std::function<int(CountedArg)> f2(c2);
VERIFY( f2(c) == 2 );
}
void
test07()
{
// Scalar types and small trivially move constructible types are passed
// by value to invoker. So int&& signature is not compatible for such types.
auto fi = [](CountedArg const& arg, int) noexcept { return arg.counter; };
std::copyable_function<int(CountedArg, int) const noexcept> ci1(fi);
VERIFY( ci1(c, 0) == 1 );
std::copyable_function<int(CountedArg, int&&) const noexcept> ci2(ci1);
VERIFY( ci2(c, 0) == 2 );
auto fs = [](CountedArg const& arg, std::string_view) noexcept { return arg.counter; };
std::copyable_function<int(CountedArg, std::string_view) const noexcept> cs1(fs);
VERIFY( cs1(c, "") == 1 );
std::copyable_function<int(CountedArg, std::string_view&&) const noexcept> cs2(cs1);
VERIFY( cs2(c, "") == 2 );
}
int main()
{
test01();
test02();
test03();
test04();
test05();
test06();
test07();
}