diff --git a/include/async_mqtt5/impl/autoconnect_stream.hpp b/include/async_mqtt5/impl/autoconnect_stream.hpp index 6a01e92..8dd2a0c 100644 --- a/include/async_mqtt5/impl/autoconnect_stream.hpp +++ b/include/async_mqtt5/impl/autoconnect_stream.hpp @@ -88,7 +88,7 @@ public: void open() { error_code ec; lowest_layer(*_stream_ptr).open(asio::ip::tcp::v4(), ec); - lowest_layer(*_stream_ptr).set_option(asio::ip::tcp::no_delay(true)); + lowest_layer(*_stream_ptr).set_option(asio::ip::tcp::no_delay(true), ec); } void cancel() { diff --git a/include/async_mqtt5/impl/internal/codecs/base_encoders.hpp b/include/async_mqtt5/impl/internal/codecs/base_encoders.hpp index d141caf..9169c39 100644 --- a/include/async_mqtt5/impl/internal/codecs/base_encoders.hpp +++ b/include/async_mqtt5/impl/internal/codecs/base_encoders.hpp @@ -7,7 +7,6 @@ #include #include - #include namespace async_mqtt5::encoders { @@ -36,11 +35,11 @@ template class flag_def : public encoder { template using least_type = std::conditional_t< - num_bits <= 8, uint8_t, + num_bits <= 8, uint8_t, std::conditional_t< num_bits <= 16, uint16_t, std::conditional_t< - num_bits <= 32, uint32_t, + num_bits <= 32, uint32_t, std::conditional_t > > @@ -63,7 +62,8 @@ public: return flag_def { val }; } else { - repr val = value.has_value() ? static_cast(std::invoke(proj, *value)) : 0; + repr val = value.has_value() ? + static_cast(std::invoke(proj, *value)) : 0; return flag_def { val }; } } @@ -126,7 +126,7 @@ private: if constexpr (std::is_same_v) return variable_length(int32_t(val)); else - return sizeof(Repr); + return sizeof(Repr); } template @@ -134,7 +134,7 @@ private: using namespace boost::endian; if constexpr (std::is_same_v) { to_variable_bytes(s, int32_t(val)); - return s; + return s; } else { size_t sz = s.size(); s.resize(sz + sizeof(Repr)); @@ -182,7 +182,9 @@ class array_val : public encoder { bool _with_length; public: array_val(T val, bool with_length) : _val(val), _with_length(with_length) { - static_assert(std::is_reference_v || std::is_same_v); + static_assert( + std::is_reference_v || std::is_same_v + ); } size_t byte_size() const { @@ -209,7 +211,7 @@ private: if constexpr (requires { val.size(); }) return val.size(); else // fallback to type const char (&)[N] (substract 1 for trailing 0) - return sizeof(val) - 1; + return sizeof(val) - 1; } template @@ -220,7 +222,9 @@ private: if (_with_length) { size_t sz = s.size(); s.resize(sz + 2); auto p = reinterpret_cast(s.data() + sz); - endian_store(p, int16_t(byte_len)); + endian_store( + p, int16_t(byte_len) + ); } s.append(std::begin(u), std::begin(u) + byte_len); return s; @@ -263,7 +267,7 @@ template class composed_val : public encoder { T _lhs; U _rhs; public: - composed_val(T lhs, U rhs) : + composed_val(T lhs, U rhs) : _lhs(std::forward(lhs)), _rhs(std::forward(rhs)) {} size_t byte_size() const { @@ -277,7 +281,10 @@ public: }; template -requires (std::derived_from, encoder> && std::derived_from, encoder>) +requires ( + std::derived_from, encoder> && + std::derived_from, encoder> +) inline auto operator&(T&& t, U&& u) { return composed_val(std::forward(t), std::forward(u)); } @@ -291,73 +298,94 @@ std::string& operator<<(std::string& s, T&& t) { } // end namespace basic namespace detail { -template -constexpr bool match_v = std::is_same_v::key>; -template >> +namespace pp = async_mqtt5::prop; + +template +constexpr bool match_v = std::is_same_v< + std::integral_constant, + typename std::tuple_element_t::key +>; + +template < + pp::property_type p, typename Tuple, + typename Idxs = std::make_index_sequence> +> struct type_index; -template typename Tuple, typename... Args, std::size_t... Is> -struct type_index, std::index_sequence> - : std::integral_constant>)+... + 0)> { - static_assert(1 == (match_v> + ... + 0), "T doesn't appear once in tuple"); +template < + pp::property_type p, template typename Tuple, + typename... Args, std::size_t... Is +> +struct type_index, std::index_sequence> : + std::integral_constant< + std::size_t, ((Is * match_v>)+... + 0) + > +{ + static_assert( + 1 == (match_v> + ... + 0), + "T doesn't appear once in tuple" + ); }; + } // end namespace detail namespace prop { - namespace pp = async_mqtt5::prop; -template -struct prop_encoder_type { using key = decltype(p); using value = T; }; +template +struct prop_encoder_type { + using key = std::integral_constant; + using value = T; +}; using encoder_types = std::tuple< - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type, - prop_encoder_type, - prop_encoder_type, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type, - prop_encoder_type>, - prop_encoder_type, - prop_encoder_type, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type, - prop_encoder_type, - prop_encoder_type, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type, - prop_encoder_type>, - prop_encoder_type>, - prop_encoder_type> + prop_encoder_type>, + prop_encoder_type>, + prop_encoder_type>, + prop_encoder_type, + prop_encoder_type, + prop_encoder_type, + prop_encoder_type>, + prop_encoder_type>, + prop_encoder_type, + prop_encoder_type>, + prop_encoder_type, + prop_encoder_type, + prop_encoder_type>, + prop_encoder_type>, + prop_encoder_type>, + prop_encoder_type, + prop_encoder_type, + prop_encoder_type, + prop_encoder_type>, + prop_encoder_type>, + prop_encoder_type>, + prop_encoder_type>, + prop_encoder_type>, + prop_encoder_type, + prop_encoder_type>, + prop_encoder_type>, + prop_encoder_type> >; -template +template constexpr auto encoder_for_prop = typename std::tuple_element_t< detail::type_index::value, encoder_types >::value {}; -template +template class prop_val; -template +template requires (!is_vector && is_optional) class prop_val : public basic::encoder { // T is always std::optional using opt_type = typename std::remove_cvref_t::value_type; // allows T to be reference type to std::optional - static inline std::optional nulltype; + static inline std::optional nulltype; T _val; public: prop_val(T val) : _val(val) { @@ -374,13 +402,13 @@ public: std::string& encode(std::string& s) const { if (!_val) return s; - s.push_back(p()); + s.push_back(p); auto sval = encoder_for_prop

(_val); - return sval.encode(s); + return sval.encode(s); } }; -template +template requires (is_vector) class prop_val : public basic::encoder { // allows T to be reference type to std::vector @@ -410,8 +438,8 @@ public: for (const auto& pr: _val) { auto sval = encoder_for_prop

(pr); - s.push_back(p()); - sval.encode(s); + s.push_back(p); + sval.encode(s); } return s; } @@ -420,16 +448,20 @@ public: template class props_val : public basic::encoder { - static inline std::decay_t nulltype; + static inline std::decay_t nulltype; - template + template static auto to_prop_val(const T& val) { return prop_val(val); } - - template + + template static auto to_prop_vals(const pp::properties& props) { - return std::make_tuple(to_prop_val(props[Ps])...); + return std::make_tuple( + to_prop_val( + props[std::integral_constant{}] + )... + ); } template @@ -441,11 +473,16 @@ class props_val : public basic::encoder { decltype(to_prop_vals(std::declval())) _prop_vals; bool _may_omit; + public: - props_val(Props val, bool may_omit) : _prop_vals(to_prop_vals(val)), _may_omit(may_omit) { + props_val(Props val, bool may_omit) : + _prop_vals(to_prop_vals(val)), _may_omit(may_omit) + { static_assert(std::is_reference_v); } - props_val(bool may_omit) : _prop_vals(to_prop_vals(nulltype)), _may_omit(may_omit) { } + props_val(bool may_omit) : + _prop_vals(to_prop_vals(nulltype)), _may_omit(may_omit) + {} size_t byte_size() const { size_t psize = props_size(); @@ -463,7 +500,9 @@ public: private: size_t props_size() const { size_t retval = 0; - apply_each([&retval](const auto& pv) { return retval += pv.byte_size(); }); + apply_each([&retval](const auto& pv) { + return retval += pv.byte_size(); + }); return retval; } }; @@ -476,7 +515,9 @@ public: if constexpr (is_optional) { if (prop_container.has_value()) return (*this)(*prop_container); - return props_val::value_type&>(true); + return props_val< + const typename std::remove_cvref_t::value_type& + >(true); } else { return props_val { prop_container, may_omit }; diff --git a/include/async_mqtt5/property_types.hpp b/include/async_mqtt5/property_types.hpp index 819a6b6..a349be9 100644 --- a/include/async_mqtt5/property_types.hpp +++ b/include/async_mqtt5/property_types.hpp @@ -9,43 +9,46 @@ namespace async_mqtt5::prop { -constexpr std::integral_constant payload_format_indicator {}; -constexpr std::integral_constant message_expiry_interval {}; -constexpr std::integral_constant content_type {}; -constexpr std::integral_constant response_topic {}; -constexpr std::integral_constant correlation_data {}; -constexpr std::integral_constant subscription_identifier {}; -constexpr std::integral_constant session_expiry_interval {}; -constexpr std::integral_constant assigned_client_identifier {}; -constexpr std::integral_constant server_keep_alive {}; -constexpr std::integral_constant authentication_method {}; -constexpr std::integral_constant authentication_data {}; -constexpr std::integral_constant request_problem_information {}; -constexpr std::integral_constant will_delay_interval {}; -constexpr std::integral_constant request_response_information {}; -constexpr std::integral_constant response_information {}; -constexpr std::integral_constant server_reference {}; -constexpr std::integral_constant reason_string {}; -constexpr std::integral_constant receive_maximum {}; -constexpr std::integral_constant topic_alias_maximum {}; -constexpr std::integral_constant topic_alias {}; -constexpr std::integral_constant maximum_qos {}; -constexpr std::integral_constant retain_available {}; -constexpr std::integral_constant user_property {}; -constexpr std::integral_constant maximum_packet_size {}; -constexpr std::integral_constant wildcard_subscription_available {}; -constexpr std::integral_constant subscription_identifier_available {}; -constexpr std::integral_constant shared_subscription_available {}; +enum property_type : uint8_t { + payload_format_indicator_t = 0x01, + message_expiry_interval_t = 0x02, + content_type_t = 0x03, + response_topic_t = 0x08, + correlation_data_t = 0x09, + subscription_identifier_t = 0x0b, + session_expiry_interval_t = 0x11, + assigned_client_identifier_t = 0x12, + server_keep_alive_t = 0x13, + authentication_method_t = 0x15, + authentication_data_t = 0x16, + request_problem_information_t = 0x17, + will_delay_interval_t = 0x18, + request_response_information_t = 0x19, + response_information_t = 0x1a, + server_reference_t = 0x1c, + reason_string_t = 0x1f, + receive_maximum_t = 0x21, + topic_alias_maximum_t = 0x22, + topic_alias_t = 0x23, + maximum_qos_t = 0x24, + retain_available_t = 0x25, + user_property_t = 0x26, + maximum_packet_size_t = 0x27, + wildcard_subscription_available_t = 0x28, + subscription_identifier_available_t = 0x29, + shared_subscription_available_t = 0x2a +}; -template +template struct property_traits; #define DEF_PROPERTY_TRAIT(Pname, Ptype) \ -template <> struct property_traits {\ +template <> \ +struct property_traits { \ static constexpr std::string_view name = #Pname; \ - using type = Ptype;\ -}\ - + using type = Ptype; \ +}; \ +constexpr std::integral_constant Pname {}; DEF_PROPERTY_TRAIT(payload_format_indicator, std::optional); DEF_PROPERTY_TRAIT(message_expiry_interval, std::optional); @@ -77,91 +80,118 @@ DEF_PROPERTY_TRAIT(shared_subscription_available, std::optional); #undef DEF_PROPERTY_TRAIT -template +template using value_type_t = typename property_traits

::type; -template +template constexpr std::string_view name_v = property_traits

::name; -template +template class properties { - template - struct prop_type { - using key = decltype(p); + + template + struct property { + using key = std::integral_constant; constexpr static std::string_view name = name_v

; value_type_t

value; }; - std::tuple...> _props; + std::tuple...> _props; public: - template - constexpr auto& operator[](std::integral_constant p) noexcept { - using Ptype = decltype(p); - return std::get>(_props).value; + template + constexpr auto& operator[](std::integral_constant) + noexcept { + return std::get>(_props).value; } - template - constexpr const auto& operator[](std::integral_constant p) const noexcept { - using Ptype = decltype(p); - return std::get>(_props).value; + template + constexpr const auto& operator[](std::integral_constant) + const noexcept { + return std::get>(_props).value; } - template - constexpr static bool is_apply_on_v = - std::conjunction_v&>...>; + template + using is_apply_on = std::conjunction< + std::is_invocable&>... + >; - template - constexpr static bool is_nothrow_apply_on_v = - std::conjunction_v&>...>; + template + using is_nothrow_apply_on = std::conjunction< + std::is_nothrow_invocable&>... + >; - template requires is_apply_on_v - constexpr bool apply_on(uint8_t property_id, Func&& func) noexcept(is_nothrow_apply_on_v) { - return std::apply([&func, property_id](auto&... props) { - auto pc = [&func, property_id](prop_type& px) { - if (prop.value == property_id) - std::invoke(func, px.value); - return prop.value != property_id; - }; - return (pc(props) && ...); - }, _props); + template < + typename Func, + typename = std::enable_if_t::value> + > + constexpr bool apply_on(uint8_t property_id, Func&& func) + noexcept (is_nothrow_apply_on::value) { + return std::apply( + [&func, property_id](auto&... ptype) { + auto pc = [&func, property_id](auto& px) { + using ptype = std::remove_reference_t; + constexpr typename ptype::key prop; + if (prop.value == property_id) + std::invoke(func, px.value); + return prop.value != property_id; + }; + return (pc(ptype) && ...); + }, + _props + ); } - template - constexpr static bool is_visitor_v = - std::conjunction_v&>...>; + template + using is_visitor = std::conjunction< + std::is_invocable_r&>... + >; - template - constexpr static bool is_nothrow_visitor_v = - std::conjunction_v&>...>; + template + using is_nothrow_visitor = std::conjunction< + std::is_nothrow_invocable&>... + >; - template requires is_visitor_v - constexpr bool visit(Func && func) const noexcept(is_nothrow_visitor_v) { + template < + typename Func, + typename = std::enable_if_t::value> + > + constexpr bool visit(Func&& func) + const noexcept (is_nothrow_visitor::value) { return std::apply( [&func](const auto&... props) { - auto pc = [&func](const prop_type& px) { + auto pc = [&func](const auto& px) { + using ptype = std::remove_reference_t; + constexpr typename ptype::key prop; return std::invoke(func, prop, px.value); }; return (pc(props) &&...); }, - _props); + _props + ); } - template requires is_visitor_v - constexpr bool visit(Func&& func) noexcept(is_nothrow_visitor_v) { + template < + typename Func, + typename = std::enable_if_t::value> + > + constexpr bool visit(Func&& func) + noexcept (is_nothrow_visitor::value) { return std::apply( [&func](auto&... props) { - auto pc = [&func](prop_type& px) { + auto pc = [&func](auto& px) { + using ptype = std::remove_reference_t; + constexpr typename ptype::key prop; return std::invoke(func, prop, px.value); }; return (pc(props) && ...); }, - _props); + _props + ); } }; - } // end namespace async_mqtt5::prop #endif // !ASYNC_MQTT5_PROPERTY_TYPES_HPP diff --git a/include/async_mqtt5/types.hpp b/include/async_mqtt5/types.hpp index c3ae35f..1ba55a0 100644 --- a/include/async_mqtt5/types.hpp +++ b/include/async_mqtt5/types.hpp @@ -210,112 +210,112 @@ reason codes: /// \cond class connect_props : public prop::properties< - prop::session_expiry_interval, - prop::receive_maximum, - prop::maximum_packet_size, - prop::topic_alias_maximum, - prop::request_response_information, - prop::request_problem_information, - prop::user_property, - prop::authentication_method, - prop::authentication_data + prop::session_expiry_interval_t, + prop::receive_maximum_t, + prop::maximum_packet_size_t, + prop::topic_alias_maximum_t, + prop::request_response_information_t, + prop::request_problem_information_t, + prop::user_property_t, + prop::authentication_method_t, + prop::authentication_data_t > {}; class connack_props : public prop::properties< - prop::session_expiry_interval, - prop::receive_maximum, - prop::maximum_qos, - prop::retain_available, - prop::maximum_packet_size, - prop::assigned_client_identifier, - prop::topic_alias_maximum, - prop::reason_string, - prop::user_property, - prop::wildcard_subscription_available, - prop::subscription_identifier_available, - prop::shared_subscription_available, - prop::server_keep_alive, - prop::response_information, - prop::server_reference, - prop::authentication_method, - prop::authentication_data + prop::session_expiry_interval_t, + prop::receive_maximum_t, + prop::maximum_qos_t, + prop::retain_available_t, + prop::maximum_packet_size_t, + prop::assigned_client_identifier_t, + prop::topic_alias_maximum_t, + prop::reason_string_t, + prop::user_property_t, + prop::wildcard_subscription_available_t, + prop::subscription_identifier_available_t, + prop::shared_subscription_available_t, + prop::server_keep_alive_t, + prop::response_information_t, + prop::server_reference_t, + prop::authentication_method_t, + prop::authentication_data_t > {}; class publish_props : public prop::properties< - prop::payload_format_indicator, - prop::message_expiry_interval, - prop::content_type, - prop::response_topic, - prop::correlation_data, - prop::subscription_identifier, - prop::topic_alias, - prop::user_property + prop::payload_format_indicator_t, + prop::message_expiry_interval_t, + prop::content_type_t, + prop::response_topic_t, + prop::correlation_data_t, + prop::subscription_identifier_t, + prop::topic_alias_t, + prop::user_property_t > {}; // puback, pubcomp class puback_props : public prop::properties< - prop::reason_string, - prop::user_property + prop::reason_string_t, + prop::user_property_t > {}; class pubcomp_props : public prop::properties< - prop::reason_string, - prop::user_property + prop::reason_string_t, + prop::user_property_t > {}; class pubrec_props : public prop::properties< - prop::reason_string, - prop::user_property + prop::reason_string_t, + prop::user_property_t > {}; class pubrel_props : public prop::properties< - prop::reason_string, - prop::user_property + prop::reason_string_t, + prop::user_property_t > {}; class subscribe_props : public prop::properties< - prop::subscription_identifier, - prop::user_property + prop::subscription_identifier_t, + prop::user_property_t > {}; class suback_props : public prop::properties< - prop::reason_string, - prop::user_property + prop::reason_string_t, + prop::user_property_t > {}; class unsubscribe_props : public prop::properties< - prop::user_property + prop::user_property_t > {}; class unsuback_props : public prop::properties< - prop::reason_string, - prop::user_property + prop::reason_string_t, + prop::user_property_t > {}; class disconnect_props : public prop::properties< - prop::session_expiry_interval, - prop::reason_string, - prop::user_property, - prop::server_reference + prop::session_expiry_interval_t, + prop::reason_string_t, + prop::user_property_t, + prop::server_reference_t > {}; class auth_props : public prop::properties< - prop::authentication_method, - prop::authentication_data, - prop::reason_string, - prop::user_property + prop::authentication_method_t, + prop::authentication_data_t, + prop::reason_string_t, + prop::user_property_t > {}; class will_props : public prop::properties< - prop::will_delay_interval, - prop::payload_format_indicator, - prop::message_expiry_interval, - prop::content_type, - prop::response_topic, - prop::correlation_data, - prop::user_property + prop::will_delay_interval_t, + prop::payload_format_indicator_t, + prop::message_expiry_interval_t, + prop::content_type_t, + prop::response_topic_t, + prop::correlation_data_t, + prop::user_property_t >{}; /// \endcond