144 lines
3.9 KiB
Markdown
144 lines
3.9 KiB
Markdown
```c++
|
|
#pragma once
|
|
|
|
|
|
#include <cstddef>
|
|
#include <new>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
|
|
namespace util {
|
|
|
|
|
|
///
|
|
/// @brief Class storing a type-erased function object/pointer.
|
|
/// @details This class does not employ dynamic memory allocation; instead, it
|
|
/// stores the function object/pointer in a fixed-size buffer, the size of which
|
|
/// can be specified as a template parameter.
|
|
/// @warning This class does not take ownership of any resources it is
|
|
/// constructed from. It is the responsibility of the user to ensure that the
|
|
/// function object/pointer outlives the InplaceFunction object.
|
|
///
|
|
/// @tparam func_sig_t Signature of the function.
|
|
/// @tparam t_storage_size Size of the storage buffer, in bytes.
|
|
///
|
|
template <typename func_sig_t, std::size_t t_storage_size = 8>
|
|
class InplaceFunction;
|
|
|
|
template <typename return_t, typename... args_t, std::size_t storage_size>
|
|
class InplaceFunction<return_t(args_t...), storage_size> {
|
|
|
|
using storage_t = char[storage_size];
|
|
using wrapper_func_t = return_t (*)(storage_t, args_t...);
|
|
|
|
public:
|
|
InplaceFunction() = default;
|
|
|
|
///
|
|
/// @brief Constructor for function objects.
|
|
///
|
|
template <typename F>
|
|
InplaceFunction(F f) {
|
|
bind(f);
|
|
}
|
|
|
|
///
|
|
/// @brief Construct from free function pointer.
|
|
///
|
|
template <auto F>
|
|
static InplaceFunction construct() {
|
|
InplaceFunction<return_t(args_t...)> func;
|
|
func.template bind<F>();
|
|
return func;
|
|
}
|
|
|
|
///
|
|
/// @brief Construct form member function pointer.
|
|
///
|
|
template <auto F, typename class_t>
|
|
static InplaceFunction construct(class_t& c) {
|
|
InplaceFunction<return_t(args_t...)> func;
|
|
func.template bind<F>(c);
|
|
return func;
|
|
}
|
|
|
|
///
|
|
/// @brief Construct from function object.
|
|
///
|
|
template <typename F>
|
|
static InplaceFunction construct(F f) {
|
|
InplaceFunction<return_t(args_t...)> func;
|
|
func.bind(f);
|
|
return func;
|
|
}
|
|
|
|
///
|
|
/// @warning If no callable has been bound, calling this function will
|
|
/// result in undefined behavior.
|
|
///
|
|
return_t operator()(args_t... args) {
|
|
return mWrapper(mStorage, args...);
|
|
}
|
|
|
|
operator bool() const {
|
|
return !(mWrapper == nullptr);
|
|
}
|
|
|
|
///
|
|
/// @brief Bind a free function.
|
|
///
|
|
/// @tparam F Pointer to free function
|
|
///
|
|
template <auto F>
|
|
requires std::is_invocable_r_v<return_t, decltype(F), args_t...>
|
|
void bind() {
|
|
mWrapper = [](storage_t, args_t... args) {
|
|
return F(std::forward<args_t>(args)...);
|
|
// return std::invoke(F, std::forward<args_t>(args)...);
|
|
};
|
|
}
|
|
|
|
///
|
|
/// @brief Bind a member function.
|
|
///
|
|
/// @tparam class_t Class the member belongs to.
|
|
/// @param c Reference to object the member function should be called on.
|
|
/// @tparam F Member function pointer.
|
|
///
|
|
template <auto F, typename class_t>
|
|
requires std::is_invocable_r_v<return_t, decltype(F), class_t&,
|
|
args_t...> &&
|
|
(sizeof(class_t*) <= storage_size)
|
|
void bind(class_t& c) {
|
|
new (mStorage)(class_t*){&c};
|
|
|
|
mWrapper = [](storage_t storage, args_t... args) {
|
|
return F(*reinterpret_cast<class_t**>(storage),
|
|
std::forward<args_t>(args)...);
|
|
};
|
|
}
|
|
|
|
///
|
|
/// @brief Bind a function object.
|
|
///
|
|
template <typename F>
|
|
requires std::is_invocable_r_v<return_t, F, args_t...> &&
|
|
(sizeof(F) <= storage_size) &&
|
|
std::is_trivially_destructible_v<F>
|
|
void bind(F f) {
|
|
new (mStorage) F{std::move(f)};
|
|
mWrapper = [](storage_t storage, args_t... args) {
|
|
return (*reinterpret_cast<F*>(storage))(
|
|
std::forward<args_t>(args)...);
|
|
};
|
|
}
|
|
|
|
private:
|
|
storage_t mStorage;
|
|
wrapper_func_t mWrapper = nullptr;
|
|
};
|
|
|
|
|
|
} // namespace util
|
|
``` |