1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
|
// 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
|