/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/Function.h>
#include <array>
#include <cstdarg>
#include <functional>
#include <folly/Memory.h>
#include <folly/lang/Keep.h>
#include <folly/portability/GTest.h>
using folly::Function;
extern "C" FOLLY_KEEP void check_folly_function_move(void* src, void* dst) {
new (dst) Function<void()>(std::move(*static_cast<Function<void()>*>(src)));
}
extern "C" FOLLY_KEEP void check_folly_function_nuke(void* fun) {
static_cast<Function<void()>*>(fun)->~Function();
}
extern "C" FOLLY_KEEP void check_folly_function_move_assign(
void* src, void* dst) {
*static_cast<Function<void()>*>(src) =
std::move(*static_cast<Function<void()>*>(dst));
}
template <bool Triv, bool NxCopy>
struct check_invocable_base;
template <bool NxCopy>
struct check_invocable_base<false, NxCopy> {
FOLLY_NOINLINE check_invocable_base(check_invocable_base const&) noexcept(
NxCopy) {}
void operator=(check_invocable_base const&) = delete;
FOLLY_NOINLINE ~check_invocable_base() {}
};
template <>
struct check_invocable_base<true, true> {};
template <bool Triv, bool NxCopy, size_t Size, size_t Align>
struct check_invocable : check_invocable_base<Triv, NxCopy> {
std::aligned_storage_t<Size, Align> storage;
using check_invocable_base<Triv, NxCopy>::check_invocable_base;
void operator()() const noexcept {}
};
extern "C" FOLLY_KEEP void check_folly_function_make_in_situ_trivial(
void* fun, void* obj) {
constexpr auto size = 6 * sizeof(void*);
constexpr auto align = folly::max_align_v;
using inv_t = check_invocable<true, true, size, align>;
static_assert(std::is_trivially_copyable_v<inv_t>);
auto& inv = *static_cast<inv_t*>(obj);
::new (fun) Function<void()>(inv);
}
extern "C" FOLLY_KEEP void check_folly_function_make_in_situ_default(
void* fun, void* obj) {
constexpr auto size = 6 * sizeof(void*);
constexpr auto align = folly::max_align_v;
using inv_t = check_invocable<false, true, size, align>;
static_assert(!std::is_trivially_copyable_v<inv_t>);
auto& inv = *static_cast<inv_t*>(obj);
::new (fun) Function<void()>(inv);
}
extern "C" FOLLY_KEEP void check_folly_function_make_on_heap_trivial(
void* fun, void* obj) {
constexpr auto size = 12 * sizeof(void*);
constexpr auto align = folly::max_align_v;
using inv_t = check_invocable<true, true, size, align>;
static_assert(std::is_trivially_copyable_v<inv_t>);
auto& inv = *static_cast<inv_t*>(obj);
::new (fun) Function<void()>(inv);
}
extern "C" FOLLY_KEEP void check_folly_function_make_on_heap_default(
void* fun, void* obj) {
constexpr auto size = 12 * sizeof(void*);
constexpr auto align = folly::max_align_v;
using inv_t = check_invocable<false, true, size, align>;
static_assert(!std::is_trivially_copyable_v<inv_t>);
auto& inv = *static_cast<inv_t*>(obj);
::new (fun) Function<void()>(inv);
}
namespace {
int func_int_int_add_25(int x) {
return x + 25;
}
int func_int_int_add_111(int x) {
return x + 111;
}
float floatMult(float a, float b) {
return a * b;
}
template <class T, size_t S>
struct Functor {
std::array<T, S> data = {{0}};
// Two operator() with different argument types.
// The InvokeReference tests use both
T const& operator()(size_t index) const { return data[index]; }
T operator()(size_t index, T const& value) {
T oldvalue = data[index];
data[index] = value;
return oldvalue;
}
};
template <typename Ret, typename... Args>
void deduceArgs(Function<Ret(Args...)>) {}
struct CallableButNotCopyable {
CallableButNotCopyable() {}
CallableButNotCopyable(CallableButNotCopyable const&) = delete;
CallableButNotCopyable(CallableButNotCopyable&&) = delete;
CallableButNotCopyable& operator=(CallableButNotCopyable const&) = delete;
CallableButNotCopyable& operator=(CallableButNotCopyable&&) = delete;
template <class... Args>
void operator()(Args&&...) const {}
};
} // namespace
// TEST =====================================================================
// Test constructibility and non-constructibility for some tricky conversions
static_assert(
!std::is_assignable<Function<void()>, CallableButNotCopyable>::value, "");
static_assert(
!std::is_constructible<Function<void()>, CallableButNotCopyable&>::value,
"");
static_assert(
!std::is_constructible<Function<void() const>, CallableButNotCopyable>::
value,
"");
static_assert(
!std::is_constructible<Function<void() const>, CallableButNotCopyable&>::
value,
"");
static_assert(
!std::is_assignable<Function<void()>, CallableButNotCopyable>::value, "");
static_assert(
!std::is_assignable<Function<void()>, CallableButNotCopyable&>::value, "");
static_assert(
!std::is_assignable<Function<void() const>, CallableButNotCopyable>::value,
"");
static_assert(
!std::is_assignable<Function<void() const>, CallableButNotCopyable&>::value,
"");
static_assert(
std::is_constructible<Function<int(int)>, Function<int(int) const>>::value,
"");
static_assert(
!std::is_constructible<Function<int(int) const>, Function<int(int)>>::value,
"");
static_assert(
std::is_constructible<Function<int(short)>, Function<short(int) const>>::
value,
"");
static_assert(
!std::is_constructible<Function<int(short) const>, Function<short(int)>>::
value,
"");
static_assert(
!std::is_constructible<Function<int(int)>, Function<int(int) const>&>::
value,
"");
static_assert(
!std::is_constructible<Function<int(int) const>, Function<int(int)>&>::
value,
"");
static_assert(
!std::is_constructible<Function<int(short)>, Function<short(int) const>&>::
value,
"");
static_assert(
!std::is_constructible<Function<int(short) const>, Function<short(int)>&>::
value,
"");
static_assert(
std::is_assignable<Function<int(int)>, Function<int(int) const>>::value,
"");
static_assert(
!std::is_assignable<Function<int(int) const>, Function<int(int)>>::value,
"");
static_assert(
std::is_assignable<Function<int(short)>, Function<short(int) const>>::value,
"");
static_assert(
!std::is_assignable<Function<int(short) const>, Function<short(int)>>::
value,
"");
static_assert(
!std::is_assignable<Function<int(int)>, Function<int(int) const>&>::value,
"");
static_assert(
!std::is_assignable<Function<int(int) const>, Function<int(int)>&>::value,
"");
static_assert(
!std::is_assignable<Function<int(short)>, Function<short(int) const>&>::
value,
"");
static_assert(
!std::is_assignable<Function<int(short) const>, Function<short(int)>&>::
value,
"");
static_assert(
std::is_nothrow_constructible<
Function<int(int)>,
Function<int(int) const>>::value,
"");
static_assert(
!std::is_nothrow_constructible<
Function<int(short)>,
Function<short(int) const>>::value,
"");
static_assert(
std::is_nothrow_assignable<Function<int(int)>, Function<int(int) const>>::
value,
"");
static_assert(
!std::is_nothrow_assignable<
Function<int(short)>,
Function<short(int) const>>::value,
"");
static_assert(
!std::is_constructible<Function<int const&()>, int (*)()>::value, "");
static_assert(
!std::is_constructible<Function<int const&() const>, int (*)()>::value, "");
static_assert( //
!std::is_constructible_v< //
Function<int() noexcept>,
int (*)()>);
static_assert( //
!std::is_constructible_v< //
Function<int() const noexcept>,
int (*)()>);
static_assert( //
std::is_constructible_v< //
Function<int() noexcept>,
int (*)() noexcept>);
static_assert( //
std::is_constructible_v< //
Function<int() const noexcept>,
int (*)() noexcept>);
static_assert( //
!std::is_constructible_v< //
Function<int const&() noexcept>,
int (*)()>);
static_assert( //
!std::is_constructible_v< //
Function<int const&() const noexcept>,
int (*)()>);
static_assert( //
!std::is_constructible_v< //
Function<int() noexcept>,
Function<int()>>);
static_assert( //
!std::is_constructible_v< //
Function<int() const noexcept>,
Function<int() const>>);
static_assert( //
std::is_constructible_v< //
Function<int()>,
Function<int() noexcept>>);
static_assert( //
std::is_constructible_v< //
Function<int() const>,
Function<int() const noexcept>>);
static_assert(std::is_nothrow_destructible<Function<int(int)>>::value, "");
struct ctor_guide {
static void fn();
static void fn_nx() noexcept;
struct call {
void operator()();
};
struct call_c {
void operator()() const;
};
struct call_nx {
void operator()() noexcept;
};
struct call_c_nx {
void operator()() const noexcept;
};
};
static_assert( //
std::is_same_v< //
Function<void()>,
decltype(Function{ctor_guide::fn})>);
static_assert( //
std::is_same_v< //
Function<void()>,
decltype(Function{&ctor_guide::fn})>);
static_assert( //
std::is_same_v< //
Function<void()>,
decltype(Function{ctor_guide::call{}})>);
static_assert( //
std::is_same_v< //
Function<void() const>,
decltype(Function{ctor_guide::call_c{}})>);
static_assert( //
std::is_same_v< //
Function<void() noexcept>,
decltype(Function{ctor_guide::fn_nx})>);
static_assert( //
std::is_same_v< //
Function<void() noexcept>,
decltype(Function{&ctor_guide::fn_nx})>);
static_assert( //
std::is_same_v< //
Function<void() noexcept>,
decltype(Function{ctor_guide::call_nx{}})>);
static_assert( //
std::is_same_v< //
Function<void() const noexcept>,
decltype(Function{ctor_guide::call_c_nx{}})>);
struct RecStd {
using type = std::function<RecStd()>;
/* implicit */ RecStd(type f) : func(f) {}
explicit operator type() { return func; }
type func;
};
// Recursive class - regression case
struct RecFolly {
using type = folly::Function<RecFolly()>;
/* implicit */ RecFolly(type f) : func(std::move(f)) {}
explicit operator type() { return std::move(func); }
type func;
};
// TEST =====================================================================
// InvokeFunctor & InvokeReference
TEST(Function, InvokeFunctor) {
Functor<int, 100> func;
static_assert(
sizeof(func) > sizeof(Function<int(size_t)>),
"sizeof(Function) is much larger than expected");
func(5, 123);
Function<int(size_t) const> getter = std::move(func);
// Function will allocate memory on the heap to store the functor object
EXPECT_GT(getter.heapAllocatedMemory(), 0);
EXPECT_EQ(123, getter(5));
}
TEST(Function, InvokeReference) {
Functor<int, 10> func;
func(5, 123);
// Have Functions for getter and setter, both referencing the same funtor
Function<int(size_t) const> getter = std::ref(func);
Function<int(size_t, int)> setter = std::ref(func);
EXPECT_EQ(123, getter(5));
EXPECT_EQ(123, setter(5, 456));
EXPECT_EQ(456, setter(5, 567));
EXPECT_EQ(567, getter(5));
}
// TEST =====================================================================
// Emptiness
TEST(Function, EmptinessT) {
Function<int(int)> f;
EXPECT_EQ(f, nullptr);
EXPECT_EQ(nullptr, f);
EXPECT_FALSE(f);
EXPECT_THROW(f(98), std::bad_function_call);
Function<int(int)> g([](int x) { return x + 1; });
EXPECT_NE(g, nullptr);
EXPECT_NE(nullptr, g);
// Explicitly convert to bool to work around
// https://github.com/google/googletest/issues/429
EXPECT_TRUE(bool(g));
EXPECT_EQ(100, g(99));
Function<int(int)> h(&func_int_int_add_25);
EXPECT_NE(h, nullptr);
EXPECT_NE(nullptr, h);
EXPECT_TRUE(bool(h));
EXPECT_EQ(125, h(100));
h = {};
EXPECT_EQ(h, nullptr);
EXPECT_EQ(nullptr, h);
EXPECT_FALSE(h);
EXPECT_THROW(h(101), std::bad_function_call);
Function<int(int)> i{Function<int(int)>{}};
EXPECT_EQ(i, nullptr);
EXPECT_EQ(nullptr, i);
EXPECT_FALSE(i);
EXPECT_THROW(i(107), std::bad_function_call);
struct CastableToBool {
bool val;
/* implicit */ CastableToBool(bool b) : val(b) {}
explicit operator bool() { return val; }
};
// models std::function
struct NullptrTestableInSitu {
int res;
[[maybe_unused]] explicit NullptrTestableInSitu(std::nullptr_t);
explicit NullptrTestableInSitu(int i) : res(i) {}
CastableToBool operator==(std::nullptr_t) const { return res % 3 != 1; }
int operator()(int in) const { return res * in; }
};
struct NullptrTestableOnHeap : NullptrTestableInSitu {
unsigned char data[1024 - sizeof(NullptrTestableInSitu)];
using NullptrTestableInSitu::NullptrTestableInSitu;
};
Function<int(int)> j(NullptrTestableInSitu(2));
EXPECT_EQ(j, nullptr);
EXPECT_EQ(nullptr, j);
EXPECT_FALSE(j);
EXPECT_THROW(j(107), std::bad_function_call);
Function<int(int)> k(NullptrTestableInSitu(4));
EXPECT_NE(k, nullptr);
EXPECT_NE(nullptr, k);
EXPECT_TRUE(k);
EXPECT_EQ(428, k(107));
Function<int(int)> l(NullptrTestableOnHeap(2));
EXPECT_EQ(l, nullptr);
EXPECT_EQ(nullptr, l);
EXPECT_FALSE(l);
EXPECT_THROW(l(107), std::bad_function_call);
Function<int(int)> m(NullptrTestableOnHeap(4));
EXPECT_NE(m, nullptr);
EXPECT_NE(nullptr, m);
EXPECT_TRUE(m);
EXPECT_EQ(428, m(107));
}
// TEST =====================================================================
// Swap
template <bool UseSwapMethod>
void swap_test() {
Function<int(int)> mf1(func_int_int_add_25);
Function<int(int)> mf2(func_int_int_add_111);
EXPECT_EQ(125, mf1(100));
EXPECT_EQ(211, mf2(100));
if (UseSwapMethod) {
mf1.swap(mf2);
} else {
swap(mf1, mf2);
}
EXPECT_EQ(125, mf2(100));
EXPECT_EQ(211, mf1(100));
Function<int(int)> mf3(nullptr);
EXPECT_EQ(mf3, nullptr);
if (UseSwapMethod) {
mf1.swap(mf3);
} else {
swap(mf1, mf3);
}
EXPECT_EQ(211, mf3(100));
EXPECT_EQ(nullptr, mf1);
Function<int(int)> mf4([](int x) { return x + 222; });
EXPECT_EQ(322, mf4(100));
if (UseSwapMethod) {
mf4.swap(mf3);
} else {
swap(mf4, mf3);
}
EXPECT_EQ(211, mf4(100));
EXPECT_EQ(322, mf3(100));
if (UseSwapMethod) {
mf3.swap(mf1);
} else {
swap(mf3, mf1);
}
EXPECT_EQ(nullptr, mf3);
EXPECT_EQ(322, mf1(100));
}
TEST(Function, SwapMethod) {
swap_test<true>();
}
TEST(Function, SwapFunction) {
swap_test<false>();
}
// TEST =====================================================================
// Bind
TEST(Function, Bind) {
Function<float(float, float)> fnc = floatMult;
auto task = std::bind(std::move(fnc), 2.f, 4.f);
EXPECT_THROW(fnc(0, 0), std::bad_function_call);
EXPECT_EQ(8, task());
auto task2(std::move(task));
EXPECT_THROW(task(), std::bad_function_call);
EXPECT_EQ(8, task2());
}
// TEST =====================================================================
// NonCopyableLambda
TEST(Function, NonCopyableLambda) {
auto unique_ptr_int = std::make_unique<int>(900);
EXPECT_EQ(900, *unique_ptr_int);
struct {
char data[64];
} fooData = {{0}};
(void)fooData; // suppress gcc warning about fooData not being used
auto functor = std::bind(
[fooData](std::unique_ptr<int>& up) mutable {
(void)fooData;
return ++*up;
},
std::move(unique_ptr_int));
EXPECT_EQ(901, functor());
Function<int(void)> func = std::move(functor);
EXPECT_GT(func.heapAllocatedMemory(), 0);
EXPECT_EQ(902, func());
}
// TEST =====================================================================
// OverloadedFunctor
TEST(Function, OverloadedFunctor) {
struct OverloadedFunctor {
// variant 1
int operator()(int x) { return 100 + 1 * x; }
// variant 2 (const-overload of v1)
int operator()(int x) const { return 100 + 2 * x; }
// variant 3
int operator()(int x, int) { return 100 + 3 * x; }
// variant 4 (const-overload of v3)
int operator()(int x, int) const { return 100 + 4 * x; }
// variant 5 (non-const, has no const-overload)
int operator()(int x, char const*) { return 100 + 5 * x; }
// variant 6 (const only)
int operator()(int x, std::vector<int> const&) const { return 100 + 6 * x; }
};
OverloadedFunctor of;
Function<int(int)> variant1 = of;
EXPECT_EQ(100 + 1 * 15, variant1(15));
Function<int(int) const> variant2 = of;
EXPECT_EQ(100 + 2 * 16, variant2(16));
Function<int(int, int)> variant3 = of;
EXPECT_EQ(100 + 3 * 17, variant3(17, 0));
Function<int(int, int) const> variant4 = of;
EXPECT_EQ(100 + 4 * 18, variant4(18, 0));
Function<int(int, char const*)> variant5 = of;
EXPECT_EQ(100 + 5 * 19, variant5(19, "foo"));
Function<int(int, std::vector<int> const&)> variant6 = of;
EXPECT_EQ(100 + 6 * 20, variant6(20, {}));
EXPECT_EQ(100 + 6 * 20, variant6(20, {1, 2, 3}));
Function<int(int, std::vector<int> const&) const> variant6const = of;
EXPECT_EQ(100 + 6 * 21, variant6const(21, {}));
// Cast const-functions to non-const and the other way around: if the functor
// has both const and non-const operator()s for a given parameter signature,
// constructing a Function must select one of them, depending on
// whether the function type template parameter is const-qualified or not.
// When the const-ness is later changed (by moving the
// Function<R(Args...)const> into a Function<R(Args...)> or by
// calling the folly::constCastFunction which moves it into a
// Function<R(Args...)const>), the Function must still execute
// the initially selected function.
auto variant1_const = folly::constCastFunction(std::move(variant1));
EXPECT_THROW(variant1(0), std::bad_function_call);
EXPECT_EQ(100 + 1 * 22, variant1_const(22));
Function<int(int)> variant2_nonconst = std::move(variant2);
EXPECT_THROW(variant2(0), std::bad_function_call);
EXPECT_EQ(100 + 2 * 23, variant2_nonconst(23));
auto variant3_const = folly::constCastFunction(std::move(variant3));
EXPECT_THROW(variant3(0, 0), std::bad_function_call);
EXPECT_EQ(100 + 3 * 24, variant3_const(24, 0));
Function<int(int, int)> variant4_nonconst = std::move(variant4);
EXPECT_THROW(variant4(0, 0), std::bad_function_call);
EXPECT_EQ(100 + 4 * 25, variant4_nonconst(25, 0));
auto variant5_const = folly::constCastFunction(std::move(variant5));
EXPECT_THROW(variant5(0, ""), std::bad_function_call);
EXPECT_EQ(100 + 5 * 26, variant5_const(26, "foo"));
auto variant6_const = folly::constCastFunction(std::move(variant6));
EXPECT_THROW(variant6(0, {}), std::bad_function_call);
EXPECT_EQ(100 + 6 * 27, variant6_const(27, {}));
Function<int(int, std::vector<int> const&)> variant6const_nonconst =
std::move(variant6const);
EXPECT_THROW(variant6const(0, {}), std::bad_function_call);
EXPECT_EQ(100 + 6 * 28, variant6const_nonconst(28, {}));
}
// TEST =====================================================================
// Lambda
TEST(Function, Lambda) {
// Non-mutable lambdas: can be stored in a non-const...
Function<int(int)> func = [](int x) { return 1000 + x; };
EXPECT_EQ(1001, func(1));
// ...as well as in a const Function
Function<int(int) const> func_const = [](int x) { return 2000 + x; };
EXPECT_EQ(2001, func_const(1));
// Mutable lambda: can only be stored in a const Function:
int number = 3000;
Function<int()> func_mutable = [number]() mutable { return ++number; };
EXPECT_EQ(3001, func_mutable());
EXPECT_EQ(3002, func_mutable());
// test after const-casting
Function<int(int) const> func_made_const =
folly::constCastFunction(std::move(func));
EXPECT_EQ(1002, func_made_const(2));
EXPECT_THROW(func(0), std::bad_function_call);
Function<int(int)> func_const_made_nonconst = std::move(func_const);
EXPECT_EQ(2002, func_const_made_nonconst(2));
EXPECT_THROW(func_const(0), std::bad_function_call);
Function<int() const> func_mutable_made_const =
folly::constCastFunction(std::move(func_mutable));
EXPECT_EQ(3003, func_mutable_made_const());
EXPECT_EQ(3004, func_mutable_made_const());
EXPECT_THROW(func_mutable(), std::bad_function_call);
}
// TEST =====================================================================
// DataMember & MemberFunction
struct MemberFunc {
int x;
int getX() const { return x; }
void setX(int xx) { x = xx; }
};
TEST(Function, DataMember) {
MemberFunc mf;
MemberFunc const& cmf = mf;
mf.x = 123;
Function<int(MemberFunc const*)> data_getter1 = &MemberFunc::x;
EXPECT_EQ(123, data_getter1(&cmf));
Function<int(MemberFunc*)> data_getter2 = &MemberFunc::x;
EXPECT_EQ(123, data_getter2(&mf));
Function<int(MemberFunc const&)> data_getter3 = &MemberFunc::x;
EXPECT_EQ(123, data_getter3(cmf));
Function<int(MemberFunc&)> data_getter4 = &MemberFunc::x;
EXPECT_EQ(123, data_getter4(mf));
}
TEST(Function, MemberFunction) {
MemberFunc mf;
MemberFunc const& cmf = mf;
mf.x = 123;
Function<int(MemberFunc const*)> getter1 = &MemberFunc::getX;
EXPECT_EQ(123, getter1(&cmf));
Function<int(MemberFunc*)> getter2 = &MemberFunc::getX;
EXPECT_EQ(123, getter2(&mf));
Function<int(MemberFunc const&)> getter3 = &MemberFunc::getX;
EXPECT_EQ(123, getter3(cmf));
Function<int(MemberFunc&)> getter4 = &MemberFunc::getX;
EXPECT_EQ(123, getter4(mf));
Function<void(MemberFunc*, int)> setter1 = &MemberFunc::setX;
setter1(&mf, 234);
EXPECT_EQ(234, mf.x);
Function<void(MemberFunc&, int)> setter2 = &MemberFunc::setX;
setter2(mf, 345);
EXPECT_EQ(345, mf.x);
}
// TEST =====================================================================
// CaptureCopyMoveCount & ParameterCopyMoveCount
class CopyMoveTracker {
public:
struct ConstructorTag {};
CopyMoveTracker() = delete;
explicit CopyMoveTracker(ConstructorTag)
: data_(std::make_shared<std::pair<size_t, size_t>>(0, 0)) {}
CopyMoveTracker(CopyMoveTracker const& o) noexcept : data_(o.data_) {
++data_->first;
}
CopyMoveTracker& operator=(CopyMoveTracker const& o) noexcept {
data_ = o.data_;
++data_->first;
return *this;
}
CopyMoveTracker(CopyMoveTracker&& o) noexcept : data_(o.data_) {
++data_->second;
}
CopyMoveTracker& operator=(CopyMoveTracker&& o) noexcept {
data_ = o.data_;
++data_->second;
return *this;
}
size_t copyCount() const { return data_->first; }
size_t moveCount() const { return data_->second; }
size_t refCount() const { return data_.use_count(); }
void resetCounters() { data_->first = data_->second = 0; }
private:
// copy, move
std::shared_ptr<std::pair<size_t, size_t>> data_;
};
TEST(Function, CaptureCopyMoveCount) {
// This test checks that no unnecessary copies/moves are made.
CopyMoveTracker cmt(CopyMoveTracker::ConstructorTag{});
EXPECT_EQ(0, cmt.copyCount());
EXPECT_EQ(0, cmt.moveCount());
EXPECT_EQ(1, cmt.refCount());
// Move into lambda, move lambda into Function
auto lambda1 = [cmt = std::move(cmt)]() { return cmt.moveCount(); };
Function<size_t(void)> uf1 = std::move(lambda1);
// Max copies: 0. Max copy+moves: 2.
EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 3);
EXPECT_LE(cmt.copyCount(), 0);
cmt.resetCounters();
// Move into lambda, copy lambda into Function
auto lambda2 = [cmt = std::move(cmt)]() { return cmt.moveCount(); };
Function<size_t(void)> uf2 = lambda2;
// Max copies: 1. Max copy+moves: 2.
EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 3);
EXPECT_LE(cmt.copyCount(), 1);
// Invoking Function must not make copies/moves of the callable
cmt.resetCounters();
uf1();
uf2();
EXPECT_EQ(0, cmt.copyCount());
EXPECT_EQ(0, cmt.moveCount());
}
TEST(Function, ParameterCopyMoveCount) {
// This test checks that no unnecessary copies/moves are made.
CopyMoveTracker cmt(CopyMoveTracker::ConstructorTag{});
EXPECT_EQ(0, cmt.copyCount());
EXPECT_EQ(0, cmt.moveCount());
EXPECT_EQ(1, cmt.refCount());
// pass by value
Function<size_t(CopyMoveTracker)> uf1 = [](CopyMoveTracker c) {
return c.moveCount();
};
cmt.resetCounters();
uf1(cmt);
// Max copies: 1. Max copy+moves: 2.
EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 2);
EXPECT_LE(cmt.copyCount(), 1);
cmt.resetCounters();
uf1(std::move(cmt));
// Max copies: 1. Max copy+moves: 2.
EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 2);
EXPECT_LE(cmt.copyCount(), 0);
// pass by reference
Function<size_t(CopyMoveTracker&)> uf2 = [](CopyMoveTracker& c) {
return c.moveCount();
};
cmt.resetCounters();
uf2(cmt);
// Max copies: 0. Max copy+moves: 0.
EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 0);
EXPECT_LE(cmt.copyCount(), 0);
// pass by const reference
Function<size_t(CopyMoveTracker const&)> uf3 = [](CopyMoveTracker const& c) {
return c.moveCount();
};
cmt.resetCounters();
uf3(cmt);
// Max copies: 0. Max copy+moves: 0.
EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 0);
EXPECT_LE(cmt.copyCount(), 0);
// pass by rvalue reference
Function<size_t(CopyMoveTracker&&)> uf4 = [](CopyMoveTracker&& c) {
return c.moveCount();
};
cmt.resetCounters();
uf4(std::move(cmt));
// Max copies: 0. Max copy+moves: 0.
EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 0);
EXPECT_LE(cmt.copyCount(), 0);
}
// TEST =====================================================================
// VariadicTemplate & VariadicArguments
struct VariadicTemplateSum {
int operator()() const { return 0; }
template <class... Args>
int operator()(int x, Args... args) const {
return x + (*this)(args...);
}
};
TEST(Function, VariadicTemplate) {
Function<int(int)> uf1 = VariadicTemplateSum();
Function<int(int, int)> uf2 = VariadicTemplateSum();
Function<int(int, int, int)> uf3 = VariadicTemplateSum();
EXPECT_EQ(66, uf1(66));
EXPECT_EQ(99, uf2(55, 44));
EXPECT_EQ(66, uf3(33, 22, 11));
}
struct VariadicArgumentsSum {
int operator()(int count, ...) const {
int result = 0;
va_list args;
va_start(args, count);
for (int i = 0; i < count; ++i) {
result += va_arg(args, int);
}
va_end(args);
return result;
}
};
TEST(Function, VariadicArguments) {
Function<int(int)> uf1 = VariadicArgumentsSum();
Function<int(int, int)> uf2 = VariadicArgumentsSum();
Function<int(int, int, int)> uf3 = VariadicArgumentsSum();
EXPECT_EQ(0, uf1(0));
EXPECT_EQ(66, uf2(1, 66));
EXPECT_EQ(99, uf3(2, 55, 44));
}
// TEST =====================================================================
// SafeCaptureByReference
// A function can use Function const& as a parameter to signal that it
// is safe to pass a lambda that captures local variables by reference.
// It is safe because we know the function called can only invoke the
// Function until it returns. It can't store a copy of the Function
// (because it's not copyable), and it can't move the Function somewhere
// else (because it gets only a const&).
template <typename T>
void for_each(
T const& range,
Function<void(typename T::value_type const&) const> const& func) {
for (auto const& elem : range) {
func(elem);
}
}
TEST(Function, SafeCaptureByReference) {
std::vector<int> const vec = {20, 30, 40, 2, 3, 4, 200, 300, 400};
int sum = 0;
// for_each's second parameter is of type Function<...> const&.
// Hence we know we can safely pass it a lambda that references local
// variables. There is no way the reference to x will be stored anywhere.
for_each(vec, [&sum](int x) { sum += x; });
EXPECT_EQ(999, sum);
}
// TEST =====================================================================
// IgnoreReturnValue
TEST(Function, IgnoreReturnValue) {
int x = 95;
// Assign a lambda that return int to a folly::Function that returns void.
Function<void()> f = [&]() -> int { return ++x; };
EXPECT_EQ(95, x);
f();
EXPECT_EQ(96, x);
Function<int()> g = [&]() -> int { return ++x; };
Function<void()> cg = std::move(g);
EXPECT_EQ(96, x);
cg();
EXPECT_EQ(97, x);
}
// TEST =====================================================================
// ReturnConvertible, ConvertReturnType
TEST(Function, ReturnConvertible) {
struct CBase {
int x;
};
struct CDerived : CBase {};
Function<double()> f1 = []() -> int { return 5; };
EXPECT_EQ(5.0, f1());
struct Convertible {
double value;
/* implicit */ Convertible(double v) noexcept : value{v} {}
/* implicit */ operator int() const noexcept { return int(value); }
};
Function<int()> f2 = []() -> Convertible { return 5.2; };
EXPECT_EQ(5, f2());
CDerived derived;
derived.x = 55;
Function<CBase const&()> f3 = [&]() -> CDerived const& { return derived; };
EXPECT_EQ(55, f3().x);
Function<CBase const&()> f4 = [&]() -> CDerived& { return derived; };
EXPECT_EQ(55, f4().x);
Function<CBase&()> f5 = [&]() -> CDerived& { return derived; };
EXPECT_EQ(55, f5().x);
Function<CBase const*()> f6 = [&]() -> CDerived const* { return &derived; };
EXPECT_EQ(f6()->x, 55);
Function<CBase const*()> f7 = [&]() -> CDerived* { return &derived; };
EXPECT_EQ(55, f7()->x);
Function<CBase*()> f8 = [&]() -> CDerived* { return &derived; };
EXPECT_EQ(55, f8()->x);
Function<CBase()> f9 = [&]() -> CDerived {
auto d = derived;
d.x = 66;
return d;
};
EXPECT_EQ(66, f9().x);
}
TEST(Function, ConvertReturnType) {
struct CBase {
int x;
};
struct CDerived : CBase {};
struct Convertible {
double value;
/* implicit */ Convertible(double v) noexcept : value{v} {}
/* implicit */ operator int() const noexcept { return int(value); }
};
Function<int()> f1 = []() -> int { return 5; };
Function<double()> cf1 = std::move(f1);
EXPECT_EQ(5.0, cf1());
Function<Convertible()> ccf1 = std::move(cf1);
EXPECT_EQ(5, ccf1());
Function<double()> f2 = []() -> double { return 5.2; };
Function<Convertible()> cf2 = std::move(f2);
EXPECT_EQ(5, cf2());
Function<double()> ccf2 = std::move(cf2);
EXPECT_EQ(5.0, ccf2());
CDerived derived;
derived.x = 55;
Function<CDerived const&()> f3 = [&]() -> CDerived const& { return derived; };
Function<CBase const&()> cf3 = std::move(f3);
EXPECT_EQ(55, cf3().x);
Function<CDerived&()> f4 = [&]() -> CDerived& { return derived; };
Function<CBase const&()> cf4 = std::move(f4);
EXPECT_EQ(55, cf4().x);
Function<CDerived&()> f5 = [&]() -> CDerived& { return derived; };
Function<CBase&()> cf5 = std::move(f5);
EXPECT_EQ(55, cf5().x);
Function<CDerived const*()> f6 = [&]() -> CDerived const* {
return &derived;
};
Function<CBase const*()> cf6 = std::move(f6);
EXPECT_EQ(55, cf6()->x);
Function<CDerived const*()> f7 = [&]() -> CDerived* { return &derived; };
Function<CBase const*()> cf7 = std::move(f7);
EXPECT_EQ(55, cf7()->x);
Function<CDerived*()> f8 = [&]() -> CDerived* { return &derived; };
Function<CBase*()> cf8 = std::move(f8);
EXPECT_EQ(55, cf8()->x);
Function<CDerived()> f9 = [&]() -> CDerived {
auto d = derived;
d.x = 66;
return d;
};
Function<CBase()> cf9 = std::move(f9);
EXPECT_EQ(66, cf9().x);
}
// TEST =====================================================================
// asStdFunction_*
TEST(Function, asStdFunctionVoid) {
int i = 0;
folly::Function<void()> f = [&] { ++i; };
auto sf = std::move(f).asStdFunction();
static_assert(
std::is_same<decltype(sf), std::function<void()>>::value,
"std::function has wrong type");
sf();
EXPECT_EQ(1, i);
}
TEST(Function, asStdFunctionVoidConst) {
int i = 0;
folly::Function<void() const> f = [&] { ++i; };
auto sf = std::move(f).asStdFunction();
static_assert(
std::is_same<decltype(sf), std::function<void()>>::value,
"std::function has wrong type");
sf();
EXPECT_EQ(1, i);
}
TEST(Function, asStdFunctionReturn) {
int i = 0;
folly::Function<int()> f = [&] {
++i;
return 42;
};
auto sf = std::move(f).asStdFunction();
static_assert(
std::is_same<decltype(sf), std::function<int()>>::value,
"std::function has wrong type");
EXPECT_EQ(42, sf());
EXPECT_EQ(1, i);
}
TEST(Function, asStdFunctionReturnConst) {
int i = 0;
folly::Function<int() const> f = [&] {
++i;
return 42;
};
auto sf = std::move(f).asStdFunction();
static_assert(
std::is_same<decltype(sf), std::function<int()>>::value,
"std::function has wrong type");
EXPECT_EQ(42, sf());
EXPECT_EQ(1, i);
}
TEST(Function, asStdFunctionArgs) {
int i = 0;
folly::Function<void(int, int)> f = [&](int x, int y) {
++i;
return x + y;
};
auto sf = std::move(f).asStdFunction();
static_assert(
std::is_same<decltype(sf), std::function<void(int, int)>>::value,
"std::function has wrong type");
sf(42, 42);
EXPECT_EQ(1, i);
}
TEST(Function, asStdFunctionArgsConst) {
int i = 0;
folly::Function<void(int, int) const> f = [&](int x, int y) {
++i;
return x + y;
};
auto sf = std::move(f).asStdFunction();
static_assert(
std::is_same<decltype(sf), std::function<void(int, int)>>::value,
"std::function has wrong type");
sf(42, 42);
EXPECT_EQ(1, i);
}
// TEST =====================================================================
// asSharedProxy_*
TEST(Function, asSharedProxyVoid) {
int i = 0;
folly::Function<void()> f = [&i] { ++i; };
auto sp = std::move(f).asSharedProxy();
auto spcopy = sp;
sp();
EXPECT_EQ(1, i);
spcopy();
EXPECT_EQ(2, i);
}
TEST(Function, asSharedProxyVoidConst) {
int i = 0;
folly::Function<void() const> f = [&i] { ++i; };
auto sp = std::move(f).asSharedProxy();
auto spcopy = sp;
sp();
EXPECT_EQ(1, i);
spcopy();
EXPECT_EQ(2, i);
}
TEST(Function, asSharedProxyReturn) {
folly::Function<int()> f = [i = 0]() mutable {
++i;
return i;
};
auto sp = std::move(f).asSharedProxy();
auto spcopy = sp;
EXPECT_EQ(1, sp());
EXPECT_EQ(2, spcopy());
}
TEST(Function, asSharedProxyReturnConst) {
int i = 0;
folly::Function<int() const> f = [&i] {
++i;
return i;
};
auto sp = std::move(f).asSharedProxy();
auto spcopy = sp;
EXPECT_EQ(1, sp());
EXPECT_EQ(2, spcopy());
}
TEST(Function, asSharedProxyArgs) {
int i = 0;
folly::Function<int(int, int)> f = [&](int x, int y) mutable {
++i;
return x + y * 2;
};
auto sp = std::move(f).asSharedProxy();
auto spcopy = sp;
EXPECT_EQ(120, sp(100, 10));
EXPECT_EQ(1, i);
EXPECT_EQ(120, spcopy(100, 10));
EXPECT_EQ(2, i);
}
TEST(Function, asSharedProxyArgsConst) {
int i = 0;
folly::Function<int(int, int) const> f = [&i](int x, int y) {
++i;
return x * 100 + y * 10 + i;
};
auto sp = std::move(f).asSharedProxy();
auto spcopy = sp;
EXPECT_EQ(561, sp(5, 6));
EXPECT_EQ(562, spcopy(5, 6));
}
TEST(Function, asSharedProxyNullptr) {
auto sp = folly::Function<int(int, int) const>::SharedProxy(nullptr);
EXPECT_THROW(sp(3, 4), std::bad_function_call);
}
TEST(Function, asSharedProxyEmpty) {
auto func = folly::Function<int(int, int) const>();
auto sp = std::move(func).asSharedProxy();
EXPECT_THROW(sp(3, 4), std::bad_function_call);
}
TEST(Function, asSharedProxyExplicitBoolConversion) {
folly::Function<void(void)> f = []() {};
auto sp = std::move(f).asSharedProxy();
auto spcopy = sp;
EXPECT_TRUE(sp);
EXPECT_TRUE(spcopy);
folly::Function<void(void)> emptyF;
auto emptySp = std::move(emptyF).asSharedProxy();
auto emptySpcopy = emptySp;
EXPECT_FALSE(emptySp);
EXPECT_FALSE(emptySpcopy);
}
struct BadCopier {
explicit BadCopier(int v) : v_(v) {}
BadCopier(const BadCopier& o) : v_(o.v_ + 1) {}
BadCopier(BadCopier&&) = default;
int v_;
};
std::array<int, 3> badCopierF(BadCopier a, const BadCopier& b, BadCopier&& c) {
std::array<int, 3> ret;
ret[0] = a.v_;
ret[1] = b.v_;
ret[2] = c.v_;
a.v_ *= -1;
c.v_ *= -1;
return ret;
}
TEST(Function, asSharedProxyForwarding) {
folly::Function<decltype(badCopierF)> ff(badCopierF);
EXPECT_TRUE((std::is_same_v<
decltype(ff),
folly::Function<std::array<int, 3>(
BadCopier a, const BadCopier& b, BadCopier&& c)>>));
auto sp = std::move(ff).asSharedProxy();
BadCopier bca(100);
BadCopier bcb(200);
BadCopier bcc(300);
auto vals = sp(bca, bcb, std::move(bcc));
// bca was passed into f by value, so was copied at least once
EXPECT_GT(vals[0], 100);
EXPECT_EQ(bca.v_, 100);
// bcb was passed into f by const&, so was never copied
EXPECT_EQ(vals[1], 200);
EXPECT_EQ(bcb.v_, 200);
// bcc was passed into f by &&, so was never copied but was mutated in f
EXPECT_EQ(vals[2], 300);
EXPECT_EQ(bcc.v_, -300);
}
TEST(Function, NoAllocatedMemoryAfterMove) {
Functor<int, 100> foo;
Function<int(size_t)> func = foo;
EXPECT_GT(func.heapAllocatedMemory(), 0);
Function<int(size_t)> func2 = std::move(func);
EXPECT_GT(func2.heapAllocatedMemory(), 0);
EXPECT_EQ(func.heapAllocatedMemory(), 0);
}
TEST(Function, ConstCastEmbedded) {
int x = 0;
auto functor = [&x]() { ++x; };
Function<void() const> func(functor);
EXPECT_EQ(func.heapAllocatedMemory(), 0);
Function<void()> func2(std::move(func));
EXPECT_EQ(func2.heapAllocatedMemory(), 0);
}
TEST(Function, EmptyAfterConstCast) {
Function<int(size_t)> func;
EXPECT_FALSE(func);
Function<int(size_t) const> func2 = constCastFunction(std::move(func));
EXPECT_FALSE(func2);
}
TEST(Function, SelfStdSwap) {
Function<int()> f = [] { return 42; };
f.swap(f);
EXPECT_TRUE(bool(f));
EXPECT_EQ(42, f());
std::swap(f, f);
EXPECT_TRUE(bool(f));
EXPECT_EQ(42, f());
folly::swap(f, f);
EXPECT_TRUE(bool(f));
EXPECT_EQ(42, f());
}
TEST(Function, SelfMove) {
Function<int()> f = [] { return 42; };
Function<int()>& g = f;
f = std::move(g); // shouldn't crash!
(void)bool(f); // valid but unspecified state
f = [] { return 43; };
EXPECT_TRUE(bool(f));
EXPECT_EQ(43, f());
}
TEST(Function, SelfMove2) {
int alive{0};
struct arg {
int* ptr_;
explicit arg(int* ptr) noexcept : ptr_(ptr) { ++*ptr_; }
arg(arg&& o) noexcept : ptr_(o.ptr_) { ++*ptr_; }
arg& operator=(arg&&) = delete;
~arg() { --*ptr_; }
};
EXPECT_EQ(0, alive);
Function<int()> f = [myarg = arg{&alive}] { return 42; };
EXPECT_EQ(1, alive);
Function<int()>& g = f;
f = std::move(g);
EXPECT_FALSE(bool(f)) << "self-assign is self-destruct";
EXPECT_EQ(0, alive) << "self-assign is self-destruct";
f = [] { return 43; };
EXPECT_EQ(0, alive) << "sanity check against double-destruction";
EXPECT_TRUE(bool(f));
EXPECT_EQ(43, f());
}
TEST(Function, DeducableArguments) {
deduceArgs(Function<void()>{[] {}});
deduceArgs(Function<void(int, float)>{[](int, float) {}});
deduceArgs(Function<int(int, float)>{[](int i, float) { return i; }});
}
TEST(Function, CtorWithCopy) {
struct X {
X() {}
X(X const&) noexcept(true) {}
X& operator=(X const&) = default;
};
struct Y {
Y() {}
Y(Y const&) noexcept(false) {}
Y(Y&&) noexcept(true) {}
Y& operator=(Y&&) = default;
Y& operator=(Y const&) = default;
};
auto lx = [x = X()] {};
auto ly = [y = Y()] {};
EXPECT_TRUE(noexcept(Function<void()>(lx)));
EXPECT_FALSE(noexcept(Function<void()>(ly)));
}
TEST(Function, BugT23346238) {
const Function<void()> nullfun;
}
TEST(Function, MaxAlignCallable) {
using A = folly::aligned_storage_for_t<folly::max_align_t>;
auto f = [a = A()] { return reinterpret_cast<uintptr_t>(&a) % alignof(A); };
EXPECT_EQ(alignof(A), alignof(decltype(f))) << "sanity";
EXPECT_EQ(0, f()) << "sanity";
EXPECT_EQ(0, Function<size_t()>(f)());
}
TEST(Function, AllocatedSize) {
Function<void(int)> defaultConstructed;
EXPECT_EQ(defaultConstructed.heapAllocatedMemory(), 0U)
<< "Default constructed Function should have zero allocations";
// On any platform this has to allocate heap storage, because the captures are
// larger than the inline size of the Function object:
constexpr size_t kCaptureBytes = sizeof(Function<void(int)>) + 1;
Function<void(int)> fromLambda{
[x = std::array<char, kCaptureBytes>()](int) { (void)x; }};
// I can't assert much about the size because it's permitted to vary from
// platform to platform or as optimization levels change, but we can be sure
// that the lambda must be at least as large as its captures
EXPECT_GE(fromLambda.heapAllocatedMemory(), kCaptureBytes)
<< "Lambda-derived Function's allocated size is smaller than the "
"lambda's capture size";
}
TEST(Function, TrivialSmallBig) {
auto tsl = [] { return 7; };
static_assert(std::is_trivially_copyable_v<decltype(tsl)>);
static_assert(sizeof(tsl) == 1);
auto thl = [x = std::array<int, 64>{{7}}] { return x[0]; };
static_assert(std::is_trivially_copyable_v<decltype(thl)>);
static_assert(sizeof(thl) >= sizeof(Function<int()>));
struct move_nx {
move_nx() {}
~move_nx() {}
move_nx(move_nx&&) noexcept {}
void operator=(move_nx&&) = delete;
};
auto sl = [o = move_nx{}] { return 7; };
static_assert(!std::is_trivially_copyable_v<decltype(sl)>);
static_assert(std::is_nothrow_move_constructible_v<decltype(sl)>);
struct move_x {
move_x() {}
~move_x() {}
move_x(move_x&&) noexcept(false) {}
void operator=(move_x&&) = delete;
};
auto hl = [o = move_x{}] { return 7; };
static_assert(!std::is_trivially_copyable_v<decltype(hl)>);
static_assert(!std::is_nothrow_move_constructible_v<decltype(hl)>);
Function<int()> ts{std::move(tsl)};
Function<int()> th{std::move(thl)};
Function<int()> s{std::move(sl)};
Function<int()> h{std::move(hl)};
EXPECT_EQ(7, ts());
EXPECT_EQ(7, th());
EXPECT_EQ(7, s());
EXPECT_EQ(7, h());
auto ts2 = std::move(ts);
auto th2 = std::move(th);
auto s2 = std::move(s);
auto h2 = std::move(h);
EXPECT_EQ(7, ts2());
EXPECT_EQ(7, th2());
EXPECT_EQ(7, s2());
EXPECT_EQ(7, h2());
}
TEST(Function, ConstInitEmpty) {
static FOLLY_CONSTINIT Function<int()> func;
EXPECT_THROW(func(), std::bad_function_call);
}
TEST(Function, ConstInitNullptr) {
static FOLLY_CONSTINIT Function<int()> func{nullptr};
EXPECT_THROW(func(), std::bad_function_call);
}
TEST(Function, ConstInitStaticLambda) {
static FOLLY_CONSTINIT Function<int()> func{[] { return 3; }};
EXPECT_EQ(3, func());
}
namespace {
template <typename T>
union consteval_immortal {
T value;
template <typename... A>
explicit FOLLY_CONSTEVAL consteval_immortal(std::in_place_t, A&&... a)
: value{static_cast<A&&>(a)...} {}
~consteval_immortal() {}
};
} // namespace
TEST(Function, ConstEvalEmpty) {
static FOLLY_CONSTINIT consteval_immortal<Function<int()>> func{
std::in_place};
EXPECT_THROW(func.value(), std::bad_function_call);
}
TEST(Function, ConstEvalNullptr) {
static FOLLY_CONSTINIT consteval_immortal<Function<int()>> func{
std::in_place, nullptr};
EXPECT_THROW(func.value(), std::bad_function_call);
}
TEST(Function, ConstEvalStaticLambda) {
static FOLLY_CONSTINIT consteval_immortal<Function<int()>> func{
std::in_place, [] { return 3; }};
EXPECT_EQ(3, func.value());
}