| // 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_ |