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 template <class Callable, class = std::enable_if_t< 91 std::is_invocable_r<Ret, Callable, Args...>::value && 92 !std::is_same_v<function_ref, std::remove_reference_t<Callable>>>> function_ref(Callable && c)93 function_ref(Callable&& c) noexcept 94 : mTypeErasedFunction([](const function_ref* self, Args... args) -> Ret { 95 // Generate a lambda that remembers the type of the passed 96 // |Callable|. 97 return (*reinterpret_cast<std::remove_reference_t<Callable>*>(self->mCallable))( 98 std::forward<Args>(args)...); 99 }), 100 mCallable(reinterpret_cast<intptr_t>(&c)) {} 101 102 template <class Callable, class = std::enable_if_t< 103 std::is_invocable_r<Ret, Callable, Args...>::value && 104 !std::is_same_v<function_ref, std::remove_reference_t<Callable>>>> 105 function_ref& operator=(Callable&& c) noexcept { 106 mTypeErasedFunction = [](const function_ref* self, Args... args) -> Ret { 107 // Generate a lambda that remembers the type of the passed 108 // |Callable|. 109 return (*reinterpret_cast<std::remove_reference_t<Callable>*>(self->mCallable))( 110 std::forward<Args>(args)...); 111 }; 112 mCallable = reinterpret_cast<intptr_t>(&c); 113 return *this; 114 } 115 operator()116 Ret operator()(Args... args) const { 117 return mTypeErasedFunction(this, std::forward<Args>(args)...); 118 } 119 120 private: 121 using TypeErasedFunc = Ret(const function_ref*, Args...); 122 TypeErasedFunc* mTypeErasedFunction; 123 intptr_t mCallable; 124 }; 125 126 } // namespace android::base 127