// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/functional/function_ref.h" #include #include #include #include "base/compiler_specific.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/functional/function_ref.h" namespace base { namespace { char Func(float) { return 'a'; } } // namespace TEST(FunctionRef, Lambda) { auto add = [](int a, int b) { return a + b; }; { const FunctionRef ref = add; EXPECT_EQ(19, ref(17, 2)); } { const auto add_const = add; const FunctionRef ref = add_const; EXPECT_EQ(19, ref(17, 2)); } } TEST(FunctionRef, CapturingLambda) { int x = 3; const auto lambda = [&x] { return x; }; FunctionRef ref = lambda; EXPECT_EQ(3, ref()); } TEST(FunctionRef, FunctionPtr) { [](FunctionRef ref) { EXPECT_EQ('a', ref(1.0)); }(&Func); const FunctionRef ref = +[](float) { return 'a'; }; EXPECT_EQ('a', ref(1.0f)); } TEST(FunctionRef, Functor) { struct S { int operator()(int x) const { return x; } }; const S s; const FunctionRef ref = s; EXPECT_EQ(17, ref(17)); } TEST(FunctionRef, Method) { struct S { int Method() const { return value; } const int value; }; const S s(25); [&s](FunctionRef ref) { EXPECT_EQ(25, ref(&s)); }(&S::Method); } // If we construct from another `FunctionRef`, that should work fine, even if // the input is destroyed before we call the output. In other words, we should // reference the underlying callable, not the `FunctionRef`. // // We construct in a `noinline` function to maximize the chance that ASAN // notices the use-after-free if we get this wrong. NOINLINE void ConstructFromLValue(std::optional>& ref) { const auto return_17 = [] { return 17; }; FunctionRef other = return_17; ref.emplace(other); } NOINLINE void ConstructFromConstLValue(std::optional>& ref) { const auto return_17 = [] { return 17; }; const FunctionRef other = return_17; ref.emplace(other); } NOINLINE void ConstructFromRValue(std::optional>& ref) { const auto return_17 = [] { return 17; }; using Ref = FunctionRef; ref.emplace(Ref(return_17)); } NOINLINE void ConstructFromConstRValue(std::optional>& ref) { const auto return_17 = [] { return 17; }; using Ref = const FunctionRef; ref.emplace(Ref(return_17)); } TEST(FunctionRef, ConstructionFromOtherFunctionRefObjects) { using Ref = FunctionRef; std::optional ref; ConstructFromLValue(ref); EXPECT_EQ(17, (*ref)()); ConstructFromConstLValue(ref); EXPECT_EQ(17, (*ref)()); ConstructFromRValue(ref); EXPECT_EQ(17, (*ref)()); ConstructFromConstRValue(ref); EXPECT_EQ(17, (*ref)()); // It shouldn't be possible to construct from `FunctionRef` objects with // differing signatures, even if they are compatible with `int()`. static_assert(!std::constructible_from>); static_assert(!std::constructible_from>); static_assert(!std::constructible_from>); // Check again with various qualifiers. static_assert(!std::constructible_from>); static_assert(!std::constructible_from&>); static_assert(!std::constructible_from&&>); static_assert(!std::constructible_from&>); static_assert(!std::constructible_from&&>); } // `FunctionRef` allows functors with convertible return types to be adapted. TEST(FunctionRef, ConvertibleReturnTypes) { { const auto lambda = [] { return true; }; const FunctionRef ref = lambda; EXPECT_EQ(1, ref()); } { class Base {}; class Derived : public Base {}; const auto lambda = []() -> Derived* { return nullptr; }; const FunctionRef ref = lambda; EXPECT_EQ(nullptr, ref()); } } TEST(FunctionRef, ConstructionFromInexactMatches) { // Lambda. const auto lambda = [](int32_t x) { return x; }; // Capturing lambda. const auto capturing_lambda = [&](int32_t x) { return lambda(x); }; // Function pointer. int32_t (*const function_ptr)(int32_t) = +lambda; // Functor. struct Functor { int32_t operator()(int32_t x) const { return x; } }; const Functor functor; // Method. struct Obj { int32_t Method(int32_t x) const { return x; } }; int32_t (Obj::*const method)(int32_t) const = &Obj::Method; // Each of the objects above should work for a `FunctionRef` with a // convertible return type. In this case, they all return `int32_t`, which // should be seamlessly convertible to `int64_t` below. static_assert( std::constructible_from, decltype(lambda)>); static_assert(std::constructible_from, decltype(capturing_lambda)>); static_assert(std::constructible_from, decltype(function_ptr)>); static_assert(std::constructible_from, decltype(functor)>); static_assert( std::constructible_from, decltype(method)>); // It shouldn't be possible to construct a `FunctionRef` from any of the // objects above if we discard the return value. static_assert( !std::constructible_from, decltype(lambda)>); static_assert(!std::constructible_from, decltype(capturing_lambda)>); static_assert(!std::constructible_from, decltype(function_ptr)>); static_assert( !std::constructible_from, decltype(functor)>); static_assert(!std::constructible_from, decltype(method)>); // It shouldn't be possible to construct a `FunctionRef` from a pointer to a // functor, even with a compatible signature. static_assert(!std::constructible_from, decltype(&functor)>); } TEST(FunctionRef, ConstructionFromAbslFunctionRef) { // It shouldn't be possible to construct a `FunctionRef` from an // `absl::FunctionRef`, whether the signatures are compatible or not. using Ref = FunctionRef; static_assert(!std::is_constructible_v>); static_assert(!std::is_constructible_v>); static_assert(!std::is_constructible_v>); // Check again with various qualifiers. using AbslRef = absl::FunctionRef; static_assert(!std::is_constructible_v); static_assert(!std::is_constructible_v); static_assert(!std::is_constructible_v); static_assert(!std::is_constructible_v); static_assert(!std::is_constructible_v); } } // namespace base