blob: 3017e1b81a51b42b0eae8f7e00fa56f5960db129 [file] [log] [blame]
#pragma once
#include <stdplus/handle/managed.hpp>
#include <tuple>
#include <utility>
namespace stdplus
{
struct Cancelable
{
virtual ~Cancelable() = default;
virtual void cancel() noexcept = 0;
};
namespace detail
{
struct CancelableF
{
inline void operator()(Cancelable* c) noexcept
{
c->cancel();
}
};
using CancelHandle = Managed<Cancelable*>::HandleF<CancelableF>;
} // namespace detail
struct Cancel : detail::CancelHandle
{
Cancel() : detail::CancelHandle(std::nullopt) {}
template <typename T>
explicit Cancel(T&& t) : detail::CancelHandle(t)
{}
};
namespace detail
{
struct fAny
{};
struct fPtr : fAny
{};
template <typename>
struct validator
{
typedef int type;
};
template <typename F,
typename validator<decltype(std::declval<F>() == nullptr)>::type = 0>
inline bool fPop(const F& f, fPtr)
{
return !(f == nullptr);
}
template <typename F>
inline bool fPop(const F&, fAny)
{
return true;
}
template <typename F>
inline bool fPop(const F& f)
{
return fPop(f, fPtr());
}
} // namespace detail
template <typename F, typename... DefaultArgs>
class AlwaysCallOnce
{
public:
template <typename Fi, typename... Vs>
explicit AlwaysCallOnce(Fi&& fi, Vs&&... default_args) :
f(std::forward<Fi>(fi)), default_args(std::forward<Vs>(default_args)...)
{}
AlwaysCallOnce(const AlwaysCallOnce&) = delete;
AlwaysCallOnce(AlwaysCallOnce&& other) noexcept :
f(std::move(other.f)), default_args(std::move(other.default_args)),
called(std::exchange(other.called, true))
{}
AlwaysCallOnce& operator=(const AlwaysCallOnce&) = delete;
AlwaysCallOnce& operator=(AlwaysCallOnce&& other) noexcept
{
finalCall();
f = std::move(other.f);
default_args = std::move(other.default_args);
called = std::exchange(other.called, true);
return *this;
}
~AlwaysCallOnce()
{
finalCall();
}
template <typename... Args>
auto operator()(Args&&... args) noexcept
{
called = true;
return f(std::forward<Args>(args)...);
}
private:
F f;
std::tuple<DefaultArgs...> default_args;
bool called = false;
void finalCall() noexcept
{
if (!called && detail::fPop(f))
{
std::apply(f, default_args);
}
}
};
template <typename... Args>
inline auto alwaysCallOnce(Args&&... args)
{
return AlwaysCallOnce<std::remove_cv_t<std::remove_reference_t<Args>>...>(
std::forward<Args>(args)...);
}
} // namespace stdplus