| | #pragma once
|
| |
|
| | #include <cassert>
|
| | #include <cstddef>
|
| | #include <cstdint>
|
| | #include <type_traits>
|
| | #include <utility>
|
| |
|
| | namespace fastsignals::detail
|
| | {
|
| |
|
| |
|
| | static constexpr size_t inplace_buffer_size = (sizeof(int) == sizeof(void*) ? 8 : 6) * sizeof(void*);
|
| |
|
| |
|
| | template <class T>
|
| | struct type_container
|
| | {
|
| | T data;
|
| | };
|
| |
|
| |
|
| | using function_buffer_t = std::aligned_storage_t<inplace_buffer_size>;
|
| |
|
| |
|
| | template <class T>
|
| | inline constexpr bool fits_inplace_buffer = (sizeof(type_container<T>) <= inplace_buffer_size);
|
| |
|
| |
|
| |
|
| | template <class T>
|
| | inline constexpr bool can_use_inplace_buffer =
|
| | fits_inplace_buffer<T> &&
|
| | std::is_nothrow_move_constructible_v<T>;
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | template <class Callable>
|
| | using callable_copy_t = std::conditional_t<std::is_function_v<std::remove_reference_t<Callable>>,
|
| | Callable,
|
| | std::remove_cv_t<std::remove_reference_t<Callable>>>;
|
| |
|
| | class base_function_proxy
|
| | {
|
| | public:
|
| | virtual ~base_function_proxy() = default;
|
| | virtual base_function_proxy* clone(void* buffer) const = 0;
|
| | virtual base_function_proxy* move(void* buffer) noexcept = 0;
|
| | };
|
| |
|
| | template <class Signature>
|
| | class function_proxy;
|
| |
|
| | template <class Return, class... Arguments>
|
| | class function_proxy<Return(Arguments...)> : public base_function_proxy
|
| | {
|
| | public:
|
| | virtual Return operator()(Arguments&&...) = 0;
|
| | };
|
| |
|
| | template <class Callable, class Return, class... Arguments>
|
| | class function_proxy_impl final : public function_proxy<Return(Arguments...)>
|
| | {
|
| | public:
|
| |
|
| |
|
| | static_assert(std::is_same_v<std::invoke_result_t<Callable, Arguments...>, Return>,
|
| | "cannot construct function<> class from callable object with different return type");
|
| |
|
| | template <class FunctionObject>
|
| | explicit function_proxy_impl(FunctionObject&& function)
|
| | : m_callable(std::forward<FunctionObject>(function))
|
| | {
|
| | }
|
| |
|
| | Return operator()(Arguments&&... args) final
|
| | {
|
| | return m_callable(std::forward<Arguments>(args)...);
|
| | }
|
| |
|
| | base_function_proxy* clone(void* buffer) const final
|
| | {
|
| | if constexpr (can_use_inplace_buffer<function_proxy_impl>)
|
| | {
|
| | return new (buffer) function_proxy_impl(*this);
|
| | }
|
| | else
|
| | {
|
| | (void)buffer;
|
| | return new function_proxy_impl(*this);
|
| | }
|
| | }
|
| |
|
| | base_function_proxy* move(void* buffer) noexcept final
|
| | {
|
| | if constexpr (can_use_inplace_buffer<function_proxy_impl>)
|
| | {
|
| | base_function_proxy* moved = new (buffer) function_proxy_impl(std::move(*this));
|
| | this->~function_proxy_impl();
|
| | return moved;
|
| | }
|
| | else
|
| | {
|
| | (void)buffer;
|
| | return this;
|
| | }
|
| | }
|
| |
|
| | private:
|
| | callable_copy_t<Callable> m_callable;
|
| | };
|
| |
|
| | template <class Fn, class Return, class... Arguments>
|
| | inline constexpr bool is_noexcept_packed_function_init = can_use_inplace_buffer<function_proxy_impl<Fn, Return, Arguments...>>;
|
| |
|
| | class packed_function final
|
| | {
|
| | public:
|
| | packed_function() = default;
|
| | packed_function(packed_function&& other) noexcept;
|
| | packed_function(const packed_function& other);
|
| | packed_function& operator=(packed_function&& other) noexcept;
|
| | packed_function& operator=(const packed_function& other);
|
| | ~packed_function() noexcept;
|
| |
|
| |
|
| |
|
| | template <class Callable, class Return, class... Arguments>
|
| | void init(Callable&& function) noexcept(is_noexcept_packed_function_init<Callable, Return, Arguments...>)
|
| | {
|
| | using proxy_t = function_proxy_impl<Callable, Return, Arguments...>;
|
| |
|
| | assert(m_proxy == nullptr);
|
| | if constexpr (can_use_inplace_buffer<proxy_t>)
|
| | {
|
| | m_proxy = new (&m_buffer) proxy_t{ std::forward<Callable>(function) };
|
| | }
|
| | else
|
| | {
|
| | m_proxy = new proxy_t{ std::forward<Callable>(function) };
|
| | }
|
| | }
|
| |
|
| | template <class Signature>
|
| | function_proxy<Signature>& get() const
|
| | {
|
| | return static_cast<function_proxy<Signature>&>(unwrap());
|
| | }
|
| |
|
| | void reset() noexcept;
|
| |
|
| | private:
|
| | base_function_proxy* move_proxy_from(packed_function&& other) noexcept;
|
| | base_function_proxy* clone_proxy_from(const packed_function &other);
|
| | base_function_proxy& unwrap() const;
|
| | bool is_buffer_allocated() const noexcept;
|
| |
|
| | function_buffer_t m_buffer[1] = {};
|
| | base_function_proxy* m_proxy = nullptr;
|
| | };
|
| |
|
| | }
|
| |
|