Lloyd Pique | ace9af9 | 2022-11-09 13:56:40 -0800 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2022 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include <ftl/function.h> |
| 18 | #include <gtest/gtest.h> |
| 19 | |
| 20 | #include <array> |
| 21 | #include <cstddef> |
| 22 | #include <cstdint> |
| 23 | #include <string_view> |
| 24 | #include <type_traits> |
| 25 | |
| 26 | namespace android::test { |
| 27 | namespace { |
| 28 | |
| 29 | // Create an alias to composite requirements defined by the trait class `T` for easier testing. |
| 30 | template <typename T, typename S> |
| 31 | inline constexpr bool is_opaquely_storable = (T::template require_trivially_copyable<S> && |
| 32 | T::template require_trivially_destructible<S> && |
| 33 | T::template require_will_fit_in_opaque_storage<S> && |
| 34 | T::template require_alignment_compatible<S>); |
| 35 | |
| 36 | // `I` gives a count of sizeof(std::intptr_t) bytes , and `J` gives a raw count of bytes |
| 37 | template <size_t I, size_t J = 0> |
| 38 | struct KnownSizeFunctionObject { |
| 39 | using Data = std::array<std::byte, sizeof(std::intptr_t) * I + J>; |
| 40 | void operator()() const {}; |
| 41 | Data data{}; |
| 42 | }; |
| 43 | |
| 44 | } // namespace |
| 45 | |
| 46 | // static_assert the expected type traits |
| 47 | static_assert(std::is_invocable_r_v<void, ftl::Function<void()>>); |
| 48 | static_assert(std::is_trivially_copyable_v<ftl::Function<void()>>); |
| 49 | static_assert(std::is_trivially_destructible_v<ftl::Function<void()>>); |
| 50 | static_assert(std::is_trivially_copy_constructible_v<ftl::Function<void()>>); |
| 51 | static_assert(std::is_trivially_move_constructible_v<ftl::Function<void()>>); |
| 52 | static_assert(std::is_trivially_copy_assignable_v<ftl::Function<void()>>); |
| 53 | static_assert(std::is_trivially_move_assignable_v<ftl::Function<void()>>); |
| 54 | |
| 55 | template <typename T> |
| 56 | using function_traits = ftl::details::function_traits<T>; |
| 57 | |
| 58 | // static_assert that the expected value of N is used for known function object sizes. |
| 59 | static_assert(function_traits<KnownSizeFunctionObject<0, 0>>::size == 0); |
| 60 | static_assert(function_traits<KnownSizeFunctionObject<0, 1>>::size == 0); |
| 61 | static_assert(function_traits<KnownSizeFunctionObject<1, 0>>::size == 0); |
| 62 | static_assert(function_traits<KnownSizeFunctionObject<1, 1>>::size == 1); |
| 63 | static_assert(function_traits<KnownSizeFunctionObject<2, 0>>::size == 1); |
| 64 | static_assert(function_traits<KnownSizeFunctionObject<2, 1>>::size == 2); |
| 65 | |
| 66 | // Check that is_function_v works |
| 67 | static_assert(!ftl::is_function_v<KnownSizeFunctionObject<0>>); |
| 68 | static_assert(!ftl::is_function_v<std::function<void()>>); |
| 69 | static_assert(ftl::is_function_v<ftl::Function<void()>>); |
| 70 | |
| 71 | // static_assert what can and cannot be stored inside the opaque storage |
| 72 | |
| 73 | template <size_t N> |
| 74 | using function_opaque_storage = ftl::details::function_opaque_storage<N>; |
| 75 | |
| 76 | // Function objects can be stored if they fit. |
| 77 | static_assert(is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<0>>); |
| 78 | static_assert(is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<1>>); |
| 79 | static_assert(!is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<2>>); |
| 80 | |
| 81 | static_assert(is_opaquely_storable<function_opaque_storage<1>, KnownSizeFunctionObject<2>>); |
| 82 | static_assert(!is_opaquely_storable<function_opaque_storage<1>, KnownSizeFunctionObject<3>>); |
| 83 | |
| 84 | static_assert(is_opaquely_storable<function_opaque_storage<2>, KnownSizeFunctionObject<3>>); |
| 85 | static_assert(!is_opaquely_storable<function_opaque_storage<2>, KnownSizeFunctionObject<4>>); |
| 86 | |
| 87 | // Another opaque storage can be stored if it fits. This property is used to copy smaller |
| 88 | // ftl::Functions into larger ones. |
| 89 | static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<0>::type>); |
| 90 | static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<1>::type>); |
| 91 | static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<2>::type>); |
| 92 | static_assert(!is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<3>::type>); |
| 93 | |
| 94 | // Function objects that aren't trivially copyable or destroyable cannot be stored. |
| 95 | auto lambda_capturing_unique_ptr = [ptr = std::unique_ptr<void*>()] { static_cast<void>(ptr); }; |
| 96 | static_assert( |
| 97 | !is_opaquely_storable<function_opaque_storage<2>, decltype(lambda_capturing_unique_ptr)>); |
| 98 | |
| 99 | // Keep in sync with "Example usage" in header file. |
| 100 | TEST(Function, Example) { |
| 101 | using namespace std::string_view_literals; |
| 102 | |
| 103 | class MyClass { |
| 104 | public: |
| 105 | void on_event() const {} |
| 106 | int on_string(int*, std::string_view) { return 1; } |
| 107 | |
| 108 | auto get_function() { |
| 109 | return ftl::make_function([this] { on_event(); }); |
| 110 | } |
| 111 | } cls; |
| 112 | |
| 113 | // A function container with no arguments, and returning no value. |
| 114 | ftl::Function<void()> f; |
| 115 | |
| 116 | // Construct a ftl::Function containing a small lambda. |
| 117 | f = cls.get_function(); |
| 118 | |
| 119 | // Construct a ftl::Function that calls `cls.on_event()`. |
| 120 | f = ftl::make_function<&MyClass::on_event>(&cls); |
| 121 | |
| 122 | // Create a do-nothing function. |
| 123 | f = ftl::no_op; |
| 124 | |
| 125 | // Invoke the contained function. |
| 126 | f(); |
| 127 | |
| 128 | // Also invokes it. |
| 129 | std::invoke(f); |
| 130 | |
| 131 | // Create a typedef to give a more meaningful name and bound the size. |
| 132 | using MyFunction = ftl::Function<int(std::string_view), 2>; |
| 133 | int* ptr = nullptr; |
| 134 | auto f1 = |
| 135 | MyFunction::make([cls = &cls, ptr](std::string_view sv) { return cls->on_string(ptr, sv); }); |
| 136 | int r = f1("abc"sv); |
| 137 | |
| 138 | // Returns a default-constructed int (0). |
| 139 | f1 = ftl::no_op; |
| 140 | r = f1("abc"sv); |
| 141 | EXPECT_EQ(r, 0); |
| 142 | } |
| 143 | |
| 144 | TEST(Function, BasicOperations) { |
| 145 | // Default constructible. |
| 146 | ftl::Function<int()> f; |
| 147 | |
| 148 | // Compares as empty |
| 149 | EXPECT_FALSE(f); |
| 150 | EXPECT_TRUE(f == nullptr); |
| 151 | EXPECT_FALSE(f != nullptr); |
| 152 | EXPECT_TRUE(ftl::Function<int()>() == f); |
| 153 | EXPECT_FALSE(ftl::Function<int()>() != f); |
| 154 | |
| 155 | // Assigning no_op sets it to not empty. |
| 156 | f = ftl::no_op; |
| 157 | |
| 158 | // Verify it can be called, and that it returns a default constructed value. |
| 159 | EXPECT_EQ(f(), 0); |
| 160 | |
| 161 | // Comparable when non-empty. |
| 162 | EXPECT_TRUE(f); |
| 163 | EXPECT_FALSE(f == nullptr); |
| 164 | EXPECT_TRUE(f != nullptr); |
| 165 | EXPECT_FALSE(ftl::Function<int()>() == f); |
| 166 | EXPECT_TRUE(ftl::Function<int()>() != f); |
| 167 | |
| 168 | // Constructing from nullptr means empty. |
| 169 | f = ftl::Function<int()>{nullptr}; |
| 170 | EXPECT_FALSE(f); |
| 171 | |
| 172 | // Assigning nullptr means it is empty. |
| 173 | f = nullptr; |
| 174 | EXPECT_FALSE(f); |
| 175 | |
| 176 | // Move construction |
| 177 | f = ftl::no_op; |
| 178 | ftl::Function<int()> g{std::move(f)}; |
| 179 | EXPECT_TRUE(g != nullptr); |
| 180 | |
| 181 | // Move assignment |
| 182 | f = nullptr; |
| 183 | f = std::move(g); |
| 184 | EXPECT_TRUE(f != nullptr); |
| 185 | |
| 186 | // Copy construction |
| 187 | ftl::Function<int()> h{f}; |
| 188 | EXPECT_TRUE(h != nullptr); |
| 189 | |
| 190 | // Copy assignment |
| 191 | g = h; |
| 192 | EXPECT_TRUE(g != nullptr); |
| 193 | } |
| 194 | |
| 195 | TEST(Function, CanMoveConstructFromLambda) { |
| 196 | auto lambda = [] {}; |
| 197 | ftl::Function<void()> f{std::move(lambda)}; |
| 198 | } |
| 199 | |
| 200 | TEST(Function, TerseDeducedConstructAndAssignFromLambda) { |
| 201 | auto f = ftl::Function([] { return 1; }); |
| 202 | EXPECT_EQ(f(), 1); |
| 203 | |
| 204 | f = [] { return 2; }; |
| 205 | EXPECT_EQ(f(), 2); |
| 206 | } |
| 207 | |
| 208 | namespace { |
| 209 | |
| 210 | struct ImplicitConversionsHelper { |
| 211 | auto exact(int) -> int { return 0; } |
| 212 | auto inexact(long) -> short { return 0; } |
| 213 | // TODO: Switch to `auto templated(auto x)` with C++20 |
| 214 | template <typename T> |
| 215 | T templated(T x) { |
| 216 | return x; |
| 217 | } |
| 218 | |
| 219 | static auto static_exact(int) -> int { return 0; } |
| 220 | static auto static_inexact(long) -> short { return 0; } |
| 221 | // TODO: Switch to `static auto static_templated(auto x)` with C++20 |
| 222 | template <typename T> |
| 223 | static T static_templated(T x) { |
| 224 | return x; |
| 225 | } |
| 226 | }; |
| 227 | |
| 228 | } // namespace |
| 229 | |
| 230 | TEST(Function, ImplicitConversions) { |
| 231 | using Function = ftl::Function<int(int)>; |
| 232 | auto check = [](Function f) { return f(0); }; |
| 233 | auto exact = [](int) -> int { return 0; }; |
| 234 | auto inexact = [](long) -> short { return 0; }; |
| 235 | auto templated = [](auto x) { return x; }; |
| 236 | |
| 237 | ImplicitConversionsHelper helper; |
| 238 | |
| 239 | // Note, `check(nullptr)` would crash, so we can only check if it would be invocable. |
| 240 | static_assert(std::is_invocable_v<decltype(check), decltype(nullptr)>); |
| 241 | |
| 242 | // Note: We invoke each of these to fully expand all the templates involved. |
| 243 | EXPECT_EQ(check(ftl::no_op), 0); |
| 244 | |
| 245 | EXPECT_EQ(check(exact), 0); |
| 246 | EXPECT_EQ(check(inexact), 0); |
| 247 | EXPECT_EQ(check(templated), 0); |
| 248 | |
| 249 | EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::exact>(&helper)), 0); |
| 250 | EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::inexact>(&helper)), 0); |
| 251 | EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::templated<int>>(&helper)), 0); |
| 252 | |
| 253 | EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_exact>()), 0); |
| 254 | EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_inexact>()), 0); |
| 255 | EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_templated<int>>()), 0); |
| 256 | } |
| 257 | |
| 258 | TEST(Function, MakeWithNonConstMemberFunction) { |
| 259 | struct Observer { |
| 260 | bool called = false; |
| 261 | void setCalled() { called = true; } |
| 262 | } observer; |
| 263 | |
| 264 | auto f = ftl::make_function<&Observer::setCalled>(&observer); |
| 265 | |
| 266 | f(); |
| 267 | |
| 268 | EXPECT_TRUE(observer.called); |
| 269 | |
| 270 | EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer)); |
| 271 | } |
| 272 | |
| 273 | TEST(Function, MakeWithConstMemberFunction) { |
| 274 | struct Observer { |
| 275 | mutable bool called = false; |
| 276 | void setCalled() const { called = true; } |
| 277 | } observer; |
| 278 | |
| 279 | const auto f = ftl::make_function<&Observer::setCalled>(&observer); |
| 280 | |
| 281 | f(); |
| 282 | |
| 283 | EXPECT_TRUE(observer.called); |
| 284 | |
| 285 | EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer)); |
| 286 | } |
| 287 | |
| 288 | TEST(Function, MakeWithConstClassPointer) { |
| 289 | const struct Observer { |
| 290 | mutable bool called = false; |
| 291 | void setCalled() const { called = true; } |
| 292 | } observer; |
| 293 | |
| 294 | const auto f = ftl::make_function<&Observer::setCalled>(&observer); |
| 295 | |
| 296 | f(); |
| 297 | |
| 298 | EXPECT_TRUE(observer.called); |
| 299 | |
| 300 | EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer)); |
| 301 | } |
| 302 | |
| 303 | TEST(Function, MakeWithNonCapturingLambda) { |
| 304 | auto f = ftl::make_function([](int a, int b) { return a + b; }); |
| 305 | EXPECT_EQ(f(1, 2), 3); |
| 306 | } |
| 307 | |
| 308 | TEST(Function, MakeWithCapturingLambda) { |
| 309 | bool called = false; |
| 310 | auto f = ftl::make_function([&called](int a, int b) { |
| 311 | called = true; |
| 312 | return a + b; |
| 313 | }); |
| 314 | EXPECT_EQ(f(1, 2), 3); |
| 315 | EXPECT_TRUE(called); |
| 316 | } |
| 317 | |
| 318 | TEST(Function, MakeWithCapturingMutableLambda) { |
| 319 | bool called = false; |
| 320 | auto f = ftl::make_function([&called](int a, int b) mutable { |
| 321 | called = true; |
| 322 | return a + b; |
| 323 | }); |
| 324 | EXPECT_EQ(f(1, 2), 3); |
| 325 | EXPECT_TRUE(called); |
| 326 | } |
| 327 | |
| 328 | TEST(Function, MakeWithThreePointerCapturingLambda) { |
| 329 | bool my_bool = false; |
| 330 | int my_int = 0; |
| 331 | float my_float = 0.f; |
| 332 | |
| 333 | auto f = ftl::make_function( |
| 334 | [ptr_bool = &my_bool, ptr_int = &my_int, ptr_float = &my_float](int a, int b) mutable { |
| 335 | *ptr_bool = true; |
| 336 | *ptr_int = 1; |
| 337 | *ptr_float = 1.f; |
| 338 | |
| 339 | return a + b; |
| 340 | }); |
| 341 | |
| 342 | EXPECT_EQ(f(1, 2), 3); |
| 343 | |
| 344 | EXPECT_TRUE(my_bool); |
| 345 | EXPECT_EQ(my_int, 1); |
| 346 | EXPECT_EQ(my_float, 1.f); |
| 347 | } |
| 348 | |
| 349 | TEST(Function, MakeWithFreeFunction) { |
| 350 | auto f = ftl::make_function<&std::make_unique<int, int>>(); |
| 351 | std::unique_ptr<int> unique_int = f(1); |
| 352 | ASSERT_TRUE(unique_int); |
| 353 | EXPECT_EQ(*unique_int, 1); |
| 354 | } |
| 355 | |
| 356 | TEST(Function, CopyToLarger) { |
| 357 | int counter = 0; |
| 358 | ftl::Function<void()> a{[ptr_counter = &counter] { (*ptr_counter)++; }}; |
| 359 | ftl::Function<void(), 1> b = a; |
| 360 | ftl::Function<void(), 2> c = a; |
| 361 | |
| 362 | EXPECT_EQ(counter, 0); |
| 363 | a(); |
| 364 | EXPECT_EQ(counter, 1); |
| 365 | b(); |
| 366 | EXPECT_EQ(counter, 2); |
| 367 | c(); |
| 368 | EXPECT_EQ(counter, 3); |
| 369 | |
| 370 | b = [ptr_counter = &counter] { (*ptr_counter) += 2; }; |
| 371 | c = [ptr_counter = &counter] { (*ptr_counter) += 3; }; |
| 372 | |
| 373 | b(); |
| 374 | EXPECT_EQ(counter, 5); |
| 375 | c(); |
| 376 | EXPECT_EQ(counter, 8); |
| 377 | } |
| 378 | |
| 379 | } // namespace android::test |