#pragma once
#include <functional>
#include <memory>
#include <type_traits>
#include <utility>
namespace slp {
template <class F>
class function_ref;
template <class R, class... Args>
class function_ref<R(Args...)> {
public:
constexpr function_ref() noexcept = delete;
constexpr function_ref(const function_ref<R(Args...)>& rhs) noexcept =
default;
template <typename F>
requires(!std::is_same_v<std::decay_t<F>, function_ref> &&
std::is_invocable_r_v<R, F &&, Args...>)
constexpr function_ref(F&& f) noexcept : obj_(const_cast<void*>(
reinterpret_cast<const void*>(std::addressof(f)))) {
callback_ = [](void* obj, Args... args) -> R {
return std::invoke(
*reinterpret_cast<typename std::add_pointer<F>::type>(obj),
std::forward<Args>(args)...);
};
}
constexpr function_ref<R(Args...)>& operator=(
const function_ref<R(Args...)>& rhs) noexcept = default;
template <typename F>
requires std::is_invocable_r_v<R, F&&, Args...>
constexpr function_ref<R(Args...)>& operator=(F&& f) noexcept {
obj_ = reinterpret_cast<void*>(std::addressof(f));
callback_ = [](void* obj, Args... args) {
return std::invoke(
*reinterpret_cast<typename std::add_pointer<F>::type>(obj),
std::forward<Args>(args)...);
};
return *this;
}
constexpr void swap(function_ref<R(Args...)>& rhs) noexcept {
std::swap(obj_, rhs.obj_);
std::swap(callback_, rhs.callback_);
}
R operator()(Args... args) const {
return callback_(obj_, std::forward<Args>(args)...);
}
private:
void* obj_ = nullptr;
R (*callback_)(void*, Args...) = nullptr;
};
template <typename R, typename... Args>
constexpr void swap(function_ref<R(Args...)>& lhs,
function_ref<R(Args...)>& rhs) noexcept {
lhs.swap(rhs);
}
template <typename R, typename... Args>
function_ref(R (*)(Args...)) -> function_ref<R(Args...)>;
}