blob: 6eb929007cb34485b02d53e4e2063796705ccf6e [file] [log] [blame]
// Copyright 2018 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 LIB_CALLBACK_ENSURE_CALLED_H_
#define LIB_CALLBACK_ENSURE_CALLED_H_
#include <optional>
#include <tuple>
#include <utility>
#include "src/lib/fxl/logging.h"
#include "src/lib/fxl/macros.h"
namespace callback {
// Given a function and a set a default arguments, ensures that the function
// will always be called exactly once.
// If the function is not called manually (by operator()) with specified
// arguments, it will be called with the default arguments on destruction.
// The function and its default arguments are destructed immediately after the
// function is called.
template <typename T, typename... ArgType>
class EnsureCalled {
public:
EnsureCalled() = default;
explicit EnsureCalled(T&& function, ArgType&&... args)
: closure_(std::pair(
std::move(function),
std::tuple<ArgType...>(std::forward<ArgType>(args)...))){};
EnsureCalled(EnsureCalled&& other) { *this = std::move(other); }
EnsureCalled& operator=(EnsureCalled&& other) {
CallDefaultIfNeeded();
// Assigning |closure_| directly does not work because lambdas are
// move-constructible but not move-assignable. We construct a new pair in
// place instead.
if (other.closure_) {
closure_.emplace(*other.TakeClosure());
}
return *this;
}
~EnsureCalled() { CallDefaultIfNeeded(); }
auto operator()(ArgType... args) {
FXL_DCHECK(closure_);
auto closure = TakeClosure();
return std::invoke(std::move(closure->first),
std::forward<ArgType>(args)...);
}
explicit operator bool() const { return closure_.has_value(); }
private:
void CallDefaultIfNeeded() {
if (!closure_) {
return;
}
auto closure = TakeClosure();
std::apply(std::move(closure->first), std::move(closure->second));
}
std::optional<std::pair<T, std::tuple<ArgType...>>> TakeClosure() {
auto closure = std::move(closure_);
closure_ = std::nullopt;
return closure;
}
std::optional<std::pair<T, std::tuple<ArgType...>>> closure_;
};
} // namespace callback
#endif // LIB_CALLBACK_ENSURE_CALLED_H_