1 // Copyright 2017 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #pragma once 6 7 #include <new> 8 #include <stddef.h> 9 #include <utility> 10 11 #include <zircon/assert.h> 12 #include <fbl/algorithm.h> 13 #include <fbl/alloc_checker.h> 14 #include <fbl/macros.h> 15 #include <fbl/type_support.h> 16 #include <fbl/unique_ptr.h> 17 18 namespace fbl { 19 namespace internal { 20 21 // Checks if |T| is null. Defaults to false. |Comparison| is the type yielded by 22 // comparing a T value with nullptr. 23 template <typename T, typename Comparison = bool> 24 struct NullEq { TestNullEq25 static constexpr bool Test(const T&) { return false; } 26 }; 27 28 // Partial specialization for |T| values comparable to nullptr. 29 template <typename T> 30 struct NullEq<T, decltype(*static_cast<T*>(nullptr) == nullptr)> { 31 // This is intended for a T that's a function pointer type. However, it 32 // also matches for a T that can be implicitly coerced to a function 33 // pointer type, such as a function type or a captureless lambda's closure 34 // type. In that case, the compiler might complain that the comparison is 35 // always false because the address of a function can never be a null 36 // pointer. It's possible to do template selection to match function 37 // types, but it's not possible to match captureless lambda closure types 38 // that way. So just suppress the warning. The compiler will optimize 39 // away the always-false comparison. 40 #pragma GCC diagnostic push 41 #pragma GCC diagnostic ignored "-Waddress" 42 static constexpr bool Test(const T& v) { return v == nullptr; } 43 #pragma GCC diagnostic pop 44 }; 45 46 template <typename T> 47 static constexpr bool IsNull(const T& v) { 48 return NullEq<T>::Test(v); 49 } 50 51 template <typename Result, typename... Args> 52 class FunctionTarget { 53 public: 54 FunctionTarget() = default; 55 virtual ~FunctionTarget() = default; 56 57 DISALLOW_COPY_ASSIGN_AND_MOVE(FunctionTarget); 58 59 virtual bool is_null() const = 0; 60 61 virtual Result operator()(Args... args) const = 0; 62 63 virtual void MoveInitializeTo(void* ptr) = 0; 64 }; 65 66 template <typename Result, typename... Args> 67 class NullFunctionTarget final : public FunctionTarget<Result, Args...> { 68 public: 69 NullFunctionTarget() = default; 70 ~NullFunctionTarget() final = default; 71 72 DISALLOW_COPY_ASSIGN_AND_MOVE(NullFunctionTarget); 73 74 bool is_null() const final { return true; } 75 76 Result operator()(Args... args) const final { 77 ZX_PANIC("Attempted to invoke fbl::Function with a null target."); 78 } 79 80 void MoveInitializeTo(void* ptr) final { 81 new (ptr) NullFunctionTarget(); 82 } 83 }; 84 85 template <typename Callable, typename Result, typename... Args> 86 class InlineFunctionTarget final : public FunctionTarget<Result, Args...> { 87 public: 88 explicit InlineFunctionTarget(Callable target) 89 : target_(std::move(target)) {} 90 InlineFunctionTarget(Callable target, AllocChecker* ac) 91 : target_(std::move(target)) { ac->arm(0u, true); } 92 InlineFunctionTarget(InlineFunctionTarget&& other) 93 : target_(std::move(other.target_)) {} 94 ~InlineFunctionTarget() final = default; 95 96 DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(InlineFunctionTarget); 97 98 bool is_null() const final { return false; } 99 100 Result operator()(Args... args) const final { 101 return target_(std::forward<Args>(args)...); 102 } 103 104 void MoveInitializeTo(void* ptr) final { 105 new (ptr) InlineFunctionTarget(std::move(*this)); 106 } 107 108 private: 109 mutable Callable target_; 110 }; 111 112 template <typename Callable, typename Result, typename... Args> 113 class HeapFunctionTarget final : public FunctionTarget<Result, Args...> { 114 public: 115 explicit HeapFunctionTarget(Callable target) 116 : target_ptr_(fbl::make_unique<Callable>(std::move(target))) {} 117 HeapFunctionTarget(Callable target, AllocChecker* ac) 118 : target_ptr_(fbl::make_unique_checked<Callable>(ac, std::move(target))) {} 119 HeapFunctionTarget(HeapFunctionTarget&& other) 120 : target_ptr_(std::move(other.target_ptr_)) {} 121 ~HeapFunctionTarget() final = default; 122 123 DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(HeapFunctionTarget); 124 125 bool is_null() const final { return false; } 126 127 Result operator()(Args... args) const final { 128 return (*target_ptr_)(std::forward<Args>(args)...); 129 } 130 131 void MoveInitializeTo(void* ptr) final { 132 new (ptr) HeapFunctionTarget(std::move(*this)); 133 } 134 135 private: 136 fbl::unique_ptr<Callable> target_ptr_; 137 }; 138 139 // Holds a function target. 140 // If a callable object is small enough, it will be stored as an |InlineFunctionTarget|. 141 // Otherwise it will be stored as a |HeapFunctionTarget|. 142 template <size_t target_size, typename Result, typename... Args> 143 struct FunctionTargetHolder final { 144 FunctionTargetHolder() = default; 145 146 DISALLOW_COPY_ASSIGN_AND_MOVE(FunctionTargetHolder); 147 148 void InitializeNullTarget() { 149 using NullFunctionTarget = fbl::internal::NullFunctionTarget<Result, Args...>; 150 static_assert(sizeof(NullFunctionTarget) <= target_size, 151 "NullFunctionTarget should fit in FunctionTargetHolder."); 152 new (&bits_) NullFunctionTarget(); 153 } 154 155 template <typename Callable> 156 struct TargetHelper { 157 using InlineFunctionTarget = fbl::internal::InlineFunctionTarget<Callable, Result, Args...>; 158 using HeapFunctionTarget = fbl::internal::HeapFunctionTarget<Callable, Result, Args...>; 159 static constexpr bool can_inline = (sizeof(InlineFunctionTarget) <= target_size); 160 using Type = typename fbl::conditional<can_inline, InlineFunctionTarget, HeapFunctionTarget>::type; 161 static_assert(sizeof(Type) <= target_size, "Target should fit in FunctionTargetHolder."); 162 }; 163 164 template <typename Callable> 165 void InitializeTarget(Callable target) { 166 new (&bits_) typename TargetHelper<Callable>::Type(std::move(target)); 167 } 168 169 template <typename Callable> 170 void InitializeTarget(Callable target, AllocChecker* ac) { 171 new (&bits_) typename TargetHelper<Callable>::Type(std::move(target), ac); 172 } 173 174 void MoveInitializeTargetFrom(FunctionTargetHolder& other) { 175 other.target().MoveInitializeTo(&bits_); 176 } 177 178 void DestroyTarget() { 179 target().~FunctionTarget(); 180 } 181 182 using FunctionTarget = fbl::internal::FunctionTarget<Result, Args...>; 183 FunctionTarget& target() { return *reinterpret_cast<FunctionTarget*>(&bits_); } 184 const FunctionTarget& target() const { return *reinterpret_cast<const FunctionTarget*>(&bits_); } 185 186 private: 187 alignas(max_align_t) union { char data[target_size]; } bits_; 188 }; 189 190 template <size_t inline_callable_size, bool require_inline, typename Result, typename... Args> 191 class Function; 192 193 template <size_t inline_callable_size, bool require_inline, typename Result, typename... Args> 194 class Function<inline_callable_size, require_inline, Result(Args...)> { 195 struct FakeCallable { 196 alignas(max_align_t) char bits[fbl::round_up(inline_callable_size, sizeof(void*))]; 197 }; 198 static constexpr size_t inline_target_size = 199 sizeof(InlineFunctionTarget<FakeCallable, Result, Args...>); 200 static constexpr size_t heap_target_size = 201 sizeof(HeapFunctionTarget<FakeCallable, Result, Args...>); 202 static constexpr size_t target_size = require_inline ? inline_target_size 203 : fbl::max(inline_target_size, heap_target_size); 204 using TargetHolder = FunctionTargetHolder<target_size, Result, Args...>; 205 206 public: 207 using result_type = Result; 208 209 Function() { holder_.InitializeNullTarget(); } 210 211 Function(decltype(nullptr)) { holder_.InitializeNullTarget(); } 212 213 Function(Function&& other) { 214 holder_.MoveInitializeTargetFrom(other.holder_); 215 other.holder_.InitializeNullTarget(); 216 } 217 218 template <typename Callable> 219 Function(Callable target) { 220 InitializeTarget(std::move(target)); 221 } 222 223 template <typename Callable> 224 Function(Callable target, AllocChecker* ac) { 225 InitializeTarget(std::move(target), ac); 226 } 227 228 ~Function() { 229 holder_.DestroyTarget(); 230 } 231 232 DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Function); 233 234 explicit operator bool() const { 235 return !holder_.target().is_null(); 236 }; 237 238 Result operator()(Args... args) const { 239 return holder_.target()(std::forward<Args>(args)...); 240 } 241 242 Function& operator=(decltype(nullptr)) { 243 holder_.DestroyTarget(); 244 holder_.InitializeNullTarget(); 245 return *this; 246 } 247 248 Function& operator=(Function&& other) { 249 holder_.DestroyTarget(); 250 holder_.MoveInitializeTargetFrom(other.holder_); 251 other.holder_.InitializeNullTarget(); 252 return *this; 253 } 254 255 template <typename Callable> 256 Function& operator=(Callable target) { 257 SetTarget(std::move(target)); 258 return *this; 259 } 260 261 template <typename Callable> 262 void SetTarget(Callable target) { 263 holder_.DestroyTarget(); 264 InitializeTarget(std::move(target)); 265 } 266 267 template <typename Callable> 268 void SetTarget(Callable target, AllocChecker* ac) { 269 holder_.DestroyTarget(); 270 InitializeTarget(std::move(target), ac); 271 } 272 273 void swap(Function& other) { 274 TargetHolder temp; 275 temp.MoveInitializeTargetFrom(holder_); 276 holder_.MoveInitializeTargetFrom(other.holder_); 277 other.holder_.MoveInitializeTargetFrom(temp); 278 } 279 280 private: 281 template <typename Callable> 282 void InitializeTarget(Callable target) { 283 static_assert(!require_inline || sizeof(Callable) <= inline_callable_size, 284 "Callable too large for InlineFunction."); 285 if (IsNull(target)) { 286 holder_.InitializeNullTarget(); 287 } else { 288 holder_.InitializeTarget(std::move(target)); 289 } 290 } 291 292 template <typename Callable> 293 void InitializeTarget(Callable target, AllocChecker* ac) { 294 static_assert(!require_inline || sizeof(Callable) <= inline_callable_size, 295 "Callable too large for InlineFunction."); 296 if (IsNull(target)) { 297 holder_.InitializeNullTarget(); 298 } else { 299 holder_.InitializeTarget(std::move(target), ac); 300 } 301 } 302 303 TargetHolder holder_; 304 }; 305 306 // Helper used by |BindMember| to invoke a pointer to member function. 307 template <typename R, typename T, typename... Args> 308 class MemberInvoker final { 309 public: 310 using MemFn = R (T::*)(Args...); 311 312 MemberInvoker(T* instance, MemFn fn) 313 : instance_(instance), fn_(fn) {} 314 315 R operator()(Args... args) const { 316 return (instance_->*fn_)(std::forward<Args>(args)...); 317 } 318 319 private: 320 T* const instance_; 321 MemFn const fn_; 322 }; 323 324 } // namespace internal 325 326 // The default size allowance for callable objects which can be inlined within 327 // a function object. This default allows for inline storage of callables 328 // consisting of a function pointer and an object pointer (or similar callables 329 // of the same size). 330 constexpr size_t kDefaultInlineCallableSize = sizeof(void*) * 2; 331 332 // A move-only callable object wrapper. 333 // 334 // fbl::Function<T> behaves like std::function<T> except that it is move-only 335 // instead of copyable. This means it can hold mutable lambdas without requiring 336 // a reference-counted wrapper. 337 // 338 // Small callable objects (smaller than |kDefaultInlineCallableSize|) are stored 339 // inline within the function. Larger callable objects will be copied to the heap 340 // if necessary. 341 // 342 // See also fbl::SizedFunction<T, size> and fbl::InlineFunction<T, size> 343 // for more control over allocation behavior. 344 // 345 // SYNOPSIS: 346 // 347 // template <typename Result, typename Args...> 348 // class Function<Result(Args...)> { 349 // public: 350 // using result_type = Result; 351 // 352 // Function(); 353 // explicit Function(decltype(nullptr)); 354 // Function(Function&& other); 355 // template <typename Callable> 356 // explicit Function(Callable target); 357 // template <typename Callable> 358 // explicit Function(Callable target, AllocChecker* ac); 359 // 360 // ~Function(); 361 // 362 // explicit operator bool() const; 363 // 364 // Result operator()(Args... args) const; 365 // 366 // Function& operator=(decltype(nullptr)); 367 // Function& operator=(Function&& other); 368 // template <typename Callable> 369 // Function& operator=(Callable target); 370 // 371 // template <typename Callable> 372 // void SetTarget(Callable target); 373 // template <typename Callable> 374 // void SetTarget(Callable target, AllocChecker* ac); 375 // 376 // void swap(Function& other); 377 // }; 378 // 379 // EXAMPLE: 380 // 381 // using FoldFunction = fbl::Function<int(int value, int item)>; 382 // 383 // int FoldVector(const fbl::Vector<int>& in, int value, const FoldFunction& f) { 384 // for (auto& item : in) { 385 // value = f(value, item); 386 // } 387 // return value; 388 // } 389 // 390 // int SumItem(int value, int item) { 391 // return value + item; 392 // } 393 // 394 // int Sum(const fbl::Vector<int>& in) { 395 // // bind to a function pointer 396 // FoldFunction sum(&SumItem); 397 // return FoldVector(in, 0, sum); 398 // } 399 // 400 // int AlternatingSum(const fbl::Vector<int>& in) { 401 // // bind to a lambda 402 // int sign = 1; 403 // FoldFunction alternating_sum([&sign](int value, int item) { 404 // value += sign * item; 405 // sign *= -1; 406 // return value; 407 // }); 408 // return FoldVector(in, 0, alternating_sum); 409 // } 410 // 411 template <typename T> 412 using Function = fbl::internal::Function<kDefaultInlineCallableSize, false, T>; 413 414 // A move-only callable object wrapper with a explicitly specified (non-default) 415 // inline callable size preference. 416 // 417 // Behaves just like fbl::Function<T> except that it guarantees that callable 418 // objects of up to |inline_callable_size| bytes will be stored inline instead 419 // of on the heap. This may be useful when you want to optimize storage of 420 // functions of a known size. 421 // 422 // Note that the effective maximum inline callable size may be slightly larger 423 // due to object alignment and rounding. 424 template <typename T, size_t inline_callable_size> 425 using SizedFunction = fbl::internal::Function<inline_callable_size, false, T>; 426 427 // A move-only callable object wrapper which forces callables to be stored inline 428 // thereby preventing heap allocation. 429 // 430 // Behaves just like fbl::Function<T> except that it will refuse to store a 431 // callable object larger than |inline_callable_size| (will fail to compile). 432 template <typename T, size_t inline_callable_size> 433 using InlineFunction = fbl::internal::Function<inline_callable_size, true, T>; 434 435 // Comparing functions with nullptr. 436 template <size_t inline_callable_size, bool require_inline, typename Result, typename... Args> 437 bool operator==(const fbl::internal::Function<inline_callable_size, require_inline, Result, Args...>& f, 438 decltype(nullptr)) { 439 return !f; 440 } 441 template <size_t inline_callable_size, bool require_inline, typename Result, typename... Args> 442 bool operator!=(const fbl::internal::Function<inline_callable_size, require_inline, Result, Args...>& f, 443 decltype(nullptr)) { 444 return !!f; 445 } 446 template <size_t inline_callable_size, bool require_inline, typename Result, typename... Args> 447 bool operator==(decltype(nullptr), 448 const fbl::internal::Function<inline_callable_size, require_inline, Result, Args...>& f) { 449 return !f; 450 } 451 template <size_t inline_callable_size, bool require_inline, typename Result, typename... Args> 452 bool operator!=(decltype(nullptr), 453 const fbl::internal::Function<inline_callable_size, require_inline, Result, Args...>& f) { 454 return !!f; 455 } 456 457 // A function which takes no arguments and produces no result. 458 using Closure = fbl::Function<void()>; 459 460 // Returns a Callable object which invokes a member function of an object. 461 // 462 // EXAMPLE: 463 // 464 // class Accumulator { 465 // public: 466 // void Add(int value) { 467 // sum += value; 468 // } 469 // 470 // int sum = 0; 471 // }; 472 // 473 // void CountToTen(fbl::Function<void(int)> function) { 474 // for (int i = 1; i <= 10; i++) { 475 // function(i); 476 // } 477 // } 478 // 479 // int SumToTen() { 480 // Accumulator accum; 481 // CountToTen(fbl::BindMember(&accum, &Accumulator::Add)); 482 // return accum.sum; 483 // } 484 template <typename R, typename T, typename... Args> 485 auto BindMember(T* instance, R (T::*fn)(Args...)) { 486 return internal::MemberInvoker<R, T, Args...>(instance, fn); 487 } 488 489 } // namespace fbl 490