| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_DEVELOPER_DEBUG_ZXDB_COMMON_COMPLETION_CALLBACK_H_ |
| #define SRC_DEVELOPER_DEBUG_ZXDB_COMMON_COMPLETION_CALLBACK_H_ |
| |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include "lib/fit/function.h" |
| #include "src/developer/debug/zxdb/common/err.h" |
| |
| namespace zxdb { |
| |
| // Represents a completion callback function that MUST be called and takes an |
| // error and an optional list of parameters. |
| // |
| // It is a wrapper around a fit::callback that asserts if is destroyed before |
| // the callback is run. When using continuations the thread of execution will |
| // be lost if any step every forgets to call the completion callback. |
| // |
| // A completion callback always takes a "const Err&" as its first parameter and |
| // returns void. The template arguments to the CompletionCallback are the |
| // parameters following the Err. |
| // |
| // The operator() overloads simplify execution by taking either an "Err" (which |
| // assumes all other parameters are default-constructed) or the parameter list |
| // (which assumes no error); |
| // |
| // Receiver of a callback example: |
| // |
| // void DoStuff(int some_param, CompletionCallback<ExprValue> cb) { |
| // Err err = Foo(); |
| // if (err.has_error()) |
| // cb(err); |
| // else |
| // cb(ExprValue(5)); |
| // } |
| // |
| // Caller example: |
| // |
| // int main() { |
| // DoStuff(1, [](const Err& err, ExprValue v) { |
| // if (err.has_error()) { |
| // ... |
| // } else { |
| // ... |
| // } |
| // }); |
| // } |
| // |
| // If the parameters can't be default-constructed in the error case there is |
| // also a version that takes all callback parameters. |
| template <typename... Args> |
| class CompletionCallback { |
| public: |
| using Callback = fit::callback<void(const Err&, Args...)>; |
| |
| // These constructors are mirrors of fit::callback. See that class for more. |
| CompletionCallback() = default; |
| |
| CompletionCallback(decltype(nullptr)) : callback_(nullptr) {} |
| |
| CompletionCallback(void (*target)(const Err& err, Args...)) : callback_(target) {} |
| |
| // For functors, we need to capture the raw type but also restrict on the |
| // existence of an appropriate operator () to resolve overloads and implicit |
| // casts properly. |
| template <typename Callable, |
| typename = std::enable_if_t<std::is_convertible< |
| decltype(std::declval<Callable&>()(Err(), std::declval<Args>()...)), void>::value>> |
| CompletionCallback(Callable target) : callback_(std::move(target)) {} |
| |
| // Delete specialization for fit::callback. |
| template <size_t other_inline_target_size, bool other_require_inline> |
| CompletionCallback( |
| ::fit::callback_impl<other_inline_target_size, other_require_inline, void(Args...)>) = delete; |
| |
| CompletionCallback(CompletionCallback&& other) : callback_(std::move(other.callback_)) {} |
| |
| ~CompletionCallback() { |
| FX_CHECK(!callback_) << "Completion callback not run before destruction."; |
| } |
| |
| // Assignment from a callable function. See fit::callback. |
| // |
| // Unlike fit::callback, this will assert if the current object has a |
| // function that has not been called. |
| template <typename Callable, |
| typename = std::enable_if_t<std::is_convertible< |
| decltype(std::declval<Callable&>()(Err(), std::declval<Args>()...)), void>::value>> |
| CompletionCallback& operator=(Callable target) { |
| FX_CHECK(!callback_) << "Overwriting a completion callback without calling it."; |
| callback_ = std::move(target); |
| return *this; |
| } |
| |
| // Move assignment |
| CompletionCallback& operator=(CompletionCallback&& other) { |
| if (&other == this) |
| return *this; |
| |
| FX_CHECK(!callback_) << "Overwriting a completion callback without calling it."; |
| callback_ = std::move(other.callback_); |
| return *this; |
| } |
| |
| explicit operator bool() const { return callback_; } |
| |
| // This version takes all parameters to the callback, the "Err" first |
| // parameter and all others. |
| // |
| // It is parameterized to only be available when there are more than one |
| // arguments to avoid collisions with the error case below when there are no |
| // other parameters. |
| template <typename = std::enable_if<(sizeof...(Args) != 0)>> |
| void operator()(const Err& err, Args... args) { |
| callback_(err, std::forward<Args>(args)...); |
| callback_ = nullptr; |
| } |
| |
| // Execute the callback with the given error. |
| // |
| // The other parameters to the callback are default-constructed. If this |
| // won't compile because a parameter can't be default constructed or the |
| // code needs to specify one of them manually in the error case, use the |
| // version above that takes all parameters. |
| void operator()(const Err& err) { |
| FX_CHECK(err.has_error()) << "Expected error to be set."; |
| callback_(err, Args()...); |
| callback_ = nullptr; |
| } |
| |
| // Executes the callback with no error. Only the callback parameters |
| // following the "Err" need to be specified. |
| void operator()(Args... args) { |
| callback_(Err(), std::forward<Args>(args)...); |
| callback_ = nullptr; |
| } |
| |
| private: |
| Callback callback_; |
| }; |
| |
| } // namespace zxdb |
| |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_COMMON_COMPLETION_CALLBACK_H_ |