blob: 2257a4c1ec7ccb5e28ea93508278270cc0184b98 [file] [log] [blame]
// 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 <stdint.h>
#include <concepts>
#include <optional>
#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<int(int, int)> ref = add;
EXPECT_EQ(19, ref(17, 2));
}
{
const auto add_const = add;
const FunctionRef<int(int, int)> ref = add_const;
EXPECT_EQ(19, ref(17, 2));
}
}
TEST(FunctionRef, CapturingLambda) {
int x = 3;
const auto lambda = [&x] { return x; };
FunctionRef<int()> ref = lambda;
EXPECT_EQ(3, ref());
}
TEST(FunctionRef, FunctionPtr) {
[](FunctionRef<char(float)> ref) { EXPECT_EQ('a', ref(1.0)); }(&Func);
const FunctionRef<char(float)> 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<int(int)> 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<int(const S*)> 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<FunctionRef<int()>>& ref) {
const auto return_17 = [] { return 17; };
FunctionRef<int()> other = return_17;
ref.emplace(other);
}
NOINLINE void ConstructFromConstLValue(std::optional<FunctionRef<int()>>& ref) {
const auto return_17 = [] { return 17; };
const FunctionRef<int()> other = return_17;
ref.emplace(other);
}
NOINLINE void ConstructFromRValue(std::optional<FunctionRef<int()>>& ref) {
const auto return_17 = [] { return 17; };
using Ref = FunctionRef<int()>;
ref.emplace(Ref(return_17));
}
NOINLINE void ConstructFromConstRValue(std::optional<FunctionRef<int()>>& ref) {
const auto return_17 = [] { return 17; };
using Ref = const FunctionRef<int()>;
ref.emplace(Ref(return_17));
}
TEST(FunctionRef, ConstructionFromOtherFunctionRefObjects) {
using Ref = FunctionRef<int()>;
std::optional<Ref> 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<Ref, FunctionRef<void()>>);
static_assert(!std::constructible_from<Ref, FunctionRef<int(int)>>);
static_assert(!std::constructible_from<Ref, FunctionRef<int64_t()>>);
// Check again with various qualifiers.
static_assert(!std::constructible_from<Ref, const FunctionRef<void()>>);
static_assert(!std::constructible_from<Ref, FunctionRef<void()>&>);
static_assert(!std::constructible_from<Ref, FunctionRef<void()>&&>);
static_assert(!std::constructible_from<Ref, const FunctionRef<void()>&>);
static_assert(!std::constructible_from<Ref, const FunctionRef<void()>&&>);
}
// `FunctionRef` allows functors with convertible return types to be adapted.
TEST(FunctionRef, ConvertibleReturnTypes) {
{
const auto lambda = [] { return true; };
const FunctionRef<int()> ref = lambda;
EXPECT_EQ(1, ref());
}
{
class Base {};
class Derived : public Base {};
const auto lambda = []() -> Derived* { return nullptr; };
const FunctionRef<const Base*()> 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<FunctionRef<int64_t(int32_t)>, decltype(lambda)>);
static_assert(std::constructible_from<FunctionRef<int64_t(int32_t)>,
decltype(capturing_lambda)>);
static_assert(std::constructible_from<FunctionRef<int64_t(int32_t)>,
decltype(function_ptr)>);
static_assert(std::constructible_from<FunctionRef<int64_t(int32_t)>,
decltype(functor)>);
static_assert(
std::constructible_from<FunctionRef<int64_t(const Obj*, int32_t)>,
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<FunctionRef<void(int32_t)>, decltype(lambda)>);
static_assert(!std::constructible_from<FunctionRef<void(int32_t)>,
decltype(capturing_lambda)>);
static_assert(!std::constructible_from<FunctionRef<void(int32_t)>,
decltype(function_ptr)>);
static_assert(
!std::constructible_from<FunctionRef<void(int32_t)>, decltype(functor)>);
static_assert(!std::constructible_from<FunctionRef<void(const Obj*, int32_t)>,
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<FunctionRef<int32_t(int32_t)>,
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<int(int)>;
static_assert(!std::is_constructible_v<Ref, absl::FunctionRef<void()>>);
static_assert(!std::is_constructible_v<Ref, absl::FunctionRef<void(int)>>);
static_assert(!std::is_constructible_v<Ref, absl::FunctionRef<int(int)>>);
// Check again with various qualifiers.
using AbslRef = absl::FunctionRef<int(int)>;
static_assert(!std::is_constructible_v<Ref, const AbslRef>);
static_assert(!std::is_constructible_v<Ref, AbslRef&>);
static_assert(!std::is_constructible_v<Ref, AbslRef&&>);
static_assert(!std::is_constructible_v<Ref, const AbslRef&>);
static_assert(!std::is_constructible_v<Ref, const AbslRef&&>);
}
} // namespace base