/** * @file Action.h * @author apio (cloudapio.eu) * @brief Wrapper for callable objects, like function pointers or lambdas. * * @copyright Copyright (c) 2023, the Luna authors. * */ #pragma once #include namespace os { /** * @brief Wrapper for callable objects. * * Certain callable types such as capture lambdas don't have a named type, thus they can't be passed to functions as * arguments. The Action type acts as a callable wrapper for such functions, allowing them to be passed as arguments * to any function. */ class Action { public: /** * @brief Construct a new empty Action object. * * Since it is not wrapping any callable object, this object is invalid and attempting to invoke its * (nonexistent) callable object will result in a crash at runtime. */ Action() { m_action = nullptr; } /** * @brief Construct a new Action object, moving it from another one. * * @param other The old Action object to move from. This object will become invalid. */ Action(Action&& other) : m_action(other.m_action) { other.m_action = nullptr; } /** * @brief Construct a new Action object from a callable object. * * @tparam T The type of the callable object. Since this can be guessed by template deduction, the object * doesn't actually need a type name. * @param function The callable object to wrap. */ template Action(T&& function) { m_action = new (std::nothrow) ActionImpl(move(function)); } /** * @brief Assign a new callable object to this Action. * * @tparam T The type of the callable object. Since this can be guessed by template deduction, the object * doesn't actually need a type name. * @param function The callable object to wrap. * @return Action& A reference to this object, as required by operator=(). */ template Action& operator=(T&& function) { if (m_action) delete m_action; m_action = new (std::nothrow) ActionImpl(move(function)); return *this; } /** * @brief Call the underlying object. */ void operator()() { expect(m_action, "os::Action called with no underlying callable object"); return m_action->call(); } ~Action() { if (m_action) delete m_action; } private: class ActionBase { public: virtual void call() = 0; virtual ~ActionBase() = default; }; template class ActionImpl final : public ActionBase { public: ActionImpl(T&& function) : m_function(move(function)) { } void call() { return m_function(); } private: T m_function; }; ActionBase* m_action; }; }