diff --git a/callback.hpp b/callback.hpp index 690b27b..1254114 100644 --- a/callback.hpp +++ b/callback.hpp @@ -259,6 +259,15 @@ namespace util bool& ret; }; + struct callback_tag_determine_used_storage_size + { + constexpr callback_tag_determine_used_storage_size(size_t& used_storage_size) noexcept + : used_storage_size(used_storage_size) + {} + + size_t& used_storage_size; + }; + struct callback_tag_clone { explicit constexpr callback_tag_clone(void* dst) noexcept : dst(dst) {} @@ -289,6 +298,7 @@ namespace util using callback_method = boost::variant< callback_tag_invoke , callback_tag_is_valid + , callback_tag_determine_used_storage_size , callback_tag_move , callback_tag_move_resize , callback_tag_clone @@ -415,6 +425,15 @@ namespace util return {}; } + callback_ret operator()(const callback_tag_determine_used_storage_size& op) const noexcept + { + op.used_storage_size = stored_internally + ? (std::is_empty::value ? 0 : sizeof(callback_impl)) + : sizeof(callback_impl*) + ; + return {}; + } + callback_ret operator()(const callback_tag_move& op) const noexcept { assert(op.src != op.dst && "move constructing to the same address is illegal!"); @@ -543,6 +562,37 @@ namespace util return impl_t::template invoke_method; } + template + callback_method_invoker construct_callback_impl(Storage& s, LockablePtr p, callback&& f, const std::size_t used_storage_size, std::integral_constant) + { + // Termination condition + assert(used_storage_size == 0); + return construct_callback_impl(s, std::move(p), callback{std::move(f)}); + } + + template + callback_method_invoker construct_callback_impl(Storage& s, LockablePtr p, callback&& f, const std::size_t used_storage_size, std::integral_constant current_size) + { + const auto needed_blocks = (used_storage_size + Storage::align - 1) / Storage::align; + constexpr auto cur_blocks = (current_size + Storage::align - 1) / Storage::align; + + if (needed_blocks < cur_blocks) + return construct_callback_impl(s, std::move(p), std::move(f), used_storage_size, std::integral_constant{}); + + // Fall back to pointer size if it won't fit in the storage area anyway + if (used_storage_size > current_size) + return construct_callback_impl(s, std::move(p), callback{std::move(f)}); + + // Seems we've found our size: go with it + return construct_callback_impl(s, std::move(p), callback{std::move(f)}); + } + + template + callback_method_invoker construct_callback_impl(Storage& s, LockablePtr p, callback&& f, const std::size_t used_storage_size) + { + return construct_callback_impl(s, std::move(p), std::move(f), used_storage_size, std::integral_constant{}); + } + template class callback_storage { @@ -652,7 +702,14 @@ namespace util || std::is_void::value )>::type> explicit callback(callback f) - : invoker(detail::construct_callback_impl(this->storage(), detail::always_valid_ptr{}, std::move(f))) + : invoker([&]() -> decltype(invoker) { + if (!f.invoker) + return nullptr; + size_t used_storage_size = 0; + f.invoker(f.data(), detail::callback_tag_determine_used_storage_size{used_storage_size}); + + return detail::construct_callback_impl(this->storage(), detail::always_valid_ptr{}, std::move(f), used_storage_size); + }()) { }