#pragma once #include #include #include #include #include namespace fastsignals::detail { /// Buffer for callable object in-place construction, /// helps to implement Small Buffer Optimization. static constexpr size_t inplace_buffer_size = (sizeof(int) == sizeof(void*) ? 8 : 6) * sizeof(void*); /// Structure that has size enough to keep type "T" including vtable. template struct type_container { T data; }; /// Type that has enough space to keep type "T" (including vtable) with suitable alignment. using function_buffer_t = std::aligned_storage_t; /// Constantly is true if callable fits function buffer, false otherwise. template inline constexpr bool fits_inplace_buffer = (sizeof(type_container) <= inplace_buffer_size); // clang-format off /// Constantly is true if callable fits function buffer and can be safely moved, false otherwise template inline constexpr bool can_use_inplace_buffer = fits_inplace_buffer && std::is_nothrow_move_constructible_v; // clang format on /// Type that is suitable to keep copy of callable object. /// - equal to pointer-to-function if Callable is pointer-to-function /// - otherwise removes const/volatile and references to allow copying callable. template using callable_copy_t = std::conditional_t>, Callable, std::remove_cv_t>>; 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 function_proxy; template class function_proxy : public base_function_proxy { public: virtual Return operator()(Arguments&&...) = 0; }; template class function_proxy_impl final : public function_proxy { public: // If you see this error, probably your function returns value and you're trying to // connect it to `signal`. Just remove return value from callback. static_assert(std::is_same_v, Return>, "cannot construct function<> class from callable object with different return type"); template explicit function_proxy_impl(FunctionObject&& function) : m_callable(std::forward(function)) { } Return operator()(Arguments&&... args) final { return m_callable(std::forward(args)...); } base_function_proxy* clone(void* buffer) const final { if constexpr (can_use_inplace_buffer) { 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) { 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 m_callable; }; template inline constexpr bool is_noexcept_packed_function_init = can_use_inplace_buffer>; 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; // Initializes packed function. // Cannot be called without reset(). template void init(Callable&& function) noexcept(is_noexcept_packed_function_init) { using proxy_t = function_proxy_impl; assert(m_proxy == nullptr); if constexpr (can_use_inplace_buffer) { m_proxy = new (&m_buffer) proxy_t{ std::forward(function) }; } else { m_proxy = new proxy_t{ std::forward(function) }; } } template function_proxy& get() const { return static_cast&>(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; }; } // namespace fastsignals::detail