From acbe52518fd26c94667d2c9ae89af2acd0225aa4 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Fri, 19 Feb 2021 07:09:55 +0100 Subject: [PATCH] Remove f2, add ofats::any_invocable --- src/App.h | 46 +- src/HttpContext.h | 6 +- src/HttpContextData.h | 6 +- src/HttpParser.h | 6 +- src/HttpResponse.h | 10 +- src/HttpResponseData.h | 8 +- src/HttpRouter.h | 6 +- src/Loop.h | 6 +- src/LoopData.h | 6 +- src/MoveOnlyFunction.h | 377 ++++++++ src/TopicTree.h | 2 +- src/WebSocket.h | 2 +- src/WebSocketContextData.h | 14 +- src/f2/LICENSE.txt | 23 - src/f2/function2.hpp | 1763 ------------------------------------ 15 files changed, 436 insertions(+), 1845 deletions(-) create mode 100644 src/MoveOnlyFunction.h delete mode 100644 src/f2/LICENSE.txt delete mode 100644 src/f2/function2.hpp diff --git a/src/App.h b/src/App.h index ee174fb..7c3dd18 100644 --- a/src/App.h +++ b/src/App.h @@ -70,7 +70,7 @@ public: return std::move(*this); } - TemplatedApp &&missingServerName(fu2::unique_function handler) { + TemplatedApp &&missingServerName(MoveOnlyFunction handler) { if (!constructorFailed()) { httpContext->getSocketContextData()->missingServerNameHandler = std::move(handler); @@ -92,7 +92,7 @@ public: } /* Attaches a "filter" function to track socket connections/disconnections */ - void filter(fu2::unique_function *, int)> &&filterHandler) { + void filter(MoveOnlyFunction *, int)> &&filterHandler) { httpContext->filter(std::move(filterHandler)); } @@ -148,13 +148,13 @@ public: bool resetIdleTimeoutOnSend = false; /* A good default, esp. for newcomers */ bool sendPingsAutomatically = true; - fu2::unique_function *, HttpRequest *, struct us_socket_context_t *)> upgrade = nullptr; - fu2::unique_function *)> open = nullptr; - fu2::unique_function *, std::string_view, uWS::OpCode)> message = nullptr; - fu2::unique_function *)> drain = nullptr; - fu2::unique_function *)> ping = nullptr; - fu2::unique_function *)> pong = nullptr; - fu2::unique_function *, int, std::string_view)> close = nullptr; + MoveOnlyFunction *, HttpRequest *, struct us_socket_context_t *)> upgrade = nullptr; + MoveOnlyFunction *)> open = nullptr; + MoveOnlyFunction *, std::string_view, uWS::OpCode)> message = nullptr; + MoveOnlyFunction *)> drain = nullptr; + MoveOnlyFunction *)> ping = nullptr; + MoveOnlyFunction *)> pong = nullptr; + MoveOnlyFunction *, int, std::string_view)> close = nullptr; }; template @@ -255,63 +255,63 @@ public: return std::move(*this); } - TemplatedApp &&get(std::string pattern, fu2::unique_function *, HttpRequest *)> &&handler) { + TemplatedApp &&get(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("get", pattern, std::move(handler)); } return std::move(*this); } - TemplatedApp &&post(std::string pattern, fu2::unique_function *, HttpRequest *)> &&handler) { + TemplatedApp &&post(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("post", pattern, std::move(handler)); } return std::move(*this); } - TemplatedApp &&options(std::string pattern, fu2::unique_function *, HttpRequest *)> &&handler) { + TemplatedApp &&options(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("options", pattern, std::move(handler)); } return std::move(*this); } - TemplatedApp &&del(std::string pattern, fu2::unique_function *, HttpRequest *)> &&handler) { + TemplatedApp &&del(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("delete", pattern, std::move(handler)); } return std::move(*this); } - TemplatedApp &&patch(std::string pattern, fu2::unique_function *, HttpRequest *)> &&handler) { + TemplatedApp &&patch(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("patch", pattern, std::move(handler)); } return std::move(*this); } - TemplatedApp &&put(std::string pattern, fu2::unique_function *, HttpRequest *)> &&handler) { + TemplatedApp &&put(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("put", pattern, std::move(handler)); } return std::move(*this); } - TemplatedApp &&head(std::string pattern, fu2::unique_function *, HttpRequest *)> &&handler) { + TemplatedApp &&head(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("head", pattern, std::move(handler)); } return std::move(*this); } - TemplatedApp &&connect(std::string pattern, fu2::unique_function *, HttpRequest *)> &&handler) { + TemplatedApp &&connect(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("connect", pattern, std::move(handler)); } return std::move(*this); } - TemplatedApp &&trace(std::string pattern, fu2::unique_function *, HttpRequest *)> &&handler) { + TemplatedApp &&trace(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("trace", pattern, std::move(handler)); } @@ -319,7 +319,7 @@ public: } /* This one catches any method */ - TemplatedApp &&any(std::string pattern, fu2::unique_function *, HttpRequest *)> &&handler) { + TemplatedApp &&any(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("*", pattern, std::move(handler)); } @@ -327,7 +327,7 @@ public: } /* Host, port, callback */ - TemplatedApp &&listen(std::string host, int port, fu2::unique_function &&handler) { + TemplatedApp &&listen(std::string host, int port, MoveOnlyFunction &&handler) { if (!host.length()) { return listen(port, std::move(handler)); } @@ -336,7 +336,7 @@ public: } /* Host, port, options, callback */ - TemplatedApp &&listen(std::string host, int port, int options, fu2::unique_function &&handler) { + TemplatedApp &&listen(std::string host, int port, int options, MoveOnlyFunction &&handler) { if (!host.length()) { return listen(port, options, std::move(handler)); } @@ -345,13 +345,13 @@ public: } /* Port, callback */ - TemplatedApp &&listen(int port, fu2::unique_function &&handler) { + TemplatedApp &&listen(int port, MoveOnlyFunction &&handler) { handler(httpContext ? httpContext->listen(nullptr, port, 0) : nullptr); return std::move(*this); } /* Port, options, callback */ - TemplatedApp &&listen(int port, int options, fu2::unique_function &&handler) { + TemplatedApp &&listen(int port, int options, MoveOnlyFunction &&handler) { handler(httpContext ? httpContext->listen(nullptr, port, options) : nullptr); return std::move(*this); } diff --git a/src/HttpContext.h b/src/HttpContext.h index a44e42c..279c78c 100644 --- a/src/HttpContext.h +++ b/src/HttpContext.h @@ -28,7 +28,7 @@ #include #include -#include "f2/function2.hpp" +#include "MoveOnlyFunction.h" namespace uWS { template struct HttpResponse; @@ -387,12 +387,12 @@ public: us_socket_context_free(SSL, getSocketContext()); } - void filter(fu2::unique_function *, int)> &&filterHandler) { + void filter(MoveOnlyFunction *, int)> &&filterHandler) { getSocketContextData()->filterHandlers.emplace_back(std::move(filterHandler)); } /* Register an HTTP route handler acording to URL pattern */ - void onHttp(std::string method, std::string pattern, fu2::unique_function *, HttpRequest *)> &&handler, bool upgrade = false) { + void onHttp(std::string method, std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler, bool upgrade = false) { HttpContextData *httpContextData = getSocketContextData(); /* Todo: This is ugly, fix */ diff --git a/src/HttpContextData.h b/src/HttpContextData.h index 3bf4412..9375994 100644 --- a/src/HttpContextData.h +++ b/src/HttpContextData.h @@ -21,7 +21,7 @@ #include "HttpRouter.h" #include -#include "f2/function2.hpp" +#include "MoveOnlyFunction.h" namespace uWS { template struct HttpResponse; @@ -33,9 +33,9 @@ struct alignas(16) HttpContextData { template friend struct HttpResponse; template friend struct TemplatedApp; private: - std::vector *, int)>> filterHandlers; + std::vector *, int)>> filterHandlers; - fu2::unique_function missingServerNameHandler; + MoveOnlyFunction missingServerNameHandler; struct RouterData { HttpResponse *httpResponse; diff --git a/src/HttpParser.h b/src/HttpParser.h index 5017548..ec18b18 100644 --- a/src/HttpParser.h +++ b/src/HttpParser.h @@ -25,7 +25,7 @@ #include #include #include -#include "f2/function2.hpp" +#include "MoveOnlyFunction.h" #include "BloomFilter.h" #include "ProxyParser.h" @@ -220,7 +220,7 @@ private: // the only caller of getHeaders template - std::pair fenceAndConsumePostPadded(char *data, unsigned int length, void *user, void *reserved, HttpRequest *req, fu2::unique_function &requestHandler, fu2::unique_function &dataHandler) { + std::pair fenceAndConsumePostPadded(char *data, unsigned int length, void *user, void *reserved, HttpRequest *req, MoveOnlyFunction &requestHandler, MoveOnlyFunction &dataHandler) { /* How much data we CONSUMED (to throw away) */ unsigned int consumedTotal = 0; @@ -286,7 +286,7 @@ private: } public: - void *consumePostPadded(char *data, unsigned int length, void *user, void *reserved, fu2::unique_function &&requestHandler, fu2::unique_function &&dataHandler, fu2::unique_function &&errorHandler) { + void *consumePostPadded(char *data, unsigned int length, void *user, void *reserved, MoveOnlyFunction &&requestHandler, MoveOnlyFunction &&dataHandler, MoveOnlyFunction &&errorHandler) { /* This resets BloomFilter by construction, but later we also reset it again. * Optimize this to skip resetting twice (req could be made global) */ diff --git a/src/HttpResponse.h b/src/HttpResponse.h index 779b461..fcff3f9 100644 --- a/src/HttpResponse.h +++ b/src/HttpResponse.h @@ -30,7 +30,7 @@ #include "WebSocket.h" #include "WebSocketContextData.h" -#include "f2/function2.hpp" +#include "MoveOnlyFunction.h" /* todo: tryWrite is missing currently, only send smaller segments with write */ @@ -427,7 +427,7 @@ public: } /* Corks the response if possible. Leaves already corked socket be. */ - HttpResponse *cork(fu2::unique_function &&handler) { + HttpResponse *cork(MoveOnlyFunction &&handler) { if (!Super::isCorked() && Super::canCork()) { Super::cork(); handler(); @@ -448,7 +448,7 @@ public: } /* Attach handler for writable HTTP response */ - HttpResponse *onWritable(fu2::unique_function &&handler) { + HttpResponse *onWritable(MoveOnlyFunction &&handler) { HttpResponseData *httpResponseData = getHttpResponseData(); httpResponseData->onWritable = std::move(handler); @@ -456,7 +456,7 @@ public: } /* Attach handler for aborted HTTP request */ - HttpResponse *onAborted(fu2::unique_function &&handler) { + HttpResponse *onAborted(MoveOnlyFunction &&handler) { HttpResponseData *httpResponseData = getHttpResponseData(); httpResponseData->onAborted = std::move(handler); @@ -464,7 +464,7 @@ public: } /* Attach a read handler for data sent. Will be called with FIN set true if last segment. */ - void onData(fu2::unique_function &&handler) { + void onData(MoveOnlyFunction &&handler) { HttpResponseData *data = getHttpResponseData(); data->inStream = std::move(handler); } diff --git a/src/HttpResponseData.h b/src/HttpResponseData.h index 803fa55..d3483f5 100644 --- a/src/HttpResponseData.h +++ b/src/HttpResponseData.h @@ -24,7 +24,7 @@ #include "AsyncSocketData.h" #include "ProxyParser.h" -#include "f2/function2.hpp" +#include "MoveOnlyFunction.h" namespace uWS { @@ -43,9 +43,9 @@ private: }; /* Per socket event handlers */ - fu2::unique_function onWritable; - fu2::unique_function onAborted; - fu2::unique_function inStream; // onData + MoveOnlyFunction onWritable; + MoveOnlyFunction onAborted; + MoveOnlyFunction inStream; // onData /* Outgoing offset */ size_t offset = 0; diff --git a/src/HttpRouter.h b/src/HttpRouter.h index b8839f5..ef83654 100644 --- a/src/HttpRouter.h +++ b/src/HttpRouter.h @@ -27,7 +27,7 @@ #include #include -#include "f2/function2.hpp" +#include "MoveOnlyFunction.h" namespace uWS { @@ -48,7 +48,7 @@ private: std::map priority; /* List of handlers */ - std::vector> handlers; + std::vector> handlers; /* Current URL cache */ std::string_view currentUrl; @@ -229,7 +229,7 @@ public: } /* Adds the corresponding entires in matching tree and handler list */ - void add(std::vector methods, std::string pattern, fu2::unique_function &&handler, uint32_t priority = MEDIUM_PRIORITY) { + void add(std::vector methods, std::string pattern, MoveOnlyFunction &&handler, uint32_t priority = MEDIUM_PRIORITY) { for (std::string method : methods) { /* Lookup method */ Node *node = getNode(&root, method, false); diff --git a/src/Loop.h b/src/Loop.h index 62b276a..56cd7d6 100644 --- a/src/Loop.h +++ b/src/Loop.h @@ -116,7 +116,7 @@ public: getLazyLoop().loop = nullptr; } - void addPostHandler(void *key, fu2::unique_function &&handler) { + void addPostHandler(void *key, MoveOnlyFunction &&handler) { LoopData *loopData = (LoopData *) us_loop_ext((us_loop_t *) this); loopData->postHandlers.emplace(key, std::move(handler)); @@ -129,7 +129,7 @@ public: loopData->postHandlers.erase(key); } - void addPreHandler(void *key, fu2::unique_function &&handler) { + void addPreHandler(void *key, MoveOnlyFunction &&handler) { LoopData *loopData = (LoopData *) us_loop_ext((us_loop_t *) this); loopData->preHandlers.emplace(key, std::move(handler)); @@ -143,7 +143,7 @@ public: } /* Defer this callback on Loop's thread of execution */ - void defer(fu2::unique_function &&cb) { + void defer(MoveOnlyFunction &&cb) { LoopData *loopData = (LoopData *) us_loop_ext((us_loop_t *) this); //if (std::thread::get_id() == ) // todo: add fast path for same thread id diff --git a/src/LoopData.h b/src/LoopData.h index 320c1d2..b4cac29 100644 --- a/src/LoopData.h +++ b/src/LoopData.h @@ -26,7 +26,7 @@ #include "PerMessageDeflate.h" -#include "f2/function2.hpp" +#include "MoveOnlyFunction.h" namespace uWS { @@ -37,10 +37,10 @@ struct alignas(16) LoopData { private: std::mutex deferMutex; int currentDeferQueue = 0; - std::vector> deferQueues[2]; + std::vector> deferQueues[2]; /* Map from void ptr to handler */ - std::map> postHandlers, preHandlers; + std::map> postHandlers, preHandlers; public: ~LoopData() { diff --git a/src/MoveOnlyFunction.h b/src/MoveOnlyFunction.h new file mode 100644 index 0000000..2ee79e8 --- /dev/null +++ b/src/MoveOnlyFunction.h @@ -0,0 +1,377 @@ +/* +MIT License + +Copyright (c) 2020 Oleg Fatkhiev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/* Sources fetched from https://github.com/ofats/any_invocable on 2021-02-19. */ + +#ifndef _ANY_INVOKABLE_H_ +#define _ANY_INVOKABLE_H_ + +#include +#include +#include + +// clang-format off +/* +namespace std { + template class any_invocable; // never defined + + template + class any_invocable { + public: + using result_type = R; + + // SECTION.3, construct/copy/destroy + any_invocable() noexcept; + any_invocable(nullptr_t) noexcept; + any_invocable(any_invocable&&) noexcept; + template any_invocable(F&&); + + template + explicit any_invocable(in_place_type_t, Args&&...); + template + explicit any_invocable(in_place_type_t, initializer_list, Args&&...); + + any_invocable& operator=(any_invocable&&) noexcept; + any_invocable& operator=(nullptr_t) noexcept; + template any_invocable& operator=(F&&); + template any_invocable& operator=(reference_wrapper) noexcept; + + ~any_invocable(); + + // SECTION.4, any_invocable modifiers + void swap(any_invocable&) noexcept; + + // SECTION.5, any_invocable capacity + explicit operator bool() const noexcept; + + // SECTION.6, any_invocable invocation + R operator()(ArgTypes...) cv ref noexcept(noex); + + // SECTION.7, null pointer comparisons + friend bool operator==(const any_invocable&, nullptr_t) noexcept; + + // SECTION.8, specialized algorithms + friend void swap(any_invocable&, any_invocable&) noexcept; + }; +} +*/ +// clang-format on + +namespace ofats { + +namespace any_detail { + +using buffer = std::aligned_storage_t; + +template +inline constexpr bool is_small_object_v = + sizeof(T) <= sizeof(buffer) && alignof(buffer) % alignof(T) == 0 && + std::is_nothrow_move_constructible_v; + +union storage { + void* ptr_ = nullptr; + buffer buf_; +}; + +enum class action { destroy, move }; + +template +struct handler_traits { + template + struct handler_base { + static void handle(action act, storage* current, storage* other = nullptr) { + switch (act) { + case (action::destroy): + Derived::destroy(*current); + break; + case (action::move): + Derived::move(*current, *other); + break; + } + } + }; + + template + struct small_handler : handler_base> { + template + static void create(storage& s, Args&&... args) { + new (static_cast(&s.buf_)) T(std::forward(args)...); + } + + static void destroy(storage& s) noexcept { + T& value = *static_cast(static_cast(&s.buf_)); + value.~T(); + } + + static void move(storage& dst, storage& src) noexcept { + create(dst, std::move(*static_cast(static_cast(&src.buf_)))); + destroy(src); + } + + static R call(storage& s, ArgTypes... args) { + return std::invoke(*static_cast(static_cast(&s.buf_)), + std::forward(args)...); + } + }; + + template + struct large_handler : handler_base> { + template + static void create(storage& s, Args&&... args) { + s.ptr_ = new T(std::forward(args)...); + } + + static void destroy(storage& s) noexcept { delete static_cast(s.ptr_); } + + static void move(storage& dst, storage& src) noexcept { + dst.ptr_ = src.ptr_; + } + + static R call(storage& s, ArgTypes... args) { + return std::invoke(*static_cast(s.ptr_), + std::forward(args)...); + } + }; + + template + using handler = std::conditional_t, small_handler, + large_handler>; +}; + +template +struct is_in_place_type : std::false_type {}; + +template +struct is_in_place_type> : std::true_type {}; + +template +inline constexpr auto is_in_place_type_v = is_in_place_type::value; + +template +class any_invocable_impl { + template + using handler = + typename any_detail::handler_traits::template handler; + + using storage = any_detail::storage; + using action = any_detail::action; + using handle_func = void (*)(any_detail::action, any_detail::storage*, + any_detail::storage*); + using call_func = R (*)(any_detail::storage&, ArgTypes...); + + public: + using result_type = R; + + any_invocable_impl() noexcept = default; + any_invocable_impl(std::nullptr_t) noexcept {} + any_invocable_impl(any_invocable_impl&& rhs) noexcept { + if (rhs.handle_) { + handle_ = rhs.handle_; + handle_(action::move, &storage_, &rhs.storage_); + call_ = rhs.call_; + rhs.handle_ = nullptr; + } + } + + any_invocable_impl& operator=(any_invocable_impl&& rhs) noexcept { + any_invocable_impl{std::move(rhs)}.swap(*this); + return *this; + } + any_invocable_impl& operator=(std::nullptr_t) noexcept { + destroy(); + return *this; + } + + ~any_invocable_impl() { destroy(); } + + void swap(any_invocable_impl& rhs) noexcept { + if (handle_) { + if (rhs.handle_) { + storage tmp; + handle_(action::move, &tmp, &storage_); + rhs.handle_(action::move, &storage_, &rhs.storage_); + handle_(action::move, &rhs.storage_, &tmp); + std::swap(handle_, rhs.handle_); + std::swap(call_, rhs.call_); + } else { + rhs.swap(*this); + } + } else if (rhs.handle_) { + rhs.handle_(action::move, &storage_, &rhs.storage_); + handle_ = rhs.handle_; + call_ = rhs.call_; + rhs.handle_ = nullptr; + } + } + + explicit operator bool() const noexcept { return handle_ != nullptr; } + + protected: + template + void create(Args&&... args) { + using hdl = handler; + hdl::create(storage_, std::forward(args)...); + handle_ = &hdl::handle; + call_ = &hdl::call; + } + + void destroy() noexcept { + if (handle_) { + handle_(action::destroy, &storage_, nullptr); + handle_ = nullptr; + } + } + + R call(ArgTypes... args) noexcept(is_noexcept) { + return call_(storage_, std::forward(args)...); + } + + friend bool operator==(const any_invocable_impl& f, std::nullptr_t) noexcept { + return !f; + } + friend bool operator==(std::nullptr_t, const any_invocable_impl& f) noexcept { + return !f; + } + friend bool operator!=(const any_invocable_impl& f, std::nullptr_t) noexcept { + return static_cast(f); + } + friend bool operator!=(std::nullptr_t, const any_invocable_impl& f) noexcept { + return static_cast(f); + } + + friend void swap(any_invocable_impl& lhs, any_invocable_impl& rhs) noexcept { + lhs.swap(rhs); + } + + private: + storage storage_; + handle_func handle_ = nullptr; + call_func call_; +}; + +template +using remove_cvref_t = std::remove_cv_t>; + +template +using can_convert = std::conjunction< + std::negation, AI>>, + std::negation>>, + std::is_invocable_r, + std::bool_constant<(!noex || + std::is_nothrow_invocable_r_v)>, + std::is_constructible, F>>; + +} // namespace any_detail + +template +class any_invocable; + +#define __OFATS_ANY_INVOCABLE(cv, ref, noex, inv_quals) \ + template \ + class any_invocable \ + : public any_detail::any_invocable_impl { \ + using base_type = any_detail::any_invocable_impl; \ + \ + public: \ + using base_type::base_type; \ + \ + template < \ + class F, \ + class = std::enable_if_t::value>> \ + any_invocable(F&& f) { \ + base_type::template create>(std::forward(f)); \ + } \ + \ + template , \ + class = std::enable_if_t< \ + std::is_move_constructible_v && \ + std::is_constructible_v && \ + std::is_invocable_r_v && \ + (!noex || std::is_nothrow_invocable_r_v)>> \ + explicit any_invocable(std::in_place_type_t, Args&&... args) { \ + base_type::template create(std::forward(args)...); \ + } \ + \ + template < \ + class T, class U, class... Args, class VT = std::decay_t, \ + class = std::enable_if_t< \ + std::is_move_constructible_v && \ + std::is_constructible_v&, Args...> && \ + std::is_invocable_r_v && \ + (!noex || \ + std::is_nothrow_invocable_r_v)>> \ + explicit any_invocable(std::in_place_type_t, \ + std::initializer_list il, Args&&... args) { \ + base_type::template create(il, std::forward(args)...); \ + } \ + \ + template > \ + std::enable_if_t && \ + std::is_move_constructible_v, \ + any_invocable&> \ + operator=(F&& f) { \ + any_invocable{std::forward(f)}.swap(*this); \ + return *this; \ + } \ + template \ + any_invocable& operator=(std::reference_wrapper f) { \ + any_invocable{f}.swap(*this); \ + return *this; \ + } \ + \ + R operator()(ArgTypes... args) cv ref noexcept(noex) { \ + return base_type::call(std::forward(args)...); \ + } \ + }; + +// cv -> {`empty`, const} +// ref -> {`empty`, &, &&} +// noex -> {true, false} +// inv_quals -> (is_empty(ref) ? & : ref) +__OFATS_ANY_INVOCABLE(, , false, &); // 000 +__OFATS_ANY_INVOCABLE(, , true, &); // 001 +__OFATS_ANY_INVOCABLE(, &, false, &); // 010 +__OFATS_ANY_INVOCABLE(, &, true, &); // 011 +__OFATS_ANY_INVOCABLE(, &&, false, &&); // 020 +__OFATS_ANY_INVOCABLE(, &&, true, &&); // 021 +__OFATS_ANY_INVOCABLE(const, , false, const&); // 100 +__OFATS_ANY_INVOCABLE(const, , true, const&); // 101 +__OFATS_ANY_INVOCABLE(const, &, false, const&); // 110 +__OFATS_ANY_INVOCABLE(const, &, true, const&); // 111 +__OFATS_ANY_INVOCABLE(const, &&, false, const&&); // 120 +__OFATS_ANY_INVOCABLE(const, &&, true, const&&); // 121 + +#undef __OFATS_ANY_INVOCABLE + +} // namespace ofats + +/* We, uWebSockets define our own type */ +namespace uWS { + template + using MoveOnlyFunction = ofats::any_invocable; +} + +#endif // _ANY_INVOKABLE_H_ diff --git a/src/TopicTree.h b/src/TopicTree.h index 92bae98..283d6a3 100644 --- a/src/TopicTree.h +++ b/src/TopicTree.h @@ -28,7 +28,7 @@ #include #include -/* We use std::function here, not fu2::unique_function */ +/* We use std::function here, not MoveOnlyFunction */ #include namespace uWS { diff --git a/src/WebSocket.h b/src/WebSocket.h index 4d87508..c303960 100644 --- a/src/WebSocket.h +++ b/src/WebSocket.h @@ -176,7 +176,7 @@ public: } /* Corks the response if possible. Leaves already corked socket be. */ - void cork(fu2::unique_function &&handler) { + void cork(MoveOnlyFunction &&handler) { if (!Super::isCorked() && Super::canCork()) { Super::cork(); handler(); diff --git a/src/WebSocketContextData.h b/src/WebSocketContextData.h index b3e4e1c..f1f16a3 100644 --- a/src/WebSocketContextData.h +++ b/src/WebSocketContextData.h @@ -18,7 +18,7 @@ #ifndef UWS_WEBSOCKETCONTEXTDATA_H #define UWS_WEBSOCKETCONTEXTDATA_H -#include "f2/function2.hpp" +#include "MoveOnlyFunction.h" #include #include "WebSocketProtocol.h" @@ -47,13 +47,13 @@ private: public: /* The callbacks for this context */ - fu2::unique_function *)> openHandler = nullptr; - fu2::unique_function *, std::string_view, uWS::OpCode)> messageHandler = nullptr; - fu2::unique_function *)> drainHandler = nullptr; - fu2::unique_function *, int, std::string_view)> closeHandler = nullptr; + MoveOnlyFunction *)> openHandler = nullptr; + MoveOnlyFunction *, std::string_view, uWS::OpCode)> messageHandler = nullptr; + MoveOnlyFunction *)> drainHandler = nullptr; + MoveOnlyFunction *, int, std::string_view)> closeHandler = nullptr; /* Todo: these should take message also; breaking change for v0.18 */ - fu2::unique_function *)> pingHandler = nullptr; - fu2::unique_function *)> pongHandler = nullptr; + MoveOnlyFunction *)> pingHandler = nullptr; + MoveOnlyFunction *)> pongHandler = nullptr; /* Settings for this context */ size_t maxPayloadLength = 0; diff --git a/src/f2/LICENSE.txt b/src/f2/LICENSE.txt deleted file mode 100644 index 36b7cd9..0000000 --- a/src/f2/LICENSE.txt +++ /dev/null @@ -1,23 +0,0 @@ -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/src/f2/function2.hpp b/src/f2/function2.hpp deleted file mode 100644 index 4ba16d0..0000000 --- a/src/f2/function2.hpp +++ /dev/null @@ -1,1763 +0,0 @@ - -// Copyright 2015-2020 Denis Blank -// Distributed under the Boost Software License, Version 1.0 -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef FU2_INCLUDED_FUNCTION2_HPP_ -#define FU2_INCLUDED_FUNCTION2_HPP_ - -#include -#include -#include -#include -#include -#include -#include - -// Defines: -// - FU2_HAS_DISABLED_EXCEPTIONS -#if defined(FU2_WITH_DISABLED_EXCEPTIONS) || \ - defined(FU2_MACRO_DISABLE_EXCEPTIONS) -#define FU2_HAS_DISABLED_EXCEPTIONS -#else // FU2_WITH_DISABLED_EXCEPTIONS -#if defined(_MSC_VER) -#if !defined(_HAS_EXCEPTIONS) || (_HAS_EXCEPTIONS == 0) -#define FU2_HAS_DISABLED_EXCEPTIONS -#endif -#elif defined(__clang__) -#if !(__EXCEPTIONS && __has_feature(cxx_exceptions)) -#define FU2_HAS_DISABLED_EXCEPTIONS -#endif -#elif defined(__GNUC__) -#if !__EXCEPTIONS -#define FU2_HAS_DISABLED_EXCEPTIONS -#endif -#endif -#endif // FU2_WITH_DISABLED_EXCEPTIONS -// - FU2_HAS_NO_FUNCTIONAL_HEADER -#if !defined(FU2_WITH_NO_FUNCTIONAL_HEADER) && \ - !defined(FU2_NO_FUNCTIONAL_HEADER) && \ - !defined(FU2_HAS_DISABLED_EXCEPTIONS) -#include -#else -#define FU2_HAS_NO_FUNCTIONAL_HEADER -#endif -// - FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE -#if defined(FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE) -#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE -#else // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE -#if defined(_MSC_VER) -#if defined(_HAS_CXX17) && _HAS_CXX17 -#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE -#endif -#elif defined(__cpp_noexcept_function_type) -#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE -#elif defined(__cplusplus) && (__cplusplus >= 201703L) -#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE -#endif -#endif // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE - -// - FU2_HAS_NO_EMPTY_PROPAGATION -#if defined(FU2_WITH_NO_EMPTY_PROPAGATION) -#define FU2_HAS_NO_EMPTY_PROPAGATION -#endif // FU2_WITH_NO_EMPTY_PROPAGATION - -#if !defined(FU2_HAS_DISABLED_EXCEPTIONS) -#include -#endif - -/// Hint for the compiler that this point should be unreachable -#if defined(_MSC_VER) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __assume(false) -#elif defined(__GNUC__) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable() -#elif defined(__has_builtin) && __has_builtin(__builtin_unreachable) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable() -#else -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define FU2_DETAIL_UNREACHABLE_INTRINSIC() abort() -#endif - -/// Causes the application to exit abnormally -#if defined(_MSC_VER) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define FU2_DETAIL_TRAP() __debugbreak() -#elif defined(__GNUC__) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define FU2_DETAIL_TRAP() __builtin_trap() -#elif defined(__has_builtin) && __has_builtin(__builtin_trap) -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define FU2_DETAIL_TRAP() __builtin_trap() -#else -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define FU2_DETAIL_TRAP() *(volatile int*)0x11 = 0 -#endif - -#ifndef NDEBUG -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define FU2_DETAIL_UNREACHABLE() ::fu2::detail::unreachable_debug() -#else -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define FU2_DETAIL_UNREACHABLE() FU2_DETAIL_UNREACHABLE_INTRINSIC() -#endif - -namespace fu2 { -inline namespace abi_400 { -namespace detail { -template -class function; - -template -struct identity {}; - -// Equivalent to C++17's std::void_t which targets a bug in GCC, -// that prevents correct SFINAE behavior. -// See http://stackoverflow.com/questions/35753920 for details. -template -struct deduce_to_void : std::common_type {}; - -template -using void_t = typename deduce_to_void::type; - -template -using unrefcv_t = std::remove_cv_t>; - -// Copy enabler helper class -template -struct copyable {}; -template <> -struct copyable { - copyable() = default; - ~copyable() = default; - copyable(copyable const&) = delete; - copyable(copyable&&) = default; - copyable& operator=(copyable const&) = delete; - copyable& operator=(copyable&&) = default; -}; - -/// Configuration trait to configure the function_base class. -template -struct config { - // Is true if the function is owning. - static constexpr auto const is_owning = Owning; - - // Is true if the function is copyable. - static constexpr auto const is_copyable = Copyable; - - // The internal capacity of the function - // used in small functor optimization. - // The object shall expose the real capacity through Capacity::capacity - // and the intended alignment through Capacity::alignment. - using capacity = Capacity; -}; - -/// A config which isn't compatible to other configs -template -struct property { - // Is true when the function throws an exception on empty invocation. - static constexpr auto const is_throwing = Throws; - - // Is true when the function throws an exception on empty invocation. - static constexpr auto const is_strong_exception_guaranteed = - HasStrongExceptGuarantee; -}; - -#ifndef NDEBUG -[[noreturn]] inline void unreachable_debug() { - FU2_DETAIL_TRAP(); - std::abort(); -} -#endif - -/// Provides utilities for invocing callable objects -namespace invocation { -/// Invokes the given callable object with the given arguments -template -constexpr auto invoke(Callable&& callable, Args&&... args) noexcept( - noexcept(std::forward(callable)(std::forward(args)...))) - -> decltype(std::forward(callable)(std::forward(args)...)) { - - return std::forward(callable)(std::forward(args)...); -} -/// Invokes the given member function pointer by reference -template -constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept( - noexcept((std::forward(self).*member)(std::forward(args)...))) - -> decltype((std::forward(self).* - member)(std::forward(args)...)) { - return (std::forward(self).*member)(std::forward(args)...); -} -/// Invokes the given member function pointer by pointer -template -constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept( - noexcept((std::forward(self)->*member)(std::forward(args)...))) - -> decltype( - (std::forward(self)->*member)(std::forward(args)...)) { - return (std::forward(self)->*member)(std::forward(args)...); -} -/// Invokes the given pointer to a scalar member by reference -template -constexpr auto -invoke(Type T::*member, - Self&& self) noexcept(noexcept(std::forward(self).*member)) - -> decltype(std::forward(self).*member) { - return (std::forward(self).*member); -} -/// Invokes the given pointer to a scalar member by pointer -template -constexpr auto -invoke(Type T::*member, - Self&& self) noexcept(noexcept(std::forward(self)->*member)) - -> decltype(std::forward(self)->*member) { - return std::forward(self)->*member; -} - -/// Deduces to a true type if the callable object can be invoked with -/// the given arguments. -/// We don't use invoke here because MSVC can't evaluate the nested expression -/// SFINAE here. -template -struct can_invoke : std::false_type {}; -template -struct can_invoke, - decltype((void)std::declval()(std::declval()...))> - : std::true_type {}; -template -struct can_invoke, - decltype((void)((std::declval().*std::declval())( - std::declval()...)))> : std::true_type {}; -template -struct can_invoke, - decltype( - (void)((std::declval().*std::declval())( - std::declval()...)))> : std::true_type {}; -template -struct can_invoke, - decltype( - (void)((std::declval()->*std::declval())( - std::declval()...)))> : std::true_type {}; -template -struct can_invoke, - decltype((void)(std::declval().*std::declval()))> - : std::true_type {}; -template -struct can_invoke, - decltype( - (void)(std::declval().*std::declval()))> - : std::true_type {}; -template -struct can_invoke, - decltype( - (void)(std::declval()->*std::declval()))> - : std::true_type {}; - -template -struct is_noexcept_correct : std::true_type {}; -template -struct is_noexcept_correct> - : std::integral_constant(), - std::declval()...))> { -}; -} // end namespace invocation - -namespace overloading { -template -struct overload_impl; -template -struct overload_impl : Current, - overload_impl { - explicit overload_impl(Current current, Next next, Rest... rest) - : Current(std::move(current)), overload_impl( - std::move(next), std::move(rest)...) { - } - - using Current::operator(); - using overload_impl::operator(); -}; -template -struct overload_impl : Current { - explicit overload_impl(Current current) : Current(std::move(current)) { - } - - using Current::operator(); -}; - -template -constexpr auto overload(T&&... callables) { - return overload_impl...>{std::forward(callables)...}; -} -} // namespace overloading - -/// Declares the namespace which provides the functionality to work with a -/// type-erased object. -namespace type_erasure { -/// Specialization to work with addresses of callable objects -template -struct address_taker { - template - static void* take(O&& obj) { - return std::addressof(obj); - } - static T& restore(void* ptr) { - return *static_cast(ptr); - } - static T const& restore(void const* ptr) { - return *static_cast(ptr); - } - static T volatile& restore(void volatile* ptr) { - return *static_cast(ptr); - } - static T const volatile& restore(void const volatile* ptr) { - return *static_cast(ptr); - } -}; -/// Specialization to work with addresses of raw function pointers -template -struct address_taker::value>> { - template - static void* take(O&& obj) { - return reinterpret_cast(obj); - } - template - static T restore(O ptr) { - return reinterpret_cast(const_cast(ptr)); - } -}; - -template -struct box_factory; -/// Store the allocator inside the box -template -struct box : private Allocator { - friend box_factory; - - T value_; - - explicit box(T value, Allocator allocator) - : Allocator(std::move(allocator)), value_(std::move(value)) { - } - - box(box&&) = default; - box(box const&) = default; - box& operator=(box&&) = default; - box& operator=(box const&) = default; - ~box() = default; -}; -template -struct box : private Allocator { - friend box_factory; - - T value_; - - explicit box(T value, Allocator allocator) - : Allocator(std::move(allocator)), value_(std::move(value)) { - } - - box(box&&) = default; - box(box const&) = delete; - box& operator=(box&&) = default; - box& operator=(box const&) = delete; - ~box() = default; -}; - -template -struct box_factory> { - using real_allocator = - typename std::allocator_traits>:: - template rebind_alloc>; - - /// Allocates space through the boxed allocator - static box* - box_allocate(box const* me) { - real_allocator allocator(*static_cast(me)); - - return static_cast*>( - std::allocator_traits::allocate(allocator, 1U)); - } - - /// Destroys the box through the given allocator - static void box_deallocate(box* me) { - real_allocator allocator(*static_cast(me)); - - me->~box(); - std::allocator_traits::deallocate(allocator, me, 1U); - } -}; - -/// Creates a box containing the given value and allocator -template -auto make_box(std::integral_constant, T&& value, - Allocator&& allocator) { - return box, std::decay_t>( - std::forward(value), std::forward(allocator)); -} - -template -struct is_box : std::false_type {}; -template -struct is_box> : std::true_type {}; - -/// Provides access to the pointer to a heal allocated erased object -/// as well to the inplace storage. -union data_accessor { - data_accessor() = default; - explicit constexpr data_accessor(std::nullptr_t) noexcept : ptr_(nullptr) { - } - explicit constexpr data_accessor(void* ptr) noexcept : ptr_(ptr) { - } - - /// The pointer we use if the object is on the heap - void* ptr_; - /// The first field of the inplace storage - std::size_t inplace_storage_; -}; - -/// See opcode::op_fetch_empty -constexpr void write_empty(data_accessor* accessor, bool empty) noexcept { - accessor->inplace_storage_ = std::size_t(empty); -} - -template -using transfer_const_t = - std::conditional_t>::value, - std::add_const_t, To>; -template -using transfer_volatile_t = - std::conditional_t>::value, - std::add_volatile_t, To>; - -/// The retriever when the object is allocated inplace -template -constexpr auto retrieve(std::true_type /*is_inplace*/, Accessor from, - std::size_t from_capacity) { - using type = transfer_const_t>*; - - /// Process the command by using the data inside the internal capacity - auto storage = &(from->inplace_storage_); - auto inplace = const_cast(static_cast(storage)); - return type(std::align(alignof(T), sizeof(T), inplace, from_capacity)); -} - -/// The retriever which is used when the object is allocated -/// through the allocator -template -constexpr auto retrieve(std::false_type /*is_inplace*/, Accessor from, - std::size_t /*from_capacity*/) { - - return from->ptr_; -} - -namespace invocation_table { -#if !defined(FU2_HAS_DISABLED_EXCEPTIONS) -#if defined(FU2_HAS_NO_FUNCTIONAL_HEADER) -struct bad_function_call : std::exception { - bad_function_call() noexcept { - } - - char const* what() const noexcept override { - return "bad function call"; - } -}; -#else -using std::bad_function_call; -#endif -#endif - -#ifdef FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE -#define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F) \ - F(, , noexcept, , &) \ - F(const, , noexcept, , &) \ - F(, volatile, noexcept, , &) \ - F(const, volatile, noexcept, , &) \ - F(, , noexcept, &, &) \ - F(const, , noexcept, &, &) \ - F(, volatile, noexcept, &, &) \ - F(const, volatile, noexcept, &, &) \ - F(, , noexcept, &&, &&) \ - F(const, , noexcept, &&, &&) \ - F(, volatile, noexcept, &&, &&) \ - F(const, volatile, noexcept, &&, &&) -#define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F) \ - F(, , noexcept) \ - F(const, , noexcept) \ - F(, volatile, noexcept) \ - F(const, volatile, noexcept) -#else // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE -#define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F) -#define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F) -#endif // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE - -#define FU2_DETAIL_EXPAND_QUALIFIERS(F) \ - F(, , , , &) \ - F(const, , , , &) \ - F(, volatile, , , &) \ - F(const, volatile, , , &) \ - F(, , , &, &) \ - F(const, , , &, &) \ - F(, volatile, , &, &) \ - F(const, volatile, , &, &) \ - F(, , , &&, &&) \ - F(const, , , &&, &&) \ - F(, volatile, , &&, &&) \ - F(const, volatile, , &&, &&) \ - FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F) -#define FU2_DETAIL_EXPAND_CV(F) \ - F(, , ) \ - F(const, , ) \ - F(, volatile, ) \ - F(const, volatile, ) \ - FU2_DETAIL_EXPAND_CV_NOEXCEPT(F) - -/// If the function is qualified as noexcept, the call will never throw -template -[[noreturn]] void throw_or_abortnoexcept( - std::integral_constant /*is_throwing*/) noexcept { - std::abort(); -} -/// Calls std::abort on empty function calls -[[noreturn]] inline void -throw_or_abort(std::false_type /*is_throwing*/) noexcept { - std::abort(); -} -/// Throws bad_function_call on empty funciton calls -[[noreturn]] inline void throw_or_abort(std::true_type /*is_throwing*/) { -#ifdef FU2_HAS_DISABLED_EXCEPTIONS - throw_or_abort(std::false_type{}); -#else - throw bad_function_call{}; -#endif -} - -template -struct function_trait; - -using is_noexcept_ = std::false_type; -using is_noexcept_noexcept = std::true_type; - -#define FU2_DEFINE_FUNCTION_TRAIT(CONST, VOLATILE, NOEXCEPT, OVL_REF, REF) \ - template \ - struct function_trait { \ - using pointer_type = Ret (*)(data_accessor CONST VOLATILE*, \ - std::size_t capacity, Args...); \ - template \ - struct internal_invoker { \ - static Ret invoke(data_accessor CONST VOLATILE* data, \ - std::size_t capacity, Args... args) NOEXCEPT { \ - auto obj = retrieve(std::integral_constant{}, \ - data, capacity); \ - auto box = static_cast(obj); \ - return invocation::invoke( \ - static_castvalue_)> CONST VOLATILE \ - REF>(box->value_), \ - std::forward(args)...); \ - } \ - }; \ - \ - template \ - struct view_invoker { \ - static Ret invoke(data_accessor CONST VOLATILE* data, std::size_t, \ - Args... args) NOEXCEPT { \ - \ - auto ptr = static_cast(data->ptr_); \ - return invocation::invoke(address_taker::restore(ptr), \ - std::forward(args)...); \ - } \ - }; \ - \ - template \ - using callable = T CONST VOLATILE REF; \ - \ - using arguments = identity; \ - \ - using is_noexcept = is_noexcept_##NOEXCEPT; \ - \ - template \ - struct empty_invoker { \ - static Ret invoke(data_accessor CONST VOLATILE* /*data*/, \ - std::size_t /*capacity*/, Args... /*args*/) NOEXCEPT { \ - throw_or_abort##NOEXCEPT(std::integral_constant{}); \ - } \ - }; \ - }; - -FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT) -#undef FU2_DEFINE_FUNCTION_TRAIT - -/// Deduces to the function pointer to the given signature -template -using function_pointer_of = typename function_trait::pointer_type; - -template -struct invoke_table; - -/// We optimize the vtable_t in case there is a single function overload -template -struct invoke_table { - using type = function_pointer_of; - - /// Return the function pointer itself - template - static constexpr auto fetch(type pointer) noexcept { - static_assert(Index == 0U, "The index should be 0 here!"); - return pointer; - } - - /// Returns the thunk of an single overloaded callable - template - static constexpr type get_invocation_table_of() noexcept { - return &function_trait::template internal_invoker::invoke; - } - /// Returns the thunk of an single overloaded callable - template - static constexpr type get_invocation_view_table_of() noexcept { - return &function_trait::template view_invoker::invoke; - } - /// Returns the thunk of an empty single overloaded callable - template - static constexpr type get_empty_invocation_table() noexcept { - return &function_trait::template empty_invoker::invoke; - } -}; -/// We generate a table in case of multiple function overloads -template -struct invoke_table { - using type = - std::tuple, function_pointer_of, - function_pointer_of...> const*; - - /// Return the function pointer at the particular index - template - static constexpr auto fetch(type table) noexcept { - return std::get(*table); - } - - /// The invocation vtable for a present object - template - struct invocation_vtable : public std::tuple, - function_pointer_of, - function_pointer_of...> { - constexpr invocation_vtable() noexcept - : std::tuple, function_pointer_of, - function_pointer_of...>(std::make_tuple( - &function_trait::template internal_invoker< - T, IsInplace>::invoke, - &function_trait::template internal_invoker< - T, IsInplace>::invoke, - &function_trait::template internal_invoker< - T, IsInplace>::invoke...)) { - } - }; - - /// Returns the thunk of an multi overloaded callable - template - static type get_invocation_table_of() noexcept { - static invocation_vtable const table; - return &table; - } - - /// The invocation vtable for a present object - template - struct invocation_view_vtable - : public std::tuple, - function_pointer_of, - function_pointer_of...> { - constexpr invocation_view_vtable() noexcept - : std::tuple, function_pointer_of, - function_pointer_of...>(std::make_tuple( - &function_trait::template view_invoker::invoke, - &function_trait::template view_invoker::invoke, - &function_trait::template view_invoker::invoke...)) { - } - }; - - /// Returns the thunk of an multi overloaded callable - template - static type get_invocation_view_table_of() noexcept { - static invocation_view_vtable const table; - return &table; - } - - /// The invocation table for an empty wrapper - template - struct empty_vtable : public std::tuple, - function_pointer_of, - function_pointer_of...> { - constexpr empty_vtable() noexcept - : std::tuple, function_pointer_of, - function_pointer_of...>( - std::make_tuple(&function_trait::template empty_invoker< - IsThrowing>::invoke, - &function_trait::template empty_invoker< - IsThrowing>::invoke, - &function_trait::template empty_invoker< - IsThrowing>::invoke...)) { - } - }; - - /// Returns the thunk of an multi single overloaded callable - template - static type get_empty_invocation_table() noexcept { - static empty_vtable const table; - return &table; - } -}; - -template -class operator_impl; - -#define FU2_DEFINE_FUNCTION_TRAIT(CONST, VOLATILE, NOEXCEPT, OVL_REF, REF) \ - template \ - class operator_impl \ - : operator_impl { \ - \ - template \ - friend class operator_impl; \ - \ - protected: \ - operator_impl() = default; \ - ~operator_impl() = default; \ - operator_impl(operator_impl const&) = default; \ - operator_impl(operator_impl&&) = default; \ - operator_impl& operator=(operator_impl const&) = default; \ - operator_impl& operator=(operator_impl&&) = default; \ - \ - using operator_impl::operator(); \ - \ - Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT { \ - auto parent = static_cast(this); \ - using erasure_t = std::decay_terasure_)>; \ - \ - /* `std::decay_terasure_)>` is a workaround for a */ \ - /* compiler regression of MSVC 16.3.1, see #29 for details. */ \ - return std::decay_terasure_)>::template invoke( \ - static_cast(parent->erasure_), \ - std::forward(args)...); \ - } \ - }; \ - template \ - class operator_impl, \ - Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> \ - : copyable { \ - \ - template \ - friend class operator_impl; \ - \ - protected: \ - operator_impl() = default; \ - ~operator_impl() = default; \ - operator_impl(operator_impl const&) = default; \ - operator_impl(operator_impl&&) = default; \ - operator_impl& operator=(operator_impl const&) = default; \ - operator_impl& operator=(operator_impl&&) = default; \ - \ - Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT { \ - auto parent = \ - static_cast CONST VOLATILE*>(this); \ - using erasure_t = std::decay_terasure_)>; \ - \ - /* `std::decay_terasure_)>` is a workaround for a */ \ - /* compiler regression of MSVC 16.3.1, see #29 for details. */ \ - return std::decay_terasure_)>::template invoke( \ - static_cast(parent->erasure_), \ - std::forward(args)...); \ - } \ - }; - -FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT) -#undef FU2_DEFINE_FUNCTION_TRAIT -} // namespace invocation_table - -namespace tables { -/// Identifies the action which is dispatched on the erased object -enum class opcode { - op_move, //< Move the object and set the vtable - op_copy, //< Copy the object and set the vtable - op_destroy, //< Destroy the object and reset the vtable - op_weak_destroy, //< Destroy the object without resetting the vtable - op_fetch_empty, //< Stores true or false into the to storage - //< to indicate emptiness -}; - -/// Abstraction for a vtable together with a command table -/// TODO Add optimization for a single formal argument -/// TODO Add optimization to merge both tables if the function is size -/// optimized -template -class vtable; -template -class vtable> { - using command_function_t = void (*)(vtable* /*this*/, opcode /*op*/, - data_accessor* /*from*/, - std::size_t /*from_capacity*/, - data_accessor* /*to*/, - std::size_t /*to_capacity*/); - - using invoke_table_t = invocation_table::invoke_table; - - command_function_t cmd_; - typename invoke_table_t::type vtable_; - - template - struct trait { - static_assert(is_box::value, - "The trait must be specialized with a box!"); - - /// The command table - template - static void process_cmd(vtable* to_table, opcode op, data_accessor* from, - std::size_t from_capacity, data_accessor* to, - std::size_t to_capacity) { - - switch (op) { - case opcode::op_move: { - /// Retrieve the pointer to the object - auto box = static_cast(retrieve( - std::integral_constant{}, from, from_capacity)); - assert(box && "The object must not be over aligned or null!"); - - if (!IsInplace) { - // Just swap both pointers if we allocated on the heap - to->ptr_ = from->ptr_; - -#ifndef NDEBUG - // We don't need to null the pointer since we know that - // we don't own the data anymore through the vtable - // which is set to empty. - from->ptr_ = nullptr; -#endif - - to_table->template set_allocated(); - - } - // The object is allocated inplace - else { - construct(std::true_type{}, std::move(*box), to_table, to, - to_capacity); - box->~T(); - } - return; - } - case opcode::op_copy: { - auto box = static_cast(retrieve( - std::integral_constant{}, from, from_capacity)); - assert(box && "The object must not be over aligned or null!"); - - assert(std::is_copy_constructible::value && - "The box is required to be copyable here!"); - - // Try to allocate the object inplace - construct(std::is_copy_constructible{}, *box, to_table, to, - to_capacity); - return; - } - case opcode::op_destroy: - case opcode::op_weak_destroy: { - - assert(!to && !to_capacity && "Arg overflow!"); - auto box = static_cast(retrieve( - std::integral_constant{}, from, from_capacity)); - - if (IsInplace) { - box->~T(); - } else { - box_factory::box_deallocate(box); - } - - if (op == opcode::op_destroy) { - to_table->set_empty(); - } - return; - } - case opcode::op_fetch_empty: { - write_empty(to, false); - return; - } - } - - FU2_DETAIL_UNREACHABLE(); - } - - template - static void - construct(std::true_type /*apply*/, Box&& box, vtable* to_table, - data_accessor* to, - std::size_t to_capacity) noexcept(HasStrongExceptGuarantee) { - // Try to allocate the object inplace - void* storage = retrieve(std::true_type{}, to, to_capacity); - if (storage) { - to_table->template set_inplace(); - } else { - // Allocate the object through the allocator - to->ptr_ = storage = - box_factory>::box_allocate(std::addressof(box)); - to_table->template set_allocated(); - } - new (storage) T(std::forward(box)); - } - - template - static void - construct(std::false_type /*apply*/, Box&& /*box*/, vtable* /*to_table*/, - data_accessor* /*to*/, - std::size_t /*to_capacity*/) noexcept(HasStrongExceptGuarantee) { - } - }; - - /// The command table - static void empty_cmd(vtable* to_table, opcode op, data_accessor* /*from*/, - std::size_t /*from_capacity*/, data_accessor* to, - std::size_t /*to_capacity*/) { - - switch (op) { - case opcode::op_move: - case opcode::op_copy: { - to_table->set_empty(); - break; - } - case opcode::op_destroy: - case opcode::op_weak_destroy: { - // Do nothing - break; - } - case opcode::op_fetch_empty: { - write_empty(to, true); - break; - } - default: { - FU2_DETAIL_UNREACHABLE(); - } - } - } - -public: - vtable() noexcept = default; - - /// Initialize an object at the given position - template - static void init(vtable& table, T&& object, data_accessor* to, - std::size_t to_capacity) { - - trait>::construct(std::true_type{}, std::forward(object), - &table, to, to_capacity); - } - - /// Moves the object at the given position - void move(vtable& to_table, data_accessor* from, std::size_t from_capacity, - data_accessor* to, - std::size_t to_capacity) noexcept(HasStrongExceptGuarantee) { - cmd_(&to_table, opcode::op_move, from, from_capacity, to, to_capacity); - set_empty(); - } - - /// Destroys the object at the given position - void copy(vtable& to_table, data_accessor const* from, - std::size_t from_capacity, data_accessor* to, - std::size_t to_capacity) const { - cmd_(&to_table, opcode::op_copy, const_cast(from), - from_capacity, to, to_capacity); - } - - /// Destroys the object at the given position - void destroy(data_accessor* from, - std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) { - cmd_(this, opcode::op_destroy, from, from_capacity, nullptr, 0U); - } - - /// Destroys the object at the given position without invalidating the - /// vtable - void - weak_destroy(data_accessor* from, - std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) { - cmd_(this, opcode::op_weak_destroy, from, from_capacity, nullptr, 0U); - } - - /// Returns true when the vtable doesn't hold any erased object - bool empty() const noexcept { - data_accessor data; - cmd_(nullptr, opcode::op_fetch_empty, nullptr, 0U, &data, 0U); - return bool(data.inplace_storage_); - } - - /// Invoke the function at the given index - template - constexpr decltype(auto) invoke(Args&&... args) const { - auto thunk = invoke_table_t::template fetch(vtable_); - return thunk(std::forward(args)...); - } - /// Invoke the function at the given index - template - constexpr decltype(auto) invoke(Args&&... args) const volatile { - auto thunk = invoke_table_t::template fetch(vtable_); - return thunk(std::forward(args)...); - } - - template - void set_inplace() noexcept { - using type = std::decay_t; - vtable_ = invoke_table_t::template get_invocation_table_of(); - cmd_ = &trait::template process_cmd; - } - - template - void set_allocated() noexcept { - using type = std::decay_t; - vtable_ = invoke_table_t::template get_invocation_table_of(); - cmd_ = &trait::template process_cmd; - } - - void set_empty() noexcept { - vtable_ = invoke_table_t::template get_empty_invocation_table(); - cmd_ = &empty_cmd; - } -}; -} // namespace tables - -/// A union which makes the pointer to the heap object share the -/// same space with the internal capacity. -/// The storage type is distinguished by multiple versions of the -/// control and vtable. -template -struct internal_capacity { - /// We extend the union through a technique similar to the tail object hack - typedef union { - /// Tag to access the structure in a type-safe way - data_accessor accessor_; - /// The internal capacity we use to allocate in-place - std::aligned_storage_t capacity_; - } type; -}; -template -struct internal_capacity< - Capacity, std::enable_if_t<(Capacity::capacity < sizeof(void*))>> { - typedef struct { - /// Tag to access the structure in a type-safe way - data_accessor accessor_; - } type; -}; - -template -class internal_capacity_holder { - // Tag to access the structure in a type-safe way - typename internal_capacity::type storage_; - -public: - constexpr internal_capacity_holder() = default; - - constexpr data_accessor* opaque_ptr() noexcept { - return &storage_.accessor_; - } - constexpr data_accessor const* opaque_ptr() const noexcept { - return &storage_.accessor_; - } - constexpr data_accessor volatile* opaque_ptr() volatile noexcept { - return &storage_.accessor_; - } - constexpr data_accessor const volatile* opaque_ptr() const volatile noexcept { - return &storage_.accessor_; - } - - static constexpr std::size_t capacity() noexcept { - return sizeof(storage_); - } -}; - -/// An owning erasure -template -class erasure : internal_capacity_holder { - template - friend class erasure; - template - friend class operator_impl; - - using vtable_t = tables::vtable; - - vtable_t vtable_; - -public: - /// Returns the capacity of this erasure - static constexpr std::size_t capacity() noexcept { - return internal_capacity_holder::capacity(); - } - - constexpr erasure() noexcept { - vtable_.set_empty(); - } - - constexpr erasure(std::nullptr_t) noexcept { - vtable_.set_empty(); - } - - constexpr erasure(erasure&& right) noexcept( - Property::is_strong_exception_guaranteed) { - right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), - this->opaque_ptr(), capacity()); - } - - constexpr erasure(erasure const& right) { - right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(), - this->opaque_ptr(), capacity()); - } - - template - constexpr erasure(erasure right) noexcept( - Property::is_strong_exception_guaranteed) { - right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), - this->opaque_ptr(), capacity()); - } - - template >> - constexpr erasure(std::false_type /*use_bool_op*/, T&& callable, - Allocator&& allocator = Allocator{}) { - vtable_t::init(vtable_, - type_erasure::make_box( - std::integral_constant{}, - std::forward(callable), - std::forward(allocator)), - this->opaque_ptr(), capacity()); - } - template >> - constexpr erasure(std::true_type /*use_bool_op*/, T&& callable, - Allocator&& allocator = Allocator{}) { - if (bool(callable)) { - vtable_t::init(vtable_, - type_erasure::make_box( - std::integral_constant{}, - std::forward(callable), - std::forward(allocator)), - this->opaque_ptr(), capacity()); - } else { - vtable_.set_empty(); - } - } - - ~erasure() { - vtable_.weak_destroy(this->opaque_ptr(), capacity()); - } - - constexpr erasure& - operator=(std::nullptr_t) noexcept(Property::is_strong_exception_guaranteed) { - vtable_.destroy(this->opaque_ptr(), capacity()); - return *this; - } - - constexpr erasure& operator=(erasure&& right) noexcept( - Property::is_strong_exception_guaranteed) { - vtable_.weak_destroy(this->opaque_ptr(), capacity()); - right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), - this->opaque_ptr(), capacity()); - return *this; - } - - constexpr erasure& operator=(erasure const& right) { - vtable_.weak_destroy(this->opaque_ptr(), capacity()); - right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(), - this->opaque_ptr(), capacity()); - return *this; - } - - template - constexpr erasure& - operator=(erasure right) noexcept( - Property::is_strong_exception_guaranteed) { - vtable_.weak_destroy(this->opaque_ptr(), capacity()); - right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), - this->opaque_ptr(), capacity()); - return *this; - } - - template >> - void assign(std::false_type /*use_bool_op*/, T&& callable, - Allocator&& allocator = {}) { - vtable_.weak_destroy(this->opaque_ptr(), capacity()); - vtable_t::init(vtable_, - type_erasure::make_box( - std::integral_constant{}, - std::forward(callable), - std::forward(allocator)), - this->opaque_ptr(), capacity()); - } - - template >> - void assign(std::true_type /*use_bool_op*/, T&& callable, - Allocator&& allocator = {}) { - if (bool(callable)) { - assign(std::false_type{}, std::forward(callable), - std::forward(allocator)); - } else { - operator=(nullptr); - } - } - - /// Returns true when the erasure doesn't hold any erased object - constexpr bool empty() const noexcept { - return vtable_.empty(); - } - - /// Invoke the function of the erasure at the given index - /// - /// We define this out of class to be able to forward the qualified - /// erasure correctly. - template - static constexpr decltype(auto) invoke(Erasure&& erasure, Args&&... args) { - auto const capacity = erasure.capacity(); - return erasure.vtable_.template invoke( - std::forward(erasure).opaque_ptr(), capacity, - std::forward(args)...); - } -}; - -// A non owning erasure -template -class erasure> { - template - friend class erasure; - template - friend class operator_impl; - - using property_t = property; - - using invoke_table_t = invocation_table::invoke_table; - typename invoke_table_t::type invoke_table_; - - /// The internal pointer to the non owned object - data_accessor view_; - -public: - // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) - constexpr erasure() noexcept - : invoke_table_( - invoke_table_t::template get_empty_invocation_table()), - view_(nullptr) { - } - - // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) - constexpr erasure(std::nullptr_t) noexcept - : invoke_table_( - invoke_table_t::template get_empty_invocation_table()), - view_(nullptr) { - } - - // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) - constexpr erasure(erasure&& right) noexcept - : invoke_table_(right.invoke_table_), view_(right.view_) { - } - - constexpr erasure(erasure const& /*right*/) = default; - - template - // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) - constexpr erasure(erasure right) noexcept - : invoke_table_(right.invoke_table_), view_(right.view_) { - } - - template - // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) - constexpr erasure(std::false_type /*use_bool_op*/, T&& object) - : invoke_table_(invoke_table_t::template get_invocation_view_table_of< - std::decay_t>()), - view_(address_taker>::take(std::forward(object))) { - } - template - // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) - constexpr erasure(std::true_type use_bool_op, T&& object) { - this->assign(use_bool_op, std::forward(object)); - } - - ~erasure() = default; - - constexpr erasure& - operator=(std::nullptr_t) noexcept(HasStrongExceptGuarantee) { - invoke_table_ = - invoke_table_t::template get_empty_invocation_table(); - view_.ptr_ = nullptr; - return *this; - } - - constexpr erasure& operator=(erasure&& right) noexcept { - invoke_table_ = right.invoke_table_; - view_ = right.view_; - right = nullptr; - return *this; - } - - constexpr erasure& operator=(erasure const& /*right*/) = default; - - template - constexpr erasure& - operator=(erasure right) noexcept { - invoke_table_ = right.invoke_table_; - view_ = right.view_; - return *this; - } - - template - constexpr void assign(std::false_type /*use_bool_op*/, T&& callable) { - invoke_table_ = invoke_table_t::template get_invocation_view_table_of< - std::decay_t>(); - view_.ptr_ = - address_taker>::take(std::forward(callable)); - } - template - constexpr void assign(std::true_type /*use_bool_op*/, T&& callable) { - if (bool(callable)) { - assign(std::false_type{}, std::forward(callable)); - } else { - operator=(nullptr); - } - } - - /// Returns true when the erasure doesn't hold any erased object - constexpr bool empty() const noexcept { - return view_.ptr_ == nullptr; - } - - template - static constexpr decltype(auto) invoke(Erasure&& erasure, T&&... args) { - auto thunk = invoke_table_t::template fetch(erasure.invoke_table_); - return thunk(&(erasure.view_), 0UL, std::forward(args)...); - } -}; -} // namespace type_erasure - -/// Deduces to a true_type if the type T provides the given signature and the -/// signature is noexcept correct callable. -template > -struct accepts_one - : std::integral_constant< - bool, invocation::can_invoke, - typename Trait::arguments>::value && - invocation::is_noexcept_correct< - Trait::is_noexcept::value, - typename Trait::template callable, - typename Trait::arguments>::value> {}; - -/// Deduces to a true_type if the type T provides all signatures -template -struct accepts_all : std::false_type {}; -template -struct accepts_all< - T, identity, - void_t::value>...>> - : std::true_type {}; - -/// Deduces to a true_type if the type T is implementing operator bool() -/// or if the type is convertible to bool directly, this also implements an -/// optimizations for function references `void(&)()` which are can never -/// be null and for such a conversion to bool would never return false. -#if defined(FU2_HAS_NO_EMPTY_PROPAGATION) -template -struct use_bool_op : std::false_type {}; -#else -template -struct has_bool_op : std::false_type {}; -template -struct has_bool_op()))>> - : std::true_type { -#ifndef NDEBUG - static_assert(!std::is_pointer::value, - "Missing deduction for function pointer!"); -#endif -}; - -template -struct use_bool_op : has_bool_op {}; - -#define FU2_DEFINE_USE_OP_TRAIT(CONST, VOLATILE, NOEXCEPT) \ - template \ - struct use_bool_op \ - : std::true_type {}; - -FU2_DETAIL_EXPAND_CV(FU2_DEFINE_USE_OP_TRAIT) -#undef FU2_DEFINE_USE_OP_TRAIT - -template -struct use_bool_op : std::false_type {}; - -#if defined(FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE) -template -struct use_bool_op : std::false_type {}; -#endif -#endif // FU2_HAS_NO_EMPTY_PROPAGATION - -template -struct assert_wrong_copy_assign { - static_assert(!Config::is_owning || !Config::is_copyable || - std::is_copy_constructible>::value, - "Can't wrap a non copyable object into a unique function!"); - - using type = void; -}; - -template -struct assert_no_strong_except_guarantee { - static_assert( - !IsStrongExceptGuaranteed || - (std::is_nothrow_move_constructible::value && - std::is_nothrow_destructible::value), - "Can't wrap a object an object that has no strong exception guarantees " - "if this is required by the wrapper!"); - - using type = void; -}; - -/// SFINAES out if the given callable is not copyable correct to the left one. -template -using enable_if_copyable_correct_t = - std::enable_if_t<(!LeftConfig::is_copyable || RightConfig::is_copyable)>; - -template -using is_owning_correct = - std::integral_constant; - -/// SFINAES out if the given function2 is not owning correct to this one -template -using enable_if_owning_correct_t = - std::enable_if_t::value>; - -template -class function> - : type_erasure::invocation_table::operator_impl< - 0U, - function>, - Args...> { - - template - friend class function; - - template - friend class type_erasure::invocation_table::operator_impl; - - using property_t = property; - using erasure_t = - type_erasure::erasure; - - template - using enable_if_can_accept_all_t = - std::enable_if_t, identity>::value>; - - template - struct is_convertible_to_this : std::false_type {}; - template - struct is_convertible_to_this< - function, - void_t, - enable_if_owning_correct_t>> - : std::true_type {}; - - template - using enable_if_not_convertible_to_this = - std::enable_if_t>::value>; - - template - using enable_if_owning_t = - std::enable_if_t::value && Config::is_owning>; - - template - using assert_wrong_copy_assign_t = - typename assert_wrong_copy_assign>::type; - - template - using assert_no_strong_except_guarantee_t = - typename assert_no_strong_except_guarantee>::type; - - erasure_t erasure_; - -public: - /// Default constructor which empty constructs the function - function() = default; - ~function() = default; - - explicit constexpr function(function const& /*right*/) = default; - explicit constexpr function(function&& /*right*/) = default; - - /// Copy construction from another copyable function - template * = nullptr, - enable_if_copyable_correct_t* = nullptr, - enable_if_owning_correct_t* = nullptr> - constexpr function(function const& right) - : erasure_(right.erasure_) { - } - - /// Move construction from another function - template * = nullptr, - enable_if_owning_correct_t* = nullptr> - constexpr function(function&& right) - : erasure_(std::move(right.erasure_)) { - } - - /// Construction from a callable object which overloads the `()` operator - template * = nullptr, - enable_if_can_accept_all_t* = nullptr, - assert_wrong_copy_assign_t* = nullptr, - assert_no_strong_except_guarantee_t* = nullptr> - constexpr function(T&& callable) - : erasure_(use_bool_op>{}, std::forward(callable)) { - } - template * = nullptr, - enable_if_can_accept_all_t* = nullptr, - enable_if_owning_t* = nullptr, - assert_wrong_copy_assign_t* = nullptr, - assert_no_strong_except_guarantee_t* = nullptr> - constexpr function(T&& callable, Allocator&& allocator) - : erasure_(use_bool_op>{}, std::forward(callable), - std::forward(allocator)) { - } - - /// Empty constructs the function - constexpr function(std::nullptr_t np) : erasure_(np) { - } - - function& operator=(function const& /*right*/) = default; - function& operator=(function&& /*right*/) = default; - - /// Copy assigning from another copyable function - template * = nullptr, - enable_if_copyable_correct_t* = nullptr, - enable_if_owning_correct_t* = nullptr> - function& operator=(function const& right) { - erasure_ = right.erasure_; - return *this; - } - - /// Move assigning from another function - template * = nullptr, - enable_if_owning_correct_t* = nullptr> - function& operator=(function&& right) { - erasure_ = std::move(right.erasure_); - return *this; - } - - /// Move assigning from a callable object - template * = nullptr, - enable_if_can_accept_all_t* = nullptr, - assert_wrong_copy_assign_t* = nullptr, - assert_no_strong_except_guarantee_t* = nullptr> - function& operator=(T&& callable) { - erasure_.assign(use_bool_op>{}, std::forward(callable)); - return *this; - } - - /// Clears the function - function& operator=(std::nullptr_t np) { - erasure_ = np; - return *this; - } - - /// Returns true when the function is empty - bool empty() const noexcept { - return erasure_.empty(); - } - - /// Returns true when the function isn't empty - explicit operator bool() const noexcept { - return !empty(); - } - - /// Assigns a new target with an optional allocator - template >, - enable_if_not_convertible_to_this* = nullptr, - enable_if_can_accept_all_t* = nullptr, - assert_wrong_copy_assign_t* = nullptr, - assert_no_strong_except_guarantee_t* = nullptr> - void assign(T&& callable, Allocator&& allocator = Allocator{}) { - erasure_.assign(use_bool_op>{}, std::forward(callable), - std::forward(allocator)); - } - - /// Swaps this function with the given function - void swap(function& other) noexcept(HasStrongExceptGuarantee) { - if (&other == this) { - return; - } - - function cache = std::move(other); - other = std::move(*this); - *this = std::move(cache); - } - - /// Swaps the left function with the right one - friend void swap(function& left, - function& right) noexcept(HasStrongExceptGuarantee) { - left.swap(right); - } - - /// Calls the wrapped callable object - using type_erasure::invocation_table::operator_impl< - 0U, function, Args...>::operator(); -}; - -template -bool operator==(function const& f, std::nullptr_t) { - return !bool(f); -} - -template -bool operator!=(function const& f, std::nullptr_t) { - return bool(f); -} - -template -bool operator==(std::nullptr_t, function const& f) { - return !bool(f); -} - -template -bool operator!=(std::nullptr_t, function const& f) { - return bool(f); -} - -// Default intended object size of the function -using object_size = std::integral_constant; -} // namespace detail -} // namespace abi_400 - -/// Can be passed to function_base as template argument which causes -/// the internal small buffer to be sized according to the given size, -/// and aligned with the given alignment. -template -struct capacity_fixed { - static constexpr std::size_t capacity = Capacity; - static constexpr std::size_t alignment = Alignment; -}; - -/// Default capacity for small functor optimization -struct capacity_default - : capacity_fixed {}; - -/// Can be passed to function_base as template argument which causes -/// the internal small buffer to be removed from the callable wrapper. -/// The owning function_base will then allocate memory for every object -/// it applies a type erasure on. -struct capacity_none : capacity_fixed<0UL> {}; - -/// Can be passed to function_base as template argument which causes -/// the internal small buffer to be sized such that it can hold -/// the given object without allocating memory for an applied type erasure. -template -struct capacity_can_hold { - static constexpr std::size_t capacity = sizeof(T); - static constexpr std::size_t alignment = alignof(T); -}; - -/// An adaptable function wrapper base for arbitrary functional types. -/// -/// \tparam IsOwning Is true when the type erasure shall be owning the object. -/// -/// \tparam IsCopyable Defines whether the function is copyable or not -/// -/// \tparam Capacity Defines the internal capacity of the function -/// for small functor optimization. -/// The size of the whole function object will be the capacity -/// plus the size of two pointers. If the capacity is zero, -/// the size will increase through one additional pointer -/// so the whole object has the size of 3 * sizeof(void*). -/// The type which is passed to the Capacity template parameter -/// shall provide a capacity and alignment member which -/// looks like the following example: -/// ```cpp -/// struct my_capacity { -/// static constexpr std::size_t capacity = sizeof(my_type); -/// static constexpr std::size_t alignment = alignof(my_type); -/// }; -/// ``` -/// -/// \tparam IsThrowing Defines whether the function throws an exception on -/// empty function call, `std::abort` is called otherwise. -/// -/// \tparam HasStrongExceptGuarantee Defines whether all objects satisfy the -/// strong exception guarantees, -/// which means the function type will satisfy -/// the strong exception guarantees too. -/// -/// \tparam Signatures Defines the signature of the callable wrapper -/// -template -using function_base = detail::function< - detail::config, - detail::property>; - -/// An owning copyable function wrapper for arbitrary callable types. -template -using function = function_base; - -/// An owning non copyable function wrapper for arbitrary callable types. -template -using unique_function = function_base; - -/// A non owning copyable function wrapper for arbitrary callable types. -template -using function_view = function_base; - -#if !defined(FU2_HAS_DISABLED_EXCEPTIONS) -/// Exception type that is thrown when invoking empty function objects -/// and exception support isn't disabled. -/// -/// Exception support is enabled if -/// the template parameter 'Throwing' is set to true (default). -/// -/// This type will default to std::bad_function_call if the -/// functional header is used, otherwise the library provides its own type. -/// -/// You may disable the inclusion of the functional header -/// through defining `FU2_WITH_NO_FUNCTIONAL_HEADER`. -/// -using detail::type_erasure::invocation_table::bad_function_call; -#endif - -/// Returns a callable object, which unifies all callable objects -/// that were passed to this function. -/// -/// ```cpp -/// auto overloaded = fu2::overload([](std::true_type) { return true; }, -/// [](std::false_type) { return false; }); -/// ``` -/// -/// \param callables A pack of callable objects with arbitrary signatures. -/// -/// \returns A callable object which exposes the -/// -template -constexpr auto overload(T&&... callables) { - return detail::overloading::overload(std::forward(callables)...); -} -} // namespace fu2 - -#undef FU2_DETAIL_EXPAND_QUALIFIERS -#undef FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT -#undef FU2_DETAIL_EXPAND_CV -#undef FU2_DETAIL_EXPAND_CV_NOEXCEPT -#undef FU2_DETAIL_UNREACHABLE_INTRINSIC -#undef FU2_DETAIL_UNREACHABLE_INTRINSIC -#undef FU2_DETAIL_TRAP - -#endif // FU2_INCLUDED_FUNCTION2_HPP_