xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/common/quiche_callbacks.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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