blob: e5711ccda4f73a78c44dfade715d886042aa6ac0 [file] [log] [blame]
// 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_