diff --git a/example/cpp20_coroutines.cpp b/example/cpp20_coroutines.cpp index 3cec2c8..33d21ec 100644 --- a/example/cpp20_coroutines.cpp +++ b/example/cpp20_coroutines.cpp @@ -1,8 +1,10 @@ +#include +#ifdef BOOST_ASIO_HAS_CO_AWAIT + #include #include #include #include -#include #include @@ -171,3 +173,5 @@ int main(int argc, char** argv) { ioc.run(); } + +#endif diff --git a/example/publisher.cpp b/example/publisher.cpp index 26d4d56..83374b8 100644 --- a/example/publisher.cpp +++ b/example/publisher.cpp @@ -10,6 +10,8 @@ #include +#ifdef BOOST_ASIO_HAS_CO_AWAIT + namespace asio = boost::asio; asio::awaitable client_publisher(asio::io_context& ioc) { @@ -53,3 +55,5 @@ int main() { } //] + +#endif diff --git a/example/receiver.cpp b/example/receiver.cpp index 4404bab..08f209c 100644 --- a/example/receiver.cpp +++ b/example/receiver.cpp @@ -10,6 +10,8 @@ #include +#ifdef BOOST_ASIO_HAS_CO_AWAIT + namespace asio = boost::asio; asio::awaitable client_receiver(asio::io_context& ioc) { @@ -78,3 +80,5 @@ int main() { } //] + +#endif diff --git a/include/async_mqtt5/detail/any_authenticator.hpp b/include/async_mqtt5/detail/any_authenticator.hpp index fb569d1..f48f8d4 100644 --- a/include/async_mqtt5/detail/any_authenticator.hpp +++ b/include/async_mqtt5/detail/any_authenticator.hpp @@ -4,6 +4,9 @@ #include #include +#include +#include + #include namespace async_mqtt5 { @@ -17,13 +20,23 @@ using auth_handler_type = asio::any_completion_handler< void (error_code ec, std::string auth_data) >; +template +using async_auth_sig = decltype( + std::declval().async_auth(std::declval()...) +); + template -concept is_authenticator = requires (T a) { - { - a.async_auth(auth_step_e {}, std::string {}, auth_handler_type {}) - } -> std::same_as; - { a.method() } -> std::same_as; -}; +using method_sig = decltype( + std::declval().method() +); + +template +constexpr bool is_authenticator = + boost::is_detected< + async_auth_sig, T, + auth_step_e, std::string, auth_handler_type + >::value && + boost::is_detected_convertible_v; class auth_fun_base { using auth_func = void(*)( @@ -36,13 +49,17 @@ public: ~auth_fun_base() = default; void async_auth( - auth_step_e step, std::string data, auth_handler_type auth_handler + auth_step_e step, std::string data, + auth_handler_type auth_handler ) { _auth_func(step, std::move(data), std::move(auth_handler), this); } }; -template +template < + typename Authenticator, + typename = std::enable_if_t> +> class auth_fun : public auth_fun_base { Authenticator _authenticator; @@ -72,7 +89,10 @@ class any_authenticator { public: any_authenticator() = default; - template + template < + typename Authenticator, + std::enable_if_t, bool> = true + > any_authenticator(Authenticator&& a) : _method(a.method()), _auth_fun( @@ -94,7 +114,7 @@ public: using Signature = void (error_code, std::string); auto initiation = []( - auto handler, any_authenticator& self, + auto handler, any_authenticator& self, auth_step_e step, std::string data ) { self._auth_fun->async_auth( @@ -110,4 +130,5 @@ public: } // end namespace async_mqtt5 + #endif // !ASYNC_MQTT5_ANY_AUTHENTICATOR diff --git a/include/async_mqtt5/detail/async_mutex.hpp b/include/async_mqtt5/detail/async_mutex.hpp index bdf0c5e..ca5865f 100644 --- a/include/async_mqtt5/detail/async_mutex.hpp +++ b/include/async_mqtt5/detail/async_mutex.hpp @@ -114,7 +114,7 @@ public: bool is_locked() const noexcept { return _locked.load(std::memory_order_relaxed); } - + // Schedules mutex for lock operation and return immediately. // Calls given completion handler when mutex is locked. // It's the responsibility of the completion handler to unlock the mutex. @@ -187,7 +187,8 @@ private: // or queues it for later execution otherwise. In both cases // the operation will be executed in a manner equivalent // to asio::post to avoid recursion. - void execute_or_queue(auto handler) noexcept { + template + void execute_or_queue(Handler&& handler) noexcept { std::unique_lock l { _thread_mutex }; tracked_op h { std::move(handler), _ex }; if (_locked.load(std::memory_order_relaxed)) { diff --git a/include/async_mqtt5/detail/async_traits.hpp b/include/async_mqtt5/detail/async_traits.hpp index efff434..5fcc1f5 100644 --- a/include/async_mqtt5/detail/async_traits.hpp +++ b/include/async_mqtt5/detail/async_traits.hpp @@ -1,7 +1,6 @@ #ifndef ASYNC_MQTT5_ASYNC_TRAITS_HPP #define ASYNC_MQTT5_ASYNC_TRAITS_HPP -#include #include #include @@ -10,15 +9,16 @@ #include +#include +#include +#include + #include namespace async_mqtt5 { namespace asio = boost::asio; -// TODO: move tls_handshake_type and assign_tls_sni to -// separate header - template struct tls_handshake_type {}; @@ -44,61 +44,94 @@ tracking_executor(const Handler& handler, const DfltExecutor& ex) { ); } +template +using async_write_sig = decltype( + std::declval().async_write(std::declval()...) +); + +constexpr auto write_handler_t = [](error_code, size_t) {}; + template -concept has_async_write = requires(T a) { - a.async_write( - std::declval(), - [](error_code, size_t) {} - ); -}; +constexpr bool has_async_write = boost::is_detected< + async_write_sig, T, B, decltype(write_handler_t) +>::value; -template -concept has_tls_handshake = requires(T a) { - a.async_handshake( - typename T::handshake_type{}, - [](error_code) {} - ); -}; -template -concept has_ws_handshake = requires(T a) { - a.async_handshake( - std::declval(), - std::declval(), - [](error_code) {} - ); -}; +constexpr auto handshake_handler_t = [](error_code) {}; template -concept has_tls_context = requires(T a) { - a.tls_context(); -}; +using tls_handshake_t = typename T::handshake_type; template -concept has_next_layer = requires(T a) { - a.next_layer(); -}; +using tls_handshake_type_of = boost::detected_or_t; + +template +using async_tls_handshake_sig = decltype( + std::declval().async_handshake(std::declval()...) +); template +constexpr bool has_tls_handshake = boost::is_detected< + async_tls_handshake_sig, T, tls_handshake_type_of, + decltype(handshake_handler_t) +>::value; + + +template +using async_ws_handshake_sig = decltype( + std::declval().async_handshake(std::declval()...) +); + +template +constexpr bool has_ws_handshake = boost::is_detected< + async_ws_handshake_sig, T, + std::string_view, std::string_view, + decltype(handshake_handler_t) +>::value; + + +template +using tls_context_sig = decltype( + std::declval().tls_context() +); + +template +constexpr bool has_tls_context = boost::is_detected< + tls_context_sig, T +>::value; + + +template +using next_layer_sig = decltype( + std::declval().next_layer() +); + +template +constexpr bool has_next_layer = boost::is_detected< + next_layer_sig, T +>::value; + +template struct next_layer_type { using type = T; }; template -requires has_next_layer -struct next_layer_type { +struct next_layer_type< + T, std::enable_if_t> +> { using type = typename std::remove_reference_t::next_layer_type; }; template -requires (!has_next_layer) -typename next_layer_type::type& next_layer(T&& a) { +typename next_layer_type>>::type& +next_layer(T&& a) { return a; } template -requires has_next_layer -typename next_layer_type::type& next_layer(T&& a) { +typename next_layer_type>>::type& +next_layer(T&& a) { return a.next_layer(); } @@ -110,23 +143,27 @@ lowest_layer_type& lowest_layer(S&& a) { return boost::beast::get_lowest_layer(std::forward(a)); } -template +template struct has_tls_layer_impl : std::false_type {}; template -requires has_tls_handshake -struct has_tls_layer_impl : std::true_type {}; +struct has_tls_layer_impl< + T, std::enable_if_t> +> : std::true_type {}; template -requires (!has_tls_handshake && has_next_layer) -struct has_tls_layer_impl : has_tls_layer_impl< - std::remove_cvref_t().next_layer())> +struct has_tls_layer_impl< + T, std::enable_if_t && has_next_layer> +> : has_tls_layer_impl< + boost::remove_cv_ref_t().next_layer())> > {}; template -concept has_tls_layer = has_tls_layer_impl>::value; +constexpr bool has_tls_layer = has_tls_layer_impl< + boost::remove_cv_ref_t +>::value; + -//TODO: move to appropriate place template < typename Stream, typename ConstBufferSequence, @@ -135,7 +172,6 @@ template < decltype(auto) async_write( Stream& stream, const ConstBufferSequence& buff, CompletionToken&& token ) { - // TODO: find layer that has async write method if constexpr (has_async_write) return stream.async_write( buff, std::forward(token) @@ -148,7 +184,7 @@ decltype(auto) async_write( template void setup_tls_sni(const authority_path& ap, TlsContext& ctx, Stream& s) { - if constexpr (has_tls_handshake) + if constexpr (has_tls_handshake) assign_tls_sni(ap, ctx, s); else if constexpr (has_next_layer) setup_tls_sni(ap, ctx, next_layer(s)); diff --git a/include/async_mqtt5/detail/cancellable_handler.hpp b/include/async_mqtt5/detail/cancellable_handler.hpp index 02b072f..c854ffd 100644 --- a/include/async_mqtt5/detail/cancellable_handler.hpp +++ b/include/async_mqtt5/detail/cancellable_handler.hpp @@ -26,8 +26,10 @@ class cancellable_handler { cancellable_handler* _owner; op_state( - Handler&& handler, const Executor& ex, cancellable_handler* owner - ) : _handler(std::move(handler)), + Handler&& handler, const Executor& ex, + cancellable_handler* owner + ) : + _handler(std::move(handler)), _handler_ex(tracking_executor(_handler, ex)), _owner(owner) {} @@ -46,17 +48,18 @@ class cancellable_handler { {} void operator()(asio::cancellation_type_t type) { - if ((type & asio::cancellation_type_t::terminal) == - asio::cancellation_type_t::none) + if ( + (type & asio::cancellation_type_t::terminal) == + asio::cancellation_type_t::none + ) return; + auto op = [wptr = _state_weak_ptr]() { if (auto state = wptr.lock()) state->cancel_op(); }; - asio::dispatch( - _executor, - std::move(op) - ); + + asio::dispatch(_executor, std::move(op)); } }; @@ -69,9 +72,11 @@ public: _state = std::allocate_shared( alloc, std::move(handler), ex, this ); + auto slot = asio::get_associated_cancellation_slot(_state->_handler); if (slot.is_connected()) slot.template emplace(_state, ex); + _executor = ex; } @@ -106,11 +111,14 @@ public: auto handler_ex = std::move(_state->_handler_ex); _state.reset(); - auto op = std::apply([&h](auto... args) { - return asio::prepend( - std::move(h), asio::error::operation_aborted, args... - ); - }, CancelArgs {}); + auto op = std::apply( + [&h](auto... args) { + return asio::prepend( + std::move(h), asio::error::operation_aborted, args... + ); + }, + CancelArgs {} + ); asio::dispatch(handler_ex, std::move(op)); } diff --git a/include/async_mqtt5/detail/channel_traits.hpp b/include/async_mqtt5/detail/channel_traits.hpp index e1732e0..95449c5 100644 --- a/include/async_mqtt5/detail/channel_traits.hpp +++ b/include/async_mqtt5/detail/channel_traits.hpp @@ -19,7 +19,9 @@ public: bounded_deque() = default; bounded_deque(size_t n) : _buffer(n) {} - size_t size() const { return _buffer.size(); } + size_t size() const { + return _buffer.size(); + } template void push_back(E&& e) { @@ -28,13 +30,21 @@ public: _buffer.push_back(std::forward(e)); } - void pop_front() { _buffer.pop_front(); } + void pop_front() { + _buffer.pop_front(); + } - void clear() { _buffer.clear(); } + void clear() { + _buffer.clear(); + } - const auto& front() const noexcept { return _buffer.front(); } + const auto& front() const noexcept { + return _buffer.front(); + } - auto& front() noexcept { return _buffer.front(); } + auto& front() noexcept { + return _buffer.front(); + } }; template @@ -48,6 +58,7 @@ struct channel_traits { template struct channel_traits { static_assert(sizeof...(Args) > 0); + template struct rebind { using other = channel_traits; diff --git a/include/async_mqtt5/detail/control_packet.hpp b/include/async_mqtt5/detail/control_packet.hpp index 2144e96..b0c0440 100644 --- a/include/async_mqtt5/detail/control_packet.hpp +++ b/include/async_mqtt5/detail/control_packet.hpp @@ -109,7 +109,10 @@ public: class packet_id_allocator { struct interval { uint16_t start, end; - interval(uint16_t start, uint16_t end) : start(start), end(end) {} + + interval(uint16_t start, uint16_t end) : + start(start), end(end) + {} }; std::mutex _mtx; @@ -152,7 +155,8 @@ public: *end_p = it->end; _free_ids.erase(it); } - } else { + } + else { if (!end_p) _free_ids.insert(it, interval(pid, pid - 1)); else diff --git a/include/async_mqtt5/detail/ring_buffer.hpp b/include/async_mqtt5/detail/ring_buffer.hpp index ac7faab..884b3c1 100644 --- a/include/async_mqtt5/detail/ring_buffer.hpp +++ b/include/async_mqtt5/detail/ring_buffer.hpp @@ -49,17 +49,32 @@ public: ring_buffer() = default; - template requires std::is_constructible_v - explicit ring_buffer(size_type capacity, Alloc&& allocator) : _alloc((Alloc&&)allocator) { + template < + typename Alloc, + std::enable_if_t< + std::is_constructible_v, bool + > = true + > + explicit ring_buffer(size_type capacity, Alloc&& allocator) : + _alloc((Alloc&&)allocator) + { reserve(capacity); } - template requires std::is_constructible_v - explicit ring_buffer(Alloc&& allocator) : _alloc((Alloc&&)allocator) { - } + template < + typename Alloc, + std::enable_if_t< + std::is_constructible_v, + bool + > = true + > + explicit ring_buffer(Alloc&& allocator) : + _alloc((Alloc&&)allocator) + {} - explicit ring_buffer(size_type capacity) : ring_buffer(capacity, allocator_type { }) { - } + explicit ring_buffer(size_type capacity) : + ring_buffer(capacity, allocator_type { }) + {} ring_buffer(ring_buffer&& other) noexcept : _buff { std::exchange(other._buff, nullptr) }, @@ -67,8 +82,7 @@ public: _back { std::exchange(other._back, 0) }, _capacity { std::exchange(other._capacity, 0) }, _alloc { other._alloc } - { - } + { } ~ring_buffer() { if (_buff) { @@ -78,7 +92,9 @@ public: } ring_buffer& operator=(ring_buffer&& other) noexcept { - if constexpr (allocator_traits::propagate_on_container_move_assignment::value) { + if constexpr ( + allocator_traits::propagate_on_container_move_assignment::value + ) { clear_and_exchange_with(other); _alloc = other._alloc; } @@ -220,7 +236,7 @@ public: _front = std::exchange(b._front, _front); _back = std::exchange(b._back, _back); _capacity = std::exchange(b._capacity, _capacity); - _alloc = std::exchange(b._alloc, _alloc); //?? allocator_traits::propagate_on_container_swap::value + _alloc = std::exchange(b._alloc, _alloc); } void reserve(size_type new_capacity) noexcept { @@ -244,7 +260,9 @@ public: if (_buff) { for (size_type i = 0; i < s; ++i) { auto& v = operator[](i); - allocator_traits::construct(_alloc, &new_buff[i], (value_type&&)v); + allocator_traits::construct( + _alloc, &new_buff[i], (value_type&&)v + ); allocator_traits::destroy(_alloc, &v); } _alloc.deallocate(_buff, _capacity); @@ -261,7 +279,7 @@ private: } void grow_if_needed() { - if (!_buff || full()) [[unlikely]] + if (!_buff || full()) reserve(_capacity == 0 ? min_capacity : _capacity * 2); } @@ -278,13 +296,14 @@ private: size_type _front = npos; size_type _back = 0; size_type _capacity = 0; - [[no_unique_address]] allocator_type _alloc; + allocator_type _alloc; static constexpr size_type npos = static_cast(-1); static constexpr size_type min_capacity = 4; }; -template template +template +template class ring_buffer::_iter { public: using value_type = T; @@ -341,8 +360,9 @@ public: private: friend class ring_buffer; - _iter(ring_pointer b, size_type i) : _b(b), _i(i), _p(&b->operator[](i)) { - } + _iter(ring_pointer b, size_type i) : + _b(b), _i(i), _p(&b->operator[](i)) + {} ring_pointer _b = nullptr; size_type _i = npos; @@ -352,7 +372,7 @@ private: return a._i == b._i; } - friend auto operator<=>(const _iter& a, const _iter& b) noexcept { + friend auto operator<(const _iter& a, const _iter& b) noexcept { return difference_type(a._i - b._i); } @@ -370,32 +390,38 @@ private: }; template -typename ring_buffer::iterator ring_buffer::begin() noexcept { +typename ring_buffer::iterator ring_buffer::begin() +noexcept { return { this, 0 }; } template -typename ring_buffer::iterator ring_buffer::end() noexcept { +typename ring_buffer::iterator ring_buffer::end() +noexcept { return { this, size() }; } template -typename ring_buffer::const_iterator ring_buffer::begin() const noexcept { +typename ring_buffer::const_iterator ring_buffer::begin() +const noexcept { return { this, 0 }; } template -typename ring_buffer::const_iterator ring_buffer::end() const noexcept { +typename ring_buffer::const_iterator ring_buffer::end() +const noexcept { return { this, size() }; } template -typename ring_buffer::const_iterator ring_buffer::cbegin() const noexcept { +typename ring_buffer::const_iterator ring_buffer::cbegin() +const noexcept { return { this, 0 }; } template -typename ring_buffer::const_iterator ring_buffer::cend() const noexcept { +typename ring_buffer::const_iterator ring_buffer::cend() +const noexcept { return { this, size() }; } @@ -405,8 +431,8 @@ namespace std { template void swap( - async_mqtt5::detail::ring_buffer& a, - async_mqtt5::detail::ring_buffer& b) + async_mqtt5::detail::ring_buffer& a, + async_mqtt5::detail::ring_buffer& b) { a.swap(b); } diff --git a/include/async_mqtt5/detail/utf8_mqtt.hpp b/include/async_mqtt5/detail/utf8_mqtt.hpp index 29dcffe..a15554c 100644 --- a/include/async_mqtt5/detail/utf8_mqtt.hpp +++ b/include/async_mqtt5/detail/utf8_mqtt.hpp @@ -1,7 +1,6 @@ #ifndef ASYNC_MQTT5_UTF8_MQTT_HPP #define ASYNC_MQTT5_UTF8_MQTT_HPP -#include #include namespace async_mqtt5::detail { diff --git a/include/async_mqtt5/error.hpp b/include/async_mqtt5/error.hpp index 37f7a49..aa94825 100644 --- a/include/async_mqtt5/error.hpp +++ b/include/async_mqtt5/error.hpp @@ -50,7 +50,7 @@ namespace client { /** * \brief MQTT client error codes. * - * \details Represents error that occur on the client side. + * \details Represents error that occur on the client side. */ enum class error : int { /// \cond INTERNAL @@ -162,7 +162,7 @@ public: {} constexpr explicit reason_code(uint8_t code) : _code(code) {} -/// \endcond +/// \endcond /// Copy constructor. reason_code(const reason_code&) = default; @@ -393,7 +393,7 @@ constexpr reason_code banned { 0x8a }; /** The Server is shutting down. */ constexpr reason_code server_shutting_down { 0x8b }; -/** The authentication method is not supported or +/** The authentication method is not supported or does not match the method currently in use. */ constexpr reason_code bad_authentication_method { 0x8c }; diff --git a/include/async_mqtt5/impl/assemble_op.hpp b/include/async_mqtt5/impl/assemble_op.hpp index 0db42b5..819da0e 100644 --- a/include/async_mqtt5/impl/assemble_op.hpp +++ b/include/async_mqtt5/impl/assemble_op.hpp @@ -15,7 +15,7 @@ #include #include -#include +#include namespace async_mqtt5::detail { @@ -199,6 +199,7 @@ private: bool is_reply = code != control_code_e::publish && code != control_code_e::auth && code != control_code_e::disconnect; + if (is_reply) { auto packet_id = decoders::decode_packet_id(first).value(); _svc._replies.dispatch(error_code {}, code, packet_id, first, last); diff --git a/include/async_mqtt5/impl/async_sender.hpp b/include/async_mqtt5/impl/async_sender.hpp index dbc1c9d..8f6cacc 100644 --- a/include/async_mqtt5/impl/async_sender.hpp +++ b/include/async_mqtt5/impl/async_sender.hpp @@ -66,7 +66,9 @@ public: } private: - bool prioritized() const { return _flags & send_flag::prioritized; } + bool prioritized() const { + return _flags & send_flag::prioritized; + } }; diff --git a/include/async_mqtt5/impl/client_service.hpp b/include/async_mqtt5/impl/client_service.hpp index 034ac61..349becc 100644 --- a/include/async_mqtt5/impl/client_service.hpp +++ b/include/async_mqtt5/impl/client_service.hpp @@ -17,12 +17,19 @@ namespace async_mqtt5::detail { namespace asio = boost::asio; -template +template < + typename StreamType, typename TlsContext, + typename Enable = void +> class stream_context; -template -requires has_tls_layer -class stream_context { +template < + typename StreamType, typename TlsContext +> +class stream_context< + StreamType, TlsContext, + std::enable_if_t> +> { using tls_context_type = TlsContext; mqtt_ctx _mqtt_context; @@ -76,8 +83,10 @@ public: }; template -requires (!has_tls_layer) -class stream_context { +class stream_context< + StreamType, std::monostate, + std::enable_if_t> +> { mqtt_ctx _mqtt_context; public: explicit stream_context(std::monostate) {} @@ -189,8 +198,11 @@ public: return _stream.get_executor(); } - decltype(auto) tls_context() - requires (!std::is_same_v) { + template < + typename Ctx = TlsContext, + std::enable_if_t, bool> = true + > + decltype(auto) tls_context() { return _stream_context.tls_context(); } @@ -215,7 +227,10 @@ public: _stream.brokers(std::move(hosts), default_port); } - template + template < + typename Authenticator, + std::enable_if_t, bool> = true + > void authenticator(Authenticator&& authenticator) { if (!is_open()) _stream_context.authenticator( @@ -339,7 +354,9 @@ public: } bool channel_store_error(error_code ec) { - return _rec_channel.try_send(ec, std::string {}, std::string {}, publish_props {}); + return _rec_channel.try_send( + ec, std::string {}, std::string {}, publish_props {} + ); } template diff --git a/include/async_mqtt5/impl/internal/codecs/base_decoders.hpp b/include/async_mqtt5/impl/codecs/base_decoders.hpp similarity index 76% rename from include/async_mqtt5/impl/internal/codecs/base_decoders.hpp rename to include/async_mqtt5/impl/codecs/base_decoders.hpp index 9efdb39..9ed0f76 100644 --- a/include/async_mqtt5/impl/internal/codecs/base_decoders.hpp +++ b/include/async_mqtt5/impl/codecs/base_decoders.hpp @@ -3,12 +3,10 @@ #include #include - #include #include - -#include +#include namespace async_mqtt5::decoders { @@ -51,7 +49,7 @@ auto type_parse(It& first, const It last, const Parser& p) { rv = std::move(value); return rv; } - + template auto type_parse(It& first, const It last, const Parser& p) { @@ -72,29 +70,39 @@ constexpr auto to(T& arg) { if constexpr (is_boost_iterator) arg = T { x3::_attr(ctx).begin(), x3::_attr(ctx).end() }; else - arg = x3::_attr(ctx); + arg = x3::_attr(ctx); }; } -template +template class scope_limit {}; template -requires (x3::traits::is_parser::value) -class scope_limit : - public x3::unary_parser> { - +class scope_limit< + LenParser, Subject, + std::enable_if_t::value> +> : + public x3::unary_parser> +{ using base_type = x3::unary_parser>; LenParser _lp; public: - using ctx_type = decltype(x3::make_context(std::declval())); - using attribute_type = typename x3::traits::attribute_of::type; + using ctx_type = + decltype(x3::make_context(std::declval())); + using attribute_type = + typename x3::traits::attribute_of::type; + static bool const has_attribute = true; - scope_limit(const LenParser& lp, const Subject& subject) : base_type(subject), _lp(lp) {} + scope_limit(const LenParser& lp, const Subject& subject) : + base_type(subject), _lp(lp) + {} template - bool parse(It& first, const It last, const Ctx& ctx, RCtx& rctx, Attr& attr) const { + bool parse( + It& first, const It last, + const Ctx& ctx, RCtx& rctx, Attr& attr + ) const { It iter = first; typename x3::traits::attribute_of::type len; @@ -112,21 +120,31 @@ public: }; template -requires (std::is_arithmetic_v) -class scope_limit : - public x3::unary_parser> { - +class scope_limit< + Size, Subject, + std::enable_if_t> +> : + public x3::unary_parser> +{ using base_type = x3::unary_parser>; size_t _limit; public: - using ctx_type = decltype(x3::make_context(std::declval())); - using attribute_type = typename x3::traits::attribute_of::type; + using ctx_type = + decltype(x3::make_context(std::declval())); + using attribute_type = + typename x3::traits::attribute_of::type; + static bool const has_attribute = true; - scope_limit(Size limit, const Subject& subject) : base_type(subject), _limit(limit) {} + scope_limit(Size limit, const Subject& subject) : + base_type(subject), _limit(limit) + {} template - bool parse(It& first, const It last, const Ctx& ctx, RCtx& rctx, Attr& attr) const { + bool parse( + It& first, const It last, + const Ctx& ctx, RCtx& rctx, Attr& attr + ) const { It iter = first; if (iter + _limit > last) @@ -140,7 +158,7 @@ public: } }; -template +template struct scope_limit_gen { template auto operator[](const Subject& p) const { @@ -150,8 +168,10 @@ struct scope_limit_gen { }; template -requires (std::is_arithmetic_v) -struct scope_limit_gen { +struct scope_limit_gen< + Size, + std::enable_if_t> +> { template auto operator[](const Subject& p) const { return scope_limit { limit, x3::as_parser(p) }; @@ -159,19 +179,22 @@ struct scope_limit_gen { Size limit; }; -template -requires (x3::traits::is_parser::value) +template < + typename Parser, + std::enable_if_t::value, bool> = true +> scope_limit_gen scope_limit_(const Parser& p) { return { p }; } -template -requires (std::is_arithmetic_v) +template < + typename Size, + std::enable_if_t, bool> = true +> scope_limit_gen scope_limit_(Size limit) { return { limit }; } - struct verbatim_parser : x3::parser { using attribute_type = std::string; static bool const has_attribute = true; @@ -191,7 +214,10 @@ struct varint_parser : x3::parser { static bool const has_attribute = true; template - bool parse(It& first, const It last, const Ctx& ctx, RCtx& rctx, Attr& attr) const { + bool parse( + It& first, const It last, + const Ctx& ctx, RCtx& rctx, Attr& attr + ) const { It iter = first; x3::skip_over(iter, last, ctx); @@ -229,7 +255,10 @@ struct len_prefix_parser : x3::parser { static bool const has_attribute = true; template - bool parse(It& first, const It last, const Ctx& ctx, RCtx& rctx, Attr& attr) const { + bool parse( + It& first, const It last, + const Ctx& ctx, RCtx& rctx, Attr& attr + ) const { It iter = first; x3::skip_over(iter, last, ctx); @@ -238,7 +267,7 @@ struct len_prefix_parser : x3::parser { if (std::distance(iter, last) < len) return false; } - else + else return false; attr = std::string(iter, iter + len); @@ -251,26 +280,34 @@ constexpr len_prefix_parser utf8_{}; constexpr len_prefix_parser binary_{}; /* - Boost Spirit incorrectly deduces atribute type for a parser of the form + Boost Spirit incorrectly deduces atribute type for a parser of the form (eps(a) | parser1) >> (eps(b) | parser) - and we had to create if_ parser to remedy the issue + and we had to create if_ parser to remedy the issue */ template -class conditional_parser : public x3::unary_parser> { +class conditional_parser : + public x3::unary_parser> { + using base_type = x3::unary_parser>; bool _condition; public: - using ctx_type = decltype(x3::make_context(std::declval())); - using subject_attr_type = typename x3::traits::attribute_of::type; + using ctx_type = + decltype(x3::make_context(std::declval())); + using subject_attr_type = + typename x3::traits::attribute_of::type; using attribute_type = boost::optional; static bool const has_attribute = true; - conditional_parser(const Subject& s, bool condition) : base_type(s), _condition(condition) {} + conditional_parser(const Subject& s, bool condition) : + base_type(s), _condition(condition) {} template - bool parse(It& first, const It last, const Ctx& ctx, RCtx& rctx, Attr& attr) const { + bool parse( + It& first, const It last, + const Ctx& ctx, RCtx& rctx, Attr& attr + ) const { if (!_condition) return true; @@ -308,12 +345,17 @@ namespace basic = async_mqtt5::decoders::basic; namespace detail { template -bool parse_to_prop(It& iter, const It last, const Ctx& ctx, RCtx& rctx, Prop& prop) { +bool parse_to_prop( + It& iter, const It last, + const Ctx& ctx, RCtx& rctx, Prop& prop +) { using prop_type = decltype(prop); bool rv = false; if constexpr (is_optional) { - using value_type = typename std::remove_reference_t::value_type; + using value_type = + typename std::remove_reference_t::value_type; + if constexpr (std::is_same_v) { uint8_t attr; rv = x3::byte_.parse(iter, last, ctx, rctx, attr); @@ -363,7 +405,10 @@ public: static bool const has_attribute = true; template - bool parse(It& first, const It last, const Ctx& ctx, RCtx& rctx, Attr& attr) const { + bool parse( + It& first, const It last, + const Ctx& ctx, RCtx& rctx, Attr& attr + ) const { It iter = first; x3::skip_over(iter, last, ctx); @@ -379,13 +424,18 @@ public: // attr = Props{}; while (iter < scoped_last) { - uint8_t prop_id = *iter++; + uint8_t prop_id = *iter++; bool rv = true; It saved = iter; - attr.apply_on(prop_id, [&rv, &iter, scoped_last, &ctx, &rctx](auto& prop) { - rv = detail::parse_to_prop(iter, scoped_last, ctx, rctx, prop); - }); + attr.apply_on( + prop_id, + [&rv, &iter, scoped_last, &ctx, &rctx](auto& prop) { + rv = detail::parse_to_prop( + iter, scoped_last, ctx, rctx, prop + ); + } + ); // either rv = false or property with prop_id was not found if (!rv || iter == saved) @@ -399,7 +449,7 @@ public: template -constexpr auto props_ = prop_parser{}; +constexpr auto props_ = prop_parser {}; } // end namespace prop diff --git a/include/async_mqtt5/impl/internal/codecs/base_encoders.hpp b/include/async_mqtt5/impl/codecs/base_encoders.hpp similarity index 87% rename from include/async_mqtt5/impl/internal/codecs/base_encoders.hpp rename to include/async_mqtt5/impl/codecs/base_encoders.hpp index 9169c39..6908bfe 100644 --- a/include/async_mqtt5/impl/internal/codecs/base_encoders.hpp +++ b/include/async_mqtt5/impl/codecs/base_encoders.hpp @@ -4,10 +4,13 @@ #include #include +#include #include +#include +#include #include -#include +#include namespace async_mqtt5::encoders { @@ -54,10 +57,13 @@ public: flag_def(repr val) : _val(val) {} flag_def() = default; - template - requires (is_optional) + template < + typename T, + typename projection = boost::identity, + std::enable_if_t, bool> = true + > auto operator()(T&& value, projection proj = {}) const { - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { repr val = value.has_value(); return flag_def { val }; } @@ -68,8 +74,11 @@ public: } } - template - requires (!is_optional) + template < + typename T, + typename projection = boost::identity, + std::enable_if_t, bool> = true + > auto operator()(T&& value, projection proj = {}) const { auto val = static_cast(std::invoke(proj, value)); return flag_def { val }; @@ -157,7 +166,7 @@ public: auto operator()(T&& val, projection proj) const { if constexpr (is_optional) { using rv_type = std::invoke_result_t< - projection, typename std::remove_cvref_t::value_type + projection, typename boost::remove_cv_ref_t::value_type >; if (val.has_value()) return (*this)(std::invoke(proj, *val)); @@ -181,7 +190,9 @@ class array_val : public encoder { T _val; bool _with_length; public: - array_val(T val, bool with_length) : _val(val), _with_length(with_length) { + array_val(T val, bool with_length) : + _val(val), _with_length(with_length) + { static_assert( std::is_reference_v || std::is_same_v ); @@ -204,11 +215,15 @@ public: } private: + template + using has_size = decltype(std::declval().size()); + template static size_t val_length(U&& val) { - if constexpr (std::same_as, const char*>) + if constexpr (std::is_same_v, const char*>) return std::strlen(val); - if constexpr (requires { val.size(); }) + + if constexpr (boost::is_detected_exact_v) return val.size(); else // fallback to type const char (&)[N] (substract 1 for trailing 0) return sizeof(val) - 1; @@ -243,7 +258,7 @@ public: auto operator()(T&& val, projection proj) const { if constexpr (is_optional) { using rv_type = std::invoke_result_t< - projection, typename std::remove_cvref_t::value_type + projection, typename boost::remove_cv_ref_t::value_type >; if (val.has_value()) return (*this)(std::invoke(proj, *val)); @@ -263,12 +278,13 @@ constexpr auto binary_ = array_def{}; // for now constexpr auto verbatim_ = array_def{}; -template +template class composed_val : public encoder { T _lhs; U _rhs; public: composed_val(T lhs, U rhs) : - _lhs(std::forward(lhs)), _rhs(std::forward(rhs)) {} + _lhs(std::forward(lhs)), _rhs(std::forward(rhs)) + {} size_t byte_size() const { return _lhs.byte_size() + _rhs.byte_size(); @@ -280,17 +296,22 @@ public: } }; -template -requires ( - std::derived_from, encoder> && - std::derived_from, encoder> -) +template < + typename T, typename U, + std::enable_if_t< + std::is_base_of_v> && + std::is_base_of_v>, + bool + > = true +> inline auto operator&(T&& t, U&& u) { return composed_val(std::forward(t), std::forward(u)); } -template -requires (std::derived_from, encoder>) +template < + typename T, + std::enable_if_t>, bool> = true +> std::string& operator<<(std::string& s, T&& t) { return t.encode(s); } @@ -347,7 +368,7 @@ 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>, @@ -376,14 +397,18 @@ constexpr auto encoder_for_prop = typename std::tuple_element_t< >::value {}; -template +template class prop_val; -template -requires (!is_vector && is_optional) -class prop_val : public basic::encoder { +template < + typename T, pp::property_type p +> +class prop_val< + T, p, + std::enable_if_t && is_optional> +> : public basic::encoder { // T is always std::optional - using opt_type = typename std::remove_cvref_t::value_type; + using opt_type = typename boost::remove_cv_ref_t::value_type; // allows T to be reference type to std::optional static inline std::optional nulltype; T _val; @@ -408,11 +433,15 @@ public: } }; -template -requires (is_vector) -class prop_val : public basic::encoder { +template < + typename T, pp::property_type p +> +class prop_val< + T, p, + std::enable_if_t> +> : public basic::encoder { // allows T to be reference type to std::vector - static inline std::remove_cvref_t nulltype; + static inline boost::remove_cv_ref_t nulltype; T _val; public: prop_val(T val) : _val(val) { @@ -464,7 +493,7 @@ class props_val : public basic::encoder { ); } - template + template auto apply_each(Func&& func) const { return std::apply([&func](const auto&... props) { return (std::invoke(func, props), ...); @@ -516,7 +545,7 @@ public: if (prop_container.has_value()) return (*this)(*prop_container); return props_val< - const typename std::remove_cvref_t::value_type& + const typename boost::remove_cv_ref_t::value_type& >(true); } else { diff --git a/include/async_mqtt5/impl/internal/codecs/message_decoders.hpp b/include/async_mqtt5/impl/codecs/message_decoders.hpp similarity index 97% rename from include/async_mqtt5/impl/internal/codecs/message_decoders.hpp rename to include/async_mqtt5/impl/codecs/message_decoders.hpp index 73672fa..280e8ae 100644 --- a/include/async_mqtt5/impl/internal/codecs/message_decoders.hpp +++ b/include/async_mqtt5/impl/codecs/message_decoders.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include namespace async_mqtt5::decoders { @@ -131,9 +131,9 @@ inline std::optional decode_publish( uint8_t flags = control_byte & 0b1111; auto qos = qos_e((flags >> 1) & 0b11); - auto publish_ = basic::scope_limit_(remain_length)[ - basic::utf8_ >> basic::if_(qos != qos_e::at_most_once)[x3::big_word] >> x3::attr(flags) >> - prop::props_ >> basic::verbatim_ + auto publish_ = basic::scope_limit_(remain_length)[ + basic::utf8_ >> basic::if_(qos != qos_e::at_most_once)[x3::big_word] >> + x3::attr(flags) >> prop::props_ >> basic::verbatim_ ]; return type_parse(it, it + remain_length, publish_); } diff --git a/include/async_mqtt5/impl/internal/codecs/message_encoders.hpp b/include/async_mqtt5/impl/codecs/message_encoders.hpp similarity index 99% rename from include/async_mqtt5/impl/internal/codecs/message_encoders.hpp rename to include/async_mqtt5/impl/codecs/message_encoders.hpp index 0bd6892..df17731 100644 --- a/include/async_mqtt5/impl/internal/codecs/message_encoders.hpp +++ b/include/async_mqtt5/impl/codecs/message_encoders.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include namespace async_mqtt5::encoders { diff --git a/include/async_mqtt5/impl/codecs/traits.hpp b/include/async_mqtt5/impl/codecs/traits.hpp new file mode 100644 index 0000000..1644247 --- /dev/null +++ b/include/async_mqtt5/impl/codecs/traits.hpp @@ -0,0 +1,42 @@ +#ifndef ASYNC_MQTT5_TRAITS_HPP +#define ASYNC_MQTT5_TRAITS_HPP + +#include +#include + +#include +#include + + +namespace async_mqtt5 { + +template +constexpr bool is_optional_impl = false; + +template +constexpr bool is_optional_impl> = true; + +template +constexpr bool is_optional = is_optional_impl>; + +template typename> +constexpr bool is_specialization = false; + +template