Downgrade C++ standard requirement to C++17

Summary: related to T13242

Reviewers: ivica

Reviewed By: ivica

Subscribers: miljen, iljazovic

Differential Revision: https://repo.mireo.local/D26860
This commit is contained in:
Korina Šimičević
2023-12-07 09:32:34 +01:00
parent 3640a4fb2a
commit e849166599
45 changed files with 699 additions and 914 deletions

View File

@ -1,8 +1,10 @@
#include <boost/asio/use_awaitable.hpp>
#ifdef BOOST_ASIO_HAS_CO_AWAIT
#include <boost/asio/as_tuple.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <boost/asio/ip/tcp.hpp>
@ -171,3 +173,5 @@ int main(int argc, char** argv) {
ioc.run();
}
#endif

View File

@ -10,6 +10,8 @@
#include <async_mqtt5.hpp>
#ifdef BOOST_ASIO_HAS_CO_AWAIT
namespace asio = boost::asio;
asio::awaitable<void> client_publisher(asio::io_context& ioc) {
@ -53,3 +55,5 @@ int main() {
}
//]
#endif

View File

@ -10,6 +10,8 @@
#include <async_mqtt5.hpp>
#ifdef BOOST_ASIO_HAS_CO_AWAIT
namespace asio = boost::asio;
asio::awaitable<void> client_receiver(asio::io_context& ioc) {
@ -78,3 +80,5 @@ int main() {
}
//]
#endif

View File

@ -4,6 +4,9 @@
#include <boost/asio/any_completion_handler.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/type_traits/is_detected.hpp>
#include <boost/type_traits/is_detected_convertible.hpp>
#include <async_mqtt5/types.hpp>
namespace async_mqtt5 {
@ -17,13 +20,23 @@ using auth_handler_type = asio::any_completion_handler<
void (error_code ec, std::string auth_data)
>;
template <typename T, typename ...Ts>
using async_auth_sig = decltype(
std::declval<T>().async_auth(std::declval<Ts>()...)
);
template <typename T>
concept is_authenticator = requires (T a) {
{
a.async_auth(auth_step_e {}, std::string {}, auth_handler_type {})
} -> std::same_as<void>;
{ a.method() } -> std::same_as<std::string_view>;
};
using method_sig = decltype(
std::declval<T>().method()
);
template <typename T>
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<std::string_view, method_sig, T>;
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 <is_authenticator Authenticator>
template <
typename Authenticator,
typename = std::enable_if_t<is_authenticator<Authenticator>>
>
class auth_fun : public auth_fun_base {
Authenticator _authenticator;
@ -72,7 +89,10 @@ class any_authenticator {
public:
any_authenticator() = default;
template <detail::is_authenticator Authenticator>
template <
typename Authenticator,
std::enable_if_t<detail::is_authenticator<Authenticator>, 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

View File

@ -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 <typename Handler>
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)) {

View File

@ -1,7 +1,6 @@
#ifndef ASYNC_MQTT5_ASYNC_TRAITS_HPP
#define ASYNC_MQTT5_ASYNC_TRAITS_HPP
#include <concepts>
#include <type_traits>
#include <boost/asio/associated_executor.hpp>
@ -10,15 +9,16 @@
#include <boost/beast/core/stream_traits.hpp>
#include <boost/type_traits/detected_or.hpp>
#include <boost/type_traits/is_detected.hpp>
#include <boost/type_traits/remove_cv_ref.hpp>
#include <async_mqtt5/types.hpp>
namespace async_mqtt5 {
namespace asio = boost::asio;
// TODO: move tls_handshake_type and assign_tls_sni to
// separate header
template <typename StreamType>
struct tls_handshake_type {};
@ -44,61 +44,94 @@ tracking_executor(const Handler& handler, const DfltExecutor& ex) {
);
}
template <typename T, typename ...Ts>
using async_write_sig = decltype(
std::declval<T&>().async_write(std::declval<Ts>()...)
);
constexpr auto write_handler_t = [](error_code, size_t) {};
template <typename T, typename B>
concept has_async_write = requires(T a) {
a.async_write(
std::declval<B>(),
[](error_code, size_t) {}
);
};
constexpr bool has_async_write = boost::is_detected<
async_write_sig, T, B, decltype(write_handler_t)
>::value;
template<typename T>
concept has_tls_handshake = requires(T a) {
a.async_handshake(
typename T::handshake_type{},
[](error_code) {}
);
};
template<typename T>
concept has_ws_handshake = requires(T a) {
a.async_handshake(
std::declval<std::string_view>(),
std::declval<std::string_view>(),
[](error_code) {}
);
};
constexpr auto handshake_handler_t = [](error_code) {};
template <typename T>
concept has_tls_context = requires(T a) {
a.tls_context();
};
using tls_handshake_t = typename T::handshake_type;
template <typename T>
concept has_next_layer = requires(T a) {
a.next_layer();
};
using tls_handshake_type_of = boost::detected_or_t<void, tls_handshake_t, T>;
template <typename T, typename ...Ts>
using async_tls_handshake_sig = decltype(
std::declval<T&>().async_handshake(std::declval<Ts>()...)
);
template <typename T>
constexpr bool has_tls_handshake = boost::is_detected<
async_tls_handshake_sig, T, tls_handshake_type_of<T>,
decltype(handshake_handler_t)
>::value;
template <typename T, typename ...Ts>
using async_ws_handshake_sig = decltype(
std::declval<T&>().async_handshake(std::declval<Ts>()...)
);
template <typename T>
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 <typename T>
using tls_context_sig = decltype(
std::declval<T&>().tls_context()
);
template <typename T>
constexpr bool has_tls_context = boost::is_detected<
tls_context_sig, T
>::value;
template <typename T>
using next_layer_sig = decltype(
std::declval<T&>().next_layer()
);
template <typename T>
constexpr bool has_next_layer = boost::is_detected<
next_layer_sig, T
>::value;
template <typename T, typename Enable = void>
struct next_layer_type {
using type = T;
};
template <typename T>
requires has_next_layer<T>
struct next_layer_type<T> {
struct next_layer_type<
T, std::enable_if_t<has_next_layer<T>>
> {
using type = typename std::remove_reference_t<T>::next_layer_type;
};
template <typename T>
requires (!has_next_layer<T>)
typename next_layer_type<T>::type& next_layer(T&& a) {
typename next_layer_type<T, std::enable_if_t<!has_next_layer<T>>>::type&
next_layer(T&& a) {
return a;
}
template <typename T>
requires has_next_layer<T>
typename next_layer_type<T>::type& next_layer(T&& a) {
typename next_layer_type<T, std::enable_if_t<has_next_layer<T>>>::type&
next_layer(T&& a) {
return a.next_layer();
}
@ -110,23 +143,27 @@ lowest_layer_type<S>& lowest_layer(S&& a) {
return boost::beast::get_lowest_layer(std::forward<S>(a));
}
template <typename T>
template <typename T, typename Enable = void>
struct has_tls_layer_impl : std::false_type {};
template <typename T>
requires has_tls_handshake<T>
struct has_tls_layer_impl<T> : std::true_type {};
struct has_tls_layer_impl<
T, std::enable_if_t<has_tls_handshake<T>>
> : std::true_type {};
template <typename T>
requires (!has_tls_handshake<T> && has_next_layer<T>)
struct has_tls_layer_impl<T> : has_tls_layer_impl<
std::remove_cvref_t<decltype(std::declval<T&>().next_layer())>
struct has_tls_layer_impl<
T, std::enable_if_t<!has_tls_handshake<T> && has_next_layer<T>>
> : has_tls_layer_impl<
boost::remove_cv_ref_t<decltype(std::declval<T&>().next_layer())>
> {};
template <typename T>
concept has_tls_layer = has_tls_layer_impl<std::remove_cvref_t<T>>::value;
constexpr bool has_tls_layer = has_tls_layer_impl<
boost::remove_cv_ref_t<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<Stream, ConstBufferSequence>)
return stream.async_write(
buff, std::forward<CompletionToken>(token)
@ -148,7 +184,7 @@ decltype(auto) async_write(
template <typename TlsContext, typename Stream>
void setup_tls_sni(const authority_path& ap, TlsContext& ctx, Stream& s) {
if constexpr (has_tls_handshake<Stream>)
if constexpr (has_tls_handshake<Stream>)
assign_tls_sni(ap, ctx, s);
else if constexpr (has_next_layer<Stream>)
setup_tls_sni(ap, ctx, next_layer(s));

View File

@ -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<op_state>(
alloc, std::move(handler), ex, this
);
auto slot = asio::get_associated_cancellation_slot(_state->_handler);
if (slot.is_connected())
slot.template emplace<cancel_proxy>(_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));
}

View File

@ -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 <typename E>
void push_back(E&& e) {
@ -28,13 +30,21 @@ public:
_buffer.push_back(std::forward<E>(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 <typename... Signatures>
@ -48,6 +58,7 @@ struct channel_traits {
template <typename R, typename... Args>
struct channel_traits<R(error_code, Args...)> {
static_assert(sizeof...(Args) > 0);
template <typename... NewSignatures>
struct rebind {
using other = channel_traits<NewSignatures...>;

View File

@ -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

View File

@ -49,17 +49,32 @@ public:
ring_buffer() = default;
template <typename Alloc> requires std::is_constructible_v<allocator_type, const Alloc&>
explicit ring_buffer(size_type capacity, Alloc&& allocator) : _alloc((Alloc&&)allocator) {
template <
typename Alloc,
std::enable_if_t<
std::is_constructible_v<allocator_type, const Alloc&>, bool
> = true
>
explicit ring_buffer(size_type capacity, Alloc&& allocator) :
_alloc((Alloc&&)allocator)
{
reserve(capacity);
}
template <typename Alloc> requires std::is_constructible_v<allocator_type, const Alloc&>
explicit ring_buffer(Alloc&& allocator) : _alloc((Alloc&&)allocator) {
}
template <
typename Alloc,
std::enable_if_t<
std::is_constructible_v<allocator_type, const Alloc&>,
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<size_type>(-1);
static constexpr size_type min_capacity = 4;
};
template <typename T, typename Alloc> template <typename P, typename R, typename ring_pointer>
template <typename T, typename Alloc>
template <typename P, typename R, typename ring_pointer>
class ring_buffer<T, Alloc>::_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 T, typename Alloc>
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::begin() noexcept {
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::begin()
noexcept {
return { this, 0 };
}
template <typename T, typename Alloc>
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::end() noexcept {
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::end()
noexcept {
return { this, size() };
}
template <typename T, typename Alloc>
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::begin() const noexcept {
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::begin()
const noexcept {
return { this, 0 };
}
template <typename T, typename Alloc>
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::end() const noexcept {
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::end()
const noexcept {
return { this, size() };
}
template <typename T, typename Alloc>
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::cbegin() const noexcept {
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::cbegin()
const noexcept {
return { this, 0 };
}
template <typename T, typename Alloc>
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::cend() const noexcept {
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::cend()
const noexcept {
return { this, size() };
}
@ -405,8 +431,8 @@ namespace std {
template <typename T, typename Alloc>
void swap(
async_mqtt5::detail::ring_buffer<T, Alloc>& a,
async_mqtt5::detail::ring_buffer<T, Alloc>& b)
async_mqtt5::detail::ring_buffer<T, Alloc>& a,
async_mqtt5::detail::ring_buffer<T, Alloc>& b)
{
a.swap(b);
}

View File

@ -1,7 +1,6 @@
#ifndef ASYNC_MQTT5_UTF8_MQTT_HPP
#define ASYNC_MQTT5_UTF8_MQTT_HPP
#include <cstdint>
#include <string>
namespace async_mqtt5::detail {

View File

@ -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 };

View File

@ -15,7 +15,7 @@
#include <async_mqtt5/detail/control_packet.hpp>
#include <async_mqtt5/detail/internal_types.hpp>
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
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);

View File

@ -66,7 +66,9 @@ public:
}
private:
bool prioritized() const { return _flags & send_flag::prioritized; }
bool prioritized() const {
return _flags & send_flag::prioritized;
}
};

View File

@ -17,12 +17,19 @@ namespace async_mqtt5::detail {
namespace asio = boost::asio;
template <typename StreamType, typename TlsContext>
template <
typename StreamType, typename TlsContext,
typename Enable = void
>
class stream_context;
template <typename StreamType, typename TlsContext>
requires has_tls_layer<StreamType>
class stream_context<StreamType, TlsContext> {
template <
typename StreamType, typename TlsContext
>
class stream_context<
StreamType, TlsContext,
std::enable_if_t<has_tls_layer<StreamType>>
> {
using tls_context_type = TlsContext;
mqtt_ctx _mqtt_context;
@ -76,8 +83,10 @@ public:
};
template <typename StreamType>
requires (!has_tls_layer<StreamType>)
class stream_context<StreamType, std::monostate> {
class stream_context<
StreamType, std::monostate,
std::enable_if_t<!has_tls_layer<StreamType>>
> {
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<TlsContext, std::monostate>) {
template <
typename Ctx = TlsContext,
std::enable_if_t<!std::is_same_v<Ctx, std::monostate>, bool> = true
>
decltype(auto) tls_context() {
return _stream_context.tls_context();
}
@ -215,7 +227,10 @@ public:
_stream.brokers(std::move(hosts), default_port);
}
template <typename Authenticator>
template <
typename Authenticator,
std::enable_if_t<is_authenticator<Authenticator>, 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 <typename CompletionToken>

View File

@ -3,12 +3,10 @@
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/binary/binary.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
#include <async_mqtt5/property_types.hpp>
#include <async_mqtt5/impl/internal/codecs/traits.hpp>
#include <async_mqtt5/impl/codecs/traits.hpp>
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 <typename AttributeType, typename It, typename Parser>
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<attr_type>)
arg = T { x3::_attr(ctx).begin(), x3::_attr(ctx).end() };
else
arg = x3::_attr(ctx);
arg = x3::_attr(ctx);
};
}
template <typename LenParser, typename Subject>
template <typename LenParser, typename Subject, typename Enable = void>
class scope_limit {};
template <typename LenParser, typename Subject>
requires (x3::traits::is_parser<LenParser>::value)
class scope_limit<LenParser, Subject> :
public x3::unary_parser<Subject, scope_limit<LenParser, Subject>> {
class scope_limit<
LenParser, Subject,
std::enable_if_t<x3::traits::is_parser<LenParser>::value>
> :
public x3::unary_parser<Subject, scope_limit<LenParser, Subject>>
{
using base_type = x3::unary_parser<Subject, scope_limit<LenParser, Subject>>;
LenParser _lp;
public:
using ctx_type = decltype(x3::make_context<struct _>(std::declval<Subject&>()));
using attribute_type = typename x3::traits::attribute_of<Subject, ctx_type>::type;
using ctx_type =
decltype(x3::make_context<struct _>(std::declval<Subject&>()));
using attribute_type =
typename x3::traits::attribute_of<Subject, ctx_type>::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 <typename It, typename Ctx, typename RCtx, typename Attr>
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<LenParser, Ctx>::type len;
@ -112,21 +120,31 @@ public:
};
template <typename Size, typename Subject>
requires (std::is_arithmetic_v<Size>)
class scope_limit<Size, Subject> :
public x3::unary_parser<Subject, scope_limit<Size, Subject>> {
class scope_limit<
Size, Subject,
std::enable_if_t<std::is_arithmetic_v<Size>>
> :
public x3::unary_parser<Subject, scope_limit<Size, Subject>>
{
using base_type = x3::unary_parser<Subject, scope_limit<Size, Subject>>;
size_t _limit;
public:
using ctx_type = decltype(x3::make_context<struct _>(std::declval<Subject&>()));
using attribute_type = typename x3::traits::attribute_of<Subject, ctx_type>::type;
using ctx_type =
decltype(x3::make_context<struct _>(std::declval<Subject&>()));
using attribute_type =
typename x3::traits::attribute_of<Subject, ctx_type>::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 <typename It, typename Ctx, typename RCtx, typename Attr>
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 <typename LenParser>
template <typename LenParser, typename Enable = void>
struct scope_limit_gen {
template <typename Subject>
auto operator[](const Subject& p) const {
@ -150,8 +168,10 @@ struct scope_limit_gen {
};
template <typename Size>
requires (std::is_arithmetic_v<Size>)
struct scope_limit_gen<Size> {
struct scope_limit_gen<
Size,
std::enable_if_t<std::is_arithmetic_v<Size>>
> {
template <typename Subject>
auto operator[](const Subject& p) const {
return scope_limit<Size, Subject> { limit, x3::as_parser(p) };
@ -159,19 +179,22 @@ struct scope_limit_gen<Size> {
Size limit;
};
template <typename Parser>
requires (x3::traits::is_parser<Parser>::value)
template <
typename Parser,
std::enable_if_t<x3::traits::is_parser<Parser>::value, bool> = true
>
scope_limit_gen<Parser> scope_limit_(const Parser& p) {
return { p };
}
template <typename Size>
requires (std::is_arithmetic_v<Size>)
template <
typename Size,
std::enable_if_t<std::is_arithmetic_v<Size>, bool> = true
>
scope_limit_gen<Size> scope_limit_(Size limit) {
return { limit };
}
struct verbatim_parser : x3::parser<verbatim_parser> {
using attribute_type = std::string;
static bool const has_attribute = true;
@ -191,7 +214,10 @@ struct varint_parser : x3::parser<varint_parser> {
static bool const has_attribute = true;
template <typename It, typename Ctx, typename RCtx, typename Attr>
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<len_prefix_parser> {
static bool const has_attribute = true;
template <typename It, typename Ctx, typename RCtx, typename Attr>
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<len_prefix_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 <typename Subject>
class conditional_parser : public x3::unary_parser<Subject, conditional_parser<Subject>> {
class conditional_parser :
public x3::unary_parser<Subject, conditional_parser<Subject>> {
using base_type = x3::unary_parser<Subject, conditional_parser<Subject>>;
bool _condition;
public:
using ctx_type = decltype(x3::make_context<struct _>(std::declval<Subject&>()));
using subject_attr_type = typename x3::traits::attribute_of<Subject, ctx_type>::type;
using ctx_type =
decltype(x3::make_context<struct _>(std::declval<Subject&>()));
using subject_attr_type =
typename x3::traits::attribute_of<Subject, ctx_type>::type;
using attribute_type = boost::optional<subject_attr_type>;
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 <typename It, typename Ctx, typename RCtx, typename Attr>
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 <typename It, typename Ctx, typename RCtx, typename Prop>
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<prop_type>) {
using value_type = typename std::remove_reference_t<prop_type>::value_type;
using value_type =
typename std::remove_reference_t<prop_type>::value_type;
if constexpr (std::is_same_v<value_type, uint8_t>) {
uint8_t attr;
rv = x3::byte_.parse(iter, last, ctx, rctx, attr);
@ -363,7 +405,10 @@ public:
static bool const has_attribute = true;
template <typename It, typename Ctx, typename RCtx, typename Attr>
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 <typename Props>
constexpr auto props_ = prop_parser<Props>{};
constexpr auto props_ = prop_parser<Props> {};
} // end namespace prop

View File

@ -4,10 +4,13 @@
#include <cstddef>
#include <cstdint>
#include <boost/core/identity.hpp>
#include <boost/endian/conversion.hpp>
#include <boost/type_traits/is_detected_exact.hpp>
#include <boost/type_traits/remove_cv_ref.hpp>
#include <async_mqtt5/property_types.hpp>
#include <async_mqtt5/impl/internal/codecs/traits.hpp>
#include <async_mqtt5/impl/codecs/traits.hpp>
namespace async_mqtt5::encoders {
@ -54,10 +57,13 @@ public:
flag_def(repr val) : _val(val) {}
flag_def() = default;
template <class T, typename projection = std::identity>
requires (is_optional<T>)
template <
typename T,
typename projection = boost::identity,
std::enable_if_t<is_optional<T>, bool> = true
>
auto operator()(T&& value, projection proj = {}) const {
if constexpr (std::is_same_v<projection, std::identity>) {
if constexpr (std::is_same_v<projection, boost::identity>) {
repr val = value.has_value();
return flag_def<bits, repr> { val };
}
@ -68,8 +74,11 @@ public:
}
}
template <class T, typename projection = std::identity>
requires (!is_optional<T>)
template <
typename T,
typename projection = boost::identity,
std::enable_if_t<!is_optional<T>, bool> = true
>
auto operator()(T&& value, projection proj = {}) const {
auto val = static_cast<repr>(std::invoke(proj, value));
return flag_def<bits, repr> { val };
@ -157,7 +166,7 @@ public:
auto operator()(T&& val, projection proj) const {
if constexpr (is_optional<T>) {
using rv_type = std::invoke_result_t<
projection, typename std::remove_cvref_t<T>::value_type
projection, typename boost::remove_cv_ref_t<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<T> || std::is_same_v<T, std::string_view>
);
@ -204,11 +215,15 @@ public:
}
private:
template <typename V>
using has_size = decltype(std::declval<V&>().size());
template <typename U>
static size_t val_length(U&& val) {
if constexpr (std::same_as<std::remove_cvref_t<U>, const char*>)
if constexpr (std::is_same_v<boost::remove_cv_ref_t<U>, const char*>)
return std::strlen(val);
if constexpr (requires { val.size(); })
if constexpr (boost::is_detected_exact_v<size_t, has_size, U>)
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<T>) {
using rv_type = std::invoke_result_t<
projection, typename std::remove_cvref_t<T>::value_type
projection, typename boost::remove_cv_ref_t<T>::value_type
>;
if (val.has_value())
return (*this)(std::invoke(proj, *val));
@ -263,12 +278,13 @@ constexpr auto binary_ = array_def<true>{}; // for now
constexpr auto verbatim_ = array_def<false>{};
template <class T, class U>
template <typename T, typename U>
class composed_val : public encoder {
T _lhs; U _rhs;
public:
composed_val(T lhs, U rhs) :
_lhs(std::forward<T>(lhs)), _rhs(std::forward<U>(rhs)) {}
_lhs(std::forward<T>(lhs)), _rhs(std::forward<U>(rhs))
{}
size_t byte_size() const {
return _lhs.byte_size() + _rhs.byte_size();
@ -280,17 +296,22 @@ public:
}
};
template <class T, class U>
requires (
std::derived_from<std::decay_t<T>, encoder> &&
std::derived_from<std::decay_t<U>, encoder>
)
template <
typename T, typename U,
std::enable_if_t<
std::is_base_of_v<encoder, std::decay_t<T>> &&
std::is_base_of_v<encoder, std::decay_t<U>>,
bool
> = true
>
inline auto operator&(T&& t, U&& u) {
return composed_val(std::forward<T>(t), std::forward<U>(u));
}
template <class T>
requires (std::derived_from<std::decay_t<T>, encoder>)
template <
typename T,
std::enable_if_t<std::is_base_of_v<encoder, std::decay_t<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<pp::content_type_t, basic::utf8_def>,
prop_encoder_type<pp::response_topic_t, basic::utf8_def>,
prop_encoder_type<pp::correlation_data_t, basic::utf8_def>,
prop_encoder_type<pp::subscription_identifier_t, basic::int_def<intptr_t>>,
prop_encoder_type<pp::subscription_identifier_t, basic::int_def<uint32_t>>,
prop_encoder_type<pp::session_expiry_interval_t, basic::int_def<int32_t>>,
prop_encoder_type<pp::assigned_client_identifier_t, basic::utf8_def>,
prop_encoder_type<pp::server_keep_alive_t, basic::int_def<int16_t>>,
@ -376,14 +397,18 @@ constexpr auto encoder_for_prop = typename std::tuple_element_t<
>::value {};
template <typename T, pp::property_type p>
template <typename T, pp::property_type p, typename Enable = void>
class prop_val;
template <typename T, pp::property_type p>
requires (!is_vector<T> && is_optional<T>)
class prop_val<T, p> : public basic::encoder {
template <
typename T, pp::property_type p
>
class prop_val<
T, p,
std::enable_if_t<!is_vector<T> && is_optional<T>>
> : public basic::encoder {
// T is always std::optional
using opt_type = typename std::remove_cvref_t<T>::value_type;
using opt_type = typename boost::remove_cv_ref_t<T>::value_type;
// allows T to be reference type to std::optional
static inline std::optional<opt_type> nulltype;
T _val;
@ -408,11 +433,15 @@ public:
}
};
template <typename T, pp::property_type p>
requires (is_vector<T>)
class prop_val<T, p> : public basic::encoder {
template <
typename T, pp::property_type p
>
class prop_val<
T, p,
std::enable_if_t<is_vector<T>>
> : public basic::encoder {
// allows T to be reference type to std::vector
static inline std::remove_cvref_t<T> nulltype;
static inline boost::remove_cv_ref_t<T> nulltype;
T _val;
public:
prop_val(T val) : _val(val) {
@ -464,7 +493,7 @@ class props_val : public basic::encoder {
);
}
template <class Func>
template <typename Func>
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<T>::value_type&
const typename boost::remove_cv_ref_t<T>::value_type&
>(true);
}
else {

View File

@ -5,7 +5,7 @@
#include <string>
#include <async_mqtt5/detail/internal_types.hpp>
#include <async_mqtt5/impl/internal/codecs/base_decoders.hpp>
#include <async_mqtt5/impl/codecs/base_decoders.hpp>
namespace async_mqtt5::decoders {
@ -131,9 +131,9 @@ inline std::optional<publish_message> 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_<publish_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_<publish_props> >> basic::verbatim_
];
return type_parse(it, it + remain_length, publish_);
}

View File

@ -5,7 +5,7 @@
#include <optional>
#include <async_mqtt5/types.hpp>
#include <async_mqtt5/impl/internal/codecs/base_encoders.hpp>
#include <async_mqtt5/impl/codecs/base_encoders.hpp>
namespace async_mqtt5::encoders {

View File

@ -0,0 +1,42 @@
#ifndef ASYNC_MQTT5_TRAITS_HPP
#define ASYNC_MQTT5_TRAITS_HPP
#include <optional>
#include <vector>
#include <boost/range/iterator_range_core.hpp>
#include <boost/type_traits/remove_cv_ref.hpp>
namespace async_mqtt5 {
template <typename>
constexpr bool is_optional_impl = false;
template <typename T>
constexpr bool is_optional_impl<std::optional<T>> = true;
template <typename T>
constexpr bool is_optional = is_optional_impl<boost::remove_cv_ref_t<T>>;
template <typename, template <typename...> typename>
constexpr bool is_specialization = false;
template <template <typename...> typename T, typename... Args>
constexpr bool is_specialization<T<Args...>, T> = true;
template <typename T>
constexpr bool is_vector = is_specialization<
boost::remove_cv_ref_t<T>, std::vector
>;
template <typename T>
constexpr bool is_boost_iterator = is_specialization<
boost::remove_cv_ref_t<T>, boost::iterator_range
>;
} // end namespace async_mqtt5
#endif // !ASYNC_MQTT5_TRAITS_HPP

View File

@ -18,8 +18,8 @@
#include <async_mqtt5/detail/control_packet.hpp>
#include <async_mqtt5/detail/internal_types.hpp>
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
namespace async_mqtt5::detail {
@ -108,7 +108,9 @@ public:
)
);
}
else if constexpr (has_tls_handshake<typename next_layer_type<Stream>::type>) {
else if constexpr (
has_tls_handshake<typename next_layer_type<Stream>::type>
) {
_stream.next_layer().async_handshake(
tls_handshake_type<typename next_layer_type<Stream>::type>::client,
asio::append(
@ -252,9 +254,8 @@ public:
asio::async_read(
_stream, buff,
asio::prepend(
asio::append(
std::move(*this), code, first, last
), on_read_packet {}
asio::append(std::move(*this), code, first, last),
on_read_packet {}
)
);
}
@ -285,7 +286,6 @@ public:
_ctx.ca_props = ca_props;
_ctx.state.session_present(session_present);
// TODO: session_present logic
// Unexpected result handling:
// - If we don't have a Session State, and we get session_present = true,
// we must close the network connection (and restart with a clean start)

View File

@ -11,7 +11,7 @@
#include <async_mqtt5/detail/internal_types.hpp>
#include <async_mqtt5/detail/cancellable_handler.hpp>
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
namespace async_mqtt5::detail {

View File

@ -84,7 +84,7 @@ public:
}
void operator()(
on_resolve, auto ord,
on_resolve, std::array<std::size_t, 2> ord,
error_code resolve_ec, epoints epts,
error_code timer_ec, authority_path ap
) {

View File

@ -1,39 +0,0 @@
#ifndef ASYNC_MQTT5_MEMORY_H
#define ASYNC_MQTT5_MEMORY_H
#include "memory_resource.h"
namespace pma {
template <typename T>
class alloc : public polymorphic_allocator<T> {
using base = polymorphic_allocator<T>;
public:
alloc(pma::memory_resource* r) noexcept : base(r) {}
template <class T2>
alloc(const alloc<T2>& other) noexcept : base(other.resource()) {}
using value_type = typename base::value_type;
using pointer = typename base::value_type*;
using const_pointer = const typename base::value_type*;
using reference = typename base::value_type&;
using const_reference = const typename base::value_type&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// https://stackoverflow.com/questions/27471053/example-usage-of-propagate-on-container-move-assignment
using propagate_on_container_copy_assignment = std::false_type;
using propagate_on_container_move_assignment = std::false_type;
using propagate_on_container_swap = std::false_type;
alloc select_on_container_copy_construction() const noexcept {
return alloc(base::resource());
}
};
} // end namespace pma
#endif // !ASYNC_MQTT5_MEMORY_H

View File

@ -1,515 +0,0 @@
#ifndef ASYNC_MQTT5_MEMORY_RESOURCE_H
#define ASYNC_MQTT5_MEMORY_RESOURCE_H
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include <tuple>
#include <cstddef>
#include <cstdlib>
#include <limits>
#include <memory>
#include <new>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace pma {
struct erased_type { };
template <size_t...> struct __tuple_indices {};
template <class _IdxType, _IdxType... _Values>
struct __integer_sequence {
template <
template <class _OIdxType, _OIdxType...> class _ToIndexSeq,
class _ToIndexType
>
using __convert = _ToIndexSeq<_ToIndexType, _Values...>;
template <size_t _Sp>
using __to_tuple_indices = __tuple_indices<(_Values + _Sp)...>;
};
template <size_t _Ep, size_t _Sp>
using __make_indices_imp =
typename __make_integer_seq<__integer_sequence, size_t, _Ep - _Sp>::template
__to_tuple_indices<_Sp>;
template <size_t _Ep, size_t _Sp = 0>
struct __make_tuple_indices {
static_assert(_Sp <= _Ep, "__make_tuple_indices input error");
using type = __make_indices_imp<_Ep, _Sp>;
};
struct allocator_arg_t {
explicit allocator_arg_t() = default;
};
template <class _Tp>
struct __has_allocator_type
{
private:
struct __two {char __lx; char __lxx;};
template <class _Up> static __two __test(...);
template <class _Up> static char __test(typename _Up::allocator_type* = 0);
public:
static const bool value = sizeof(__test<_Tp>(0)) == 1;
};
template <class _Tp, class _Alloc, bool = __has_allocator_type<_Tp>::value>
struct __uses_allocator :
public std::integral_constant<
bool,
std::is_convertible<_Alloc, typename _Tp::allocator_type>::value
>
{};
template <class _Tp, class _Alloc>
struct __uses_allocator<_Tp, _Alloc, false> : public std::false_type {};
template <class _Tp, class _Alloc>
struct uses_allocator : public __uses_allocator<_Tp, _Alloc> {};
template <class _Tp, class _Alloc>
inline constexpr size_t uses_allocator_v = uses_allocator<_Tp, _Alloc>::value;
template <
class _Tp, class _Alloc,
bool = std::uses_allocator<_Tp, _Alloc>::value,
bool = __has_allocator_type<_Tp>::value
>
struct __lfts_uses_allocator : public std::false_type {};
template <class _Tp, class _Alloc>
struct __lfts_uses_allocator<_Tp, _Alloc, false, false> :
public std::false_type {};
template <class _Tp, class _Alloc, bool HasAlloc>
struct __lfts_uses_allocator<_Tp, _Alloc, true, HasAlloc> :
public std::true_type {};
template <class _Tp, class _Alloc>
struct __lfts_uses_allocator<_Tp, _Alloc, false, true> :
public std::integral_constant<
bool,
std::is_convertible<_Alloc, typename _Tp::allocator_type>::value
|| std::is_same<erased_type, typename _Tp::allocator_type>::value
>
{};
template <bool _UsesAlloc, class _Tp, class _Alloc, class ..._Args>
struct __lfts_uses_alloc_ctor_imp {
static const int value = 0;
};
template <class _Tp, class _Alloc, class ..._Args>
struct __lfts_uses_alloc_ctor_imp<true, _Tp, _Alloc, _Args...>
{
static const bool __ic_first =
std::is_constructible<_Tp, std::allocator_arg_t, _Alloc, _Args...>::value;
static const bool __ic_second =
std::conditional<
__ic_first,
std::false_type,
std::is_constructible<_Tp, _Args..., _Alloc>
>::type::value;
static_assert(
__ic_first || __ic_second,
"Request for uses allocator construction is ill-formed"
);
static const int value = __ic_first ? 1 : 2;
};
template <class _Tp, class _Alloc, class ..._Args>
struct __lfts_uses_alloc_ctor :
std::integral_constant<int,
__lfts_uses_alloc_ctor_imp<
__lfts_uses_allocator<_Tp, _Alloc>::value
, _Tp, _Alloc, _Args...
>::value
>
{};
template <class _Tp, class _Allocator, class... _Args>
inline void __user_alloc_construct_impl(
std::integral_constant<int, 2>, _Tp *__storage,
const _Allocator &__a, _Args &&... __args
) {
new (__storage) _Tp (std::forward<_Args>(__args)..., __a);
}
template <class _Tp, class _Alloc, class ..._Args>
inline void __lfts_user_alloc_construct(
_Tp * __store, const _Alloc & __a, _Args &&... __args
) {
__user_alloc_construct_impl(
typename __lfts_uses_alloc_ctor<_Tp, _Alloc, _Args...>::type(),
__store, __a, std::forward<_Args>(__args)...
);
}
inline size_t __aligned_allocation_size(size_t __s, size_t __a) noexcept {
return (__s + __a - 1) & ~(__a - 1);
}
class memory_resource {
static const size_t __max_align = alignof(max_align_t);
public:
virtual ~memory_resource() = default;
void* allocate(size_t __bytes, size_t __align = __max_align) {
return do_allocate(__bytes, __align);
}
void deallocate(void * __p, size_t __bytes, size_t __align = __max_align) {
do_deallocate(__p, __bytes, __align);
}
bool is_equal(memory_resource const & __other) const noexcept {
return do_is_equal(__other);
}
private:
virtual void* do_allocate(size_t, size_t) = 0;
virtual void do_deallocate(void*, size_t, size_t) = 0;
virtual bool do_is_equal(memory_resource const &) const noexcept = 0;
};
inline bool operator==(
memory_resource const & __lhs,
memory_resource const & __rhs
) noexcept {
return &__lhs == &__rhs || __lhs.is_equal(__rhs);
}
inline
bool operator!=(
memory_resource const & __lhs,
memory_resource const & __rhs
) noexcept {
return !(__lhs == __rhs);
}
memory_resource* new_delete_resource() noexcept;
memory_resource* null_memory_resource() noexcept;
memory_resource* get_default_resource() noexcept;
// memory_resource* set_default_resource(memory_resource * __new_res) noexcept;
template <class _ValueType>
class polymorphic_allocator {
public:
using value_type = _ValueType;
polymorphic_allocator() noexcept :
__res_(get_default_resource())
{}
polymorphic_allocator(memory_resource* __r) noexcept :
__res_(__r)
{}
polymorphic_allocator(polymorphic_allocator const &) = default;
template <class _Tp>
polymorphic_allocator(polymorphic_allocator<_Tp> const & __other) noexcept :
__res_(__other.resource())
{}
polymorphic_allocator &
operator=(polymorphic_allocator const &) = delete;
_ValueType* allocate(size_t __n) {
if (__n > __max_size())
throw std::bad_array_new_length();
return static_cast<_ValueType*>(
__res_->allocate(__n * sizeof(_ValueType), alignof(_ValueType))
);
}
void deallocate(_ValueType * __p, size_t __n) noexcept {
__res_->deallocate(__p, __n * sizeof(_ValueType), alignof(_ValueType));
}
template <class _Tp, class ..._Ts>
void construct(_Tp* __p, _Ts &&... __args) {
__lfts_user_alloc_construct(
__p, *this, std::forward<_Ts>(__args)...
);
}
template <class _T1, class _T2, class ..._Args1, class ..._Args2>
void construct(
std::pair<_T1, _T2>* __p, std::piecewise_construct_t,
std::tuple<_Args1...> __x, std::tuple<_Args2...> __y
) {
::new ((void*)__p) std::pair<_T1, _T2>(
std::piecewise_construct,
__transform_tuple(
typename __lfts_uses_alloc_ctor<
_T1, polymorphic_allocator&, _Args1...
>::type(),
std::move(__x),
typename __make_tuple_indices<sizeof...(_Args1)>::type{}
),
__transform_tuple(
typename __lfts_uses_alloc_ctor<
_T2, polymorphic_allocator&, _Args2...
>::type(),
std::move(__y),
typename __make_tuple_indices<sizeof...(_Args2)>::type{}
)
);
}
template <class _T1, class _T2>
void construct(std::pair<_T1, _T2>* __p) {
construct(__p, std::piecewise_construct, std::tuple<>(), std::tuple<>());
}
template <class _T1, class _T2, class _Up, class _Vp>
void construct(std::pair<_T1, _T2> * __p, _Up && __u, _Vp && __v) {
construct(
__p, std::piecewise_construct,
std::forward_as_tuple(std::forward<_Up>(__u)),
std::forward_as_tuple(std::forward<_Vp>(__v))
);
}
template <class _T1, class _T2, class _U1, class _U2>
void construct(std::pair<_T1, _T2> * __p, std::pair<_U1, _U2> const & __pr) {
construct(
__p, std::piecewise_construct,
std::forward_as_tuple(__pr.first),
std::forward_as_tuple(__pr.second)
);
}
template <class _T1, class _T2, class _U1, class _U2>
void construct(std::pair<_T1, _T2> * __p, std::pair<_U1, _U2> && __pr) {
construct(
__p, std::piecewise_construct,
std::forward_as_tuple(std::forward<_U1>(__pr.first)),
std::forward_as_tuple(std::forward<_U2>(__pr.second))
);
}
template <class _Tp>
void destroy(_Tp * __p) noexcept {
__p->~_Tp();
}
polymorphic_allocator
select_on_container_copy_construction() const noexcept {
return polymorphic_allocator();
}
memory_resource* resource() const noexcept {
return __res_;
}
private:
template <class ..._Args, size_t ..._Idx>
std::tuple<_Args&&...>
__transform_tuple(
std::integral_constant<int, 0>, std::tuple<_Args...>&& __t,
__tuple_indices<_Idx...>
) const {
return std::forward_as_tuple(std::get<_Idx>(std::move(__t))...);
}
template <class ..._Args, size_t ..._Idx>
std::tuple<allocator_arg_t const&, polymorphic_allocator&, _Args&&...>
__transform_tuple(
std::integral_constant<int, 1>, std::tuple<_Args...> && __t,
__tuple_indices<_Idx...>
) {
using _Tup = std::tuple<
allocator_arg_t const&,
polymorphic_allocator&, _Args&&...
>;
return _Tup(allocator_arg_t{}, *this, std::get<_Idx>(std::move(__t))...);
}
template <class ..._Args, size_t ..._Idx>
std::tuple<_Args&&..., polymorphic_allocator&>
__transform_tuple(
std::integral_constant<int, 2>, std::tuple<_Args...> && __t,
__tuple_indices<_Idx...>
) {
using _Tup = std::tuple<_Args&&..., polymorphic_allocator&>;
return _Tup(std::get<_Idx>(std::move(__t))..., *this);
}
size_t __max_size() const noexcept {
return std::numeric_limits<size_t>::max() / sizeof(value_type);
}
memory_resource * __res_;
};
template <class _Tp, class _Up>
inline bool operator==(
polymorphic_allocator<_Tp> const & __lhs,
polymorphic_allocator<_Up> const & __rhs
) noexcept {
return *__lhs.resource() == *__rhs.resource();
}
template <class _Tp, class _Up>
inline bool operator!=(
polymorphic_allocator<_Tp> const & __lhs,
polymorphic_allocator<_Up> const & __rhs
) noexcept {
return !(__lhs == __rhs);
}
template <class _CharAlloc>
class __resource_adaptor_imp : public memory_resource {
using _CTraits = std::allocator_traits<_CharAlloc>;
static_assert(
std::is_same<typename _CTraits::value_type, char>::value &&
std::is_same<typename _CTraits::pointer, char*>::value &&
std::is_same<typename _CTraits::void_pointer, void*>::value
);
static const size_t _MaxAlign = alignof(max_align_t);
using _Alloc = typename _CTraits::template rebind_alloc<
typename std::aligned_storage<_MaxAlign, _MaxAlign>::type
>;
using _ValueType = typename _Alloc::value_type;
_Alloc __alloc_;
public:
using allocator_type = _CharAlloc;
__resource_adaptor_imp() = default;
__resource_adaptor_imp(__resource_adaptor_imp const &) = default;
__resource_adaptor_imp(__resource_adaptor_imp &&) noexcept = default;
explicit __resource_adaptor_imp(allocator_type const & __a) :
__alloc_(__a)
{}
explicit __resource_adaptor_imp(allocator_type && __a) :
__alloc_(std::move(__a))
{}
__resource_adaptor_imp &
operator=(__resource_adaptor_imp const &) = default;
allocator_type get_allocator() const {
return __alloc_;
}
private:
void * do_allocate(size_t __bytes, size_t) override {
if (__bytes > __max_size())
throw std::bad_array_new_length();
size_t __s = __aligned_allocation_size(__bytes, _MaxAlign) / _MaxAlign;
return __alloc_.allocate(__s);
}
void do_deallocate(void * __p, size_t __bytes, size_t) override {
size_t __s = __aligned_allocation_size(__bytes, _MaxAlign) / _MaxAlign;
__alloc_.deallocate((_ValueType*)__p, __s);
}
bool do_is_equal(memory_resource const & __other) const noexcept override {
auto __p = dynamic_cast<__resource_adaptor_imp const *>(&__other);
return __p ? __alloc_ == __p->__alloc_ : false;
}
size_t __max_size() const noexcept {
return std::numeric_limits<size_t>::max() - _MaxAlign;
}
};
template <class _Alloc>
using resource_adaptor = __resource_adaptor_imp<
typename std::allocator_traits<_Alloc>::template rebind_alloc<char>
>;
class __new_delete_memory_resource_imp : public memory_resource {
void* do_allocate(size_t size, size_t /*align*/) override {
return new std::byte[size];
}
void do_deallocate(void *p, size_t n, size_t align) override {
delete [](std::byte*)(p);
}
bool do_is_equal(memory_resource const & other) const noexcept override {
return &other == this;
}
public:
~__new_delete_memory_resource_imp() override = default;
};
class __null_memory_resource_imp : public memory_resource {
public:
~__null_memory_resource_imp() override = default;
protected:
void* do_allocate(size_t, size_t) override {
throw std::bad_alloc();
}
void do_deallocate(void*, size_t, size_t) override {}
bool do_is_equal(memory_resource const & __other) const noexcept override {
return &__other == this;
}
};
inline memory_resource* new_delete_resource() noexcept {
static __new_delete_memory_resource_imp inst { };
return &inst;
}
/*
// Commented out to prevent creation of polymorphic_allocator without
// explicitly provided memory_resource
inline std::atomic<memory_resource*>& __default_memory_resource(
bool set = false, memory_resource* = nullptr
) {
static std::atomic<memory_resource*> def {
new_delete_resource()
};
return def;
}
inline memory_resource * get_default_resource() noexcept {
return __default_memory_resource();
}
inline memory_resource * set_default_resource(memory_resource * __new_res) noexcept {
return __default_memory_resource(true, __new_res);
}
*/
} // end namespace pma
#endif // !ASYNC_MQTT5_MEMORY_RESOURCE_H

View File

@ -1,22 +0,0 @@
#ifndef ASYNC_MQTT5_STRING_H
#define ASYNC_MQTT5_STRING_H
#include <string>
#include "memory.h"
namespace pma {
template <class _CharT, class _Traits = std::char_traits<_CharT>>
using basic_string =
std::basic_string<_CharT, _Traits, alloc<_CharT>>;
using string = basic_string<char>;
using u16string = basic_string<char16_t>;
using u32string = basic_string<char32_t>;
using wstring = basic_string<wchar_t>;
} // namespace pma
#endif // !ASYNC_MQTT5_STRING_H

View File

@ -1,15 +0,0 @@
#ifndef ASYNC_MQTT5_VECTOR_H
#define ASYNC_MQTT5_VECTOR_H
#include <vector>
#include "memory.h"
namespace pma {
template <class _ValueT>
using vector = std::vector<_ValueT, alloc<_ValueT>>;
} // namespace pma
#endif // !ASYNC_MQTT5_VECTOR_H

View File

@ -1,33 +0,0 @@
#ifndef ASYNC_MQTT5_TRAITS_HPP
#define ASYNC_MQTT5_TRAITS_HPP
#include <optional>
#include <vector>
#include <boost/range/iterator_range_core.hpp>
namespace async_mqtt5 {
template <typename> constexpr bool is_optional_impl = false;
template <typename T> constexpr bool is_optional_impl<std::optional<T>> = true;
template <typename T>
constexpr bool is_optional = is_optional_impl<std::remove_cvref_t<T>>;
template <class, template<class...> class>
constexpr bool is_specialization = false;
template <template<class...> class T, class... Args>
constexpr bool is_specialization<T<Args...>, T> = true;
template <typename T>
concept is_vector = is_specialization<std::remove_cvref_t<T>, std::vector>;
template <typename T>
concept is_boost_iterator = is_specialization<
std::remove_cvref_t<T>, boost::iterator_range
>;
} // end namespace async_mqtt5
#endif // !ASYNC_MQTT5_TRAITS_HPP

View File

@ -12,7 +12,7 @@
#include <async_mqtt5/detail/control_packet.hpp>
#include <async_mqtt5/detail/internal_types.hpp>
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
namespace async_mqtt5::detail {
@ -30,9 +30,7 @@ class ping_op {
std::unique_ptr<asio::steady_timer> _ping_timer;
public:
ping_op(
const std::shared_ptr<client_service>& svc_ptr
) :
ping_op(const std::shared_ptr<client_service>& svc_ptr) :
_svc_ptr(svc_ptr),
_ping_timer(new asio::steady_timer(svc_ptr->get_executor()))
{}

View File

@ -15,8 +15,8 @@
#include <async_mqtt5/detail/internal_types.hpp>
#include <async_mqtt5/impl/disconnect_op.hpp>
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
namespace async_mqtt5::detail {

View File

@ -13,8 +13,8 @@
#include <async_mqtt5/detail/utf8_mqtt.hpp>
#include <async_mqtt5/impl/disconnect_op.hpp>
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
namespace async_mqtt5::detail {
@ -75,7 +75,8 @@ class publish_send_op {
public:
publish_send_op(
const std::shared_ptr<client_service>& svc_ptr, Handler&& handler
const std::shared_ptr<client_service>& svc_ptr,
Handler&& handler
) :
_svc_ptr(svc_ptr),
_handler(std::move(handler), get_executor())
@ -178,12 +179,14 @@ public:
}
}
template <
qos_e q = qos_type,
std::enable_if_t<q == qos_e::at_least_once, bool> = true
>
void operator()(
on_puback, control_packet<allocator_type> publish,
error_code ec, byte_citer first, byte_citer last
)
requires (qos_type == qos_e::at_least_once) {
) {
if (ec == asio::error::try_again) // "resend unanswered"
return send_publish(std::move(publish.set_dup()));
@ -210,12 +213,14 @@ public:
complete(ec, *rc, packet_id, std::move(props));
}
template <
qos_e q = qos_type,
std::enable_if_t<q == qos_e::exactly_once, bool> = true
>
void operator()(
on_pubrec, control_packet<allocator_type> publish,
error_code ec, byte_citer first, byte_citer last
)
requires (qos_type == qos_e::exactly_once) {
) {
if (ec == asio::error::try_again) // "resend unanswered"
return send_publish(std::move(publish.set_dup()));
@ -262,11 +267,13 @@ public:
);
}
template <
qos_e q = qos_type,
std::enable_if_t<q == qos_e::exactly_once, bool> = true
>
void operator()(
on_pubrel, control_packet<allocator_type> pubrel, error_code ec
)
requires (qos_type == qos_e::exactly_once) {
) {
if (ec == asio::error::try_again)
return send_pubrel(std::move(pubrel), true);
@ -283,13 +290,15 @@ public:
);
}
template <
qos_e q = qos_type,
std::enable_if_t<q == qos_e::exactly_once, bool> = true
>
void operator()(
on_pubcomp, control_packet<allocator_type> pubrel,
error_code ec,
byte_citer first, byte_citer last
)
requires (qos_type == qos_e::exactly_once) {
) {
if (ec == asio::error::try_again) // "resend unanswered"
return send_pubrel(std::move(pubrel), true);
@ -329,16 +338,21 @@ private:
if (max_qos && uint8_t(qos_type) > *max_qos)
return client::error::qos_not_supported;
auto retain_available = _svc_ptr->connack_prop(prop::retain_available);
if (retain_available && *retain_available == 0 && retain == retain_e::yes)
auto retain_avail = _svc_ptr->connack_prop(prop::retain_available);
if (retain_avail && *retain_avail == 0 && retain == retain_e::yes)
return client::error::retain_not_available;
auto topic_alias_max = _svc_ptr->connack_prop(prop::topic_alias_maximum);
auto topic_alias = props[prop::topic_alias];
if ((!topic_alias_max || topic_alias_max && *topic_alias_max == 0) && topic_alias)
if (
(!topic_alias_max || topic_alias_max && *topic_alias_max == 0) &&
topic_alias
)
return client::error::topic_alias_maximum_reached;
if (topic_alias_max && topic_alias && *topic_alias > *topic_alias_max)
return client::error::topic_alias_maximum_reached;
return {};
}
@ -351,23 +365,30 @@ private:
);
}
void complete(error_code ec)
requires (qos_type == qos_e::at_most_once)
{
template <
qos_e q = qos_type,
std::enable_if_t<q == qos_e::at_most_once, bool> = true
>
void complete(error_code ec) {
_handler.complete(ec);
}
void complete_post(error_code ec)
requires (qos_type == qos_e::at_most_once)
{
template <
qos_e q = qos_type,
std::enable_if_t<q == qos_e::at_most_once, bool> = true
>
void complete_post(error_code ec) {
_handler.complete_post(ec);
}
template <typename Props = on_publish_props_type<qos_type>>
requires (
std::is_same_v<Props, puback_props> ||
std::is_same_v<Props, pubcomp_props>
)
template <
typename Props = on_publish_props_type<qos_type>,
std::enable_if_t<
std::is_same_v<Props, puback_props> ||
std::is_same_v<Props, pubcomp_props>,
bool
> = true
>
void complete(
error_code ec, reason_code rc,
uint16_t packet_id, Props&& props
@ -376,11 +397,14 @@ private:
_handler.complete(ec, rc, std::forward<Props>(props));
}
template <typename Props = on_publish_props_type<qos_type>>
requires (
std::is_same_v<Props, puback_props> ||
std::is_same_v<Props, pubcomp_props>
)
template <
typename Props = on_publish_props_type<qos_type>,
std::enable_if_t<
std::is_same_v<Props, puback_props> ||
std::is_same_v<Props, pubcomp_props>,
bool
> = true
>
void complete_post(error_code ec) {
_handler.complete_post(ec, reason_codes::empty, Props {});
}

View File

@ -8,8 +8,8 @@
#include <async_mqtt5/detail/control_packet.hpp>
#include <async_mqtt5/detail/any_authenticator.hpp>
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
#include <async_mqtt5/impl/disconnect_op.hpp>

View File

@ -14,7 +14,7 @@
#include <async_mqtt5/impl/publish_rec_op.hpp>
#include <async_mqtt5/impl/re_auth_op.hpp>
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
namespace async_mqtt5::detail {
@ -84,6 +84,7 @@ private:
byte_citer first, byte_citer last
) {
auto code = control_code_e(control_byte & 0b11110000);
switch (code) {
case control_code_e::publish: {
auto msg = decoders::decode_publish(
@ -123,6 +124,7 @@ private:
auto props = disconnect_props {};
props[prop::reason_string] = reason;
auto svc_ptr = _svc_ptr; // copy before this is moved
async_disconnect(
disconnect_rc_e::malformed_packet, props, false, svc_ptr,
asio::prepend(std::move(*this), on_disconnect {})

View File

@ -109,7 +109,7 @@ private:
static bool should_reconnect(error_code ec) {
using namespace asio::error;
return ec == connection_aborted || ec == not_connected ||
ec == timed_out || ec == connection_reset ||
ec == timed_out || ec == connection_reset ||
ec == broken_pipe || ec == asio::error::eof;
}
};

View File

@ -149,7 +149,8 @@ public:
void operator()(
on_connect, typename Owner::stream_ptr sptr,
auto ord, error_code connect_ec, error_code timer_ec
std::array<std::size_t, 2> ord,
error_code connect_ec, error_code timer_ec
) {
// connect_ec may be any of stream.async_connect() error codes
// plus access_denied, connection_refused and

View File

@ -13,8 +13,8 @@
#include <async_mqtt5/detail/internal_types.hpp>
#include <async_mqtt5/detail/utf8_mqtt.hpp>
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
#include <async_mqtt5/impl/disconnect_op.hpp>
@ -38,7 +38,8 @@ class subscribe_op {
public:
subscribe_op(
const std::shared_ptr<client_service>& svc_ptr, Handler&& handler
const std::shared_ptr<client_service>& svc_ptr,
Handler&& handler
) :
_svc_ptr(svc_ptr),
_handler(std::move(handler), get_executor())
@ -138,14 +139,18 @@ public:
private:
static error_code validate_topics(const std::vector<subscribe_topic>& topics) {
static error_code validate_topics(
const std::vector<subscribe_topic>& topics
) {
for (const auto& topic: topics)
if (!is_valid_utf8_topic(topic.topic_filter))
return client::error::invalid_topic;
return error_code {};
}
static std::vector<reason_code> to_reason_codes(std::vector<uint8_t> codes) {
static std::vector<reason_code> to_reason_codes(
std::vector<uint8_t> codes
) {
std::vector<reason_code> ret;
for (uint8_t code : codes) {
auto rc = to_reason_code<reason_codes::category::suback>(code);
@ -167,7 +172,8 @@ private:
void complete_post(error_code ec, size_t num_topics) {
_handler.complete_post(
ec, std::vector<reason_code> { num_topics, reason_codes::empty }, suback_props {}
ec, std::vector<reason_code> { num_topics, reason_codes::empty },
suback_props {}
);
}

View File

@ -12,8 +12,8 @@
#include <async_mqtt5/detail/utf8_mqtt.hpp>
#include <async_mqtt5/impl/disconnect_op.hpp>
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
namespace async_mqtt5::detail {
@ -35,7 +35,8 @@ class unsubscribe_op {
public:
unsubscribe_op(
const std::shared_ptr<client_service>& svc_ptr, Handler&& handler
const std::shared_ptr<client_service>& svc_ptr,
Handler&& handler
) :
_svc_ptr(svc_ptr),
_handler(std::move(handler), get_executor())
@ -128,7 +129,6 @@ public:
}
auto& [props, reason_codes] = *unsuback;
// TODO: perhaps do something with the topics we unsubscribed from (one day)
complete(
ec, packet_id,
@ -151,7 +151,6 @@ private:
auto rc = to_reason_code<reason_codes::category::unsuback>(code);
if (rc)
ret.push_back(*rc);
// TODO: on off chance that one of the rcs is invalid, should we push something to mark that?
}
return ret;
}
@ -169,7 +168,8 @@ private:
void complete_post(error_code ec, size_t num_topics) {
_handler.complete_post(
ec, std::vector<reason_code> { num_topics, reason_codes::empty }, unsuback_props {}
ec, std::vector<reason_code> { num_topics, reason_codes::empty },
unsuback_props {}
);
}

View File

@ -76,8 +76,13 @@ public:
* std::is_convertible_v<ExecutionContext&, asio::execution_context&>
* \endcode
*/
template <typename ExecutionContext>
requires (std::is_convertible_v<ExecutionContext&, asio::execution_context&>)
template <
typename ExecutionContext,
std::enable_if_t<
std::is_convertible_v<ExecutionContext&, asio::execution_context&>,
bool
> = true
>
explicit mqtt_client(
ExecutionContext& context,
const std::string& cnf,
@ -134,8 +139,12 @@ public:
* !std::is_same_v<TlsContext, std::monostate>
* \endcode
*/
template <
typename Ctx = TlsContext,
std::enable_if_t<!std::is_same_v<Ctx, std::monostate>, bool> = true
>
decltype(auto) tls_context()
requires (!std::is_same_v<TlsContext, std::monostate>) {
{
return _svc_ptr->tls_context();
}
@ -182,8 +191,10 @@ public:
*
* \see See \__CONNACK_PROPS\__ for all eligible properties.
*/
template <uint8_t p>
decltype(auto) connection_property(std::integral_constant<uint8_t, p> prop) {
template <prop::property_type p>
decltype(auto) connection_property(
std::integral_constant<prop::property_type, p> prop
) {
return _svc_ptr->connack_prop(prop);
}
@ -272,7 +283,10 @@ public:
* before the \ref run function is invoked again.
*
*/
template <detail::is_authenticator Authenticator>
template <
typename Authenticator,
std::enable_if_t<detail::is_authenticator<Authenticator>, bool> = true
>
mqtt_client& authenticator(Authenticator&& authenticator) {
_svc_ptr->authenticator(std::forward<Authenticator>(authenticator));
return *this;

View File

@ -123,7 +123,7 @@ public:
template <
typename Func,
typename = std::enable_if_t<is_apply_on<Func>::value>
std::enable_if_t<is_apply_on<Func>::value, bool> = true
>
constexpr bool apply_on(uint8_t property_id, Func&& func)
noexcept (is_nothrow_apply_on<Func>::value) {
@ -154,7 +154,7 @@ public:
template <
typename Func,
typename = std::enable_if_t<is_visitor<Func>::value>
std::enable_if_t<is_visitor<Func>::value, bool> = true
>
constexpr bool visit(Func&& func)
const noexcept (is_nothrow_visitor<Func>::value) {
@ -173,7 +173,7 @@ public:
template <
typename Func,
typename = std::enable_if_t<is_visitor<Func>::value>
std::enable_if_t<is_visitor<Func>::value, bool> = true
>
constexpr bool visit(Func&& func)
noexcept (is_nothrow_visitor<Func>::value) {

View File

@ -390,12 +390,12 @@ public:
will& operator=(will&&) noexcept = default;
/// Get the Topic Name.
constexpr std::string_view topic() const {
std::string_view topic() const {
return _topic;
}
/// Get the Application Message.
constexpr std::string_view message() const {
std::string_view message() const {
return _message;
}

View File

@ -5,8 +5,8 @@
#include <async_mqtt5/detail/control_packet.hpp>
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
namespace async_mqtt5::test {

View File

@ -14,7 +14,7 @@
#include <boost/asio/ip/tcp.hpp>
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
#include "test_common/message_exchange.hpp"
#include "test_common/packet_util.hpp"

View File

@ -5,7 +5,7 @@
#include <boost/asio/io_context.hpp>
#include <boost/asio/steady_timer.hpp>
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
#include <async_mqtt5.hpp>

View File

@ -0,0 +1,105 @@
#include <boost/test/unit_test.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/beast/websocket/stream.hpp>
#include <async_mqtt5/detail/any_authenticator.hpp>
#include <async_mqtt5/detail/async_traits.hpp>
#include <async_mqtt5/mqtt_client.hpp>
using namespace async_mqtt5;
BOOST_AUTO_TEST_SUITE(traits/*, *boost::unit_test::disabled()*/)
class good_authenticator {
public:
good_authenticator() = default;
template <typename CompletionToken>
decltype(auto) async_auth(
auth_step_e step, std::string data,
CompletionToken&& token
) {
using error_code = boost::system::error_code;
using Signature = void(error_code, std::string);
auto initiate = [](auto, auth_step_e, std::string) {};
return asio::async_initiate<CompletionToken, Signature>(
initiate, token, step, std::move(data)
);
}
std::string_view method() const {
return "method";
}
};
class bad_authenticator {
public:
bad_authenticator() = default;
void async_auth(std::string data) {}
std::string_view method() const {
return "method";
}
};
BOOST_AUTO_TEST_CASE(is_authenticator) {
BOOST_STATIC_ASSERT(detail::is_authenticator<good_authenticator>);
BOOST_STATIC_ASSERT(!detail::is_authenticator<bad_authenticator>);
}
namespace asio = boost::asio;
namespace beast = boost::beast;
using tcp_layer = asio::ip::tcp::socket;
using tls_layer = asio::ssl::stream<asio::ip::tcp::socket>;
using websocket_tcp_layer = beast::websocket::stream<tcp_layer>;
using websocket_tls_layer = beast::websocket::stream<tls_layer>;
BOOST_AUTO_TEST_CASE(async_traits) {
BOOST_STATIC_ASSERT(!detail::has_next_layer<tcp_layer>);
BOOST_STATIC_ASSERT(detail::has_next_layer<tls_layer>);
BOOST_STATIC_ASSERT(detail::has_next_layer<websocket_tcp_layer>);
BOOST_STATIC_ASSERT(detail::has_next_layer<websocket_tls_layer>);
BOOST_STATIC_ASSERT(!detail::has_tls_layer<tcp_layer>);
BOOST_STATIC_ASSERT(detail::has_tls_layer<tls_layer>);
BOOST_STATIC_ASSERT(!detail::has_tls_layer<websocket_tcp_layer>);
BOOST_STATIC_ASSERT(detail::has_tls_layer<websocket_tls_layer>);
BOOST_STATIC_ASSERT(!detail::has_ws_handshake<tcp_layer>);
BOOST_STATIC_ASSERT(!detail::has_ws_handshake<tls_layer>);
BOOST_STATIC_ASSERT(detail::has_ws_handshake<websocket_tcp_layer>);
BOOST_STATIC_ASSERT(detail::has_ws_handshake<websocket_tls_layer>);
BOOST_STATIC_ASSERT(!detail::has_tls_handshake<tcp_layer>);
BOOST_STATIC_ASSERT(detail::has_tls_handshake<tls_layer>);
BOOST_STATIC_ASSERT(!detail::has_tls_handshake<websocket_tcp_layer>);
BOOST_STATIC_ASSERT(!detail::has_tls_handshake<websocket_tls_layer>);
}
BOOST_AUTO_TEST_CASE(client_functions) {
asio::io_context ioc;
mqtt_client<tcp_layer> tcp_client(ioc, "");
tcp_client.authenticator(good_authenticator());
auto data = tcp_client.connection_property(prop::authentication_data);
asio::ssl::context ctx(asio::ssl::context::tls_client);
mqtt_client<
tls_layer, asio::ssl::context
> tls_client(ioc.get_executor(), "", std::move(ctx));
tls_client.tls_context();
}
BOOST_AUTO_TEST_SUITE_END();

View File

@ -2,8 +2,8 @@
#include <async_mqtt5/types.hpp>
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
using namespace async_mqtt5;
using byte_citer = detail::byte_citer;