diff --git a/.gitmodules b/.gitmodules index e69de29bb..d2e779d0a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "external/function2"] + path = external/function2 + url = https://github.com/Naios/function2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 94824b689..dc5aed3f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 14.0.0 +* <<<< breaking change >>>> Replaced std::function with move_only_function. (#953) +* Removed old NetworkTS code. (#952) +* Fixed invalid timing async_shutdown handler call. (#950) +* Improved build system. (#948) + ## 13.1.0 * Added clear username and password functionality. (#944) * Refined utility tools. (#941, #942) diff --git a/CMakeLists.txt b/CMakeLists.txt index c75282465..2c4833fc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ CMAKE_MINIMUM_REQUIRED (VERSION 3.13.0) -PROJECT (mqtt_cpp_iface VERSION 13.1.0) +PROJECT (mqtt_cpp_iface VERSION 14.0.0) SET(CMAKE_CXX_STANDARD 14) SET(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/README.md b/README.md index a4a544d53..63cdbc130 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MQTT client/server for C++14 based on Boost.Asio -Version 13.1.0 [![Actions Status](https://github.com/redboltz/mqtt_cpp/workflows/CI/badge.svg)](https://github.com/redboltz/mqtt_cpp/actions)[![Build Status](https://dev.azure.com/redboltz/redboltz/_apis/build/status/redboltz.mqtt_cpp?branchName=master)](https://dev.azure.com/redboltz/redboltz/_build/latest?definitionId=6&branchName=master)[![codecov](https://codecov.io/gh/redboltz/mqtt_cpp/branch/master/graph/badge.svg)](https://codecov.io/gh/redboltz/mqtt_cpp) +Version 14.0.0 [![Actions Status](https://github.com/redboltz/mqtt_cpp/workflows/CI/badge.svg)](https://github.com/redboltz/mqtt_cpp/actions)[![Build Status](https://dev.azure.com/redboltz/redboltz/_apis/build/status/redboltz.mqtt_cpp?branchName=master)](https://dev.azure.com/redboltz/redboltz/_build/latest?definitionId=6&branchName=master)[![codecov](https://codecov.io/gh/redboltz/mqtt_cpp/branch/master/graph/badge.svg)](https://codecov.io/gh/redboltz/mqtt_cpp) Important note https://github.com/redboltz/mqtt_cpp/wiki/News. diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 73f704b6a..8c61721e2 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -64,7 +64,7 @@ ADD_LIBRARY(check_deps OBJECT EXCLUDE_FROM_ALL ${CHK_MQTT}) # We don't need to actually run the whole compiler. We're just checking that all the includes are valid # So here we ask for only the syntax checking mode to be used. # We also don't mind that there might be unused constant variables when doing deps checking. -TARGET_COMPILE_OPTIONS(check_deps PUBLIC -Wno-unused-const-variable $,/Zs,-fsyntax-only>) +TARGET_COMPILE_OPTIONS(check_deps PUBLIC -Wno-unneeded-internal-declaration -Wno-unused-const-variable $,/Zs,-fsyntax-only>) TARGET_LINK_LIBRARIES (check_deps PUBLIC ${PROJECT_NAME}) diff --git a/include/mqtt/broker/broker.hpp b/include/mqtt/broker/broker.hpp index 1299105f5..a326dfc03 100644 --- a/include/mqtt/broker/broker.hpp +++ b/include/mqtt/broker/broker.hpp @@ -721,43 +721,43 @@ class broker_t { pubcomp_props_ = force_move(props); } - void set_connect_props_handler(std::function h) { + void set_connect_props_handler(move_only_function h) { h_connect_props_ = force_move(h); } - void set_disconnect_props_handler(std::function h) { + void set_disconnect_props_handler(move_only_function h) { h_disconnect_props_ = force_move(h); } - void set_publish_props_handler(std::function h) { + void set_publish_props_handler(move_only_function h) { h_publish_props_ = force_move(h); } - void set_puback_props_handler(std::function h) { + void set_puback_props_handler(move_only_function h) { h_puback_props_ = force_move(h); } - void set_pubrec_props_handler(std::function h) { + void set_pubrec_props_handler(move_only_function h) { h_pubrec_props_ = force_move(h); } - void set_pubrel_props_handler(std::function h) { + void set_pubrel_props_handler(move_only_function h) { h_pubrel_props_ = force_move(h); } - void set_pubcomp_props_handler(std::function h) { + void set_pubcomp_props_handler(move_only_function h) { h_pubcomp_props_ = force_move(h); } - void set_subscribe_props_handler(std::function h) { + void set_subscribe_props_handler(move_only_function h) { h_subscribe_props_ = force_move(h); } - void set_unsubscribe_props_handler(std::function h) { + void set_unsubscribe_props_handler(move_only_function h) { h_unsubscribe_props_ = force_move(h); } - void set_auth_props_handler(std::function h) { + void set_auth_props_handler(move_only_function h) { h_auth_props_ = force_move(h); } @@ -1174,7 +1174,7 @@ class broker_t { bool session_present, bool authenticated, v5::properties props, - std::function finish = [](error_code){} + move_only_function finish = [](error_code){} ) { // Reply to the connect message. switch (ep.get_protocol_version()) { @@ -1184,7 +1184,7 @@ class broker_t { authenticated ? connect_return_code::accepted : connect_return_code::not_authorized, [finish = force_move(finish)] - (error_code ec) { + (error_code ec) mutable { finish(ec); } ); @@ -1201,7 +1201,7 @@ class broker_t { : v5::connect_reason_code::not_authorized, force_move(props), [finish = force_move(finish)] - (error_code ec) { + (error_code ec) mutable { finish(ec); } ); @@ -1214,7 +1214,7 @@ class broker_t { : v5::connect_reason_code::not_authorized, connack_props_, [finish = force_move(finish)] - (error_code ec) { + (error_code ec) mutable { finish(ec); } ); @@ -1792,7 +1792,7 @@ class broker_t { ); }; - std::vector> retain_deliver; + std::vector> retain_deliver; retain_deliver.reserve(entries.size()); // subscription identifier @@ -1906,7 +1906,7 @@ class broker_t { break; } - for (auto const& f : retain_deliver) { + for (auto& f : retain_deliver) { f(); } return true; @@ -2175,16 +2175,16 @@ class broker_t { v5::properties pubrec_props_; v5::properties pubrel_props_; v5::properties pubcomp_props_; - std::function h_connect_props_; - std::function h_disconnect_props_; - std::function h_publish_props_; - std::function h_puback_props_; - std::function h_pubrec_props_; - std::function h_pubrel_props_; - std::function h_pubcomp_props_; - std::function h_subscribe_props_; - std::function h_unsubscribe_props_; - std::function h_auth_props_; + move_only_function h_connect_props_; + move_only_function h_disconnect_props_; + move_only_function h_publish_props_; + move_only_function h_puback_props_; + move_only_function h_pubrec_props_; + move_only_function h_pubrel_props_; + move_only_function h_pubcomp_props_; + move_only_function h_subscribe_props_; + move_only_function h_unsubscribe_props_; + move_only_function h_auth_props_; bool pingresp_ = true; bool connack_ = true; }; diff --git a/include/mqtt/broker/session_state.hpp b/include/mqtt/broker/session_state.hpp index 6f0ab8ba6..7bcf2bc8e 100644 --- a/include/mqtt/broker/session_state.hpp +++ b/include/mqtt/broker/session_state.hpp @@ -52,7 +52,7 @@ class session_states; * Retained messages do not form part of the Session State in the Server, they are not deleted as a result of a Session ending. */ struct session_state { - using will_sender_t = std::function< + using will_sender_t = move_only_function< void( session_state const& source_ss, buffer topic, @@ -283,7 +283,7 @@ struct session_state { } } - void set_clean_handler(std::function handler) { + void set_clean_handler(move_only_function handler) { clean_handler_ = force_move(handler); } @@ -601,7 +601,7 @@ struct session_state { std::set qos2_publish_handled_; optional response_topic_; - std::function clean_handler_; + move_only_function clean_handler_; }; class session_states { diff --git a/include/mqtt/broker/topic_filter.hpp b/include/mqtt/broker/topic_filter.hpp index 055f0b80c..7c8e6eb5a 100644 --- a/include/mqtt/broker/topic_filter.hpp +++ b/include/mqtt/broker/topic_filter.hpp @@ -8,6 +8,7 @@ #define MQTT_BROKER_TOPIC_FILTER_HPP #include +#include #include #include diff --git a/include/mqtt/callable_overlay.hpp b/include/mqtt/callable_overlay.hpp index 0dbf03c65..01648fb5d 100644 --- a/include/mqtt/callable_overlay.hpp +++ b/include/mqtt/callable_overlay.hpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace MQTT_NS { template @@ -758,7 +759,7 @@ struct callable_overlay final : public Impl * 3.13 PINGREQ – PING request * @return if the handler returns true, then continue receiving, otherwise quit. */ - using pingreq_handler = std::function; + using pingreq_handler = move_only_function; /** * @brief Pingresp handler @@ -766,7 +767,7 @@ struct callable_overlay final : public Impl * 3.13 PINGRESP – PING response * @return if the handler returns true, then continue receiving, otherwise quit. */ - using pingresp_handler = std::function; + using pingresp_handler = move_only_function; // MQTT v3_1_1 handlers @@ -808,7 +809,7 @@ struct callable_overlay final : public Impl * @return if the handler returns true, then continue receiving, otherwise quit. * */ - using connect_handler = std::function< + using connect_handler = move_only_function< bool(buffer client_id, optional user_name, optional password, @@ -828,7 +829,7 @@ struct callable_overlay final : public Impl * 3.2.2.3 Connect Return code * @return if the handler returns true, then continue receiving, otherwise quit. */ - using connack_handler = std::function; + using connack_handler = move_only_function; /** * @brief Publish handler @@ -847,7 +848,7 @@ struct callable_overlay final : public Impl * Published contents * @return if the handler returns true, then continue receiving, otherwise quit. */ - using publish_handler = std::function packet_id, + using publish_handler = move_only_function packet_id, publish_options pubopts, buffer topic_name, buffer contents)>; @@ -860,7 +861,7 @@ struct callable_overlay final : public Impl * 3.4.2 Variable header * @return if the handler returns true, then continue receiving, otherwise quit. */ - using puback_handler = std::function; + using puback_handler = move_only_function; /** * @brief Pubrec handler @@ -870,7 +871,7 @@ struct callable_overlay final : public Impl * 3.5.2 Variable header * @return if the handler returns true, then continue receiving, otherwise quit. */ - using pubrec_handler = std::function; + using pubrec_handler = move_only_function; /** * @brief Pubrel handler @@ -880,7 +881,7 @@ struct callable_overlay final : public Impl * 3.6.2 Variable header * @return if the handler returns true, then continue receiving, otherwise quit. */ - using pubrel_handler = std::function; + using pubrel_handler = move_only_function; /** * @brief Pubcomp handler @@ -890,7 +891,7 @@ struct callable_overlay final : public Impl * 3.7.2 Variable header * @return if the handler returns true, then continue receiving, otherwise quit. */ - using pubcomp_handler = std::function; + using pubcomp_handler = move_only_function; /** * @brief Subscribe handler @@ -902,7 +903,7 @@ struct callable_overlay final : public Impl * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc385349802
* @return if the handler returns true, then continue receiving, otherwise quit. */ - using subscribe_handler = std::function entries)>; /** @@ -916,7 +917,7 @@ struct callable_overlay final : public Impl * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc398718071
* @return if the handler returns true, then continue receiving, otherwise quit. */ - using suback_handler = std::function qoss)>; /** @@ -929,7 +930,7 @@ struct callable_overlay final : public Impl * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc384800448
* @return if the handler returns true, then continue receiving, otherwise quit. */ - using unsubscribe_handler = std::function entries)>; /** @@ -939,14 +940,14 @@ struct callable_overlay final : public Impl * 3.11.2 Variable header * @return if the handler returns true, then continue receiving, otherwise quit. */ - using unsuback_handler = std::function; + using unsuback_handler = move_only_function; /** * @brief Disconnect handler * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc384800463
* 3.14 DISCONNECT – Disconnect notification */ - using disconnect_handler = std::function; + using disconnect_handler = move_only_function; // MQTT v5 handlers @@ -993,7 +994,7 @@ struct callable_overlay final : public Impl * @return if the handler returns true, then continue receiving, otherwise quit. * */ - using v5_connect_handler = std::function< + using v5_connect_handler = move_only_function< bool(buffer client_id, optional user_name, optional password, @@ -1019,7 +1020,7 @@ struct callable_overlay final : public Impl * 3.2.2.3 CONNACK Properties * @return if the handler returns true, then continue receiving, otherwise quit. */ - using v5_connack_handler = std::function< + using v5_connack_handler = move_only_function< bool(bool session_present, v5::connect_reason_code reason_code, v5::properties props) @@ -1050,7 +1051,7 @@ struct callable_overlay final : public Impl * 3.3.2.3 PUBLISH Properties * @return if the handler returns true, then continue receiving, otherwise quit. */ - using v5_publish_handler = std::function< + using v5_publish_handler = move_only_function< bool(optional packet_id, publish_options pubopts, buffer topic_name, @@ -1074,7 +1075,7 @@ struct callable_overlay final : public Impl * 3.4.2.2 PUBACK Properties * @return if the handler returns true, then continue receiving, otherwise quit. */ - using v5_puback_handler = std::function< + using v5_puback_handler = move_only_function< bool(packet_id_t packet_id, v5::puback_reason_code reason_code, v5::properties props) @@ -1096,7 +1097,7 @@ struct callable_overlay final : public Impl * 3.5.2.2 PUBREC Properties * @return if the handler returns true, then continue receiving, otherwise quit. */ - using v5_pubrec_handler = std::function< + using v5_pubrec_handler = move_only_function< bool(packet_id_t packet_id, v5::pubrec_reason_code reason_code, v5::properties props) @@ -1118,7 +1119,7 @@ struct callable_overlay final : public Impl * 3.6.2.2 PUBREL Properties * @return if the handler returns true, then continue receiving, otherwise quit. */ - using v5_pubrel_handler = std::function< + using v5_pubrel_handler = move_only_function< bool(packet_id_t packet_id, v5::pubrel_reason_code reason_code, v5::properties props) @@ -1140,7 +1141,7 @@ struct callable_overlay final : public Impl * 3.7.2.2 PUBCOMP Properties * @return if the handler returns true, then continue receiving, otherwise quit. */ - using v5_pubcomp_handler = std::function< + using v5_pubcomp_handler = move_only_function< bool(packet_id_t packet_id, v5::pubcomp_reason_code reason_code, v5::properties props) @@ -1160,7 +1161,7 @@ struct callable_overlay final : public Impl * 3.8.2.1 SUBSCRIBE Properties * @return if the handler returns true, then continue receiving, otherwise quit. */ - using v5_subscribe_handler = std::function< + using v5_subscribe_handler = move_only_function< bool(packet_id_t packet_id, std::vector entries, v5::properties props) @@ -1181,7 +1182,7 @@ struct callable_overlay final : public Impl * 3.9.2.1 SUBACK Properties * @return if the handler returns true, then continue receiving, otherwise quit. */ - using v5_suback_handler = std::function< + using v5_suback_handler = move_only_function< bool(packet_id_t packet_id, std::vector reasons, v5::properties props) @@ -1202,7 +1203,7 @@ struct callable_overlay final : public Impl * 3.10.2.1 UNSUBSCRIBE Properties * @return if the handler returns true, then continue receiving, otherwise quit. */ - using v5_unsubscribe_handler = std::function< + using v5_unsubscribe_handler = move_only_function< bool(packet_id_t packet_id, std::vector entries, v5::properties props) @@ -1223,7 +1224,7 @@ struct callable_overlay final : public Impl * 3.11.2.1 UNSUBACK Properties * @return if the handler returns true, then continue receiving, otherwise quit. */ - using v5_unsuback_handler = std::function< + using v5_unsuback_handler = move_only_function< bool(packet_id_t, std::vector reasons, v5::properties props) @@ -1242,7 +1243,7 @@ struct callable_overlay final : public Impl * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901209
* 3.14.2.2 DISCONNECT Properties */ - using v5_disconnect_handler = std::function< + using v5_disconnect_handler = move_only_function< void(v5::disconnect_reason_code reason_code, v5::properties props) >; @@ -1261,7 +1262,7 @@ struct callable_overlay final : public Impl * 3.15.2.2 AUTH Properties * @return if the handler returns true, then continue receiving, otherwise quit. */ - using v5_auth_handler = std::function< + using v5_auth_handler = move_only_function< bool(v5::auth_reason_code reason_code, v5::properties props) >; @@ -1275,7 +1276,7 @@ struct callable_overlay final : public Impl * This handler is called if the client called `disconnect()` and the server closed the socket cleanly. * If the socket is closed by other reasons, error_handler is called. */ - using close_handler = std::function; + using close_handler = move_only_function; /** * @brief Error handler @@ -1284,7 +1285,7 @@ struct callable_overlay final : public Impl * * @param ec error code */ - using error_handler = std::function; + using error_handler = move_only_function; /** * @brief Publish response sent handler @@ -1294,7 +1295,7 @@ struct callable_overlay final : public Impl * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901026
* 2.2.1 Packet Identifier */ - using pub_res_sent_handler = std::function; + using pub_res_sent_handler = move_only_function; /** * @brief Serialize publish handler @@ -1302,7 +1303,7 @@ struct callable_overlay final : public Impl * To restore the message, use restore_serialized_message(). * @param msg publish message */ - using serialize_publish_message_handler = std::function msg)>; + using serialize_publish_message_handler = move_only_function msg)>; /** * @brief Serialize publish handler @@ -1310,7 +1311,7 @@ struct callable_overlay final : public Impl * To restore the message, use restore_serialized_message(). * @param msg v5::publish message */ - using serialize_v5_publish_message_handler = std::function msg)>; + using serialize_v5_publish_message_handler = move_only_function msg)>; /** * @brief Serialize publish handler @@ -1320,7 +1321,7 @@ struct callable_overlay final : public Impl * @param data pointer to the serializing message * @param size size of the serializing message */ - using serialize_publish_handler = std::function; + using serialize_publish_handler = move_only_function; /** * @brief Serialize pubrel handler @@ -1330,7 +1331,7 @@ struct callable_overlay final : public Impl * To restore the message, use restore_serialized_message(). * @param msg pubrel message */ - using serialize_pubrel_message_handler = std::function msg)>; + using serialize_pubrel_message_handler = move_only_function msg)>; /** * @brief Serialize pubrel handler @@ -1340,7 +1341,7 @@ struct callable_overlay final : public Impl * To restore the message, use restore_serialized_message(). * @param msg pubrel message */ - using serialize_v5_pubrel_message_handler = std::function msg)>; + using serialize_v5_pubrel_message_handler = move_only_function msg)>; /** * @brief Serialize pubrel handler @@ -1352,19 +1353,19 @@ struct callable_overlay final : public Impl * @param data pointer to the serializing message * @param size size of the serializing message */ - using serialize_pubrel_handler = std::function; + using serialize_pubrel_handler = move_only_function; /** * @brief Remove serialized message * @param packet_id packet identifier of the removing message */ - using serialize_remove_handler = std::function; + using serialize_remove_handler = move_only_function; /** * @brief Pre-send handler * This handler is called when any mqtt control packet is decided to send. */ - using pre_send_handler = std::function; + using pre_send_handler = move_only_function; /** * @brief is valid length handler @@ -1374,7 +1375,7 @@ struct callable_overlay final : public Impl * @return true if check is success, otherwise false */ using is_valid_length_handler = - std::function; + move_only_function; /** * @brief next read handler @@ -1382,7 +1383,7 @@ struct callable_overlay final : public Impl * @param func A callback function that is called when async operation will finish. */ using mqtt_message_processed_handler = - std::function; + move_only_function; @@ -1879,7 +1880,7 @@ struct callable_overlay final : public Impl serialize_remove_handler h_remove) { h_serialize_publish_ = [h_publish = force_move(h_publish)] - (basic_publish_message msg) { + (basic_publish_message msg) mutable { if (h_publish) { auto buf = msg.continuous_buffer(); h_publish(msg.packet_id(), buf.data(), buf.size()); @@ -1887,7 +1888,7 @@ struct callable_overlay final : public Impl }; h_serialize_pubrel_ = [h_pubrel = force_move(h_pubrel)] - (basic_pubrel_message msg) { + (basic_pubrel_message msg) mutable { if (h_pubrel) { auto buf = msg.continuous_buffer(); h_pubrel(msg.packet_id(), buf.data(), buf.size()); @@ -1908,7 +1909,7 @@ struct callable_overlay final : public Impl serialize_remove_handler h_remove) { h_serialize_v5_publish_ = [h_publish = force_move(h_publish)] - (v5::basic_publish_message msg) { + (v5::basic_publish_message msg) mutable { if (h_publish) { auto buf = msg.continuous_buffer(); h_publish(msg.packet_id(), buf.data(), buf.size()); @@ -1916,7 +1917,7 @@ struct callable_overlay final : public Impl }; h_serialize_v5_pubrel_ = [h_pubrel = force_move(h_pubrel)] - (v5::basic_pubrel_message msg) { + (v5::basic_pubrel_message msg) mutable { if (h_pubrel) { auto buf = msg.continuous_buffer(); h_pubrel(msg.packet_id(), buf.data(), buf.size()); @@ -2042,7 +2043,7 @@ struct callable_overlay final : public Impl * @brief Get mqtt_message_processed_handler. * @return mqtt_message_processed_handler. */ - mqtt_message_processed_handler get_mqtt_message_processed_handler() const { + mqtt_message_processed_handler const& get_mqtt_message_processed_handler() const { return h_mqtt_message_processed_; } @@ -2066,7 +2067,7 @@ struct callable_overlay final : public Impl * @brief Get close handler * @return handler */ - close_handler get_close_handler() const { + close_handler const& get_close_handler() const { return h_close_; } @@ -2074,7 +2075,7 @@ struct callable_overlay final : public Impl * @brief Get error handler * @return handler */ - error_handler get_error_handler() const { + error_handler const& get_error_handler() const { return h_error_; } diff --git a/include/mqtt/client.hpp b/include/mqtt/client.hpp index 527bf303d..3f4713834 100644 --- a/include/mqtt/client.hpp +++ b/include/mqtt/client.hpp @@ -26,6 +26,7 @@ #include #include #include +#include namespace MQTT_NS { @@ -1444,30 +1445,51 @@ class client : public endpoint { * When the endpoint disconnects using disconnect(), a will won't send.
* See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901205
* @param timeout after timeout elapsed, force_disconnect() is automatically called. + * @param reason_code + * DISCONNECT Reason Code
+ * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901208
+ * 3.14.2.1 Disconnect Reason Code + * @param props + * Properties
+ * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901209
+ * 3.14.2.2 DISCONNECT Properties * @param func A callback function that is called when async operation will finish. */ - void async_disconnect( + template < + typename CompletionToken = async_handler_t, + typename std::enable_if_t< + is_invocable::value + >* = nullptr + > + auto async_disconnect( std::chrono::steady_clock::duration timeout, - async_handler_t func = async_handler_t()) { + v5::disconnect_reason_code reason_code, + v5::properties props, + CompletionToken&& token = [](error_code){} + ) { + if (ping_duration_ != std::chrono::steady_clock::duration::zero()) tim_ping_.cancel(); - if (base::connected()) { - std::weak_ptr wp(std::static_pointer_cast(this->shared_from_this())); - tim_close_.expires_after(force_move(timeout)); - tim_close_.async_wait( - [wp = force_move(wp)](error_code ec) { - if (auto sp = wp.lock()) { - if (!ec) { - sp->socket()->post( - [sp] { - sp->force_disconnect(); - } - ); - } + std::weak_ptr wp(std::static_pointer_cast(this->shared_from_this())); + tim_close_.expires_after(force_move(timeout)); + tim_close_.async_wait( + [wp = force_move(wp)](error_code ec) { + if (auto sp = wp.lock()) { + if (!ec) { + sp->socket()->post( + [sp] { + sp->force_disconnect(); + } + ); } } + } + ); + return + base::async_disconnect( + reason_code, + force_move(props), + std::forward(token) ); - base::async_disconnect(force_move(func)); - } } /** @@ -1476,7 +1498,6 @@ class client : public endpoint { * The broker disconnects the endpoint after receives the disconnect packet.
* When the endpoint disconnects using disconnect(), a will won't send.
* See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901205
- * @param timeout after timeout elapsed, force_disconnect() is automatically called. * @param reason_code * DISCONNECT Reason Code
* See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901208
@@ -1487,30 +1508,24 @@ class client : public endpoint { * 3.14.2.2 DISCONNECT Properties * @param func A callback function that is called when async operation will finish. */ - void async_disconnect( - std::chrono::steady_clock::duration timeout, + template < + typename CompletionToken = async_handler_t, + typename std::enable_if_t< + is_invocable::value + >* = nullptr + > + auto async_disconnect( v5::disconnect_reason_code reason_code, v5::properties props, - async_handler_t func = async_handler_t()) { + CompletionToken&& token = [](error_code){} + ) { if (ping_duration_ != std::chrono::steady_clock::duration::zero()) tim_ping_.cancel(); - if (base::connected()) { - std::weak_ptr wp(std::static_pointer_cast(this->shared_from_this())); - tim_close_.expires_after(force_move(timeout)); - tim_close_.async_wait( - [wp = force_move(wp)](error_code ec) { - if (auto sp = wp.lock()) { - if (!ec) { - sp->socket()->post( - [sp] { - sp->force_disconnect(); - } - ); - } - } - } + return + base::async_disconnect( + reason_code, + force_move(props), + std::forward(token) ); - base::async_disconnect(reason_code, force_move(props), force_move(func)); - } } /** @@ -1519,14 +1534,39 @@ class client : public endpoint { * The broker disconnects the endpoint after receives the disconnect packet.
* When the endpoint disconnects using disconnect(), a will won't send.
* See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901205
+ * @param timeout after timeout elapsed, force_disconnect() is automatically called. * @param func A callback function that is called when async operation will finish. */ - void async_disconnect( - async_handler_t func = async_handler_t()) { + template < + typename CompletionToken = async_handler_t, + typename std::enable_if_t< + is_invocable::value + >* = nullptr + > + auto async_disconnect( + std::chrono::steady_clock::duration timeout, + CompletionToken&& token = [](error_code){} + ) { if (ping_duration_ != std::chrono::steady_clock::duration::zero()) tim_ping_.cancel(); - if (base::connected()) { - base::async_disconnect(force_move(func)); - } + std::weak_ptr wp(std::static_pointer_cast(this->shared_from_this())); + tim_close_.expires_after(force_move(timeout)); + tim_close_.async_wait( + [wp = force_move(wp)](error_code ec) { + if (auto sp = wp.lock()) { + if (!ec) { + sp->socket()->post( + [sp] { + sp->force_disconnect(); + } + ); + } + } + } + ); + return + base::async_disconnect( + std::forward(token) + ); } /** @@ -1535,24 +1575,19 @@ class client : public endpoint { * The broker disconnects the endpoint after receives the disconnect packet.
* When the endpoint disconnects using disconnect(), a will won't send.
* See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901205
- * @param reason_code - * DISCONNECT Reason Code
- * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901208
- * 3.14.2.1 Disconnect Reason Code - * @param props - * Properties
- * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901209
- * 3.14.2.2 DISCONNECT Properties * @param func A callback function that is called when async operation will finish. */ - void async_disconnect( - v5::disconnect_reason_code reason_code, - v5::properties props, - async_handler_t func = async_handler_t()) { + template < + typename CompletionToken = async_handler_t, + typename std::enable_if_t< + is_invocable::value + >* = nullptr + > + auto async_disconnect( + CompletionToken&& token = [](error_code){} + ) { if (ping_duration_ != std::chrono::steady_clock::duration::zero()) tim_ping_.cancel(); - if (base::connected()) { - base::async_disconnect(reason_code, force_move(props), force_move(func)); - } + return base::async_disconnect(std::forward(token)); } /** diff --git a/include/mqtt/endpoint.hpp b/include/mqtt/endpoint.hpp index 179cfa2b9..3ae2055e8 100644 --- a/include/mqtt/endpoint.hpp +++ b/include/mqtt/endpoint.hpp @@ -71,6 +71,8 @@ #include #include #include +#include +#include #if defined(MQTT_USE_WS) #include @@ -173,7 +175,7 @@ class endpoint : public std::enable_shared_from_this; public: - using async_handler_t = std::function; + using async_handler_t = move_only_function; using packet_id_t = typename packet_id_type::type; /** @@ -2213,27 +2215,26 @@ class endpoint : public std::enable_shared_from_this * When the endpoint disconnects using disconnect(), a will won't send.
*/ - void async_disconnect( - async_handler_t func = {} + template < + typename CompletionToken = async_handler_t, + typename std::enable_if_t< + is_invocable::value + >* = nullptr + > + auto async_disconnect( + CompletionToken&& token = [](error_code){} ) { MQTT_LOG("mqtt_api", info) << MQTT_ADD_VALUE(address, this) << "async_disconnect"; - - if (connected_ && mqtt_connected_) { - disconnect_requested_ = true; - // The reason code and property vector are only used if we're using mqttv5. - async_send_disconnect(v5::disconnect_reason_code::normal_disconnection, - v5::properties{}, - force_move(func)); - } - else { - socket_->post( - [func = force_move(func)] { - if (func) func(boost::system::errc::make_error_code(boost::system::errc::success)); - } + return + as::async_compose< + CompletionToken, + void(error_code) + >( + async_disconnect_impl{*this}, + token ); - } } /** @@ -2252,27 +2253,30 @@ class endpoint : public std::enable_shared_from_this * When the endpoint disconnects using disconnect(), a will won't send.
*/ - void async_disconnect( + template < + typename CompletionToken = async_handler_t, + typename std::enable_if_t< + is_invocable::value + >* = nullptr + > + auto async_disconnect( v5::disconnect_reason_code reason, v5::properties props = {}, - async_handler_t func = {} + CompletionToken&& token = [](error_code){} ) { MQTT_LOG("mqtt_api", info) << MQTT_ADD_VALUE(address, this) << "async_disconnect" << " reason:" << reason; - if (connected_ && mqtt_connected_) { - disconnect_requested_ = true; - async_send_disconnect(reason, force_move(props), force_move(func)); - } - else { - socket_->post( - [func = force_move(func)] { - if (func) func(boost::system::errc::make_error_code(boost::system::errc::success)); - } + return + as::async_compose< + CompletionToken, + void(error_code) + >( + async_disconnect_impl{*this, reason, force_move(props)}, + token ); - } } /** @@ -2756,7 +2760,7 @@ class endpoint : public std::enable_shared_from_this const& f) { + void for_each_store(move_only_function f) { MQTT_LOG("mqtt_api", info) << MQTT_ADD_VALUE(address, this) << "for_each_store(ptr, size)"; LockGuard lck (store_mtx_); store_.for_each( - [f]( + [f = force_move(f)] + ( basic_store_message_variant const& message, any const& /*life_keeper*/ - ) { + ) mutable { auto cb = continuous_buffer(message); f(cb.data(), cb.size()); return false; // no erase @@ -4343,16 +4348,17 @@ class endpoint : public std::enable_shared_from_this)> const& f) { + void for_each_store(move_only_function)> f) { MQTT_LOG("mqtt_api", info) << MQTT_ADD_VALUE(address, this) << "for_each_store(store_message_variant)"; LockGuard lck (store_mtx_); store_.for_each( - [f]( + [f = force_move(f)] + ( basic_store_message_variant const& message, any const& /*life_keeper*/ - ) { + ) mutable { f(message); return false; // no erase } @@ -4363,17 +4369,18 @@ class endpoint : public std::enable_shared_from_this, any)> const& f) { + void for_each_store_with_life_keeper(move_only_function, any)> f) { MQTT_LOG("mqtt_api", info) << MQTT_ADD_VALUE(address, this) << "for_each_store(store_message_variant, life_keeper)"; LockGuard lck (store_mtx_); store_.for_each( - [f]( + [f = force_move(f)] + ( basic_store_message_variant const& message, any const& life_keeper - ) { + ) mutable { f(message, life_keeper); return false; // no erase } @@ -4782,7 +4789,7 @@ class endpoint : public std::enable_shared_from_this(std::get<0>(msg_lk))) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -4799,7 +4806,7 @@ class endpoint : public std::enable_shared_from_this(msg_lk)), [func = force_move(func), life_keeper = force_move(std::get<1>(msg_lk))] - (error_code ec) { + (error_code ec) mutable { if (func) func(ec); } ); @@ -4810,7 +4817,7 @@ class endpoint : public std::enable_shared_from_this(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -4859,14 +4866,14 @@ class endpoint : public std::enable_shared_from_this&& msg) mutable { + [this, func = force_move(func)] (v5::basic_publish_message&& msg) mutable { if (publish_send_count_.load() == publish_send_max_) { { LockGuard lck (publish_send_queue_mtx_); publish_send_queue_.emplace_back(force_move(msg), true); } socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { // message has already been stored so func should be called with success here if (func) func(boost::system::errc::make_error_code(boost::system::errc::success)); } @@ -5365,7 +5372,7 @@ class endpoint : public std::enable_shared_from_this; using parse_handler = - std::function< + move_only_function< void( this_type_sp&& spep, any&& session_life_keeper, @@ -10492,7 +10499,7 @@ class endpoint : public std::enable_shared_from_this(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10512,7 +10519,7 @@ class endpoint : public std::enable_shared_from_this(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10543,7 +10550,8 @@ class endpoint : public std::enable_shared_from_this(std::get<0>(msg_lk))) { if (packet_id != 0) { @@ -10551,7 +10559,7 @@ class endpoint : public std::enable_shared_from_thispost( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10561,12 +10569,18 @@ class endpoint : public std::enable_shared_from_this(serialize_publish), - std::forward(receive_maximum_proc) + [ + &func, + receive_maximum_proc = std::forward(receive_maximum_proc) + ] + (auto&& msg) mutable { + return receive_maximum_proc(force_move(msg), func); + } ) ) { do_async_write( force_move(std::get<0>(msg_lk)), - [life_keeper = force_move(std::get<1>(msg_lk)), func](error_code ec) { + [life_keeper = force_move(std::get<1>(msg_lk)), func = force_move(func)](error_code ec) mutable { if (func) func(ec); } ); @@ -10583,7 +10597,7 @@ class endpoint : public std::enable_shared_from_this&& msg) mutable { + [this] (v5::basic_publish_message&& msg, async_handler_t& func) mutable { if (publish_send_count_.load() == publish_send_max_) { { LockGuard lck (publish_send_queue_mtx_); publish_send_queue_.emplace_back(force_move(msg), true); } socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { // message has already been stored so func should be called with success here if (func) func(boost::system::errc::make_error_code(boost::system::errc::success)); } @@ -10635,7 +10649,7 @@ class endpoint : public std::enable_shared_from_this(packet_id); if (maximum_packet_size_send_ < size(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10644,7 +10658,7 @@ class endpoint : public std::enable_shared_from_thisshared_from_this(), packet_id, func = force_move(func)] - (error_code ec) { + (error_code ec) mutable { if (func) func(ec); on_pub_res_sent(packet_id); } @@ -10654,7 +10668,7 @@ class endpoint : public std::enable_shared_from_this(packet_id, reason, force_move(props)); if (maximum_packet_size_send_ < size(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10663,7 +10677,7 @@ class endpoint : public std::enable_shared_from_thisshared_from_this(), packet_id, func = force_move(func)] - (error_code ec) { + (error_code ec) mutable { erase_publish_received(packet_id); if (func) func(ec); on_pub_res_sent(packet_id); @@ -10687,7 +10701,7 @@ class endpoint : public std::enable_shared_from_this(packet_id); if (maximum_packet_size_send_ < size(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10702,7 +10716,7 @@ class endpoint : public std::enable_shared_from_this(packet_id, reason, force_move(props)); if (maximum_packet_size_send_ < size(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10711,7 +10725,7 @@ class endpoint : public std::enable_shared_from_thisshared_from_this(), packet_id, func = force_move(func)] - (error_code ec) { + (error_code ec) mutable { erase_publish_received(packet_id); if (func) func(ec); } @@ -10738,7 +10752,7 @@ class endpoint : public std::enable_shared_from_this(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10777,7 +10791,8 @@ class endpoint : public std::enable_shared_from_this(packet_id); if (maximum_packet_size_send_ < size(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10822,7 +10837,7 @@ class endpoint : public std::enable_shared_from_thisshared_from_this(), packet_id, func = force_move(func)] - (error_code ec) { + (error_code ec) mutable { if (func) func(ec); on_pub_res_sent(packet_id); } @@ -10832,7 +10847,7 @@ class endpoint : public std::enable_shared_from_this(packet_id, reason, force_move(props)); if (maximum_packet_size_send_ < size(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10841,7 +10856,7 @@ class endpoint : public std::enable_shared_from_thisshared_from_this(), packet_id, func = force_move(func)] - (error_code ec) { + (error_code ec) mutable { if (func) func(ec); on_pub_res_sent(packet_id); } @@ -10868,7 +10883,7 @@ class endpoint : public std::enable_shared_from_thispost( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10891,7 +10906,7 @@ class endpoint : public std::enable_shared_from_thispost( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10926,7 +10941,7 @@ class endpoint : public std::enable_shared_from_this(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10945,7 +10960,7 @@ class endpoint : public std::enable_shared_from_this(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -10977,7 +10992,7 @@ class endpoint : public std::enable_shared_from_thispost( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -11000,7 +11015,7 @@ class endpoint : public std::enable_shared_from_thispost( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -11030,7 +11045,7 @@ class endpoint : public std::enable_shared_from_this(packet_id); if (maximum_packet_size_send_ < size(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -11064,7 +11079,7 @@ class endpoint : public std::enable_shared_from_this(force_move(params), packet_id, force_move(props)); if (maximum_packet_size_send_ < size(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -11087,7 +11102,7 @@ class endpoint : public std::enable_shared_from_this(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -11100,7 +11115,7 @@ class endpoint : public std::enable_shared_from_this(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -11121,7 +11136,7 @@ class endpoint : public std::enable_shared_from_this(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -11133,7 +11148,7 @@ class endpoint : public std::enable_shared_from_this(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -11160,7 +11175,7 @@ class endpoint : public std::enable_shared_from_this(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -11174,6 +11189,36 @@ class endpoint : public std::enable_shared_from_this + void operator()( + Self& self + ) { + if (ep.connected_ && ep.mqtt_connected_) { + ep.disconnect_requested_ = true; + // The reason code and property vector are only used if we're using mqttv5. + ep.async_send_disconnect( + reason, + force_move(props), + [self = force_move(self)] (error_code ec) mutable { + self.complete(ec); + } + ); + } + else { + ep.socket_->post( + [self = force_move(self)] () mutable { + self.complete(boost::system::errc::make_error_code(boost::system::errc::success)); + } + ); + } + } + }; + void async_send_disconnect( v5::disconnect_reason_code reason, v5::properties props, @@ -11184,7 +11229,7 @@ class endpoint : public std::enable_shared_from_this(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -11196,7 +11241,7 @@ class endpoint : public std::enable_shared_from_this(msg)) { socket_->post( - [func = force_move(func)] { + [func = force_move(func)] () mutable { if (func) func(boost::system::errc::make_error_code(boost::system::errc::message_size)); } ); @@ -11210,13 +11255,14 @@ class endpoint : public std::enable_shared_from_this func) { + template + void async_send_store(Func&& func) { // packet_id has already been registered - auto g = shared_scope_guard( - [func = force_move(func)] { + auto g{shared_scope_guard( + [func = std::forward(func)] () mutable { func(); } - ); + )}; LockGuard lck (store_mtx_); if (store_.empty()) { socket().post( @@ -11359,7 +11405,7 @@ class endpoint : public std::enable_shared_from_thisqueue_.pop_front(); @@ -11379,7 +11425,7 @@ class endpoint : public std::enable_shared_from_thistotal_bytes_sent_ += bytes_transferred; for (std::size_t i = 0; i != num_of_messages_; ++i) { @@ -11418,7 +11464,7 @@ class endpoint : public std::enable_shared_from_this(iterator_count)); // And further, only up to the specified maximum bytes @@ -11446,11 +11492,11 @@ class endpoint : public std::enable_shared_from_thisshared_from_this(), [handlers = force_move(handlers)] - (error_code ec) { - for (auto const& h : handlers) { + (error_code ec) mutable { + for (auto& h : handlers) { if (h) h(ec); } }, diff --git a/include/mqtt/external/function2.hpp b/include/mqtt/external/function2.hpp new file mode 100644 index 000000000..542c72ba5 --- /dev/null +++ b/include/mqtt/external/function2.hpp @@ -0,0 +1,1829 @@ + +// 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_LIMITED_EMPTY_PROPAGATION +#if defined(FU2_WITH_LIMITED_EMPTY_PROPAGATION) +#define FU2_HAS_LIMITED_EMPTY_PROPAGATION +#endif // FU2_WITH_NO_EMPTY_PROPAGATION +// - FU2_HAS_NO_FUNCTIONAL_HEADER +#if !defined(FU2_WITH_NO_FUNCTIONAL_HEADER) && \ + !defined(FU2_NO_FUNCTIONAL_HEADER) && \ + (!defined(FU2_HAS_DISABLED_EXCEPTIONS) || \ + defined(FU2_HAS_LIMITED_EMPTY_PROPAGATION)) +#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 + +#if defined(__cpp_constexpr) && (__cpp_constexpr >= 201304) +#define FU2_DETAIL_CXX14_CONSTEXPR constexpr +#elif defined(__clang__) && defined(__has_feature) +#if __has_feature(__cxx_generic_lambdas__) && \ + __has_feature(__cxx_relaxed_constexpr__) +#define FU2_DETAIL_CXX14_CONSTEXPR constexpr +#endif +#elif defined(_MSC_VER) && (_MSC_VER >= 1915) && (_MSVC_LANG >= 201402) +#define FU2_DETAIL_CXX14_CONSTEXPR constexpr +#endif +#ifndef FU2_DETAIL_CXX14_CONSTEXPR +#define FU2_DETAIL_CXX14_CONSTEXPR +#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) +#if __has_builtin(__builtin_unreachable) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable() +#endif +#endif +#ifndef FU2_DETAIL_UNREACHABLE_INTRINSIC +// 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) +#if __has_builtin(__builtin_trap) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define FU2_DETAIL_TRAP() __builtin_trap() +#endif +#endif +#ifndef FU2_DETAIL_TRAP +// 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>; + +template +struct lazy_and; + +template +struct lazy_and : B1 {}; + +template +struct lazy_and : std::conditional::type {}; + +// template +// struct lazy_and +// : std::conditional, B1>::type {}; + +// 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 +static FU2_DETAIL_CXX14_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 +FU2_DETAIL_CXX14_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; + + FU2_DETAIL_CXX14_CONSTEXPR data_accessor* opaque_ptr() noexcept { + return &storage_.accessor_; + } + constexpr data_accessor const* opaque_ptr() const noexcept { + return &storage_.accessor_; + } + FU2_DETAIL_CXX14_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(); + } + + FU2_DETAIL_CXX14_CONSTEXPR erasure() noexcept { + vtable_.set_empty(); + } + + FU2_DETAIL_CXX14_CONSTEXPR erasure(std::nullptr_t) noexcept { + vtable_.set_empty(); + } + + FU2_DETAIL_CXX14_CONSTEXPR + erasure(erasure&& right) noexcept(Property::is_strong_exception_guaranteed) { + right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), + this->opaque_ptr(), capacity()); + } + + FU2_DETAIL_CXX14_CONSTEXPR erasure(erasure const& right) { + right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(), + this->opaque_ptr(), capacity()); + } + + template + FU2_DETAIL_CXX14_CONSTEXPR + erasure(erasure right) noexcept( + Property::is_strong_exception_guaranteed) { + right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), + this->opaque_ptr(), capacity()); + } + + template >> + FU2_DETAIL_CXX14_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 >> + FU2_DETAIL_CXX14_CONSTEXPR erasure(std::true_type /*use_bool_op*/, + T&& callable, + Allocator&& allocator_ = Allocator{}) { + if (!!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()); + } + + FU2_DETAIL_CXX14_CONSTEXPR erasure& + operator=(std::nullptr_t) noexcept(Property::is_strong_exception_guaranteed) { + vtable_.destroy(this->opaque_ptr(), capacity()); + return *this; + } + + FU2_DETAIL_CXX14_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; + } + + FU2_DETAIL_CXX14_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 + FU2_DETAIL_CXX14_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) + FU2_DETAIL_CXX14_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 + : detail::lazy_and< // both are std::integral_constant + invocation::can_invoke, + typename Trait::arguments>, + invocation::is_noexcept_correct, + typename Trait::arguments>> {}; + +/// 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 {}; + +#if defined(FU2_HAS_NO_EMPTY_PROPAGATION) +template +struct use_bool_op : std::false_type {}; +#elif defined(FU2_HAS_LIMITED_EMPTY_PROPAGATION) +/// Implementation for use_bool_op based on the behaviour of std::function, +/// propagating empty state for pointers, `std::function` and +/// `fu2::detail::function` types only. +template +struct use_bool_op : std::false_type {}; + +#if !defined(FU2_HAS_NO_FUNCTIONAL_HEADER) +template +struct use_bool_op> : std::true_type {}; +#endif + +template +struct use_bool_op> : std::true_type {}; + +template +struct use_bool_op : std::true_type {}; + +template +struct use_bool_op : std::true_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 +}; + +/// 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. +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 FU2_DETAIL_CXX14_CONSTEXPR + function(function const& /*right*/) = default; + explicit FU2_DETAIL_CXX14_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> + FU2_DETAIL_CXX14_CONSTEXPR + function(function const& right) + : erasure_(right.erasure_) { + } + + /// Move construction from another function + template * = nullptr, + enable_if_owning_correct_t* = nullptr> + FU2_DETAIL_CXX14_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> + FU2_DETAIL_CXX14_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> + FU2_DETAIL_CXX14_CONSTEXPR function(T&& callable, Allocator&& allocator_) + : erasure_(use_bool_op>{}, std::forward(callable), + std::forward(allocator_)) { + } + + /// Empty constructs the function + FU2_DETAIL_CXX14_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_TRAP +#undef FU2_DETAIL_CXX14_CONSTEXPR + +#endif // FU2_INCLUDED_FUNCTION2_HPP_ diff --git a/include/mqtt/is_invocable.hpp b/include/mqtt/is_invocable.hpp new file mode 100644 index 000000000..9147c2d69 --- /dev/null +++ b/include/mqtt/is_invocable.hpp @@ -0,0 +1,39 @@ +// Copyright Takatoshi Kondo 2022 +// +// 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) + +#if !defined(MQTT_IS_INVOCABLE_HPP) +#define MQTT_IS_INVOCABLE_HPP + +#include +#include + +#if __cplusplus >= 201703L + +namespace MQTT_NS { + +template +using is_invocable = typename std::is_invocable; + +} // namespace MQTT_NS + +#else // __cplusplus >= 201703L + +#include + +namespace MQTT_NS { + +template +struct is_invocable : std::is_constructible< + move_only_function, + std::reference_wrapper::type> +> +{}; + +} // namespace MQTT_NS + +#endif // __cplusplus >= 201703L + +#endif // MQTT_IS_INVOCABLE_HPP diff --git a/include/mqtt/move_only_function.hpp b/include/mqtt/move_only_function.hpp new file mode 100644 index 000000000..8cd6f1a70 --- /dev/null +++ b/include/mqtt/move_only_function.hpp @@ -0,0 +1,30 @@ +// Copyright Takatoshi Kondo 2022 +// +// 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) + +#if !defined(MQTT_MOVE_ONLY_FUNCTION_HPP) +#define MQTT_MOVE_ONLY_FUNCTION_HPP + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waddress" +#endif // defined(__GNUC__) + +#include + +#include + +namespace MQTT_NS { + +template +using move_only_function = fu2::unique_function; + +} // namespace MQTT_NS + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif // defined(__GNUC__) + +#endif // MQTT_MOVE_ONLY_FUNCTION_HPP diff --git a/include/mqtt/null_strand.hpp b/include/mqtt/null_strand.hpp index a9f56b0cc..f14fe1247 100644 --- a/include/mqtt/null_strand.hpp +++ b/include/mqtt/null_strand.hpp @@ -17,9 +17,77 @@ namespace MQTT_NS { namespace as = boost::asio; -// Using standard executor style null_strand / simple executor -using null_strand = as::io_context::executor_type; +namespace detail { + +struct null_strand { + using inner_executor_type = as::io_context::executor_type; + template + explicit null_strand(Executor e) noexcept + : exe_{force_move(e)} + {} + + template + void defer(Func&& f, Allocator) const { + as::defer( + exe_, + [f = std::forward(f)] () mutable { + std::move(f)(); + } + ); + } + template + void dispatch(Func&& f, Allocator) const { + as::dispatch( + exe_, + [f = std::forward(f)] () mutable { + std::move(f)(); + } + ); + } + template + void post(Func&& f, Allocator) const { + as::post( + exe_, + [f = std::forward(f)] () mutable { + std::move(f)(); + } + ); + } + as::any_io_executor get_inner_executor() const { + return exe_; + } + void on_work_started() const noexcept {} + void on_work_finished() const noexcept {} + bool running_in_this_thread() const noexcept { return true; } + operator as::any_io_executor() const { + return exe_; + } +private: + as::io_context::executor_type exe_; +}; + +} // namespace detail + +using null_strand = detail::null_strand; + +inline bool operator==(null_strand const& lhs, null_strand const& rhs) { + return std::addressof(lhs) == std::addressof(rhs); +} + +inline bool operator!=(null_strand const& lhs, null_strand const& rhs) { + return !(lhs == rhs); +} } // namespace MQTT_NS +namespace boost { +namespace asio { + +template<> +struct is_executor : std::true_type { +}; + +} // namespace asio +} // namespace boost + #endif // MQTT_NULL_STRAND_HPP diff --git a/include/mqtt/server.hpp b/include/mqtt/server.hpp index 1cdc6b99f..1e8c85fdc 100644 --- a/include/mqtt/server.hpp +++ b/include/mqtt/server.hpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace MQTT_NS { @@ -53,7 +54,7 @@ class server { * After this handler called, the next accept will automatically start. * @param ep endpoint of the connecting client */ - using accept_handler = std::function ep)>; + using accept_handler = move_only_function ep)>; /** * @brief Error handler during after accepted before connection established @@ -61,7 +62,7 @@ class server { * @param ec error code * @param ioc_con io_context for incoming connection */ - using connection_error_handler = std::function; + using connection_error_handler = move_only_function; /** * @brief Error handler for listen and accpet @@ -69,7 +70,7 @@ class server { * You need to call listen() again if you want to restart accepting. * @param ec error code */ - using error_handler = std::function; + using error_handler = move_only_function; /** * @brief Error handler for listen and accpet @@ -78,7 +79,7 @@ class server { * @param ec error code * @param ioc_con io_context for listen or accept */ - using error_handler_with_ioc = std::function; + using error_handler_with_ioc = move_only_function; template server( @@ -119,7 +120,7 @@ class server { server( AsioEndpoint&& ep, as::io_context& ioc_accept, - std::function ioc_con_getter, + move_only_function ioc_con_getter, AcceptorConfig&& config = [](as::ip::tcp::acceptor&) {}) : ep_(std::forward(ep)), ioc_accept_(ioc_accept), @@ -173,7 +174,7 @@ class server { void set_error_handler(error_handler h) { h_error_ = [h = force_move(h)] - (error_code ec, as::io_context&) { + (error_code ec, as::io_context&) mutable { if (h) h(ec); }; } @@ -230,9 +231,9 @@ class server { as::ip::tcp::endpoint ep_; as::io_context& ioc_accept_; as::io_context* ioc_con_ = nullptr; - std::function ioc_con_getter_; + move_only_function ioc_con_getter_; optional acceptor_; - std::function config_; + move_only_function config_; bool close_request_{false}; accept_handler h_accept_; connection_error_handler h_connection_error_; @@ -258,7 +259,7 @@ class server_tls { * After this handler called, the next accept will automatically start. * @param ep endpoint of the connecting client */ - using accept_handler = std::function ep)>; + using accept_handler = move_only_function ep)>; /** * @brief Error handler during after accepted before connection established @@ -266,7 +267,7 @@ class server_tls { * @param ec error code * @param ioc_con io_context for incoming connection */ - using connection_error_handler = std::function; + using connection_error_handler = move_only_function; /** * @brief Error handler for listen and accpet @@ -274,7 +275,7 @@ class server_tls { * You need to call listen() again if you want to restart accepting. * @param ec error code */ - using error_handler = std::function; + using error_handler = move_only_function; /** * @brief Error handler for listen and accpet @@ -283,7 +284,7 @@ class server_tls { * @param ec error code * @param ioc_con io_context for listen or accept */ - using error_handler_with_ioc = std::function; + using error_handler_with_ioc = move_only_function; template server_tls( @@ -330,7 +331,7 @@ class server_tls { AsioEndpoint&& ep, tls::context&& ctx, as::io_context& ioc_accept, - std::function ioc_con_getter, + move_only_function ioc_con_getter, AcceptorConfig&& config = [](as::ip::tcp::acceptor&) {}) : ep_(std::forward(ep)), ioc_accept_(ioc_accept), @@ -385,7 +386,7 @@ class server_tls { void set_error_handler(error_handler h) { h_error_ = [h = force_move(h)] - (error_code ec, as::io_context&) { + (error_code ec, as::io_context&) mutable { if (h) h(ec); }; } @@ -445,10 +446,10 @@ class server_tls { return ctx_; } - using verify_cb_t = std::function> const&) >; + using verify_cb_t = move_only_function> const&) >; void set_verify_callback(verify_cb_t verify_cb) { - verify_cb_with_username_ = verify_cb; + verify_cb_with_username_ = force_move(verify_cb); } private: @@ -551,9 +552,9 @@ class server_tls { as::ip::tcp::endpoint ep_; as::io_context& ioc_accept_; as::io_context* ioc_con_ = nullptr; - std::function ioc_con_getter_; + move_only_function ioc_con_getter_; optional acceptor_; - std::function config_; + move_only_function config_; bool close_request_{false}; accept_handler h_accept_; connection_error_handler h_connection_error_; @@ -582,7 +583,7 @@ class server_ws { * @brief Accept handler * @param ep endpoint of the connecting client */ - using accept_handler = std::function ep)>; + using accept_handler = move_only_function ep)>; /** * @brief Error handler during after accepted before connection established @@ -590,7 +591,7 @@ class server_ws { * @param ec error code * @param ioc_con io_context for incoming connection */ - using connection_error_handler = std::function; + using connection_error_handler = move_only_function; /** * @brief Error handler for listen and accpet @@ -598,7 +599,7 @@ class server_ws { * You need to call listen() again if you want to restart accepting. * @param ec error code */ - using error_handler = std::function; + using error_handler = move_only_function; /** * @brief Error handler for listen and accpet @@ -607,7 +608,7 @@ class server_ws { * @param ec error code * @param ioc_con io_context for listen or accept */ - using error_handler_with_ioc = std::function; + using error_handler_with_ioc = move_only_function; template server_ws( @@ -648,7 +649,7 @@ class server_ws { server_ws( AsioEndpoint&& ep, as::io_context& ioc_accept, - std::function ioc_con_getter, + move_only_function ioc_con_getter, AcceptorConfig&& config = [](as::ip::tcp::acceptor&) {}) : ep_(std::forward(ep)), ioc_accept_(ioc_accept), @@ -701,7 +702,7 @@ class server_ws { void set_error_handler(error_handler h) { h_error_ = [h = force_move(h)] - (error_code ec, as::io_context&) { + (error_code ec, as::io_context&) mutable { if (h) h(ec); }; } @@ -917,9 +918,9 @@ class server_ws { as::ip::tcp::endpoint ep_; as::io_context& ioc_accept_; as::io_context* ioc_con_ = nullptr; - std::function ioc_con_getter_; + move_only_function ioc_con_getter_; optional acceptor_; - std::function config_; + move_only_function config_; bool close_request_{false}; accept_handler h_accept_; connection_error_handler h_connection_error_; @@ -946,7 +947,7 @@ class server_tls_ws { * @brief Accept handler * @param ep endpoint of the connecting client */ - using accept_handler = std::function ep)>; + using accept_handler = move_only_function ep)>; /** * @brief Error handler during after accepted before connection established @@ -954,7 +955,7 @@ class server_tls_ws { * @param ec error code * @param ioc_con io_context for incoming connection */ - using connection_error_handler = std::function; + using connection_error_handler = move_only_function; /** * @brief Error handler for listen and accpet @@ -962,7 +963,7 @@ class server_tls_ws { * You need to call listen() again if you want to restart accepting. * @param ec error code */ - using error_handler = std::function; + using error_handler = move_only_function; /** * @brief Error handler for listen and accpet @@ -971,7 +972,7 @@ class server_tls_ws { * @param ec error code * @param ioc_con io_context for listen or accept */ - using error_handler_with_ioc = std::function; + using error_handler_with_ioc = move_only_function; template server_tls_ws( @@ -1018,7 +1019,7 @@ class server_tls_ws { AsioEndpoint&& ep, tls::context&& ctx, as::io_context& ioc_accept, - std::function ioc_con_getter, + move_only_function ioc_con_getter, AcceptorConfig&& config = [](as::ip::tcp::acceptor&) {}) : ep_(std::forward(ep)), ioc_accept_(ioc_accept), @@ -1073,7 +1074,7 @@ class server_tls_ws { void set_error_handler(error_handler h) { h_error_ = [h = force_move(h)] - (error_code ec, as::io_context&) { + (error_code ec, as::io_context&) mutable { if (h) h(ec); }; } @@ -1133,10 +1134,10 @@ class server_tls_ws { return ctx_; } - using verify_cb_t = std::function> const&) >; + using verify_cb_t = move_only_function> const&) >; void set_verify_callback(verify_cb_t verify_cb) { - verify_cb_with_username_ = verify_cb; + verify_cb_with_username_ = force_move(verify_cb); } private: @@ -1361,9 +1362,9 @@ class server_tls_ws { as::ip::tcp::endpoint ep_; as::io_context& ioc_accept_; as::io_context* ioc_con_ = nullptr; - std::function ioc_con_getter_; + move_only_function ioc_con_getter_; optional acceptor_; - std::function config_; + move_only_function config_; bool close_request_{false}; accept_handler h_accept_; connection_error_handler h_connection_error_; diff --git a/include/mqtt/store.hpp b/include/mqtt/store.hpp index 717ad4f44..3fa0cdcc9 100644 --- a/include/mqtt/store.hpp +++ b/include/mqtt/store.hpp @@ -18,6 +18,7 @@ #include #include #include +#include namespace MQTT_NS { @@ -84,10 +85,10 @@ class store { } void for_each( - std::function< + move_only_function< // if return true, then erase element bool(basic_store_message_variant const&, any const&) - > const& f + > f ) { auto& idx = elems_.template get(); auto it = idx.begin(); diff --git a/include/mqtt/strand.hpp b/include/mqtt/strand.hpp index fa83f45d1..0b0e29e73 100644 --- a/include/mqtt/strand.hpp +++ b/include/mqtt/strand.hpp @@ -18,6 +18,6 @@ namespace as = boost::asio; using strand = as::strand; -} +} // namespace MQTT_NS #endif // MQTT_STRAND_HPP diff --git a/include/mqtt/tcp_endpoint.hpp b/include/mqtt/tcp_endpoint.hpp index 295ea3109..295294d71 100644 --- a/include/mqtt/tcp_endpoint.hpp +++ b/include/mqtt/tcp_endpoint.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace MQTT_NS { @@ -32,7 +33,7 @@ class tcp_endpoint : public socket { MQTT_ALWAYS_INLINE void async_read( as::mutable_buffer buffers, - std::function handler + move_only_function handler ) override final { as::async_read( tcp_, @@ -46,7 +47,7 @@ class tcp_endpoint : public socket { MQTT_ALWAYS_INLINE void async_write( std::vector buffers, - std::function handler + move_only_function handler ) override final { as::async_write( tcp_, @@ -65,21 +66,21 @@ class tcp_endpoint : public socket { return as::write(tcp_,force_move(buffers), ec); } - MQTT_ALWAYS_INLINE void post(std::function handler) override final { + MQTT_ALWAYS_INLINE void post(move_only_function handler) override final { as::post( strand_, force_move(handler) ); } - MQTT_ALWAYS_INLINE void dispatch(std::function handler) override final { + MQTT_ALWAYS_INLINE void dispatch(move_only_function handler) override final { as::dispatch( strand_, force_move(handler) ); } - MQTT_ALWAYS_INLINE void defer(std::function handler) override final { + MQTT_ALWAYS_INLINE void defer(move_only_function handler) override final { as::defer( strand_, force_move(handler) @@ -102,7 +103,7 @@ class tcp_endpoint : public socket { shutdown_and_close_impl(tcp_, ec); } - MQTT_ALWAYS_INLINE void async_clean_shutdown_and_close(std::function handler) override final { + MQTT_ALWAYS_INLINE void async_clean_shutdown_and_close(move_only_function handler) override final { async_shutdown_and_close_impl(tcp_, force_move(handler)); } @@ -156,7 +157,7 @@ class tcp_endpoint : public socket { << ec.message(); } - void async_shutdown_and_close_impl(as::basic_socket& s, std::function handler) { + void async_shutdown_and_close_impl(as::basic_socket& s, move_only_function handler) { post( [this, &s, handler = force_move(handler)] () mutable { error_code ec; @@ -175,7 +176,7 @@ class tcp_endpoint : public socket { << ec.message(); shutdown_and_close_impl(lowest_layer(), ec); } - void async_shutdown_and_close_impl(tls::stream& s, std::function handler) { + void async_shutdown_and_close_impl(tls::stream& s, move_only_function handler) { s.async_shutdown( as::bind_executor( strand_, diff --git a/include/mqtt/type_erased_socket.hpp b/include/mqtt/type_erased_socket.hpp index dffd29223..4041311dc 100644 --- a/include/mqtt/type_erased_socket.hpp +++ b/include/mqtt/type_erased_socket.hpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace MQTT_NS { @@ -22,17 +23,17 @@ namespace as = boost::asio; class socket { public: virtual ~socket() = default; - virtual void async_read(as::mutable_buffer, std::function) = 0; - virtual void async_write(std::vector, std::function) = 0; + virtual void async_read(as::mutable_buffer, move_only_function) = 0; + virtual void async_write(std::vector, move_only_function) = 0; virtual std::size_t write(std::vector, boost::system::error_code&) = 0; - virtual void post(std::function) = 0; - virtual void dispatch(std::function) = 0; - virtual void defer(std::function) = 0; + virtual void post(move_only_function) = 0; + virtual void dispatch(move_only_function) = 0; + virtual void defer(move_only_function) = 0; virtual bool running_in_this_thread() const = 0; virtual as::ip::tcp::socket::lowest_layer_type& lowest_layer() = 0; virtual any native_handle() = 0; virtual void clean_shutdown_and_close(boost::system::error_code&) = 0; - virtual void async_clean_shutdown_and_close(std::function) = 0; + virtual void async_clean_shutdown_and_close(move_only_function) = 0; virtual void force_shutdown_and_close(boost::system::error_code&) = 0; virtual as::any_io_executor get_executor() = 0; }; diff --git a/include/mqtt/ws_endpoint.hpp b/include/mqtt/ws_endpoint.hpp index 7b5025de6..9d784ea86 100644 --- a/include/mqtt/ws_endpoint.hpp +++ b/include/mqtt/ws_endpoint.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include namespace MQTT_NS { @@ -44,12 +45,12 @@ class ws_endpoint : public socket { MQTT_ALWAYS_INLINE void async_read( as::mutable_buffer buffers, - std::function handler + move_only_function handler ) override final { auto req_size = as::buffer_size(buffers); using beast_read_handler_t = - std::function)>; + move_only_function)>; std::shared_ptr beast_read_handler; if (req_size <= buffer_.size()) { @@ -107,7 +108,7 @@ class ws_endpoint : public socket { MQTT_ALWAYS_INLINE void async_write( std::vector buffers, - std::function handler + move_only_function handler ) override final { ws_.async_write( buffers, @@ -126,21 +127,21 @@ class ws_endpoint : public socket { return as::buffer_size(buffers); } - MQTT_ALWAYS_INLINE void post(std::function handler) override final { + MQTT_ALWAYS_INLINE void post(move_only_function handler) override final { as::post( strand_, force_move(handler) ); } - MQTT_ALWAYS_INLINE void dispatch(std::function handler) override final { + MQTT_ALWAYS_INLINE void dispatch(move_only_function handler) override final { as::dispatch( strand_, force_move(handler) ); } - MQTT_ALWAYS_INLINE void defer(std::function handler) override final { + MQTT_ALWAYS_INLINE void defer(move_only_function handler) override final { as::defer( strand_, force_move(handler) @@ -187,7 +188,7 @@ class ws_endpoint : public socket { shutdown_and_close_impl(next_layer(), ec); } - MQTT_ALWAYS_INLINE void async_clean_shutdown_and_close(std::function handler) override final { + MQTT_ALWAYS_INLINE void async_clean_shutdown_and_close(move_only_function handler) override final { if (ws_.is_open()) { // WebSocket closing process MQTT_LOG("mqtt_impl", trace) @@ -272,7 +273,7 @@ class ws_endpoint : public socket { } private: - void async_read_until_closed(std::function handler) { + void async_read_until_closed(move_only_function handler) { auto buffer = std::make_shared(); ws_.async_read( *buffer, @@ -311,7 +312,7 @@ class ws_endpoint : public socket { << ec.message(); } - void async_shutdown_and_close_impl(as::basic_socket& s, std::function handler) { + void async_shutdown_and_close_impl(as::basic_socket& s, move_only_function handler) { post( [this, &s, handler = force_move(handler)] () mutable { error_code ec; @@ -330,7 +331,7 @@ class ws_endpoint : public socket { << ec.message(); shutdown_and_close_impl(lowest_layer(), ec); } - void async_shutdown_and_close_impl(tls::stream& s, std::function handler) { + void async_shutdown_and_close_impl(tls::stream& s, move_only_function handler) { s.async_shutdown( as::bind_executor( strand_, diff --git a/test/system/st_offline.cpp b/test/system/st_offline.cpp index fb60d0199..35d75e54d 100644 --- a/test/system/st_offline.cpp +++ b/test/system/st_offline.cpp @@ -545,12 +545,12 @@ BOOST_AUTO_TEST_CASE( async_publish_qos1 ) { "topic1", "topic1_contents", MQTT_NS::qos::at_least_once | MQTT_NS::retain::no, - [&chk](MQTT_NS::error_code ec){ + [&](MQTT_NS::error_code ec){ BOOST_TEST( ! ec); MQTT_CHK("h_pub_finish"); + async_connect_no_clean(c); } ); - async_connect_no_clean(c); }, [&] { MQTT_CHK("h_close2"); diff --git a/test/system/st_pubsub_no_strand.cpp b/test/system/st_pubsub_no_strand.cpp index d9795f838..500f68f42 100644 --- a/test/system/st_pubsub_no_strand.cpp +++ b/test/system/st_pubsub_no_strand.cpp @@ -78,13 +78,11 @@ BOOST_AUTO_TEST_CASE( pub_qos0_sub_qos0 ) { () { MQTT_CHK("h_close"); finish(); - return true; }); c->set_error_handler( [] (MQTT_NS::error_code) { BOOST_CHECK(false); - return true; }); c->set_puback_handler( [] diff --git a/update_external.sh b/update_external.sh new file mode 100755 index 000000000..0cf05898d --- /dev/null +++ b/update_external.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +git submodule update --init +cp external/function2/include/function2/function2.hpp include/mqtt/external/