diff --git a/include/boost/mqtt5/detail/traits.hpp b/include/boost/mqtt5/detail/traits.hpp index 3f45a8b..4b2abf7 100644 --- a/include/boost/mqtt5/detail/traits.hpp +++ b/include/boost/mqtt5/detail/traits.hpp @@ -58,6 +58,19 @@ constexpr bool is_boost_iterator = is_specialization< boost::remove_cv_ref_t, boost::iterator_range >; +template +constexpr bool is_container_of_impl = false; + +template +constexpr bool is_container_of_impl> = + std::is_same_v; + +template +constexpr bool is_container_of = is_container_of_impl< + boost::remove_cv_ref_t, U +>; + } // end namespace boost::mqtt5::detail #endif // !BOOST_MQTT5_TRAITS_HPP diff --git a/include/boost/mqtt5/impl/codecs/message_encoders.hpp b/include/boost/mqtt5/impl/codecs/message_encoders.hpp index 430308a..bfc972f 100644 --- a/include/boost/mqtt5/impl/codecs/message_encoders.hpp +++ b/include/boost/mqtt5/impl/codecs/message_encoders.hpp @@ -12,11 +12,13 @@ #include +#include + #include #include #include #include -#include +#include namespace boost::mqtt5::encoders { @@ -231,7 +233,7 @@ inline std::string encode_pubcomp( inline std::string encode_subscribe( uint16_t packet_id, - const std::vector& topics, + boost::span topics, const subscribe_props& props ) { @@ -270,7 +272,7 @@ inline std::string encode_subscribe( inline std::string encode_suback( uint16_t packet_id, - const std::vector& reason_codes, + boost::span reason_codes, const suback_props& props ) { @@ -296,9 +298,17 @@ inline std::string encode_suback( return s; } +inline std::string encode_suback( + uint16_t packet_id, + std::initializer_list reason_codes, + const suback_props& props +) { + return encode_suback(packet_id, std::vector(reason_codes), props); +} + inline std::string encode_unsubscribe( uint16_t packet_id, - const std::vector& topics, + boost::span topics, const unsubscribe_props& props ) { @@ -330,7 +340,7 @@ inline std::string encode_unsubscribe( inline std::string encode_unsuback( uint16_t packet_id, - const std::vector& reason_codes, + boost::span reason_codes, const unsuback_props& props ) { @@ -356,6 +366,14 @@ inline std::string encode_unsuback( return s; } +inline std::string encode_unsuback( + uint16_t packet_id, + std::initializer_list reason_codes, + const unsuback_props& props +) { + return encode_unsuback(packet_id, std::vector(reason_codes), props); +} + inline std::string encode_pingreq() { auto packet_type_ = basic::flag<4>(0b1100) | diff --git a/include/boost/mqtt5/impl/subscribe_op.hpp b/include/boost/mqtt5/impl/subscribe_op.hpp index 8193bf7..69d1037 100644 --- a/include/boost/mqtt5/impl/subscribe_op.hpp +++ b/include/boost/mqtt5/impl/subscribe_op.hpp @@ -29,10 +29,13 @@ #include #include +#include + #include #include #include #include +#include namespace boost::mqtt5::detail { @@ -91,7 +94,7 @@ class subscribe_op { } void perform( - const std::vector& topics, + boost::span topics, const subscribe_props& props ) { _num_topics = topics.size(); @@ -119,6 +122,13 @@ class subscribe_op { send_subscribe(std::move(subscribe)); } + void perform( + std::initializer_list topics, + const subscribe_props& props + ) { + perform(std::vector(topics), props); + } + void send_subscribe(control_packet subscribe) { auto wire_data = subscribe.wire_data(); _svc_ptr->async_send( @@ -223,7 +233,7 @@ class subscribe_op { } static error_code validate_subscribe( - const std::vector& topics, + boost::span topics, const subscribe_props& props, validation_context& ctx ) { error_code ec; @@ -345,7 +355,7 @@ class initiate_async_subscribe { template void operator()( Handler&& handler, - const std::vector& topics, const subscribe_props& props + span topics, const subscribe_props& props ) { detail::subscribe_op { _svc_ptr, std::move(handler) } .perform(topics, props); diff --git a/include/boost/mqtt5/impl/unsubscribe_op.hpp b/include/boost/mqtt5/impl/unsubscribe_op.hpp index e087059..0ad9669 100644 --- a/include/boost/mqtt5/impl/unsubscribe_op.hpp +++ b/include/boost/mqtt5/impl/unsubscribe_op.hpp @@ -29,10 +29,13 @@ #include #include +#include + #include #include #include #include +#include namespace boost::mqtt5::detail { @@ -87,7 +90,7 @@ class unsubscribe_op { } void perform( - const std::vector& topics, + boost::span topics, const unsubscribe_props& props ) { _num_topics = topics.size(); @@ -115,6 +118,13 @@ class unsubscribe_op { send_unsubscribe(std::move(unsubscribe)); } + void perform( + std::initializer_list topics, + const unsubscribe_props& props + ) { + perform(std::vector(topics), props); + } + void send_unsubscribe(control_packet unsubscribe) { auto wire_data = unsubscribe.wire_data(); _svc_ptr->async_send( @@ -196,7 +206,7 @@ class unsubscribe_op { private: static error_code validate_unsubscribe( - const std::vector& topics, + boost::span topics, const unsubscribe_props& props ) { for (const auto& topic : topics) @@ -268,7 +278,7 @@ class initiate_async_unsubscribe { template void operator()( Handler&& handler, - const std::vector& topics, const unsubscribe_props& props + boost::span topics, const unsubscribe_props& props ) { detail::unsubscribe_op { _svc_ptr, std::move(handler) } .perform(topics, props); diff --git a/include/boost/mqtt5/mqtt_client.hpp b/include/boost/mqtt5/mqtt_client.hpp index df3b133..860783a 100644 --- a/include/boost/mqtt5/mqtt_client.hpp +++ b/include/boost/mqtt5/mqtt_client.hpp @@ -30,6 +30,8 @@ #include #include // std::monostate #include +#include +#include namespace boost::mqtt5 { @@ -577,11 +579,13 @@ class mqtt_client { * */ template < + typename TopicSequence, typename CompletionToken = - typename asio::default_completion_token::type + typename asio::default_completion_token::type, + typename = std::enable_if_t> > decltype(auto) async_subscribe( - const std::vector& topics, + const TopicSequence& topics, const subscribe_props& props, CompletionToken&& token = {} ) { @@ -594,6 +598,74 @@ class mqtt_client { ); } + /** + * \brief Send a \__SUBSCRIBE\__ packet to Broker to create a Subscription + * to one or more Topics of interest. + * + * \details After the Subscription has been established, the Broker will send + * PUBLISH packets to the Client to forward Application Messages that were published + * to Topics that the Client subscribed to. The Application Messages can be received + * with \ref mqtt_client::async_receive function. + * + * \param topics A list of \ref subscribe_topic of interest. + * \param props An instance of \__SUBSCRIBE_PROPS\__. + * \param token Completion token that will be used to produce a + * completion handler. The handler will be invoked when the operation completes. + * On immediate completion, invocation of the handler will be performed in a manner + * equivalent to using \__ASYNC_IMMEDIATE\__. + * + * \par Handler signature + * The handler signature for this operation: + * \code + * void ( + * __ERROR_CODE__, // Result of operation. + * std::vector<__REASON_CODE__>, // Vector of Reason Codes indicating + * // the Subscription result for each Topic + * // in the SUBSCRIBE packet. + * __SUBACK_PROPS__, // Properties received in the SUBACK packet. + * ) + * \endcode + * + * \par Completion condition + * The asynchronous operation will complete when one of the following conditions is true:\n + * - The Client has successfully sent a \__SUBSCRIBE\__ packet + * and has received a \__SUBACK\__ response from the Broker.\n + * - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n + * + * \par Error codes + * The list of all possible error codes that this operation can finish with:\n + * - `boost::system::errc::errc_t::success` \n + * - `boost::asio::error::no_recovery` \n + * - `boost::asio::error::operation_aborted` \n + * - \ref boost::mqtt5::client::error::malformed_packet + * - \ref boost::mqtt5::client::error::packet_too_large + * - \ref boost::mqtt5::client::error::pid_overrun + * - \ref boost::mqtt5::client::error::invalid_topic + * - \ref boost::mqtt5::client::error::wildcard_subscription_not_available + * - \ref boost::mqtt5::client::error::subscription_identifier_not_available + * - \ref boost::mqtt5::client::error::shared_subscription_not_available + * + * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code. + * + * \par Per-Operation Cancellation + * This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n + * - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n + * - `cancellation_type::partial` & `cancellation_type::total` - prevents potential resending of the \__SUBSCRIBE\__ packet \n + * + */ + template < + typename CompletionToken = + typename asio::default_completion_token::type + > + decltype(auto) async_subscribe( + std::initializer_list topics, + const subscribe_props& props, + CompletionToken&& token = {} + ) { + return async_subscribe(std::vector(topics), props, + std::forward(token)); + } + /** * \brief Send a \__SUBSCRIBE\__ packet to Broker to create a Subscription * to one Topic of interest. @@ -658,7 +730,7 @@ class mqtt_client { CompletionToken&& token = {} ) { return async_subscribe( - std::vector { topic }, props, + std::array { topic }, props, std::forward(token) ); } @@ -716,11 +788,14 @@ class mqtt_client { * */ template < + typename TopicSequence, typename CompletionToken = - typename asio::default_completion_token::type + typename asio::default_completion_token::type, + typename = std::enable_if_t> > decltype(auto) async_unsubscribe( - const std::vector& topics, const unsubscribe_props& props, + const TopicSequence& topics, + const unsubscribe_props& props, CompletionToken&& token = {} ) { using Signature = void ( @@ -732,6 +807,70 @@ class mqtt_client { ); } + /** + * \brief Send an \__UNSUBSCRIBE\__ packet to Broker to unsubscribe from one + * or more Topics. + * + * \note The Client may still receive residual Application Messages + * through the \ref mqtt_client::async_receive function + * from Topics the Client just unsubscribed to. + * + * \param topics List of Topics to unsubscribe from. + * \param props An instance of \__UNSUBSCRIBE_PROPS\__. + * \param token Completion token that will be used to produce a + * completion handler. The handler will be invoked when the operation completes. + * On immediate completion, invocation of the handler will be performed in a manner + * equivalent to using \__ASYNC_IMMEDIATE\__. + * + * \par Handler signature + * The handler signature for this operation: + * \code + * void ( + * __ERROR_CODE__, // Result of operation. + * std::vector<__REASON_CODE__>, // Vector of Reason Codes indicating + * // the result of unsubscribe operation + * // for each Topic in the UNSUBSCRIBE packet. + * __UNSUBACK_PROPS__, // Properties received in the UNSUBACK packet. + * ) + * \endcode + * + * \par Completion condition + * The asynchronous operation will complete when one of the following conditions is true:\n + * - The Client has successfully sent an \__UNSUBSCRIBE\__ packet + * and has received an \__UNSUBACK\__ response from the Broker.\n + * - An error occurred. This is indicated by an associated \__ERROR_CODE\__ in the handler.\n + * + * \par Error codes + * The list of all possible error codes that this operation can finish with:\n + * - `boost::system::errc::errc_t::success` \n + * - `boost::asio::error::no_recovery` \n + * - `boost::asio::error::operation_aborted` \n + * - \ref boost::mqtt5::client::error::malformed_packet + * - \ref boost::mqtt5::client::error::packet_too_large + * - \ref boost::mqtt5::client::error::pid_overrun + * - \ref boost::mqtt5::client::error::invalid_topic + * + * Refer to the section on \__ERROR_HANDLING\__ to find the underlying causes for each error code. + * + * \par Per-Operation Cancellation + * This asynchronous operation supports cancellation for the following \__CANCELLATION_TYPE\__ values:\n + * - `cancellation_type::terminal` - invokes \ref mqtt_client::cancel \n + * - `cancellation_type::partial` & `cancellation_type::total` - prevents potential resending of the \__UNSUBSCRIBE\__ packet \n + * + */ + template < + typename CompletionToken = + typename asio::default_completion_token::type + > + decltype(auto) async_unsubscribe( + std::initializer_list topics, + const unsubscribe_props& props, + CompletionToken&& token = {} + ) { + return async_unsubscribe(std::vector(topics), props, + std::forward(token)); + } + /** * \brief Send an \__UNSUBSCRIBE\__ packet to Broker to unsubscribe * from one Topic. @@ -792,7 +931,7 @@ class mqtt_client { CompletionToken&& token = {} ) { return async_unsubscribe( - std::vector { topic }, props, + std::array { topic }, props, std::forward(token) ); }