From e879df37f6b47ff5d9ed31f8876f695ee0d5df51 Mon Sep 17 00:00:00 2001
From: Takatoshi Kondo <redboltz@gmail.com>
Date: Fri, 7 Oct 2022 18:45:03 +0900
Subject: [PATCH 1/7] Introduced move_only_function.

---
 .gitmodules                          |    3 +
 include/CMakeLists.txt               |    2 +-
 include/mqtt/broker/topic_filter.hpp |    1 +
 include/mqtt/callable_overlay.hpp    |   95 +-
 include/mqtt/endpoint.hpp            |  177 +--
 include/mqtt/external/function2.hpp  | 1829 ++++++++++++++++++++++++++
 include/mqtt/move_only_function.hpp  |   30 +
 include/mqtt/null_strand.hpp         |   72 +-
 include/mqtt/server.hpp              |   73 +-
 include/mqtt/store.hpp               |    5 +-
 include/mqtt/strand.hpp              |    2 +-
 include/mqtt/tcp_endpoint.hpp        |   17 +-
 include/mqtt/type_erased_socket.hpp  |   13 +-
 include/mqtt/ws_endpoint.hpp         |   21 +-
 test/system/st_offline.cpp           |    4 +-
 test/system/st_pubsub_no_strand.cpp  |    2 -
 update_external.sh                   |    4 +
 17 files changed, 2151 insertions(+), 199 deletions(-)
 create mode 100644 include/mqtt/external/function2.hpp
 create mode 100644 include/mqtt/move_only_function.hpp
 create mode 100755 update_external.sh

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/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 $<IF:$<CXX_COMPILER_ID:MSVC>,/Zs,-fsyntax-only>)
+TARGET_COMPILE_OPTIONS(check_deps PUBLIC -Wno-unneeded-internal-declaration -Wno-unused-const-variable $<IF:$<CXX_COMPILER_ID:MSVC>,/Zs,-fsyntax-only>)
 
 TARGET_LINK_LIBRARIES (check_deps PUBLIC ${PROJECT_NAME})
 
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 <algorithm>
+#include <limits>
 
 #include <mqtt/broker/broker_namespace.hpp>
 #include <mqtt/string_view.hpp>
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 <mqtt/attributes.hpp>
 #include <mqtt/endpoint.hpp>
 #include <mqtt/move.hpp>
