| // Copyright 2023 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // quiche_callbacks.h provides definitions for the callback types used by |
| // QUICHE. Those aliases should be used instead of the function types provided |
| // by the standard library (std::function) or Abseil (absl::FunctionRef, |
| // absl::AnyInvocable). |
| // |
| // The aliases defined in this class are: |
| // - quiche::UnretainedCallback |
| // - quiche::SingleUseCallback |
| // - quiche::MultiUseCallback |
| // Each is documented below near its definition. |
| // |
| // As a general principle, there are following ways of constructing a callback: |
| // - Using a lambda expression (preferred) |
| // - Using absl::bind_front |
| // - Passing an already defined local function |
| // |
| // The following methods, however, should be avoided: |
| // - std::bind (<https://abseil.io/tips/108>) |
| // - Binding or taking a pointer to a function outside of the current module, |
| // especially the methods provided by the C++ standard library |
| // (use lambda instead; see go/totw/133 internally for motivation) |
| |
| #ifndef QUICHE_COMMON_QUICHE_CALLBACKS_H_ |
| #define QUICHE_COMMON_QUICHE_CALLBACKS_H_ |
| |
| #include <type_traits> |
| |
| #include "absl/functional/any_invocable.h" |
| #include "absl/functional/function_ref.h" |
| #include "quiche/common/platform/api/quiche_export.h" |
| |
| namespace quiche { |
| |
| namespace callbacks_internal { |
| template <class Sig> |
| class QUICHE_EXPORT SignatureChanger {}; |
| |
| template <typename ReturnType, typename... Args> |
| class QUICHE_NO_EXPORT SignatureChanger<ReturnType(Args...)> { |
| public: |
| using Rvalue = ReturnType(Args...) &&; |
| using Const = ReturnType(Args...) const; |
| }; |
| } // namespace callbacks_internal |
| |
| // UnretainedCallback is the QUICHE alias for absl::FunctionRef. |
| // |
| // UnretainedCallback should be used when a function needs another function |
| // passed into it, but it will not retain any pointers to it long-term. |
| // |
| // For example, a QuicSession class may have function: |
| // void DoForAllStreams(quiche::UnretainedCallback<void(QuicStream*)>); |
| // |
| // Then a method could call it like this: |
| // int num_bidi_streams = 0; |
| // DoForAllStreams([&num_bidi_streams](QuicStream* stream) { |
| // if (stream->is_bidirectional()) { |
| // ++num_bidi_streams; |
| // } |
| // }); |
| // |
| // Note that similarly to absl::string_view, FunctionRef/UnretainedCallback does |
| // not own the underlying memory. This means that the code below will not work: |
| // |
| // quiche::UnretainedCallback f = [&i]() { ++i; } // <- INVALID CODE |
| // |
| // The code above allocates a lambda object that stores a pointer to `i` in it, |
| // stores a reference to that object inside `f`, and then immediately frees the |
| // lambda object. Attempting to compile the code above will fail with an error |
| // that says "Temporary whose address is used as value of local variable 'f' |
| // will be destroyed at the end of the full-expression". |
| template <class T> |
| using UnretainedCallback = absl::FunctionRef<T>; |
| |
| // SingleUseCallback<T(...)> is the QUICHE alias for |
| // absl::AnyInvocable<T(...) &&>. |
| // |
| // SingleUseCallback is meant to be used for callbacks that may be called at |
| // most once. For instance, a class may have a method that looks like this: |
| // |
| // void SetOnSessionDestroyed(quiche::SingleUseCallback<void()> callback) { |
| // on_session_destroyed_callback_ = std::move(callback); |
| // } |
| // |
| // Then it can execute the callback like this: |
| // |
| // ~Session() { |
| // std::move(on_session_destroyed_callback_ )(); |
| // } |
| // |
| // Note that as with other types of similar nature, calling the callback after |
| // it has been moved is undefined behavior (it will result in an |
| // ABSL_HARDENING_ASSERT() call). |
| template <class T> |
| using SingleUseCallback = absl::AnyInvocable< |
| typename callbacks_internal::SignatureChanger<T>::Rvalue>; |
| |
| static_assert(std::is_same_v<SingleUseCallback<void(int, int &, int &&)>, |
| absl::AnyInvocable<void(int, int &, int &&) &&>>); |
| |
| // MultiUseCallback<T(...)> is the QUICHE alias for |
| // absl::AnyInvocable<T(...) const>. |
| // |
| // MultiUseCallback is intended for situations where a callback may be invoked |
| // multiple times. It is probably the closest equivalent to std::function |
| // in this file, notable differences being that MultiUseCallback is move-only |
| // and immutable (meaning that it cannot have an internal state that mutates; it |
| // can still point to things that are mutable). |
| template <class T> |
| using MultiUseCallback = |
| absl::AnyInvocable<typename callbacks_internal::SignatureChanger<T>::Const>; |
| |
| static_assert( |
| std::is_same_v<MultiUseCallback<void()>, absl::AnyInvocable<void() const>>); |
| |
| // Use cases that are intentionally not covered by this header file: |
| // |
| // (a) Mutable callbacks. |
| // |
| // In C++, it's possible for a lambda to mutate its own state, e.g.: |
| // |
| // absl::AnyInvocable<void()> inc = [i = 0]() mutable { |
| // std::cout << (i++) << std::endl; |
| // }; |
| // inc(); |
| // inc(); |
| // inc(); |
| // |
| // The code above will output numbers 0, 1, 2. The fact that a callback can |
| // mutate its internal representation is counterintuitive, and thus not |
| // supported. Note that this limitation can be trivially worked around by |
| // passing a pointer (e.g., in the example below, `i = 0` could be replaced with |
| // `i = std::make_unique<int>(0)` to the similar effect). |
| // |
| // (b) Copyable callbacks. |
| // |
| // In C++, this would typically achieved by using std::function. This file |
| // could contain an alias for std::function; it currently does not, since this |
| // use case is expected to be fairly rare. |
| // |
| // (c) noexpect support. |
| // |
| // We do not use C++ exceptions in QUICHE. |
| |
| } // namespace quiche |
| |
| #endif // QUICHE_COMMON_QUICHE_CALLBACKS_H_ |