apio
53f8a583dc
Some checks failed
Build and test / build (push) Failing after 1m21s
This commit adds an error-propagating constructor for Action and Function, which makes them usable in the kernel.
301 lines
8.6 KiB
C++
301 lines
8.6 KiB
C++
/**
|
|
* @file Action.h
|
|
* @author apio (cloudapio.eu)
|
|
* @brief Wrapper for callable objects, like function pointers or lambdas.
|
|
*
|
|
* @copyright Copyright (c) 2024, the Luna authors.
|
|
*
|
|
*/
|
|
|
|
#pragma once
|
|
#include <luna/Heap.h>
|
|
#include <luna/SharedPtr.h>
|
|
|
|
/**
|
|
* @brief Wrapper for callable objects with no arguments.
|
|
*
|
|
* 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 = {};
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a new Action object, copying it from another one.
|
|
*
|
|
* @param other The old Action object to copy from.
|
|
*/
|
|
Action(Action& other) : m_action(other.m_action)
|
|
{
|
|
}
|
|
|
|
#ifndef USE_FREESTANDING
|
|
/**
|
|
* @brief Construct a new Action object, copying it from another one.
|
|
*
|
|
* @param other The old Action object to copy from.
|
|
*/
|
|
Action(const Action& other) : m_action(other.m_action)
|
|
{
|
|
// FIXME: Doesn't actually copy?
|
|
}
|
|
|
|
/**
|
|
* @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 <typename T> Action(T&& function)
|
|
{
|
|
m_action =
|
|
adopt_shared_if_nonnull((ActionBase*)new (std::nothrow) ActionImpl<T>(move(function))).release_value();
|
|
}
|
|
|
|
/**
|
|
* @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 <typename T> Action& operator=(T&& function)
|
|
{
|
|
m_action =
|
|
adopt_shared_if_nonnull((ActionBase*)new (std::nothrow) ActionImpl<T>(move(function))).release_value();
|
|
return *this;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief Construct a new Action object, wrapping a callable object. This version propagates errors, and thus can
|
|
* be used in freestanding contexts, unlike the constructors above.
|
|
*
|
|
* @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 <typename T> static Result<Action> wrap(T&& function)
|
|
{
|
|
auto base = TRY(adopt_shared_if_nonnull((ActionBase*)new (std::nothrow) ActionImpl<T>(move(function))));
|
|
return Action(base);
|
|
}
|
|
|
|
/**
|
|
* @brief Call the underlying object.
|
|
*/
|
|
void operator()()
|
|
{
|
|
expect(m_action, "Action called with no underlying callable object");
|
|
return m_action->call();
|
|
}
|
|
|
|
private:
|
|
class ActionBase : public Shareable
|
|
{
|
|
public:
|
|
virtual void call() = 0;
|
|
virtual ~ActionBase() = default;
|
|
};
|
|
|
|
template <typename T> class ActionImpl final : public ActionBase
|
|
{
|
|
public:
|
|
ActionImpl(T&& function) : m_function(move(function))
|
|
{
|
|
}
|
|
|
|
void call()
|
|
{
|
|
m_function();
|
|
}
|
|
|
|
private:
|
|
T m_function;
|
|
};
|
|
|
|
Action(SharedPtr<ActionBase> action) : m_action(action)
|
|
{
|
|
}
|
|
|
|
SharedPtr<ActionBase> m_action;
|
|
};
|
|
|
|
/**
|
|
* @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 Function type acts as a callable wrapper for such functions, allowing them to be passed as
|
|
* arguments to any function.
|
|
*/
|
|
template <class... Args> class Function
|
|
{
|
|
public:
|
|
/**
|
|
* @brief Construct a new empty Function 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.
|
|
*/
|
|
Function()
|
|
{
|
|
m_action = nullptr;
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a new Function object, moving it from another one.
|
|
*
|
|
* @param other The old Function object to move from. This object will become invalid.
|
|
*/
|
|
Function(Function<Args...>&& other) : m_action(other.m_action)
|
|
{
|
|
other.m_action = {};
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a new Function object, copying it from another one.
|
|
*
|
|
* @param other The old Function object to copy from.
|
|
*/
|
|
Function(Function<Args...>& other) : m_action(other.m_action)
|
|
{
|
|
}
|
|
|
|
#ifndef USE_FREESTANDING
|
|
/**
|
|
* @brief Construct a new Function object, copying it from another one.
|
|
*
|
|
* @param other The old Function object to copy from.
|
|
*/
|
|
Function(const Function<Args...>& other) : m_action(other.m_action)
|
|
{
|
|
// FIXME: Doesn't actually copy?
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a new Function 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 <typename T> Function(T&& function)
|
|
{
|
|
m_action =
|
|
adopt_shared_if_nonnull((FunctionBase*)new (std::nothrow) FunctionImpl<T>(move(function))).release_value();
|
|
}
|
|
|
|
/**
|
|
* @brief Assign a new callable object to this Function.
|
|
*
|
|
* @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 <typename T> Function& operator=(T&& function)
|
|
{
|
|
m_action =
|
|
adopt_shared_if_nonnull((FunctionBase*)new (std::nothrow) FunctionImpl<T>(move(function))).release_value();
|
|
return *this;
|
|
}
|
|
|
|
#endif
|
|
|
|
/**
|
|
* @brief Construct a new Function object, wrapping a callable object. This version propagates errors, and thus can
|
|
* be used in freestanding contexts, unlike the constructors above.
|
|
*
|
|
* @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 <typename T> static Result<Function> wrap(T&& function)
|
|
{
|
|
auto base = TRY(adopt_shared_if_nonnull((FunctionBase*)new (std::nothrow) FunctionImpl<T>(move(function))));
|
|
return Function(base);
|
|
}
|
|
|
|
/**
|
|
* @brief Call the underlying object.
|
|
*/
|
|
void operator()(Args... args)
|
|
{
|
|
expect(m_action, "Function called with no underlying callable object");
|
|
return m_action->call(args...);
|
|
}
|
|
|
|
/**
|
|
* @brief Call the underlying object.
|
|
*/
|
|
void operator()(Args... args) const
|
|
{
|
|
expect(m_action, "Function called with no underlying callable object");
|
|
return m_action->call(args...);
|
|
}
|
|
|
|
private:
|
|
class FunctionBase : public Shareable
|
|
{
|
|
public:
|
|
virtual void call(Args... args) = 0;
|
|
virtual void call(Args... args) const = 0;
|
|
virtual ~FunctionBase() = default;
|
|
};
|
|
|
|
template <typename T> class FunctionImpl final : public FunctionBase
|
|
{
|
|
public:
|
|
FunctionImpl(FunctionImpl<T>&& function) : m_function(move(function.m_function))
|
|
{
|
|
}
|
|
|
|
FunctionImpl(T&& function) : m_function(move(function))
|
|
{
|
|
}
|
|
|
|
void call(Args... args) override
|
|
{
|
|
m_function(args...);
|
|
}
|
|
|
|
void call(Args... args) const override
|
|
{
|
|
m_function(args...);
|
|
}
|
|
|
|
private:
|
|
T m_function;
|
|
};
|
|
|
|
Function(SharedPtr<FunctionBase> action) : m_action(action)
|
|
{
|
|
}
|
|
|
|
SharedPtr<FunctionBase> m_action;
|
|
};
|