+#include <mqtt/move_only_function.hpp>
 
 namespace MQTT_NS {
 template<typename Impl>
@@ -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<bool()>;
+    using pingreq_handler = move_only_function<bool()>;
 
     /**
      * @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<bool()>;
+    using pingresp_handler = move_only_function<bool()>;
 
 
     // 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<buffer> user_name,
              optional<buffer> 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<bool(bool session_present, connect_return_code return_code)>;
+    using connack_handler = move_only_function<bool(bool session_present, connect_return_code return_code)>;
 
     /**
      * @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<bool(optional<packet_id_t> packet_id,
+    using publish_handler = move_only_function<bool(optional<packet_id_t> 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<bool(packet_id_t packet_id)>;
+    using puback_handler = move_only_function<bool(packet_id_t packet_id)>;
 
     /**
      * @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<bool(packet_id_t packet_id)>;
+    using pubrec_handler = move_only_function<bool(packet_id_t packet_id)>;
 
     /**
      * @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<bool(packet_id_t packet_id)>;
+    using pubrel_handler = move_only_function<bool(packet_id_t packet_id)>;
 
     /**
      * @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<bool(packet_id_t packet_id)>;
+    using pubcomp_handler = move_only_function<bool(packet_id_t packet_id)>;
 
     /**
      * @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<BR>
      * @return if the handler returns true, then continue receiving, otherwise quit.
      */
-    using subscribe_handler = std::function<bool(packet_id_t packet_id,
+    using subscribe_handler = move_only_function<bool(packet_id_t packet_id,
                                                  std::vector<subscribe_entry> 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<BR>
      * @return if the handler returns true, then continue receiving, otherwise quit.
      */
-    using suback_handler = std::function<bool(packet_id_t packet_id,
+    using suback_handler = move_only_function<bool(packet_id_t packet_id,
                                               std::vector<suback_return_code> 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<BR>
      * @return if the handler returns true, then continue receiving, otherwise quit.
      */
-    using unsubscribe_handler = std::function<bool(packet_id_t packet_id,
+    using unsubscribe_handler = move_only_function<bool(packet_id_t packet_id,
                                                    std::vector<unsubscribe_entry> 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<bool(packet_id_t)>;
+    using unsuback_handler = move_only_function<bool(packet_id_t)>;
 
     /**
      * @brief Disconnect handler
      *        See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc384800463<BR>
      *        3.14 DISCONNECT – Disconnect notification
      */
-    using disconnect_handler = std::function<void()>;
+    using disconnect_handler = move_only_function<void()>;
 
     // 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<buffer> user_name,
              optional<buffer> 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_t> 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<subscribe_entry> 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<v5::suback_reason_code> 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<unsubscribe_entry> 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<v5::unsuback_reason_code> 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<BR>
      *        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<void()>;
+    using close_handler = move_only_function<void()>;
 
     /**
      * @brief Error handler
@@ -1284,7 +1285,7 @@ struct callable_overlay final : public Impl
      *
      * @param ec error code
      */
-    using error_handler = std::function<void(error_code ec)>;
+    using error_handler = move_only_function<void(error_code ec)>;
 
     /**
      * @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<BR>
      *        2.2.1 Packet Identifier
      */
-    using pub_res_sent_handler = std::function<void(packet_id_t packet_id)>;
+    using pub_res_sent_handler = move_only_function<void(packet_id_t packet_id)>;
 
     /**
      * @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<void(basic_publish_message<sizeof(packet_id_t)> msg)>;
+    using serialize_publish_message_handler = move_only_function<void(basic_publish_message<sizeof(packet_id_t)> 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<void(v5::basic_publish_message<sizeof(packet_id_t)> msg)>;
+    using serialize_v5_publish_message_handler = move_only_function<void(v5::basic_publish_message<sizeof(packet_id_t)> 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<void(packet_id_t packet_id, char const* data, std::size_t size)>;
+    using serialize_publish_handler = move_only_function<void(packet_id_t packet_id, char const* data, std::size_t size)>;
 
     /**
      * @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<void(basic_pubrel_message<sizeof(packet_id_t)> msg)>;
+    using serialize_pubrel_message_handler = move_only_function<void(basic_pubrel_message<sizeof(packet_id_t)> 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<void(v5::basic_pubrel_message<sizeof(packet_id_t)> msg)>;
+    using serialize_v5_pubrel_message_handler = move_only_function<void(v5::basic_pubrel_message<sizeof(packet_id_t)> 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<void(packet_id_t packet_id, char const* data, std::size_t size)>;
+    using serialize_pubrel_handler = move_only_function<void(packet_id_t packet_id, char const* data, std::size_t size)>;
 
     /**
      * @brief Remove serialized message
      * @param packet_id packet identifier of the removing message
      */
-    using serialize_remove_handler = std::function<void(packet_id_t packet_id)>;
+    using serialize_remove_handler = move_only_function<void(packet_id_t packet_id)>;
 
     /**
      * @brief Pre-send handler
      *        This handler is called when any mqtt control packet is decided to send.
      */
-    using pre_send_handler = std::function<void()>;
+    using pre_send_handler = move_only_function<void()>;
 
     /**
      * @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<bool(control_packet_type packet_type, std::size_t remaining_length)>;
+        move_only_function<bool(control_packet_type packet_type, std::size_t remaining_length)>;
 
     /**
      * @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<void(any session_life_keeper)>;
+        move_only_function<void(any session_life_keeper)>;
 
 
 
@@ -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<sizeof(packet_id_t)> msg) {
+            (basic_publish_message<sizeof(packet_id_t)> 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<sizeof(packet_id_t)> msg) {
+            (basic_pubrel_message<sizeof(packet_id_t)> 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<sizeof(packet_id_t)> msg) {
+            (v5::basic_publish_message<sizeof(packet_id_t)> 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<sizeof(packet_id_t)> msg) {
+            (v5::basic_pubrel_message<sizeof(packet_id_t)> 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/endpoint.hpp b/include/mqtt/endpoint.hpp
index 179cfa2b9..49dd21e25 100644
--- a/include/mqtt/endpoint.hpp
+++ b/include/mqtt/endpoint.hpp
@@ -71,6 +71,7 @@
 #include <mqtt/shared_subscriptions.hpp>
 #include <mqtt/packet_id_manager.hpp>
 #include <mqtt/store.hpp>
+#include <mqtt/move_only_function.hpp>
 
 #if defined(MQTT_USE_WS)
 #include <mqtt/ws_endpoint.hpp>
@@ -173,7 +174,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
     using this_type_sp = std::shared_ptr<this_type>;
 
 public:
-    using async_handler_t = std::function<void(error_code ec)>;
+    using async_handler_t = move_only_function<void(error_code ec)>;
     using packet_id_t = typename packet_id_type<PacketIdBytes>::type;
 
     /**
@@ -2229,7 +2230,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
         }
         else {
             socket_->post(
-                [func = force_move(func)] {
+                [func = force_move(func)] () mutable {
                     if (func) func(boost::system::errc::make_error_code(boost::system::errc::success));
                 }
             );
@@ -2268,7 +2269,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
         }
         else {
             socket_->post(
-                [func = force_move(func)] {
+                [func = force_move(func)] () mutable {
                     if (func) func(boost::system::errc::make_error_code(boost::system::errc::success));
                 }
             );
@@ -2756,7 +2757,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             packet_id,
             v5::properties{},
             [life_keeper = force_move(sp_topic_filter), func = force_move(func)]
-            (error_code ec) {
+            (error_code ec) mutable {
                 if(func) func(ec);
             }
         );
@@ -2807,7 +2808,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             packet_id,
             force_move(props),
             [life_keeper = force_move(sp_topic_filter), func = force_move(func)]
-            (error_code ec) {
+            (error_code ec) mutable {
                 if(func) func(ec);
             }
         );
@@ -2938,7 +2939,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             packet_id,
             v5::properties{},
             [life_keeper = force_move(topic_filter), func = force_move(func)]
-            (error_code ec) {
+            (error_code ec) mutable {
                 if(func) func(ec);
             }
         );
@@ -2987,7 +2988,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             packet_id,
             force_move(props),
             [life_keeper = force_move(topic_filter), func = force_move(func)]
-            (error_code ec) {
+            (error_code ec) mutable {
                 if(func) func(ec);
             }
         );
@@ -3034,7 +3035,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             packet_id,
             v5::properties{},
             [life_keeper = force_move(life_keepers), func = force_move(func)]
-            (error_code ec) {
+            (error_code ec) mutable {
                 if(func) func(ec);
             }
         );
@@ -3085,7 +3086,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             packet_id,
             force_move(props),
             [life_keeper = force_move(life_keepers), func = force_move(func)]
-            (error_code ec) {
+            (error_code ec) mutable {
                 if(func) func(ec);
             }
         );
@@ -3191,7 +3192,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             packet_id,
             v5::properties{},
             [life_keeper = force_move(params), func = force_move(func)]
-            (error_code ec) {
+            (error_code ec) mutable {
                 if(func) func(ec);
             }
         );
@@ -3237,7 +3238,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             packet_id,
             force_move(props),
             [life_keeper = force_move(params), func = force_move(func)]
-            (error_code ec) {
+            (error_code ec) mutable {
                 if(func) func(ec);
             }
         );
@@ -3272,7 +3273,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             packet_id,
             v5::properties{},
             [life_keeper = force_move(sp_topic_filter), func = force_move(func)]
-            (error_code ec) {
+            (error_code ec) mutable {
                 if(func) func(ec);
             }
         );
@@ -3331,7 +3332,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                                packet_id,
                                v5::properties{},
                                [life_keeper = force_move(topic_filter), func = force_move(func)]
-                               (error_code ec) {
+                               (error_code ec) mutable {
                                    if(func) func(ec);
                                });
     }
@@ -3368,7 +3369,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                                packet_id,
                                force_move(props),
                                [life_keeper = force_move(topic_filter), func = force_move(func)]
-                               (error_code ec) {
+                               (error_code ec) mutable {
                                    if(func) func(ec);
                                });
     }
@@ -3411,7 +3412,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             packet_id,
             v5::properties{},
             [life_keeper = force_move(life_keepers), func = force_move(func)]
-            (error_code ec) {
+            (error_code ec) mutable {
                 if(func) func(ec);
             }
         );
@@ -3465,7 +3466,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             packet_id,
             force_move(props),
             [life_keeper = force_move(life_keepers), func = force_move(func)]
-            (error_code ec) {
+            (error_code ec) mutable {
                 if(func) func(ec);
             }
         );
@@ -3571,7 +3572,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             packet_id,
             v5::properties{},
             [life_keeper = force_move(params), func = force_move(func)]
-            (error_code ec) {
+            (error_code ec) mutable {
                 if(func) func(ec);
             }
         );
@@ -3615,7 +3616,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             packet_id,
             force_move(props),
             [life_keeper = force_move(params), func = force_move(func)]
-            (error_code ec) {
+            (error_code ec) mutable {
                 if(func) func(ec);
             }
         );
@@ -4322,16 +4323,17 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
      * @brief Apply f to stored messages.
      * @param f applying function. f should be void(char const*, std::size_t)
      */
-    void for_each_store(std::function<void(char const*, std::size_t)> const& f) {
+    void for_each_store(move_only_function<void(char const*, std::size_t)> f) {
         MQTT_LOG("mqtt_api", info)
             << MQTT_ADD_VALUE(address, this)
             << "for_each_store(ptr, size)";
         LockGuard<Mutex> lck (store_mtx_);
         store_.for_each(
-            [f](
+            [f = force_move(f)]
+            (
                 basic_store_message_variant<PacketIdBytes> const& message,
                 any const& /*life_keeper*/
-            ) {
+            ) mutable {
                 auto cb = continuous_buffer(message);
                 f(cb.data(), cb.size());
                 return false; // no erase
@@ -4343,16 +4345,17 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
      * @brief Apply f to stored messages.
      * @param f applying function. f should be void(store_message_variant)
      */
-    void for_each_store(std::function<void(basic_store_message_variant<PacketIdBytes>)> const& f) {
+    void for_each_store(move_only_function<void(basic_store_message_variant<PacketIdBytes>)> f) {
         MQTT_LOG("mqtt_api", info)
             << MQTT_ADD_VALUE(address, this)
             << "for_each_store(store_message_variant)";
         LockGuard<Mutex> lck (store_mtx_);
         store_.for_each(
-            [f](
+            [f = force_move(f)]
+            (
                 basic_store_message_variant<PacketIdBytes> const& message,
                 any const& /*life_keeper*/
-            ) {
+            ) mutable {
                 f(message);
                 return false; // no erase
             }
@@ -4363,17 +4366,18 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
      * @brief Apply f to stored messages.
      * @param f applying function. f should be void(store_message_variant, any)
      */
-    void for_each_store_with_life_keeper(std::function<void(basic_store_message_variant<PacketIdBytes>, any)> const& f) {
+    void for_each_store_with_life_keeper(move_only_function<void(basic_store_message_variant<PacketIdBytes>, any)> f) {
         MQTT_LOG("mqtt_api", info)
             << MQTT_ADD_VALUE(address, this)
 
             << "for_each_store(store_message_variant, life_keeper)";
         LockGuard<Mutex> lck (store_mtx_);
         store_.for_each(
-            [f](
+            [f = force_move(f)]
+            (
                 basic_store_message_variant<PacketIdBytes> const& message,
                 any const& life_keeper
-            ) {
+            ) mutable {
                 f(message, life_keeper);
                 return false; // no erase
             }
@@ -4782,7 +4786,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                 auto msg_lk = apply_topic_alias(msg, life_keeper);
                 if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +4803,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                     do_async_write(
                         force_move(std::get<0>(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 +4814,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             [&](auto msg, auto const& serialize) {
                 if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +4863,14 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                     publish_proc(
                         force_move(m),
                         &endpoint::on_serialize_v5_publish_message,
-                        [this, func] (v5::basic_publish_message<PacketIdBytes>&& msg) mutable {
+                        [this, func = force_move(func)] (v5::basic_publish_message<PacketIdBytes>&& msg) mutable {
                             if (publish_send_count_.load() == publish_send_max_) {
                                 {
                                     LockGuard<Mutex> 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 +5369,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                     std::back_inserter(handlers)
                 );
                 async_shutdown_handler_queue_.clear();
-                for (auto const& h : handlers) {
+                for (auto& h : handlers) {
                     if (h) h(boost::system::errc::make_error_code(boost::system::errc::success));
                 }
                 async_shutdown_handler_called_ = true;
@@ -5745,7 +5749,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
         v5::properties
     >;
     using parse_handler =
-        std::function<
+        move_only_function<
             void(
                 this_type_sp&& spep,
                 any&& session_life_keeper,
@@ -10492,7 +10496,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             );
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +10516,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             );
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +10547,8 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
         async_handler_t func
     ) {
         auto do_async_send_publish =
-            [&](auto msg, auto&& serialize_publish, auto&& receive_maximum_proc) {
+            [&, func = force_move(func)]
+            (auto msg, auto&& serialize_publish, auto&& receive_maximum_proc) mutable {
                 auto msg_lk = apply_topic_alias(msg, life_keeper);
                 if (maximum_packet_size_send_ < size<PacketIdBytes>(std::get<0>(msg_lk))) {
                     if (packet_id != 0) {
@@ -10551,7 +10556,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                         pid_man_.release_id(packet_id);
                     }
                     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));
                         }
                     );
@@ -10561,12 +10566,18 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                         msg,
                         life_keeper,
                         std::forward<decltype(serialize_publish)>(serialize_publish),
-                        std::forward<decltype(receive_maximum_proc)>(receive_maximum_proc)
+                        [
+                            &func,
+                            receive_maximum_proc = std::forward<decltype(receive_maximum_proc)>(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 +10594,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                     pubopts
                 ),
                 &endpoint::on_serialize_publish_message,
-                [] (auto&&) { return true; }
+                [] (auto&&, async_handler_t&) { return true; }
             );
             break;
         case protocol_version::v5:
@@ -10596,14 +10607,14 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                     force_move(props)
                 ),
                 &endpoint::on_serialize_v5_publish_message,
-                [this, func] (v5::basic_publish_message<PacketIdBytes>&& msg) mutable {
+                [this] (v5::basic_publish_message<PacketIdBytes>&& msg, async_handler_t& func) mutable {
                     if (publish_send_count_.load() == publish_send_max_) {
                         {
                             LockGuard<Mutex> 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 +10646,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v3_1_1::basic_puback_message<PacketIdBytes>(packet_id);
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +10655,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             do_async_write(
                 force_move(msg),
                 [this, self = this->shared_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 +10665,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v5::basic_puback_message<PacketIdBytes>(packet_id, reason, force_move(props));
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +10674,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             do_async_write(
                 force_move(msg),
                 [this, self = this->shared_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 +10698,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v3_1_1::basic_pubrec_message<PacketIdBytes>(packet_id);
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +10713,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v5::basic_pubrec_message<PacketIdBytes>(packet_id, reason, force_move(props));
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +10722,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             do_async_write(
                 force_move(msg),
                 [this, self = this->shared_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 +10749,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                 {
                     if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +10788,8 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
 
                 do_async_write(
                     force_move(msg),
-                    [life_keeper = force_move(life_keeper), func = force_move(func)](error_code ec) {
+                    [life_keeper = force_move(life_keeper), func = force_move(func)]
+                    (error_code ec) mutable {
                         if(func) func(ec);
                     }
                 );
@@ -10813,7 +10825,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v3_1_1::basic_pubcomp_message<PacketIdBytes>(packet_id);
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +10834,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             do_async_write(
                 force_move(msg),
                 [this, self = this->shared_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 +10844,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v5::basic_pubcomp_message<PacketIdBytes>(packet_id, reason, force_move(props));
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +10853,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             do_async_write(
                 force_move(msg),
                 [this, self = this->shared_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 +10880,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                     pid_man_.release_id(packet_id);
                 }
                 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));
                     }
                 );
@@ -10891,7 +10903,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                     pid_man_.release_id(packet_id);
                 }
                 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));
                     }
                 );
@@ -10926,7 +10938,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             );
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +10957,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             );
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +10989,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                     pid_man_.release_id(packet_id);
                 }
                 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));
                     }
                 );
@@ -11000,7 +11012,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                     pid_man_.release_id(packet_id);
                 }
                 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));
                     }
                 );
@@ -11030,7 +11042,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v3_1_1::basic_unsuback_message<PacketIdBytes>(packet_id);
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +11076,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v5::basic_unsuback_message<PacketIdBytes>(force_move(params), packet_id, force_move(props));
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +11099,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v3_1_1::pingreq_message();
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +11112,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v5::pingreq_message();
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +11133,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v3_1_1::pingresp_message();
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +11145,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v5::pingresp_message();
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +11172,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v5::auth_message(reason, force_move(props));
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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));
                     }
                 );
@@ -11184,7 +11196,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v3_1_1::disconnect_message();
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +11208,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             auto msg = v5::disconnect_message(reason, force_move(props));
             if (maximum_packet_size_send_ < size<PacketIdBytes>(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 +11222,14 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
         }
     }
 
-    void async_send_store(std::function<void()> func) {
+    template <typename Func>
+    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>(func)] () mutable {
                 func();
             }
-        );
+        )};
         LockGuard<Mutex> lck (store_mtx_);
         if (store_.empty()) {
             socket().post(
@@ -11359,7 +11372,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             // it's a bug if the handler is invalid when constructed.
             BOOST_ASSERT(func_);
         }
-        void operator()(error_code ec) const {
+        void operator()(error_code ec) {
             func_(ec);
             for (std::size_t i = 0; i != num_of_messages_; ++i) {
                 self_->queue_.pop_front();
@@ -11379,7 +11392,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
         }
         void operator()(
             error_code ec,
-            std::size_t bytes_transferred) const {
+            std::size_t bytes_transferred) {
             func_(ec);
             self_->total_bytes_sent_ += bytes_transferred;
             for (std::size_t i = 0; i != num_of_messages_; ++i) {
@@ -11418,7 +11431,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
         std::size_t iterator_count = (max_queue_send_count_ == 0)
                                 ? queue_.size()
                                 : std::min(max_queue_send_count_, queue_.size());
-        auto const& start = queue_.cbegin();
+        auto start = queue_.begin();
         auto end = std::next(start, boost::numeric_cast<difference_t>(iterator_count));
 
         // And further, only up to the specified maximum bytes
@@ -11446,11 +11459,11 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
         handlers.reserve(iterator_count);
 
         for (auto it = start; it != end; ++it) {
-            auto const& elem = *it;
+            auto& elem = *it;
             auto const& mv = elem.message();
             auto const& cbs = const_buffer_sequence(mv);
             std::copy(cbs.begin(), cbs.end(), std::back_inserter(buf));
-            handlers.emplace_back(elem.handler());
+            handlers.emplace_back(force_move(elem.handler()));
         }
 
         on_pre_send();
@@ -11460,8 +11473,8 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
             write_completion_handler(
                 this->shared_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 <denis.blank at outlook dot com>
+//     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 <cassert>
+#include <cstddef>
+#include <cstdlib>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+// 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 <functional>
+#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 <exception>
+#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 <typename Config, typename Property>
+class function;
+
+template <typename...>
+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 <typename...>
+struct deduce_to_void : std::common_type<void> {};
+
+template <typename... T>
+using void_t = typename deduce_to_void<T...>::type;
+
+template <typename T>
+using unrefcv_t = std::remove_cv_t<std::remove_reference_t<T>>;
+
+template <typename...>
+struct lazy_and;
+
+template <typename B1>
+struct lazy_and<B1> : B1 {};
+
+template <typename B1, typename B2>
+struct lazy_and<B1, B2> : std::conditional<B1::value, B2, B1>::type {};
+
+// template <typename B1, typename B2, typename B3, typename... Bn>
+// struct lazy_and<B1, B2, B3, Bn...>
+//     : std::conditional<B1::value, lazy_and<B2, B3, Bn...>, B1>::type {};
+
+// Copy enabler helper class
+template <bool /*Copyable*/>
+struct copyable {};
+template <>
+struct copyable<false> {
+  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 <bool Owning, bool Copyable, typename Capacity>
+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 <bool Throws, bool HasStrongExceptGuarantee, typename... Args>
+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 <typename Callable, typename... Args>
+constexpr auto invoke(Callable&& callable, Args&&... args) noexcept(
+    noexcept(std::forward<Callable>(callable)(std::forward<Args>(args)...)))
+    -> decltype(std::forward<Callable>(callable)(std::forward<Args>(args)...)) {
+
+  return std::forward<Callable>(callable)(std::forward<Args>(args)...);
+}
+/// Invokes the given member function pointer by reference
+template <typename T, typename Type, typename Self, typename... Args>
+constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept(
+    noexcept((std::forward<Self>(self).*member)(std::forward<Args>(args)...)))
+    -> decltype((std::forward<Self>(self).*
+                 member)(std::forward<Args>(args)...)) {
+  return (std::forward<Self>(self).*member)(std::forward<Args>(args)...);
+}
+/// Invokes the given member function pointer by pointer
+template <typename T, typename Type, typename Self, typename... Args>
+constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept(
+    noexcept((std::forward<Self>(self)->*member)(std::forward<Args>(args)...)))
+    -> decltype((std::forward<Self>(self)->*member)(
+        std::forward<Args>(args)...)) {
+  return (std::forward<Self>(self)->*member)(std::forward<Args>(args)...);
+}
+/// Invokes the given pointer to a scalar member by reference
+template <typename T, typename Type, typename Self>
+constexpr auto
+invoke(Type T::*member,
+       Self&& self) noexcept(noexcept(std::forward<Self>(self).*member))
+    -> decltype(std::forward<Self>(self).*member) {
+  return (std::forward<Self>(self).*member);
+}
+/// Invokes the given pointer to a scalar member by pointer
+template <typename T, typename Type, typename Self>
+constexpr auto
+invoke(Type T::*member,
+       Self&& self) noexcept(noexcept(std::forward<Self>(self)->*member))
+    -> decltype(std::forward<Self>(self)->*member) {
+  return std::forward<Self>(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 <typename T, typename Args, typename = void>
+struct can_invoke : std::false_type {};
+template <typename T, typename... Args>
+struct can_invoke<T, identity<Args...>,
+                  decltype((void)std::declval<T>()(std::declval<Args>()...))>
+    : std::true_type {};
+template <typename Pointer, typename T, typename... Args>
+struct can_invoke<Pointer, identity<T&, Args...>,
+                  decltype((void)((std::declval<T&>().*std::declval<Pointer>())(
+                      std::declval<Args>()...)))> : std::true_type {};
+template <typename Pointer, typename T, typename... Args>
+struct can_invoke<Pointer, identity<T&&, Args...>,
+                  decltype((
+                      void)((std::declval<T&&>().*std::declval<Pointer>())(
+                      std::declval<Args>()...)))> : std::true_type {};
+template <typename Pointer, typename T, typename... Args>
+struct can_invoke<Pointer, identity<T*, Args...>,
+                  decltype((
+                      void)((std::declval<T*>()->*std::declval<Pointer>())(
+                      std::declval<Args>()...)))> : std::true_type {};
+template <typename Pointer, typename T>
+struct can_invoke<Pointer, identity<T&>,
+                  decltype((void)(std::declval<T&>().*std::declval<Pointer>()))>
+    : std::true_type {};
+template <typename Pointer, typename T>
+struct can_invoke<Pointer, identity<T&&>,
+                  decltype((void)(std::declval<T&&>().*
+                                  std::declval<Pointer>()))> : std::true_type {
+};
+template <typename Pointer, typename T>
+struct can_invoke<Pointer, identity<T*>,
+                  decltype((
+                      void)(std::declval<T*>()->*std::declval<Pointer>()))>
+    : std::true_type {};
+
+template <bool RequiresNoexcept, typename T, typename Args>
+struct is_noexcept_correct : std::true_type {};
+template <typename T, typename... Args>
+struct is_noexcept_correct<true, T, identity<Args...>>
+    : std::integral_constant<bool,
+                             noexcept(::fu2::detail::invocation::invoke(
+                                 std::declval<T>(), std::declval<Args>()...))> {
+};
+} // end namespace invocation
+
+namespace overloading {
+template <typename... Args>
+struct overload_impl;
+template <typename Current, typename Next, typename... Rest>
+struct overload_impl<Current, Next, Rest...> : Current,
+                                               overload_impl<Next, Rest...> {
+  explicit overload_impl(Current current, Next next, Rest... rest)
+      : Current(std::move(current)), overload_impl<Next, Rest...>(
+                                         std::move(next), std::move(rest)...) {
+  }
+
+  using Current::operator();
+  using overload_impl<Next, Rest...>::operator();
+};
+template <typename Current>
+struct overload_impl<Current> : Current {
+  explicit overload_impl(Current current) : Current(std::move(current)) {
+  }
+
+  using Current::operator();
+};
+
+template <typename... T>
+constexpr auto overload(T&&... callables) {
+  return overload_impl<std::decay_t<T>...>{std::forward<T>(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 <typename T, typename = void>
+struct address_taker {
+  template <typename O>
+  static void* take(O&& obj) {
+    return std::addressof(obj);
+  }
+  static T& restore(void* ptr) {
+    return *static_cast<T*>(ptr);
+  }
+  static T const& restore(void const* ptr) {
+    return *static_cast<T const*>(ptr);
+  }
+  static T volatile& restore(void volatile* ptr) {
+    return *static_cast<T volatile*>(ptr);
+  }
+  static T const volatile& restore(void const volatile* ptr) {
+    return *static_cast<T const volatile*>(ptr);
+  }
+};
+/// Specialization to work with addresses of raw function pointers
+template <typename T>
+struct address_taker<T, std::enable_if_t<std::is_pointer<T>::value>> {
+  template <typename O>
+  static void* take(O&& obj) {
+    return reinterpret_cast<void*>(obj);
+  }
+  template <typename O>
+  static T restore(O ptr) {
+    return reinterpret_cast<T>(const_cast<void*>(ptr));
+  }
+};
+
+template <typename Box>
+struct box_factory;
+/// Store the allocator inside the box
+template <bool IsCopyable, typename T, typename Allocator>
+struct box : private Allocator {
+  friend box_factory<box>;
+
+  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 <typename T, typename Allocator>
+struct box<false, T, Allocator> : private Allocator {
+  friend box_factory<box>;
+
+  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 <bool IsCopyable, typename T, typename Allocator>
+struct box_factory<box<IsCopyable, T, Allocator>> {
+  using real_allocator =
+      typename std::allocator_traits<std::decay_t<Allocator>>::
+          template rebind_alloc<box<IsCopyable, T, Allocator>>;
+
+  /// Allocates space through the boxed allocator
+  static box<IsCopyable, T, Allocator>*
+  box_allocate(box<IsCopyable, T, Allocator> const* me) {
+    real_allocator allocator_(*static_cast<Allocator const*>(me));
+
+    return static_cast<box<IsCopyable, T, Allocator>*>(
+        std::allocator_traits<real_allocator>::allocate(allocator_, 1U));
+  }
+
+  /// Destroys the box through the given allocator
+  static void box_deallocate(box<IsCopyable, T, Allocator>* me) {
+    real_allocator allocator_(*static_cast<Allocator const*>(me));
+
+    me->~box();
+    std::allocator_traits<real_allocator>::deallocate(allocator_, me, 1U);
+  }
+};
+
+/// Creates a box containing the given value and allocator
+template <bool IsCopyable, typename T, typename Allocator>
+auto make_box(std::integral_constant<bool, IsCopyable>, T&& value,
+              Allocator&& allocator_) {
+  return box<IsCopyable, std::decay_t<T>, std::decay_t<Allocator>>(
+      std::forward<T>(value), std::forward<Allocator>(allocator_));
+}
+
+template <typename T>
+struct is_box : std::false_type {};
+template <bool IsCopyable, typename T, typename Allocator>
+struct is_box<box<IsCopyable, T, Allocator>> : 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 <typename From, typename To>
+using transfer_const_t =
+    std::conditional_t<std::is_const<std::remove_pointer_t<From>>::value,
+                       std::add_const_t<To>, To>;
+template <typename From, typename To>
+using transfer_volatile_t =
+    std::conditional_t<std::is_volatile<std::remove_pointer_t<From>>::value,
+                       std::add_volatile_t<To>, To>;
+
+/// The retriever when the object is allocated inplace
+template <typename T, typename Accessor>
+FU2_DETAIL_CXX14_CONSTEXPR auto retrieve(std::true_type /*is_inplace*/,
+                                         Accessor from,
+                                         std::size_t from_capacity) {
+  using type = transfer_const_t<Accessor, transfer_volatile_t<Accessor, void>>*;
+
+  /// Process the command by using the data inside the internal capacity
+  auto storage = &(from->inplace_storage_);
+  auto inplace = const_cast<void*>(static_cast<type>(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 <typename T, typename Accessor>
+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 <bool IsNoexcept>
+[[noreturn]] void throw_or_abortnoexcept(
+    std::integral_constant<bool, IsNoexcept> /*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 <typename T>
+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 <typename Ret, typename... Args>                                    \
+  struct function_trait<Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> {        \
+    using pointer_type = Ret (*)(data_accessor CONST VOLATILE*,                \
+                                 std::size_t capacity, Args...);               \
+    template <typename T, bool IsInplace>                                      \
+    struct internal_invoker {                                                  \
+      static Ret invoke(data_accessor CONST VOLATILE* data,                    \
+                        std::size_t capacity, Args... args) NOEXCEPT {         \
+        auto obj = retrieve<T>(std::integral_constant<bool, IsInplace>{},      \
+                               data, capacity);                                \
+        auto box = static_cast<T CONST VOLATILE*>(obj);                        \
+        return invocation::invoke(                                             \
+            static_cast<std::decay_t<decltype(box->value_)> CONST VOLATILE     \
+                            REF>(box->value_),                                 \
+            std::forward<Args>(args)...);                                      \
+      }                                                                        \
+    };                                                                         \
+                                                                               \
+    template <typename T>                                                      \
+    struct view_invoker {                                                      \
+      static Ret invoke(data_accessor CONST VOLATILE* data, std::size_t,       \
+                        Args... args) NOEXCEPT {                               \
+                                                                               \
+        auto ptr = static_cast<void CONST VOLATILE*>(data->ptr_);              \
+        return invocation::invoke(address_taker<T>::restore(ptr),              \
+                                  std::forward<Args>(args)...);                \
+      }                                                                        \
+    };                                                                         \
+                                                                               \
+    template <typename T>                                                      \
+    using callable = T CONST VOLATILE REF;                                     \
+                                                                               \
+    using arguments = identity<Args...>;                                       \
+                                                                               \
+    using is_noexcept = is_noexcept_##NOEXCEPT;                                \
+                                                                               \
+    template <bool Throws>                                                     \
+    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<bool, Throws>{});      \
+      }                                                                        \
+    };                                                                         \
+  };
+
+FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
+#undef FU2_DEFINE_FUNCTION_TRAIT
+
+/// Deduces to the function pointer to the given signature
+template <typename Signature>
+using function_pointer_of = typename function_trait<Signature>::pointer_type;
+
+template <typename... Args>
+struct invoke_table;
+
+/// We optimize the vtable_t in case there is a single function overload
+template <typename First>
+struct invoke_table<First> {
+  using type = function_pointer_of<First>;
+
+  /// Return the function pointer itself
+  template <std::size_t Index>
+  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 <typename T, bool IsInplace>
+  static constexpr type get_invocation_table_of() noexcept {
+    return &function_trait<First>::template internal_invoker<T,
+                                                             IsInplace>::invoke;
+  }
+  /// Returns the thunk of an single overloaded callable
+  template <typename T>
+  static constexpr type get_invocation_view_table_of() noexcept {
+    return &function_trait<First>::template view_invoker<T>::invoke;
+  }
+  /// Returns the thunk of an empty single overloaded callable
+  template <bool IsThrowing>
+  static constexpr type get_empty_invocation_table() noexcept {
+    return &function_trait<First>::template empty_invoker<IsThrowing>::invoke;
+  }
+};
+/// We generate a table in case of multiple function overloads
+template <typename First, typename Second, typename... Args>
+struct invoke_table<First, Second, Args...> {
+  using type =
+      std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
+                 function_pointer_of<Args>...> const*;
+
+  /// Return the function pointer at the particular index
+  template <std::size_t Index>
+  static constexpr auto fetch(type table) noexcept {
+    return std::get<Index>(*table);
+  }
+
+  /// The invocation vtable for a present object
+  template <typename T, bool IsInplace>
+  struct invocation_vtable : public std::tuple<function_pointer_of<First>,
+                                               function_pointer_of<Second>,
+                                               function_pointer_of<Args>...> {
+    constexpr invocation_vtable() noexcept
+        : std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
+                     function_pointer_of<Args>...>(std::make_tuple(
+              &function_trait<First>::template internal_invoker<
+                  T, IsInplace>::invoke,
+              &function_trait<Second>::template internal_invoker<
+                  T, IsInplace>::invoke,
+              &function_trait<Args>::template internal_invoker<
+                  T, IsInplace>::invoke...)) {
+    }
+  };
+
+  /// Returns the thunk of an multi overloaded callable
+  template <typename T, bool IsInplace>
+  static type get_invocation_table_of() noexcept {
+    static invocation_vtable<T, IsInplace> const table;
+    return &table;
+  }
+
+  /// The invocation vtable for a present object
+  template <typename T>
+  struct invocation_view_vtable
+      : public std::tuple<function_pointer_of<First>,
+                          function_pointer_of<Second>,
+                          function_pointer_of<Args>...> {
+    constexpr invocation_view_vtable() noexcept
+        : std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
+                     function_pointer_of<Args>...>(std::make_tuple(
+              &function_trait<First>::template view_invoker<T>::invoke,
+              &function_trait<Second>::template view_invoker<T>::invoke,
+              &function_trait<Args>::template view_invoker<T>::invoke...)) {
+    }
+  };
+
+  /// Returns the thunk of an multi overloaded callable
+  template <typename T>
+  static type get_invocation_view_table_of() noexcept {
+    static invocation_view_vtable<T> const table;
+    return &table;
+  }
+
+  /// The invocation table for an empty wrapper
+  template <bool IsThrowing>
+  struct empty_vtable : public std::tuple<function_pointer_of<First>,
+                                          function_pointer_of<Second>,
+                                          function_pointer_of<Args>...> {
+    constexpr empty_vtable() noexcept
+        : std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
+                     function_pointer_of<Args>...>(
+              std::make_tuple(&function_trait<First>::template empty_invoker<
+                                  IsThrowing>::invoke,
+                              &function_trait<Second>::template empty_invoker<
+                                  IsThrowing>::invoke,
+                              &function_trait<Args>::template empty_invoker<
+                                  IsThrowing>::invoke...)) {
+    }
+  };
+
+  /// Returns the thunk of an multi single overloaded callable
+  template <bool IsThrowing>
+  static type get_empty_invocation_table() noexcept {
+    static empty_vtable<IsThrowing> const table;
+    return &table;
+  }
+};
+
+template <std::size_t Index, typename Function, typename... Signatures>
+class operator_impl;
+
+#define FU2_DEFINE_FUNCTION_TRAIT(CONST, VOLATILE, NOEXCEPT, OVL_REF, REF)     \
+  template <std::size_t Index, typename Function, typename Ret,                \
+            typename... Args, typename Next, typename... Signatures>           \
+  class operator_impl<Index, Function,                                         \
+                      Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT, Next,      \
+                      Signatures...>                                           \
+      : operator_impl<Index + 1, Function, Next, Signatures...> {              \
+                                                                               \
+    template <std::size_t, typename, typename...>                              \
+    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<Index + 1, Function, Next, Signatures...>::operator(); \
+                                                                               \
+    Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT {             \
+      auto parent = static_cast<Function CONST VOLATILE*>(this);               \
+      using erasure_t = std::decay_t<decltype(parent->erasure_)>;              \
+                                                                               \
+      /* `std::decay_t<decltype(parent->erasure_)>` is a workaround for a   */ \
+      /* compiler regression of MSVC 16.3.1, see #29 for details.           */ \
+      return std::decay_t<decltype(parent->erasure_)>::template invoke<Index>( \
+          static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_),         \
+          std::forward<Args>(args)...);                                        \
+    }                                                                          \
+  };                                                                           \
+  template <std::size_t Index, typename Config, typename Property,             \
+            typename Ret, typename... Args>                                    \
+  class operator_impl<Index, function<Config, Property>,                       \
+                      Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT>            \
+      : copyable<!Config::is_owning || Config::is_copyable> {                  \
+                                                                               \
+    template <std::size_t, typename, typename...>                              \
+    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<function<Config, Property> CONST VOLATILE*>(this);       \
+      using erasure_t = std::decay_t<decltype(parent->erasure_)>;              \
+                                                                               \
+      /* `std::decay_t<decltype(parent->erasure_)>` is a workaround for a   */ \
+      /* compiler regression of MSVC 16.3.1, see #29 for details.           */ \
+      return std::decay_t<decltype(parent->erasure_)>::template invoke<Index>( \
+          static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_),         \
+          std::forward<Args>(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 <typename Property>
+class vtable;
+template <bool IsThrowing, bool HasStrongExceptGuarantee,
+          typename... FormalArgs>
+class vtable<property<IsThrowing, HasStrongExceptGuarantee, FormalArgs...>> {
+  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<FormalArgs...>;
+
+  command_function_t cmd_;
+  typename invoke_table_t::type vtable_;
+
+  template <typename T>
+  struct trait {
+    static_assert(is_box<T>::value,
+                  "The trait must be specialized with a box!");
+
+    /// The command table
+    template <bool IsInplace>
+    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<T*>(retrieve<T>(
+              std::integral_constant<bool, IsInplace>{}, 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<T>();
+
+          }
+          // 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<T const*>(retrieve<T>(
+              std::integral_constant<bool, IsInplace>{}, from, from_capacity));
+          assert(box && "The object must not be over aligned or null!");
+
+          assert(std::is_copy_constructible<T>::value &&
+                 "The box is required to be copyable here!");
+
+          // Try to allocate the object inplace
+          construct(std::is_copy_constructible<T>{}, *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<T*>(retrieve<T>(
+              std::integral_constant<bool, IsInplace>{}, from, from_capacity));
+
+          if (IsInplace) {
+            box->~T();
+          } else {
+            box_factory<T>::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 <typename Box>
+    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<T>(std::true_type{}, to, to_capacity);
+      if (storage) {
+        to_table->template set_inplace<T>();
+      } else {
+        // Allocate the object through the allocator
+        to->ptr_ = storage =
+            box_factory<std::decay_t<Box>>::box_allocate(std::addressof(box));
+        to_table->template set_allocated<T>();
+      }
+      new (storage) T(std::forward<Box>(box));
+    }
+
+    template <typename Box>
+    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 <typename T>
+  static void init(vtable& table, T&& object, data_accessor* to,
+                   std::size_t to_capacity) {
+
+    trait<std::decay_t<T>>::construct(std::true_type{}, std::forward<T>(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<data_accessor*>(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 <std::size_t Index, typename... Args>
+  constexpr decltype(auto) invoke(Args&&... args) const {
+    auto thunk = invoke_table_t::template fetch<Index>(vtable_);
+    return thunk(std::forward<Args>(args)...);
+  }
+  /// Invoke the function at the given index
+  template <std::size_t Index, typename... Args>
+  constexpr decltype(auto) invoke(Args&&... args) const volatile {
+    auto thunk = invoke_table_t::template fetch<Index>(vtable_);
+    return thunk(std::forward<Args>(args)...);
+  }
+
+  template <typename T>
+  void set_inplace() noexcept {
+    using type = std::decay_t<T>;
+    vtable_ = invoke_table_t::template get_invocation_table_of<type, true>();
+    cmd_ = &trait<type>::template process_cmd<true>;
+  }
+
+  template <typename T>
+  void set_allocated() noexcept {
+    using type = std::decay_t<T>;
+    vtable_ = invoke_table_t::template get_invocation_table_of<type, false>();
+    cmd_ = &trait<type>::template process_cmd<false>;
+  }
+
+  void set_empty() noexcept {
+    vtable_ = invoke_table_t::template get_empty_invocation_table<IsThrowing>();
+    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 <typename Capacity, typename = void>
+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::capacity, Capacity::alignment> capacity_;
+  } type;
+};
+template <typename Capacity>
+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 <typename Capacity>
+class internal_capacity_holder {
+  // Tag to access the structure in a type-safe way
+  typename internal_capacity<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 <bool IsOwning /* = true*/, typename Config, typename Property>
+class erasure : internal_capacity_holder<typename Config::capacity> {
+  template <bool, typename, typename>
+  friend class erasure;
+  template <std::size_t, typename, typename...>
+  friend class operator_impl;
+
+  using vtable_t = tables::vtable<Property>;
+
+  vtable_t vtable_;
+
+public:
+  /// Returns the capacity of this erasure
+  static constexpr std::size_t capacity() noexcept {
+    return internal_capacity_holder<typename Config::capacity>::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 <typename OtherConfig>
+  FU2_DETAIL_CXX14_CONSTEXPR
+  erasure(erasure<true, OtherConfig, Property> right) noexcept(
+      Property::is_strong_exception_guaranteed) {
+    right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(),
+                       this->opaque_ptr(), capacity());
+  }
+
+  template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
+  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<bool, Config::is_copyable>{},
+                       std::forward<T>(callable),
+                       std::forward<Allocator>(allocator_)),
+                   this->opaque_ptr(), capacity());
+  }
+  template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
+  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<bool, Config::is_copyable>{},
+                         std::forward<T>(callable),
+                         std::forward<Allocator>(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 <typename OtherConfig>
+  FU2_DETAIL_CXX14_CONSTEXPR erasure&
+  operator=(erasure<true, OtherConfig, Property> 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 <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
+  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<bool, Config::is_copyable>{},
+                       std::forward<T>(callable),
+                       std::forward<Allocator>(allocator_)),
+                   this->opaque_ptr(), capacity());
+  }
+
+  template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
+  void assign(std::true_type /*use_bool_op*/, T&& callable,
+              Allocator&& allocator_ = {}) {
+    if (bool(callable)) {
+      assign(std::false_type{}, std::forward<T>(callable),
+             std::forward<Allocator>(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 <std::size_t Index, typename Erasure, typename... Args>
+  static constexpr decltype(auto) invoke(Erasure&& erasure, Args&&... args) {
+    auto const capacity = erasure.capacity();
+    return erasure.vtable_.template invoke<Index>(
+        std::forward<Erasure>(erasure).opaque_ptr(), capacity,
+        std::forward<Args>(args)...);
+  }
+};
+
+// A non owning erasure
+template </*bool IsOwning = false, */ typename Config, bool IsThrowing,
+          bool HasStrongExceptGuarantee, typename... Args>
+class erasure<false, Config,
+              property<IsThrowing, HasStrongExceptGuarantee, Args...>> {
+  template <bool, typename, typename>
+  friend class erasure;
+  template <std::size_t, typename, typename...>
+  friend class operator_impl;
+
+  using property_t = property<IsThrowing, HasStrongExceptGuarantee, Args...>;
+
+  using invoke_table_t = invocation_table::invoke_table<Args...>;
+  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<IsThrowing>()),
+        view_(nullptr) {
+  }
+
+  // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
+  constexpr erasure(std::nullptr_t) noexcept
+      : invoke_table_(
+            invoke_table_t::template get_empty_invocation_table<IsThrowing>()),
+        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 <typename OtherConfig>
+  // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
+  constexpr erasure(erasure<false, OtherConfig, property_t> right) noexcept
+      : invoke_table_(right.invoke_table_), view_(right.view_) {
+  }
+
+  template <typename T>
+  // 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<T>>()),
+        view_(address_taker<std::decay_t<T>>::take(std::forward<T>(object))) {
+  }
+  template <typename T>
+  // 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<T>(object));
+  }
+
+  ~erasure() = default;
+
+  constexpr erasure&
+  operator=(std::nullptr_t) noexcept(HasStrongExceptGuarantee) {
+    invoke_table_ =
+        invoke_table_t::template get_empty_invocation_table<IsThrowing>();
+    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 <typename OtherConfig>
+  constexpr erasure&
+  operator=(erasure<true, OtherConfig, property_t> right) noexcept {
+    invoke_table_ = right.invoke_table_;
+    view_ = right.view_;
+    return *this;
+  }
+
+  template <typename T>
+  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<T>>();
+    view_.ptr_ =
+        address_taker<std::decay_t<T>>::take(std::forward<T>(callable));
+  }
+  template <typename T>
+  constexpr void assign(std::true_type /*use_bool_op*/, T&& callable) {
+    if (bool(callable)) {
+      assign(std::false_type{}, std::forward<T>(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 <std::size_t Index, typename Erasure, typename... T>
+  static constexpr decltype(auto) invoke(Erasure&& erasure, T&&... args) {
+    auto thunk = invoke_table_t::template fetch<Index>(erasure.invoke_table_);
+    return thunk(&(erasure.view_), 0UL, std::forward<T>(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 <typename T, typename Signature,
+          typename Trait =
+              type_erasure::invocation_table::function_trait<Signature>>
+struct accepts_one
+    : detail::lazy_and< // both are std::integral_constant
+          invocation::can_invoke<typename Trait::template callable<T>,
+                                 typename Trait::arguments>,
+          invocation::is_noexcept_correct<Trait::is_noexcept::value,
+                                          typename Trait::template callable<T>,
+                                          typename Trait::arguments>> {};
+
+/// Deduces to a true_type if the type T provides all signatures
+template <typename T, typename Signatures, typename = void>
+struct accepts_all : std::false_type {};
+template <typename T, typename... Signatures>
+struct accepts_all<
+    T, identity<Signatures...>,
+    void_t<std::enable_if_t<accepts_one<T, Signatures>::value>...>>
+    : std::true_type {};
+
+#if defined(FU2_HAS_NO_EMPTY_PROPAGATION)
+template <typename T>
+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 <typename T>
+struct use_bool_op : std::false_type {};
+
+#if !defined(FU2_HAS_NO_FUNCTIONAL_HEADER)
+template <typename Signature>
+struct use_bool_op<std::function<Signature>> : std::true_type {};
+#endif
+
+template <typename Config, typename Property>
+struct use_bool_op<function<Config, Property>> : std::true_type {};
+
+template <typename T>
+struct use_bool_op<T*> : std::true_type {};
+
+template <typename Class, typename T>
+struct use_bool_op<T Class::*> : std::true_type {};
+#else
+template <typename T, typename = void>
+struct has_bool_op : std::false_type {};
+template <typename T>
+struct has_bool_op<T, void_t<decltype(bool(std::declval<T>()))>>
+    : std::true_type {
+#ifndef NDEBUG
+  static_assert(!std::is_pointer<T>::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 <typename T>
+struct use_bool_op : has_bool_op<T> {};
+
+#define FU2_DEFINE_USE_OP_TRAIT(CONST, VOLATILE, NOEXCEPT)                     \
+  template <typename Ret, typename... Args>                                    \
+  struct use_bool_op<Ret (*CONST VOLATILE)(Args...) NOEXCEPT>                  \
+      : std::true_type {};
+
+FU2_DETAIL_EXPAND_CV(FU2_DEFINE_USE_OP_TRAIT)
+#undef FU2_DEFINE_USE_OP_TRAIT
+
+template <typename Ret, typename... Args>
+struct use_bool_op<Ret(Args...)> : std::false_type {};
+
+#if defined(FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE)
+template <typename Ret, typename... Args>
+struct use_bool_op<Ret(Args...) noexcept> : std::false_type {};
+#endif
+#endif // FU2_HAS_NO_EMPTY_PROPAGATION
+
+template <typename Config, typename T>
+struct assert_wrong_copy_assign {
+  static_assert(!Config::is_owning || !Config::is_copyable ||
+                    std::is_copy_constructible<std::decay_t<T>>::value,
+                "Can't wrap a non copyable object into a unique function!");
+
+  using type = void;
+};
+
+template <bool IsStrongExceptGuaranteed, typename T>
+struct assert_no_strong_except_guarantee {
+  static_assert(
+      !IsStrongExceptGuaranteed ||
+          (std::is_nothrow_move_constructible<T>::value &&
+           std::is_nothrow_destructible<T>::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 <typename LeftConfig, typename RightConfig>
+using enable_if_copyable_correct_t =
+    std::enable_if_t<(!LeftConfig::is_copyable || RightConfig::is_copyable)>;
+
+template <typename LeftConfig, typename RightConfig>
+using is_owning_correct =
+    std::integral_constant<bool,
+                           (LeftConfig::is_owning == RightConfig::is_owning)>;
+
+/// SFINAES out if the given function2 is not owning correct to this one
+template <typename LeftConfig, typename RightConfig>
+using enable_if_owning_correct_t =
+    std::enable_if_t<is_owning_correct<LeftConfig, RightConfig>::value>;
+
+template <typename Config, bool IsThrowing, bool HasStrongExceptGuarantee,
+          typename... Args>
+class function<Config, property<IsThrowing, HasStrongExceptGuarantee, Args...>>
+    : type_erasure::invocation_table::operator_impl<
+          0U,
+          function<Config,
+                   property<IsThrowing, HasStrongExceptGuarantee, Args...>>,
+          Args...> {
+
+  template <typename, typename>
+  friend class function;
+
+  template <std::size_t, typename, typename...>
+  friend class type_erasure::invocation_table::operator_impl;
+
+  using property_t = property<IsThrowing, HasStrongExceptGuarantee, Args...>;
+  using erasure_t =
+      type_erasure::erasure<Config::is_owning, Config, property_t>;
+
+  template <typename T>
+  using enable_if_can_accept_all_t =
+      std::enable_if_t<accepts_all<std::decay_t<T>, identity<Args...>>::value>;
+
+  template <typename Function, typename = void>
+  struct is_convertible_to_this : std::false_type {};
+  template <typename RightConfig>
+  struct is_convertible_to_this<
+      function<RightConfig, property_t>,
+      void_t<enable_if_copyable_correct_t<Config, RightConfig>,
+             enable_if_owning_correct_t<Config, RightConfig>>>
+      : std::true_type {};
+
+  template <typename T>
+  using enable_if_not_convertible_to_this =
+      std::enable_if_t<!is_convertible_to_this<std::decay_t<T>>::value>;
+
+  template <typename T>
+  using enable_if_owning_t =
+      std::enable_if_t<std::is_same<T, T>::value && Config::is_owning>;
+
+  template <typename T>
+  using assert_wrong_copy_assign_t =
+      typename assert_wrong_copy_assign<Config, std::decay_t<T>>::type;
+
+  template <typename T>
+  using assert_no_strong_except_guarantee_t =
+      typename assert_no_strong_except_guarantee<HasStrongExceptGuarantee,
+                                                 std::decay_t<T>>::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 <typename RightConfig,
+            std::enable_if_t<RightConfig::is_copyable>* = nullptr,
+            enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
+            enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
+  FU2_DETAIL_CXX14_CONSTEXPR
+  function(function<RightConfig, property_t> const& right)
+      : erasure_(right.erasure_) {
+  }
+
+  /// Move construction from another function
+  template <typename RightConfig,
+            enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
+            enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
+  FU2_DETAIL_CXX14_CONSTEXPR function(function<RightConfig, property_t>&& right)
+      : erasure_(std::move(right.erasure_)) {
+  }
+
+  /// Construction from a callable object which overloads the `()` operator
+  template <typename T, //
+            enable_if_not_convertible_to_this<T>* = nullptr,
+            enable_if_can_accept_all_t<T>* = nullptr,
+            assert_wrong_copy_assign_t<T>* = nullptr,
+            assert_no_strong_except_guarantee_t<T>* = nullptr>
+  FU2_DETAIL_CXX14_CONSTEXPR function(T&& callable)
+      : erasure_(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable)) {
+  }
+  template <typename T, typename Allocator, //
+            enable_if_not_convertible_to_this<T>* = nullptr,
+            enable_if_can_accept_all_t<T>* = nullptr,
+            enable_if_owning_t<T>* = nullptr,
+            assert_wrong_copy_assign_t<T>* = nullptr,
+            assert_no_strong_except_guarantee_t<T>* = nullptr>
+  FU2_DETAIL_CXX14_CONSTEXPR function(T&& callable, Allocator&& allocator_)
+      : erasure_(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable),
+                 std::forward<Allocator>(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 <typename RightConfig,
+            std::enable_if_t<RightConfig::is_copyable>* = nullptr,
+            enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
+            enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
+  function& operator=(function<RightConfig, property_t> const& right) {
+    erasure_ = right.erasure_;
+    return *this;
+  }
+
+  /// Move assigning from another function
+  template <typename RightConfig,
+            enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
+            enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
+  function& operator=(function<RightConfig, property_t>&& right) {
+    erasure_ = std::move(right.erasure_);
+    return *this;
+  }
+
+  /// Move assigning from a callable object
+  template <typename T, // ...
+            enable_if_not_convertible_to_this<T>* = nullptr,
+            enable_if_can_accept_all_t<T>* = nullptr,
+            assert_wrong_copy_assign_t<T>* = nullptr,
+            assert_no_strong_except_guarantee_t<T>* = nullptr>
+  function& operator=(T&& callable) {
+    erasure_.assign(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(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 <typename T, typename Allocator = std::allocator<std::decay_t<T>>,
+            enable_if_not_convertible_to_this<T>* = nullptr,
+            enable_if_can_accept_all_t<T>* = nullptr,
+            assert_wrong_copy_assign_t<T>* = nullptr,
+            assert_no_strong_except_guarantee_t<T>* = nullptr>
+  void assign(T&& callable, Allocator&& allocator_ = Allocator{}) {
+    erasure_.assign(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable),
+                    std::forward<Allocator>(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<Config, property_t>, Args...>::operator();
+};
+
+template <typename Config, typename Property>
+bool operator==(function<Config, Property> const& f, std::nullptr_t) {
+  return !bool(f);
+}
+
+template <typename Config, typename Property>
+bool operator!=(function<Config, Property> const& f, std::nullptr_t) {
+  return bool(f);
+}
+
+template <typename Config, typename Property>
+bool operator==(std::nullptr_t, function<Config, Property> const& f) {
+  return !bool(f);
+}
+
+template <typename Config, typename Property>
+bool operator!=(std::nullptr_t, function<Config, Property> const& f) {
+  return bool(f);
+}
+
+// Default intended object size of the function
+using object_size = std::integral_constant<std::size_t, 32U>;
+} // 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 <std::size_t Capacity,
+          std::size_t Alignment = alignof(std::max_align_t)>
+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<detail::object_size::value - (2 * sizeof(void*))> {};
+
+/// 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 <typename T>
+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 <bool IsOwning, bool IsCopyable, typename Capacity, bool IsThrowing,
+          bool HasStrongExceptGuarantee, typename... Signatures>
+using function_base = detail::function<
+    detail::config<IsOwning, IsCopyable, Capacity>,
+    detail::property<IsThrowing, HasStrongExceptGuarantee, Signatures...>>;
+
+/// An owning copyable function wrapper for arbitrary callable types.
+template <typename... Signatures>
+using function = function_base<true, true, capacity_default, //
+                               true, false, Signatures...>;
+
+/// An owning non copyable function wrapper for arbitrary callable types.
+template <typename... Signatures>
+using unique_function = function_base<true, false, capacity_default, //
+                                      true, false, Signatures...>;
+
+/// A non owning copyable function wrapper for arbitrary callable types.
+template <typename... Signatures>
+using function_view = function_base<false, true, capacity_default, //
+                                    true, false, Signatures...>;
+
+#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 <typename... T>
+constexpr auto overload(T&&... callables) {
+  return detail::overloading::overload(std::forward<T>(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/move_only_function.hpp b/include/mqtt/move_only_function.hpp
new file mode 100644
index 000000000..08b0513bb
--- /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
+
+#pragma GCC diagnostic push
+#if defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Waddress"
+#endif // defined(__GNUC__)
+
+#include <mqtt/external/function2.hpp>
+
+#include <mqtt/namespace.hpp>
+
+namespace MQTT_NS {
+
+template <typename... Params>
+using move_only_function = fu2::unique_function<Params...>;
+
+} // 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 <typename Executor>
+    explicit null_strand(Executor e) noexcept
+        : exe_{force_move(e)}
+    {}
+
+    template <typename Func, typename Allocator>
+    void defer(Func&& f, Allocator) const {
+        as::defer(
+            exe_,
+            [f = std::forward<Func>(f)] () mutable {
+                std::move(f)();
+            }
+        );
+    }
+    template <typename Func, typename Allocator>
+    void dispatch(Func&& f, Allocator) const {
+        as::dispatch(
+            exe_,
+            [f = std::forward<Func>(f)] () mutable {
+                std::move(f)();
+            }
+        );
+    }
+    template <typename Func, typename Allocator>
+    void post(Func&& f, Allocator) const {
+        as::post(
+            exe_,
+            [f = std::forward<Func>(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<MQTT_NS::null_strand> : 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 <mqtt/callable_overlay.hpp>
 #include <mqtt/strand.hpp>
 #include <mqtt/null_strand.hpp>
+#include <mqtt/move_only_function.hpp>
 
 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<void(std::shared_ptr<endpoint_t> ep)>;
+    using accept_handler = move_only_function<void(std::shared_ptr<endpoint_t> 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<void(error_code ec, as::io_context& ioc_con)>;
+    using connection_error_handler = move_only_function<void(error_code ec, as::io_context& ioc_con)>;
 
     /**
      * @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<void(error_code ec)>;
+    using error_handler = move_only_function<void(error_code ec)>;
 
     /**
      * @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<void(error_code ec, as::io_context& ioc_accept)>;
+    using error_handler_with_ioc = move_only_function<void(error_code ec, as::io_context& ioc_accept)>;
 
     template <typename AsioEndpoint, typename AcceptorConfig>
     server(
@@ -119,7 +120,7 @@ class server {
     server(
         AsioEndpoint&& ep,
         as::io_context& ioc_accept,
-        std::function<as::io_context&()> ioc_con_getter,
+        move_only_function<as::io_context&()> ioc_con_getter,
         AcceptorConfig&& config = [](as::ip::tcp::acceptor&) {})
         : ep_(std::forward<AsioEndpoint>(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<as::io_context&()> ioc_con_getter_;
+    move_only_function<as::io_context&()> ioc_con_getter_;
     optional<as::ip::tcp::acceptor> acceptor_;
-    std::function<void(as::ip::tcp::acceptor&)> config_;
+    move_only_function<void(as::ip::tcp::acceptor&)> 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<void(std::shared_ptr<endpoint_t> ep)>;
+    using accept_handler = move_only_function<void(std::shared_ptr<endpoint_t> 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<void(error_code ec, as::io_context& ioc_con)>;
+    using connection_error_handler = move_only_function<void(error_code ec, as::io_context& ioc_con)>;
 
     /**
      * @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<void(error_code ec)>;
+    using error_handler = move_only_function<void(error_code ec)>;
 
     /**
      * @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<void(error_code ec, as::io_context& ioc_accept)>;
+    using error_handler_with_ioc = move_only_function<void(error_code ec, as::io_context& ioc_accept)>;
 
     template <typename AsioEndpoint, typename AcceptorConfig>
     server_tls(
@@ -330,7 +331,7 @@ class server_tls {
         AsioEndpoint&& ep,
         tls::context&& ctx,
         as::io_context& ioc_accept,
-        std::function<as::io_context&()> ioc_con_getter,
+        move_only_function<as::io_context&()> ioc_con_getter,
         AcceptorConfig&& config = [](as::ip::tcp::acceptor&) {})
         : ep_(std::forward<AsioEndpoint>(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<bool (bool, boost::asio::ssl::verify_context&, std::shared_ptr<optional<std::string>> const&) >;
+    using verify_cb_t = move_only_function<bool (bool, boost::asio::ssl::verify_context&, std::shared_ptr<optional<std::string>> 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<as::io_context&()> ioc_con_getter_;
+    move_only_function<as::io_context&()> ioc_con_getter_;
     optional<as::ip::tcp::acceptor> acceptor_;
-    std::function<void(as::ip::tcp::acceptor&)> config_;
+    move_only_function<void(as::ip::tcp::acceptor&)> 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<void(std::shared_ptr<endpoint_t> ep)>;
+    using accept_handler = move_only_function<void(std::shared_ptr<endpoint_t> 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<void(error_code ec, as::io_context& ioc_con)>;
+    using connection_error_handler = move_only_function<void(error_code ec, as::io_context& ioc_con)>;
 
     /**
      * @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<void(error_code ec)>;
+    using error_handler = move_only_function<void(error_code ec)>;
 
     /**
      * @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<void(error_code ec, as::io_context& ioc_accept)>;
+    using error_handler_with_ioc = move_only_function<void(error_code ec, as::io_context& ioc_accept)>;
 
     template <typename AsioEndpoint, typename AcceptorConfig>
     server_ws(
@@ -648,7 +649,7 @@ class server_ws {
     server_ws(
         AsioEndpoint&& ep,
         as::io_context& ioc_accept,
-        std::function<as::io_context&()> ioc_con_getter,
+        move_only_function<as::io_context&()> ioc_con_getter,
         AcceptorConfig&& config = [](as::ip::tcp::acceptor&) {})
         : ep_(std::forward<AsioEndpoint>(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<as::io_context&()> ioc_con_getter_;
+    move_only_function<as::io_context&()> ioc_con_getter_;
     optional<as::ip::tcp::acceptor> acceptor_;
-    std::function<void(as::ip::tcp::acceptor&)> config_;
+    move_only_function<void(as::ip::tcp::acceptor&)> 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<void(std::shared_ptr<endpoint_t> ep)>;
+    using accept_handler = move_only_function<void(std::shared_ptr<endpoint_t> 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<void(error_code ec, as::io_context& ioc_con)>;
+    using connection_error_handler = move_only_function<void(error_code ec, as::io_context& ioc_con)>;
 
     /**
      * @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<void(error_code ec)>;
+    using error_handler = move_only_function<void(error_code ec)>;
 
     /**
      * @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<void(error_code ec, as::io_context& ioc_accept)>;
+    using error_handler_with_ioc = move_only_function<void(error_code ec, as::io_context& ioc_accept)>;
 
     template <typename AsioEndpoint, typename AcceptorConfig>
     server_tls_ws(
@@ -1018,7 +1019,7 @@ class server_tls_ws {
         AsioEndpoint&& ep,
         tls::context&& ctx,
         as::io_context& ioc_accept,
-        std::function<as::io_context&()> ioc_con_getter,
+        move_only_function<as::io_context&()> ioc_con_getter,
         AcceptorConfig&& config = [](as::ip::tcp::acceptor&) {})
         : ep_(std::forward<AsioEndpoint>(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<bool (bool, boost::asio::ssl::verify_context&, std::shared_ptr<optional<std::string>> const&) >;
+    using verify_cb_t = move_only_function<bool (bool, boost::asio::ssl::verify_context&, std::shared_ptr<optional<std::string>> 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<as::io_context&()> ioc_con_getter_;
+    move_only_function<as::io_context&()> ioc_con_getter_;
     optional<as::ip::tcp::acceptor> acceptor_;
-    std::function<void(as::ip::tcp::acceptor&)> config_;
+    move_only_function<void(as::ip::tcp::acceptor&)> 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 <mqtt/any.hpp>
 #include <mqtt/message_variant.hpp>
 #include <mqtt/packet_id_type.hpp>
+#include <mqtt/move_only_function.hpp>
 
 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<PacketIdBytes> const&, any const&)
-        > const& f
+        > f
     ) {
         auto& idx = elems_.template get<tag_seq>();
         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<as::io_context::executor_type>;
 
-}
+} // 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 <mqtt/move.hpp>
 #include <mqtt/attributes.hpp>
 #include <mqtt/tls.hpp>
+#include <mqtt/move_only_function.hpp>
 #include <mqtt/log.hpp>
 
 namespace MQTT_NS {
@@ -32,7 +33,7 @@ class tcp_endpoint : public socket {
 
     MQTT_ALWAYS_INLINE void async_read(
         as::mutable_buffer buffers,
-        std::function<void(error_code, std::size_t)> handler
+        move_only_function<void(error_code, std::size_t)> handler
     ) override final {
         as::async_read(
             tcp_,
@@ -46,7 +47,7 @@ class tcp_endpoint : public socket {
 
     MQTT_ALWAYS_INLINE void async_write(
         std::vector<as::const_buffer> buffers,
-        std::function<void(error_code, std::size_t)> handler
+        move_only_function<void(error_code, std::size_t)> 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<void()> handler) override final {
+    MQTT_ALWAYS_INLINE void post(move_only_function<void()> handler) override final {
         as::post(
             strand_,
             force_move(handler)
         );
     }
 
-    MQTT_ALWAYS_INLINE void dispatch(std::function<void()> handler) override final {
+    MQTT_ALWAYS_INLINE void dispatch(move_only_function<void()> handler) override final {
         as::dispatch(
             strand_,
             force_move(handler)
         );
     }
 
-    MQTT_ALWAYS_INLINE void defer(std::function<void()> handler) override final {
+    MQTT_ALWAYS_INLINE void defer(move_only_function<void()> 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<void(error_code)> handler) override final {
+    MQTT_ALWAYS_INLINE void async_clean_shutdown_and_close(move_only_function<void(error_code)> 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<boost::asio::ip::tcp>& s, std::function<void(error_code)> handler) {
+    void async_shutdown_and_close_impl(as::basic_socket<boost::asio::ip::tcp>& s, move_only_function<void(error_code)> 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<as::ip::tcp::socket>& s, std::function<void(error_code)> handler) {
+    void async_shutdown_and_close_impl(tls::stream<as::ip::tcp::socket>& s, move_only_function<void(error_code)> 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 <mqtt/namespace.hpp>
 #include <mqtt/error_code.hpp>
 #include <mqtt/any.hpp>
+#include <mqtt/move_only_function.hpp>
 
 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<void(error_code, std::size_t)>) = 0;
-    virtual void async_write(std::vector<as::const_buffer>, std::function<void(error_code, std::size_t)>) = 0;
+    virtual void async_read(as::mutable_buffer, move_only_function<void(error_code, std::size_t)>) = 0;
+    virtual void async_write(std::vector<as::const_buffer>, move_only_function<void(error_code, std::size_t)>) = 0;
     virtual std::size_t write(std::vector<as::const_buffer>, boost::system::error_code&) = 0;
-    virtual void post(std::function<void()>) = 0;
-    virtual void dispatch(std::function<void()>) = 0;
-    virtual void defer(std::function<void()>) = 0;
+    virtual void post(move_only_function<void()>) = 0;
+    virtual void dispatch(move_only_function<void()>) = 0;
+    virtual void defer(move_only_function<void()>) = 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<void(error_code)>) = 0;
+    virtual void async_clean_shutdown_and_close(move_only_function<void(error_code)>) = 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 <mqtt/string_view.hpp>
 #include <mqtt/error_code.hpp>
 #include <mqtt/tls.hpp>
+#include <mqtt/move_only_function.hpp>
 #include <mqtt/log.hpp>
 
 namespace MQTT_NS {
@@ -44,12 +45,12 @@ class ws_endpoint : public socket {
 
     MQTT_ALWAYS_INLINE void async_read(
         as::mutable_buffer buffers,
-        std::function<void(error_code, std::size_t)> handler
+        move_only_function<void(error_code, std::size_t)> handler
     ) override final {
         auto req_size = as::buffer_size(buffers);
 
         using beast_read_handler_t =
-            std::function<void(error_code ec, std::shared_ptr<void>)>;
+            move_only_function<void(error_code ec, std::shared_ptr<void>)>;
 
         std::shared_ptr<beast_read_handler_t> 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<as::const_buffer> buffers,
-        std::function<void(error_code, std::size_t)> handler
+        move_only_function<void(error_code, std::size_t)> 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<void()> handler) override final {
+    MQTT_ALWAYS_INLINE void post(move_only_function<void()> handler) override final {
         as::post(
             strand_,
             force_move(handler)
         );
     }
 
-    MQTT_ALWAYS_INLINE void dispatch(std::function<void()> handler) override final {
+    MQTT_ALWAYS_INLINE void dispatch(move_only_function<void()> handler) override final {
         as::dispatch(
             strand_,
             force_move(handler)
         );
     }
 
-    MQTT_ALWAYS_INLINE void defer(std::function<void()> handler) override final {
+    MQTT_ALWAYS_INLINE void defer(move_only_function<void()> 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<void(error_code)> handler) override final {
+    MQTT_ALWAYS_INLINE void async_clean_shutdown_and_close(move_only_function<void(error_code)> 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<void(error_code)> handler) {
+    void async_read_until_closed(move_only_function<void(error_code)> handler) {
         auto buffer = std::make_shared<boost::beast::flat_buffer>();
         ws_.async_read(
             *buffer,
@@ -311,7 +312,7 @@ class ws_endpoint : public socket {
             << ec.message();
     }
 
-    void async_shutdown_and_close_impl(as::basic_socket<boost::asio::ip::tcp>& s, std::function<void(error_code)> handler) {
+    void async_shutdown_and_close_impl(as::basic_socket<boost::asio::ip::tcp>& s, move_only_function<void(error_code)> 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<as::ip::tcp::socket>& s, std::function<void(error_code)> handler) {
+    void async_shutdown_and_close_impl(tls::stream<as::ip::tcp::socket>& s, move_only_function<void(error_code)> 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/

From ebfdba7283c6301a5056fe9e2689c4e4ff05cd0c Mon Sep 17 00:00:00 2001
From: Takatoshi Kondo <redboltz@gmail.com>
Date: Fri, 7 Oct 2022 19:56:59 +0900
Subject: [PATCH 2/7] Applied move_only_function for broker.

---
 include/mqtt/broker/broker.hpp        | 52 +++++++++++++--------------
 include/mqtt/broker/session_state.hpp |  6 ++--
 2 files changed, 29 insertions(+), 29 deletions(-)

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<void(v5::properties const&)> h) {
+    void set_connect_props_handler(move_only_function<void(v5::properties const&)> h) {
         h_connect_props_ = force_move(h);
     }
 
-    void set_disconnect_props_handler(std::function<void(v5::properties const&)> h) {
+    void set_disconnect_props_handler(move_only_function<void(v5::properties const&)> h) {
         h_disconnect_props_ = force_move(h);
     }
 
-    void set_publish_props_handler(std::function<void(v5::properties const&)> h) {
+    void set_publish_props_handler(move_only_function<void(v5::properties const&)> h) {
         h_publish_props_ = force_move(h);
     }
 
-    void set_puback_props_handler(std::function<void(v5::properties const&)> h) {
+    void set_puback_props_handler(move_only_function<void(v5::properties const&)> h) {
         h_puback_props_ = force_move(h);
     }
 
-    void set_pubrec_props_handler(std::function<void(v5::properties const&)> h) {
+    void set_pubrec_props_handler(move_only_function<void(v5::properties const&)> h) {
         h_pubrec_props_ = force_move(h);
     }
 
-    void set_pubrel_props_handler(std::function<void(v5::properties const&)> h) {
+    void set_pubrel_props_handler(move_only_function<void(v5::properties const&)> h) {
         h_pubrel_props_ = force_move(h);
     }
 
-    void set_pubcomp_props_handler(std::function<void(v5::properties const&)> h) {
+    void set_pubcomp_props_handler(move_only_function<void(v5::properties const&)> h) {
         h_pubcomp_props_ = force_move(h);
     }
 
-    void set_subscribe_props_handler(std::function<void(v5::properties const&)> h) {
+    void set_subscribe_props_handler(move_only_function<void(v5::properties const&)> h) {
         h_subscribe_props_ = force_move(h);
     }
 
-    void set_unsubscribe_props_handler(std::function<void(v5::properties const&)> h) {
+    void set_unsubscribe_props_handler(move_only_function<void(v5::properties const&)> h) {
         h_unsubscribe_props_ = force_move(h);
     }
 
-    void set_auth_props_handler(std::function<void(v5::properties const&)> h) {
+    void set_auth_props_handler(move_only_function<void(v5::properties const&)> h) {
         h_auth_props_ = force_move(h);
     }
 
@@ -1174,7 +1174,7 @@ class broker_t {
         bool session_present,
         bool authenticated,
         v5::properties props,
-        std::function<void(error_code)> finish = [](error_code){}
+        move_only_function<void(error_code)> 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<std::function<void()>> retain_deliver;
+        std::vector<move_only_function<void()>> 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<void(v5::properties const&)> h_connect_props_;
-    std::function<void(v5::properties const&)> h_disconnect_props_;
-    std::function<void(v5::properties const&)> h_publish_props_;
-    std::function<void(v5::properties const&)> h_puback_props_;
-    std::function<void(v5::properties const&)> h_pubrec_props_;
-    std::function<void(v5::properties const&)> h_pubrel_props_;
-    std::function<void(v5::properties const&)> h_pubcomp_props_;
-    std::function<void(v5::properties const&)> h_subscribe_props_;
-    std::function<void(v5::properties const&)> h_unsubscribe_props_;
-    std::function<void(v5::properties const&)> h_auth_props_;
+    move_only_function<void(v5::properties const&)> h_connect_props_;
+    move_only_function<void(v5::properties const&)> h_disconnect_props_;
+    move_only_function<void(v5::properties const&)> h_publish_props_;
+    move_only_function<void(v5::properties const&)> h_puback_props_;
+    move_only_function<void(v5::properties const&)> h_pubrec_props_;
+    move_only_function<void(v5::properties const&)> h_pubrel_props_;
+    move_only_function<void(v5::properties const&)> h_pubcomp_props_;
+    move_only_function<void(v5::properties const&)> h_subscribe_props_;
+    move_only_function<void(v5::properties const&)> h_unsubscribe_props_;
+    move_only_function<void(v5::properties const&)> 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<void()> handler) {
+    void set_clean_handler(move_only_function<void()> handler) {
         clean_handler_ = force_move(handler);
     }
 
@@ -601,7 +601,7 @@ struct session_state {
     std::set<packet_id_t> qos2_publish_handled_;
 
     optional<std::string> response_topic_;
-    std::function<void()> clean_handler_;
+    move_only_function<void()> clean_handler_;
 };
 
 class session_states {

From 43d9b837d99112871b6c77ff6d2f3a1e19928d28 Mon Sep 17 00:00:00 2001
From: Takatoshi Kondo <redboltz@gmail.com>
Date: Fri, 7 Oct 2022 20:01:39 +0900
Subject: [PATCH 3/7] Version updated.

---
 CHANGELOG.md   | 6 ++++++
 CMakeLists.txt | 2 +-
 README.md      | 2 +-
 3 files changed, 8 insertions(+), 2 deletions(-)

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.
 

From 6bc599db70f5f3c184413a612e685694cd723f54 Mon Sep 17 00:00:00 2001
From: Takatoshi Kondo <redboltz@gmail.com>
Date: Wed, 5 Oct 2022 20:31:07 +0900
Subject: [PATCH 4/7] Added completion token support.

---
 include/mqtt/client.hpp   | 14 +++++++--
 include/mqtt/endpoint.hpp | 66 +++++++++++++++++++++++++++++----------
 2 files changed, 60 insertions(+), 20 deletions(-)

diff --git a/include/mqtt/client.hpp b/include/mqtt/client.hpp
index 527bf303d..520665fe8 100644
--- a/include/mqtt/client.hpp
+++ b/include/mqtt/client.hpp
@@ -1521,11 +1521,19 @@ class client : public endpoint<std::mutex, std::lock_guard, PacketIdBytes> {
      * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901205<BR>
      * @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>
+    auto async_disconnect(
+        CompletionToken&& token = async_handler_t{}
+    )
+        ->
+        typename as::async_result<
+            typename std::decay<CompletionToken>::type,
+            void(error_code)
+        >::return_type
+    {
         if (ping_duration_ != std::chrono::steady_clock::duration::zero()) tim_ping_.cancel();
         if (base::connected()) {
-            base::async_disconnect(force_move(func));
+            base::async_disconnect(std::forward<CompletionToken>(token));
         }
     }
 
diff --git a/include/mqtt/endpoint.hpp b/include/mqtt/endpoint.hpp
index 49dd21e25..945036494 100644
--- a/include/mqtt/endpoint.hpp
+++ b/include/mqtt/endpoint.hpp
@@ -2206,6 +2206,38 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
         }
     }
 
+    struct async_disconnect_impl {
+        this_type& ep;
+        enum {
+            first,
+            second
+        } state = first;
+
+        template <typename Self>
+        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(
+                    v5::disconnect_reason_code::normal_disconnection,
+                    v5::properties{},
+                    [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));
+                    }
+                );
+            }
+        }
+    };
+
     /**
      * @brief Disconnect
      * @param func
@@ -2214,27 +2246,27 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
      * The broker disconnects the endpoint after receives the disconnect packet.<BR>
      * When the endpoint disconnects using disconnect(), a will won't send.<BR>
      */
-    void async_disconnect(
-        async_handler_t func = {}
-    ) {
+    template <typename CompletionToken>
+    auto async_disconnect(
+        CompletionToken&& token = async_handler_t{}
+    )
+        ->
+        typename as::async_result<
+            typename std::decay<CompletionToken>::type,
+            void(error_code)
+        >::return_type {
         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)] () mutable {
-                    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,
+                get_executor()
             );
-        }
     }
 
     /**

From a2f1a1507735273d0eab36ec3e092c1be4cf140d Mon Sep 17 00:00:00 2001
From: Takatoshi Kondo <redboltz@gmail.com>
Date: Sat, 8 Oct 2022 09:43:15 +0900
Subject: [PATCH 5/7] Added CompletionToken support for async_disconnect.

---
 include/mqtt/client.hpp             | 157 ++++++++++++++++------------
 include/mqtt/endpoint.hpp           | 109 +++++++++----------
 include/mqtt/is_invocable.hpp       |  31 ++++++
 include/mqtt/move_only_function.hpp |   2 +-
 4 files changed, 179 insertions(+), 120 deletions(-)
 create mode 100644 include/mqtt/is_invocable.hpp

diff --git a/include/mqtt/client.hpp b/include/mqtt/client.hpp
index 520665fe8..3f4713834 100644
--- a/include/mqtt/client.hpp
+++ b/include/mqtt/client.hpp
@@ -26,6 +26,7 @@
 #include <mqtt/callable_overlay.hpp>
 #include <mqtt/strand.hpp>
 #include <mqtt/null_strand.hpp>
+#include <mqtt/is_invocable.hpp>
 
 namespace MQTT_NS {
 
@@ -1444,30 +1445,51 @@ class client : public endpoint<std::mutex, std::lock_guard, PacketIdBytes> {
      * When the endpoint disconnects using disconnect(), a will won't send.<BR>
      * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901205<BR>
      * @param timeout after timeout elapsed, force_disconnect() is automatically called.
+     * @param reason_code
+     *        DISCONNECT Reason Code<BR>
+     *        See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901208<BR>
+     *        3.14.2.1 Disconnect Reason Code
+     * @param props
+     *        Properties<BR>
+     *        See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901209<BR>
+     *        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<CompletionToken, error_code>::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<this_type> wp(std::static_pointer_cast<this_type>(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<this_type> wp(std::static_pointer_cast<this_type>(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<CompletionToken>(token)
             );
-            base::async_disconnect(force_move(func));
-        }
     }
 
     /**
@@ -1476,7 +1498,6 @@ class client : public endpoint<std::mutex, std::lock_guard, PacketIdBytes> {
      * The broker disconnects the endpoint after receives the disconnect packet.<BR>
      * When the endpoint disconnects using disconnect(), a will won't send.<BR>
      * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901205<BR>
-     * @param timeout after timeout elapsed, force_disconnect() is automatically called.
      * @param reason_code
      *        DISCONNECT Reason Code<BR>
      *        See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901208<BR>
@@ -1487,30 +1508,24 @@ class client : public endpoint<std::mutex, std::lock_guard, PacketIdBytes> {
      *        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<CompletionToken, error_code>::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<this_type> wp(std::static_pointer_cast<this_type>(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<CompletionToken>(token)
             );
-            base::async_disconnect(reason_code, force_move(props), force_move(func));
-        }
     }
 
     /**
@@ -1519,22 +1534,39 @@ class client : public endpoint<std::mutex, std::lock_guard, PacketIdBytes> {
      * The broker disconnects the endpoint after receives the disconnect packet.<BR>
      * When the endpoint disconnects using disconnect(), a will won't send.<BR>
      * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901205<BR>
+     * @param timeout after timeout elapsed, force_disconnect() is automatically called.
      * @param func A callback function that is called when async operation will finish.
      */
-    template <typename CompletionToken>
+    template <
+        typename CompletionToken = async_handler_t,
+        typename std::enable_if_t<
+            is_invocable<CompletionToken, error_code>::value
+        >* = nullptr
+    >
     auto async_disconnect(
-        CompletionToken&& token = async_handler_t{}
-    )
-        ->
-        typename as::async_result<
-            typename std::decay<CompletionToken>::type,
-            void(error_code)
-        >::return_type
-    {
+        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(std::forward<CompletionToken>(token));
-        }
+        std::weak_ptr<this_type> wp(std::static_pointer_cast<this_type>(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<CompletionToken>(token)
+            );
     }
 
     /**
@@ -1543,24 +1575,19 @@ class client : public endpoint<std::mutex, std::lock_guard, PacketIdBytes> {
      * The broker disconnects the endpoint after receives the disconnect packet.<BR>
      * When the endpoint disconnects using disconnect(), a will won't send.<BR>
      * See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901205<BR>
-     * @param reason_code
-     *        DISCONNECT Reason Code<BR>
-     *        See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901208<BR>
-     *        3.14.2.1 Disconnect Reason Code
-     * @param props
-     *        Properties<BR>
-     *        See https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901209<BR>
-     *        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<CompletionToken, error_code>::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<CompletionToken>(token));
     }
 
     /**
diff --git a/include/mqtt/endpoint.hpp b/include/mqtt/endpoint.hpp
index 945036494..3ae2055e8 100644
--- a/include/mqtt/endpoint.hpp
+++ b/include/mqtt/endpoint.hpp
@@ -72,6 +72,7 @@
 #include <mqtt/packet_id_manager.hpp>
 #include <mqtt/store.hpp>
 #include <mqtt/move_only_function.hpp>
+#include <mqtt/is_invocable.hpp>
 
 #if defined(MQTT_USE_WS)
 #include <mqtt/ws_endpoint.hpp>
@@ -2206,38 +2207,6 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
         }
     }
 
-    struct async_disconnect_impl {
-        this_type& ep;
-        enum {
-            first,
-            second
-        } state = first;
-
-        template <typename Self>
-        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(
-                    v5::disconnect_reason_code::normal_disconnection,
-                    v5::properties{},
-                    [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));
-                    }
-                );
-            }
-        }
-    };
-
     /**
      * @brief Disconnect
      * @param func
@@ -2246,15 +2215,15 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
      * The broker disconnects the endpoint after receives the disconnect packet.<BR>
      * When the endpoint disconnects using disconnect(), a will won't send.<BR>
      */
-    template <typename CompletionToken>
+    template <
+        typename CompletionToken = async_handler_t,
+        typename std::enable_if_t<
+            is_invocable<CompletionToken, error_code>::value
+        >* = nullptr
+    >
     auto async_disconnect(
-        CompletionToken&& token = async_handler_t{}
-    )
-        ->
-        typename as::async_result<
-            typename std::decay<CompletionToken>::type,
-            void(error_code)
-        >::return_type {
+        CompletionToken&& token = [](error_code){}
+    ) {
         MQTT_LOG("mqtt_api", info)
             << MQTT_ADD_VALUE(address, this)
             << "async_disconnect";
@@ -2264,8 +2233,7 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
                 void(error_code)
             >(
                 async_disconnect_impl{*this},
-                token,
-                get_executor()
+                token
             );
     }
 
@@ -2285,27 +2253,30 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
      * The broker disconnects the endpoint after receives the disconnect packet.<BR>
      * When the endpoint disconnects using disconnect(), a will won't send.<BR>
      */
-    void async_disconnect(
+    template <
+        typename CompletionToken = async_handler_t,
+        typename std::enable_if_t<
+            is_invocable<CompletionToken, error_code>::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)] () mutable {
-                    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
             );
-        }
     }
 
     /**
@@ -11218,6 +11189,36 @@ class endpoint : public std::enable_shared_from_this<endpoint<Mutex, LockGuard,
         }
     }
 
+    struct async_disconnect_impl {
+        this_type& ep;
+        v5::disconnect_reason_code reason = v5::disconnect_reason_code::normal_disconnection;
+        v5::properties props = {};
+
+        template <typename Self>
+        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,
diff --git a/include/mqtt/is_invocable.hpp b/include/mqtt/is_invocable.hpp
new file mode 100644
index 000000000..85961c721
--- /dev/null
+++ b/include/mqtt/is_invocable.hpp
@@ -0,0 +1,31 @@
+// 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 <type_traits>
+#include <boost/callable_traits.hpp>
+
+#include <mqtt/namespace.hpp>
+
+namespace MQTT_NS {
+
+#if __cplusplus >= 201703L
+
+template <typename Func, typename... Params>
+using is_invocable = typename std::is_invocable<Func, Params...>;
+
+#else  // __cplusplus >= 201703L
+
+template <typename Func, typename... Params>
+using is_invocable = typename boost::callable_traits::is_invocable<Func, Params...>;
+
+#endif // __cplusplus >= 201703L
+
+} // namespace MQTT_NS
+
+#endif // MQTT_IS_INVOCABLE_HPP
diff --git a/include/mqtt/move_only_function.hpp b/include/mqtt/move_only_function.hpp
index 08b0513bb..8cd6f1a70 100644
--- a/include/mqtt/move_only_function.hpp
+++ b/include/mqtt/move_only_function.hpp
@@ -7,8 +7,8 @@
 #if !defined(MQTT_MOVE_ONLY_FUNCTION_HPP)
 #define MQTT_MOVE_ONLY_FUNCTION_HPP
 
-#pragma GCC diagnostic push
 #if defined(__GNUC__)
+#pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Waddress"
 #endif // defined(__GNUC__)
 

From ffd3a745fab9bf9b4914a4646130194061fdee57 Mon Sep 17 00:00:00 2001
From: Takatoshi Kondo <redboltz@gmail.com>
Date: Sat, 8 Oct 2022 18:37:55 +0900
Subject: [PATCH 6/7] Test MSVC to std::is_invocable.

---
 include/mqtt/is_invocable.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/mqtt/is_invocable.hpp b/include/mqtt/is_invocable.hpp
index 85961c721..6a04f624a 100644
--- a/include/mqtt/is_invocable.hpp
+++ b/include/mqtt/is_invocable.hpp
@@ -14,7 +14,7 @@
 
 namespace MQTT_NS {
 
-#if __cplusplus >= 201703L
+#if __cplusplus >= 201703L || defined(_MSC_VER)
 
 template <typename Func, typename... Params>
 using is_invocable = typename std::is_invocable<Func, Params...>;

From dc0d1eb96f7073e951a7b12ea78fc2187799e948 Mon Sep 17 00:00:00 2001
From: Takatoshi Kondo <redboltz@gmail.com>
Date: Sun, 9 Oct 2022 10:50:25 +0900
Subject: [PATCH 7/7] Fixed before C++17 workaround for is_invocable.

---
 include/mqtt/is_invocable.hpp | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/include/mqtt/is_invocable.hpp b/include/mqtt/is_invocable.hpp
index 6a04f624a..9147c2d69 100644
--- a/include/mqtt/is_invocable.hpp
+++ b/include/mqtt/is_invocable.hpp
@@ -8,24 +8,32 @@
 #define MQTT_IS_INVOCABLE_HPP
 
 #include <type_traits>
-#include <boost/callable_traits.hpp>
-
 #include <mqtt/namespace.hpp>
 
-namespace MQTT_NS {
+#if __cplusplus >= 201703L
 
-#if __cplusplus >= 201703L || defined(_MSC_VER)
+namespace MQTT_NS {
 
 template <typename Func, typename... Params>
 using is_invocable = typename std::is_invocable<Func, Params...>;
 
+} // namespace MQTT_NS
+
 #else  // __cplusplus >= 201703L
 
-template <typename Func, typename... Params>
-using is_invocable = typename boost::callable_traits::is_invocable<Func, Params...>;
+#include <mqtt/move_only_function.hpp>
 
-#endif // __cplusplus >= 201703L
+namespace MQTT_NS {
+
+template <typename Func, typename... Params>
+struct is_invocable : std::is_constructible<
+    move_only_function<void(Params...)>,
+    std::reference_wrapper<typename std::remove_reference<Func>::type>
+>
+{};
 
 } // namespace MQTT_NS
 
+#endif // __cplusplus >= 201703L
+
 #endif // MQTT_IS_INVOCABLE_HPP