1 // Copyright 2023 The Chromium 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 // quiche_callbacks.h provides definitions for the callback types used by 6 // QUICHE. Those aliases should be used instead of the function types provided 7 // by the standard library (std::function) or Abseil (absl::FunctionRef, 8 // absl::AnyInvocable). 9 // 10 // The aliases defined in this class are: 11 // - quiche::UnretainedCallback 12 // - quiche::SingleUseCallback 13 // - quiche::MultiUseCallback 14 // Each is documented below near its definition. 15 // 16 // As a general principle, there are following ways of constructing a callback: 17 // - Using a lambda expression (preferred) 18 // - Using absl::bind_front 19 // - Passing an already defined local function 20 // 21 // The following methods, however, should be avoided: 22 // - std::bind (<https://abseil.io/tips/108>) 23 // - Binding or taking a pointer to a function outside of the current module, 24 // especially the methods provided by the C++ standard library 25 // (use lambda instead; see go/totw/133 internally for motivation) 26 27 #ifndef QUICHE_COMMON_QUICHE_CALLBACKS_H_ 28 #define QUICHE_COMMON_QUICHE_CALLBACKS_H_ 29 30 #include <type_traits> 31 32 #include "absl/functional/any_invocable.h" 33 #include "absl/functional/function_ref.h" 34 #include "quiche/common/platform/api/quiche_export.h" 35 36 namespace quiche { 37 38 namespace callbacks_internal { 39 template <class Sig> 40 class QUICHE_EXPORT SignatureChanger {}; 41 42 template <typename ReturnType, typename... Args> 43 class QUICHE_NO_EXPORT SignatureChanger<ReturnType(Args...)> { 44 public: 45 using Rvalue = ReturnType(Args...) &&; 46 using Const = ReturnType(Args...) const; 47 }; 48 } // namespace callbacks_internal 49 50 // UnretainedCallback is the QUICHE alias for absl::FunctionRef. 51 // 52 // UnretainedCallback should be used when a function needs another function 53 // passed into it, but it will not retain any pointers to it long-term. 54 // 55 // For example, a QuicSession class may have function: 56 // void DoForAllStreams(quiche::UnretainedCallback<void(QuicStream*)>); 57 // 58 // Then a method could call it like this: 59 // int num_bidi_streams = 0; 60 // DoForAllStreams([&num_bidi_streams](QuicStream* stream) { 61 // if (stream->is_bidirectional()) { 62 // ++num_bidi_streams; 63 // } 64 // }); 65 // 66 // Note that similarly to absl::string_view, FunctionRef/UnretainedCallback does 67 // not own the underlying memory. This means that the code below will not work: 68 // 69 // quiche::UnretainedCallback f = [&i]() { ++i; } // <- INVALID CODE 70 // 71 // The code above allocates a lambda object that stores a pointer to `i` in it, 72 // stores a reference to that object inside `f`, and then immediately frees the 73 // lambda object. Attempting to compile the code above will fail with an error 74 // that says "Temporary whose address is used as value of local variable 'f' 75 // will be destroyed at the end of the full-expression". 76 template <class T> 77 using UnretainedCallback = absl::FunctionRef<T>; 78 79 // SingleUseCallback<T(...)> is the QUICHE alias for 80 // absl::AnyInvocable<T(...) &&>. 81 // 82 // SingleUseCallback is meant to be used for callbacks that may be called at 83 // most once. For instance, a class may have a method that looks like this: 84 // 85 // void SetOnSessionDestroyed(quiche::SingleUseCallback<void()> callback) { 86 // on_session_destroyed_callback_ = std::move(callback); 87 // } 88 // 89 // Then it can execute the callback like this: 90 // 91 // ~Session() { 92 // std::move(on_session_destroyed_callback_ )(); 93 // } 94 // 95 // Note that as with other types of similar nature, calling the callback after 96 // it has been moved is undefined behavior (it will result in an 97 // ABSL_HARDENING_ASSERT() call). 98 template <class T> 99 using SingleUseCallback = absl::AnyInvocable< 100 typename callbacks_internal::SignatureChanger<T>::Rvalue>; 101 102 static_assert(std::is_same_v<SingleUseCallback<void(int, int &, int &&)>, 103 absl::AnyInvocable<void(int, int &, int &&) &&>>); 104 105 // MultiUseCallback<T(...)> is the QUICHE alias for 106 // absl::AnyInvocable<T(...) const>. 107 // 108 // MultiUseCallback is intended for situations where a callback may be invoked 109 // multiple times. It is probably the closest equivalent to std::function 110 // in this file, notable differences being that MultiUseCallback is move-only 111 // and immutable (meaning that it cannot have an internal state that mutates; it 112 // can still point to things that are mutable). 113 template <class T> 114 using MultiUseCallback = 115 absl::AnyInvocable<typename callbacks_internal::SignatureChanger<T>::Const>; 116 117 static_assert( 118 std::is_same_v<MultiUseCallback<void()>, absl::AnyInvocable<void() const>>); 119 120 // Use cases that are intentionally not covered by this header file: 121 // 122 // (a) Mutable callbacks. 123 // 124 // In C++, it's possible for a lambda to mutate its own state, e.g.: 125 // 126 // absl::AnyInvocable<void()> inc = [i = 0]() mutable { 127 // std::cout << (i++) << std::endl; 128 // }; 129 // inc(); 130 // inc(); 131 // inc(); 132 // 133 // The code above will output numbers 0, 1, 2. The fact that a callback can 134 // mutate its internal representation is counterintuitive, and thus not 135 // supported. Note that this limitation can be trivially worked around by 136 // passing a pointer (e.g., in the example below, `i = 0` could be replaced with 137 // `i = std::make_unique<int>(0)` to the similar effect). 138 // 139 // (b) Copyable callbacks. 140 // 141 // In C++, this would typically achieved by using std::function. This file 142 // could contain an alias for std::function; it currently does not, since this 143 // use case is expected to be fairly rare. 144 // 145 // (c) noexpect support. 146 // 147 // We do not use C++ exceptions in QUICHE. 148 149 } // namespace quiche 150 151 #endif // QUICHE_COMMON_QUICHE_CALLBACKS_H_ 152