1 /* 2 * Copyright (C) 2021 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 #pragma once 18 19 #include <stdint.h> 20 #include <type_traits> 21 #include <utility> 22 23 namespace android::base { 24 25 // 26 // function_ref<> - a class that stores a reference to a callable object, 27 // similar to string_view for strings. 28 // 29 // We need to pass around lots of callbacks. The standard way of doing it 30 // is via std::function<> class, and it usually works OK. But there are some 31 // noticeable drawbacks: 32 // 33 // 1. std::function<> in most STLs performs heap allocation for all callables 34 // bigger than a single poiner to a function. 35 // 2. std::function<> goes through at least two pointers + a vptr call to call 36 // the stored function. 37 // 3. std::function<> copies the passed object inside at least once; this also 38 // means it can't work with non-copyable functors. 39 // 40 // function_ref is an alternative way of passing functors around. Instead of 41 // storing a copy of the functor inside, it follows the path of string_view and 42 // merely captures a pointer to the object to call. This allows for a simple, 43 // fast and lightweight wrapper design; it also dictates the limitations: 44 // 45 // 1. function_ref<> stores a pointer to outside functor. That functor _must_ 46 // outlive the ref. 47 // 2. function_ref<> has two calls through a function pointer in its call 48 // operator. That's still better than std::function<>, but slower compared 49 // to a raw function pointer call with a "void* opaque" context parameter. 50 // 51 // Limitation #1 dictates the best use case: a function parameter type for some 52 // generic callback which doesn't get stored inside an object field but only 53 // gets called in this call. E.g.: 54 // 55 // void someLongOperation(function_ref<void(int progress)> onProgress) { 56 // firstStep(onProgress); 57 // ... 58 // onProgress(50); 59 // ... 60 // lastStep(onProgress); 61 // onProgress(100); 62 // } 63 // 64 // In this code std::function<> is an overkill as the whole use of |onProgresss| 65 // callback is scoped and easy to track. An alternative design - making it a 66 // template parameter (template <class Callback> ... (Callback onProgress)) 67 // forces one to put someLongOperation() + some private functions into the 68 // header. function_ref<> is the choice then. 69 // 70 // NOTE: Beware of passing temporary functions via function_ref<>! Temporaries 71 // live until the end of full expression (usually till the next semicolon), and 72 // having a function_ref<> that refers to a dangling pointer is a bug that's 73 // hard to debug. E.g.: 74 // function_ref<...> v = [](){}; // this is fine 75 // function_ref<...> v = std::function<...>([](){}); // this will kill you 76 // 77 // NOTE2: function_ref<> should not have an empty state, but it doesn't have a 78 // runtime check against that. Don't construct it from a null function! 79 80 template <class Signature> 81 class function_ref; 82 83 template <class Ret, class... Args> 84 class function_ref<Ret(Args...)> final { 85 public: 86 constexpr function_ref() noexcept = delete; 87 constexpr function_ref(const function_ref& other) noexcept = default; 88 constexpr function_ref& operator=(const function_ref&) noexcept = default; 89 90 using RawFunc = Ret(Args...); 91 function_ref(RawFunc * funcptr)92 function_ref(RawFunc* funcptr) noexcept { *this = funcptr; } 93 94 template <class Callable, class = std::enable_if_t< 95 std::is_invocable_r_v<Ret, Callable, Args...> && 96 !std::is_same_v<function_ref, std::remove_reference_t<Callable>>>> function_ref(Callable && c)97 function_ref(Callable&& c) noexcept { 98 *this = std::forward<Callable>(c); 99 } 100 101 function_ref& operator=(RawFunc* funcptr) noexcept { 102 mTypeErasedFunction = [](uintptr_t funcptr, Args... args) -> Ret { 103 return (reinterpret_cast<RawFunc*>(funcptr))(std::forward<Args>(args)...); 104 }; 105 mCallable = reinterpret_cast<uintptr_t>(funcptr); 106 return *this; 107 } 108 109 template <class Callable, class = std::enable_if_t< 110 std::is_invocable_r_v<Ret, Callable, Args...> && 111 !std::is_same_v<function_ref, std::remove_reference_t<Callable>>>> 112 function_ref& operator=(Callable&& c) noexcept { 113 mTypeErasedFunction = [](uintptr_t callable, Args... args) -> Ret { 114 // Generate a lambda that remembers the type of the passed 115 // |Callable|. 116 return (*reinterpret_cast<std::remove_reference_t<Callable>*>(callable))( 117 std::forward<Args>(args)...); 118 }; 119 mCallable = reinterpret_cast<uintptr_t>(&c); 120 return *this; 121 } 122 operator()123 Ret operator()(Args... args) const { 124 return mTypeErasedFunction(mCallable, std::forward<Args>(args)...); 125 } 126 127 private: 128 using TypeErasedFunc = Ret(uintptr_t, Args...); 129 TypeErasedFunc* mTypeErasedFunction; 130 uintptr_t mCallable; 131 }; 132 133 } // namespace android::base 134