// // Copyright (c) 2023-2025 Ivica Siladic, Bruno Iljazovic, Korina Simicevic // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_MQTT5_PROPERTY_TYPES_HPP #define BOOST_MQTT5_PROPERTY_TYPES_HPP #include #include #include #include #include #include #include #include #include namespace boost::mqtt5::prop { /// \cond internal enum property_type : uint8_t { payload_format_indicator_t = 0x01, message_expiry_interval_t = 0x02, content_type_t = 0x03, response_topic_t = 0x08, correlation_data_t = 0x09, subscription_identifier_t = 0x0b, session_expiry_interval_t = 0x11, assigned_client_identifier_t = 0x12, server_keep_alive_t = 0x13, authentication_method_t = 0x15, authentication_data_t = 0x16, request_problem_information_t = 0x17, will_delay_interval_t = 0x18, request_response_information_t = 0x19, response_information_t = 0x1a, server_reference_t = 0x1c, reason_string_t = 0x1f, receive_maximum_t = 0x21, topic_alias_maximum_t = 0x22, topic_alias_t = 0x23, maximum_qos_t = 0x24, retain_available_t = 0x25, user_property_t = 0x26, maximum_packet_size_t = 0x27, wildcard_subscription_available_t = 0x28, subscription_identifier_available_t = 0x29, shared_subscription_available_t = 0x2a }; /// \endcond /** * \brief A class holding any number of Subscription Identifiers. * * \details Subscription Identifier is an integer that can be set in \__SUBSCRIBE_PROPS\__ when subscribing to a Topic. * Broker will store the mapping between that Subscription and the Subscription Identifier. * When an incoming \__PUBLISH\__ message matches one or more Subscriptions, respective Subscription * Identifiers will be forwarded in the \__PUBLISH_PROPS\__ of the received message. */ class alignas(8) subscription_identifiers : public boost::container::small_vector { using base_type = boost::container::small_vector; public: using base_type::base_type; /// Constructs Subscription Identifiers with given parameters. subscription_identifiers(int32_t val) : base_type { val } {} /// \{ Checks whether there are any Subscription Identifiers. bool has_value() const noexcept { return !empty(); } explicit operator bool() const noexcept { return !empty(); } /// \} /// \{ Accesses the (first) Subscription Identifier. int32_t& operator*() noexcept { return front(); } int32_t operator*() const noexcept { return front(); } /// \} /// Sets or replaces the (first) Subscription Identifier. void emplace(int32_t val = 0) { *this = val; } /// Returns the first Subscription Identifier. int32_t value() const { return front(); } /// Returns the first Subscription Identifier if it exists, otherwise returns \p default_val int32_t value_or(int32_t default_val) const noexcept { return empty() ? default_val : front(); } /// Clears the Subscription Identifiers. void reset() noexcept { clear(); } }; template struct property_traits; using user_property_value_t = std::vector>; #define DEF_PROPERTY_TRAIT(Pname, Ptype) \ template <> \ struct property_traits { \ static constexpr std::string_view name = #Pname; \ using type = Ptype; \ }; \ constexpr std::integral_constant Pname {}; DEF_PROPERTY_TRAIT(payload_format_indicator, std::optional); DEF_PROPERTY_TRAIT(message_expiry_interval, std::optional); DEF_PROPERTY_TRAIT(content_type, std::optional); DEF_PROPERTY_TRAIT(response_topic, std::optional); DEF_PROPERTY_TRAIT(correlation_data, std::optional); DEF_PROPERTY_TRAIT(subscription_identifier, subscription_identifiers); DEF_PROPERTY_TRAIT(session_expiry_interval, std::optional); DEF_PROPERTY_TRAIT(assigned_client_identifier, std::optional); DEF_PROPERTY_TRAIT(server_keep_alive, std::optional); DEF_PROPERTY_TRAIT(authentication_method, std::optional); DEF_PROPERTY_TRAIT(authentication_data, std::optional); DEF_PROPERTY_TRAIT(request_problem_information, std::optional); DEF_PROPERTY_TRAIT(will_delay_interval, std::optional); DEF_PROPERTY_TRAIT(request_response_information, std::optional); DEF_PROPERTY_TRAIT(response_information, std::optional); DEF_PROPERTY_TRAIT(server_reference, std::optional); DEF_PROPERTY_TRAIT(reason_string, std::optional); DEF_PROPERTY_TRAIT(receive_maximum, std::optional); DEF_PROPERTY_TRAIT(topic_alias_maximum, std::optional); DEF_PROPERTY_TRAIT(topic_alias, std::optional); DEF_PROPERTY_TRAIT(maximum_qos, std::optional); DEF_PROPERTY_TRAIT(retain_available, std::optional); DEF_PROPERTY_TRAIT(user_property, user_property_value_t); DEF_PROPERTY_TRAIT(maximum_packet_size, std::optional); DEF_PROPERTY_TRAIT(wildcard_subscription_available, std::optional); DEF_PROPERTY_TRAIT(subscription_identifier_available, std::optional); DEF_PROPERTY_TRAIT(shared_subscription_available, std::optional); #undef DEF_PROPERTY_TRAIT template using value_type_t = typename property_traits

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

::name; /** * \brief Base class for different Property classes that contains common access and modification functions. */ template class properties { template struct property { using key = std::integral_constant; constexpr static std::string_view name = name_v

; value_type_t

value; }; std::tuple...> _props; public: /// \brief Accesses the value of a Property by its compile-time constant ID. template constexpr auto& operator[](std::integral_constant) noexcept { return std::get>(_props).value; } /// \copydoc operator[] template constexpr const auto& operator[](std::integral_constant) const noexcept { return std::get>(_props).value; } /// \cond internal template using is_apply_on = std::conjunction< std::is_invocable&>... >; template using is_nothrow_apply_on = std::conjunction< std::is_nothrow_invocable&>... >; /// \endcond /** * \brief Applies a function to a Property identified by its ID. * * This function iterates over all Properties and invokes the provided function * on the value of the Property that matches the given property ID. * * \param property_id The ID of the Property to apply the function on. * \param func The function to invoke on the Property value. * \return `true` if the function was applied to a Property and `false` otherwise. */ template < typename Func, std::enable_if_t::value, bool> = true > constexpr bool apply_on(uint8_t property_id, Func&& func) noexcept (is_nothrow_apply_on::value) { return std::apply( [&func, property_id](auto&... ptype) { auto pc = [&func, property_id](auto& px) { using ptype = std::remove_reference_t; constexpr typename ptype::key prop; if (prop.value == property_id) std::invoke(func, px.value); return prop.value == property_id; }; return (pc(ptype) || ...); }, _props ); } /// \cond internal template using is_visitor = std::conjunction< std::is_invocable_r&>... >; template using is_nothrow_visitor = std::conjunction< std::is_nothrow_invocable&>... >; /// \endcond /** * \brief Invokes a visitor function on each Property. * * This function iterates over Properties and invokes the provided function * on each Property, passing both the compile-time constant Property ID and its value. * The visitor function should return a boolean value that determines whether iteration continues. * * @param func The visitor function to invoke. It must accept two arguments: * a Property ID and the corresponding Property value. * @return `true` if the visitor function returns `true` for all Properties, * `false` otherwise. */ template < typename Func, std::enable_if_t::value, bool> = true > constexpr bool visit(Func&& func) const noexcept (is_nothrow_visitor::value) { return std::apply( [&func](const auto&... props) { auto pc = [&func](const auto& px) { using ptype = std::remove_reference_t; constexpr typename ptype::key prop; return std::invoke(func, prop, px.value); }; return (pc(props) && ...); }, _props ); } /// \copydoc visit template < typename Func, std::enable_if_t::value, bool> = true > constexpr bool visit(Func&& func) noexcept (is_nothrow_visitor::value) { return std::apply( [&func](auto&... props) { auto pc = [&func](auto& px) { using ptype = std::remove_reference_t; constexpr typename ptype::key prop; return std::invoke(func, prop, px.value); }; return (pc(props) && ...); }, _props ); } }; } // end namespace boost::mqtt5::prop #endif // !BOOST_MQTT5_PROPERTY_TYPES_HPP