diff --git a/examples/pub/CMakeLists.txt b/examples/pub/CMakeLists.txt index 0e3c279..d26ac52 100644 --- a/examples/pub/CMakeLists.txt +++ b/examples/pub/CMakeLists.txt @@ -5,7 +5,7 @@ include(projectheader) project_header(test_mqtt_pub) include_directories( SYSTEM - ${CMAKE_CURRENT_SOURCE_DIR}/../../src + ${CMAKE_CURRENT_SOURCE_DIR}/../../include ) include(compiler) @@ -21,7 +21,7 @@ add_executable( ${PROJECT_NAME} target_link_libraries( ${PROJECT_NAME} - mqtt + mqtt-cpp ) set_target_properties( ${PROJECT_NAME} PROPERTIES diff --git a/examples/pub/main.cpp b/examples/pub/main.cpp index dd85a8e..9c181de 100644 --- a/examples/pub/main.cpp +++ b/examples/pub/main.cpp @@ -78,7 +78,7 @@ int main( int argc, char* argv[] ) { std::cout << "{OK}" << std::endl; std::cout << "Connecting to the broker : "; - pPublisher->connect( "localhost", 1883, "", "" ); + pPublisher->connect( "office.osdev.nl", 1883, "", "" ); // Assume we are connected now, start publishing. while( 1 ) @@ -86,7 +86,7 @@ int main( int argc, char* argv[] ) std::string payload = "" ; pPublisher->publish( std::string( "test/publisher/TestPublisher" ), payload ); - sleepcp( 1, T_SECONDS ); + sleepcp( 1, T_MICRO ); if( messageNumber > 2000000000 ) messageNumber = -1; diff --git a/examples/pub/publisher.cpp b/examples/pub/publisher.cpp index f41785b..07c0ee0 100644 --- a/examples/pub/publisher.cpp +++ b/examples/pub/publisher.cpp @@ -21,7 +21,7 @@ * ***************************************************************************/ // osdev::components::mqtt -#include "token.h" +// #include "token.h" // mqtt_tests #include "publisher.h" diff --git a/examples/sub/CMakeLists.txt b/examples/sub/CMakeLists.txt index f118452..281089e 100644 --- a/examples/sub/CMakeLists.txt +++ b/examples/sub/CMakeLists.txt @@ -5,7 +5,7 @@ include(projectheader) project_header(test_mqtt_sub) include_directories( SYSTEM - ${CMAKE_CURRENT_SOURCE_DIR}/../../src + ${CMAKE_CURRENT_SOURCE_DIR}/../../include ) include(compiler) @@ -21,7 +21,7 @@ add_executable( ${PROJECT_NAME} target_link_libraries( ${PROJECT_NAME} - mqtt + mqtt-cpp ) set_target_properties( ${PROJECT_NAME} PROPERTIES diff --git a/examples/sub/main.cpp b/examples/sub/main.cpp index 8288545..57e365c 100644 --- a/examples/sub/main.cpp +++ b/examples/sub/main.cpp @@ -71,12 +71,12 @@ int main( int argc, char* argv[] ) std::cout << "Creating the subscriber : "; // Create the subscriber - Subscriber *pSubscriber = new Subscriber(); + Subscriber *pSubscriber = new Subscriber( "Test_Subscriber" ); if( pSubscriber != nullptr ) { std::cout << "[OK]" << std::endl; std::cout << "Connecting to the test-broker : " << std::endl; - pSubscriber->connect( "localhost", 1883, "", "" ); + pSubscriber->connect( "office.osdev.nl", 1883, "", "" ); std::cout << "Subscribing to the test-topic....." << std::endl; pSubscriber->subscribe( "test/publisher/TestPublisher" ); diff --git a/examples/sub/subscriber.cpp b/examples/sub/subscriber.cpp index 61d0d5e..8354dd6 100644 --- a/examples/sub/subscriber.cpp +++ b/examples/sub/subscriber.cpp @@ -20,27 +20,13 @@ * DEALINGS IN THE SOFTWARE. * * ***************************************************************************/ #include "subscriber.h" -#include "mqttmessage.h" -#include "credentials.h" -Subscriber::Subscriber() - : m_mqtt_client( "TestSubscriber" ) -{ - -} +#include -void Subscriber::connect( const std::string &hostname, int portnumber, const std::string &username, const std::string &password ) +Subscriber::Subscriber( const std::string &client_id ) + : MqttSubscriberBase( client_id ) { - m_mqtt_client.connect( hostname, portnumber, osdev::components::mqtt::Credentials( username, password ) ); - std::cout << "Client state : " << m_mqtt_client.state() << std::endl; -} -void Subscriber::subscribe( const std::string &message_topic ) -{ - m_mqtt_client.subscribe( message_topic, 1, [this](const osdev::components::mqtt::MqttMessage &message) - { - this->receive_data(message.topic(), message.payload() ); - }); } void Subscriber::receive_data( const std::string &message_topic, const std::string &message_payload ) diff --git a/examples/sub/subscriber.h b/examples/sub/subscriber.h index a24fcca..8c67ef4 100644 --- a/examples/sub/subscriber.h +++ b/examples/sub/subscriber.h @@ -22,27 +22,19 @@ #pragma once // std -#include #include -// osdev::components::mqtt -#include "mqttclient.h" -#include "compat-c++14.h" +// mqtt-cpp +#include "mqttsubscriberbase.h" -class Subscriber +class Subscriber : public MqttSubscriberBase { public: - Subscriber(); + Subscriber( const std::string &client_id ); virtual ~Subscriber() {} - void connect( const std::string &hostname, int portnumber, const std::string &username, const std::string &password ); - - void subscribe( const std::string &message_topic ); - -private: +protected: void receive_data( const std::string &message_topic, const std::string &message_payload ); -private: - osdev::components::mqtt::MqttClient m_mqtt_client; }; diff --git a/include/mqttsubscriberbase.h b/include/mqttsubscriberbase.h index e69de29..956a8f5 100644 --- a/include/mqttsubscriberbase.h +++ b/include/mqttsubscriberbase.h @@ -0,0 +1,81 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ +#pragma once + +// std +#include +#include + +#include "mqttclient.h" + + +class MqttSubscriberBase +{ +public: + /*! + * \brief SubscriberBase + */ + MqttSubscriberBase( const std::string &client_id ); + + /*! + * \brief ~SubscriberBase + */ + virtual ~MqttSubscriberBase() {} + + /*! + * \brief getClientId + * \return + */ + std::string getClientId() const; + + /*! + * \brief connect + * \param hostname + * \param portnumber + * \param username + * \param password + */ + void connect( const std::string &hostname, int portnumber, const std::string &username, const std::string &password ); + + /*! + * \brief subscribe + * \param message_topic + */ + void subscribe( const std::string &message_topic ); + + /*! + * \brief disconnect + */ + void disconnect(); + +protected: + /*! + * \brief receive_data + * \param message_topic + * \param message_payload + */ + virtual void receive_data( const std::string &message_topic, const std::string &message_payload ) = 0; + +private: + osdev::components::mqtt::MqttClient m_mqtt_client; + +}; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 34432bc..e4f8c37 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,15 +23,14 @@ endif() # ============================================================================== include(projectheader) -project_header(mqtt) +project_header(mqtt-cpp) find_package( Boost REQUIRED COMPONENTS regex ) include(compiler) include_directories( - ${CMAKE_CURRENT_SOURCE_DIR}/../logutils - ${CMAKE_CURRENT_SOURCE_DIR}/../include + ${CMAKE_SOURCE_DIR}/include ) set(SRC_LIST @@ -40,8 +39,6 @@ set(SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/clientpaho.cpp ${CMAKE_CURRENT_SOURCE_DIR}/commondefs.cpp ${CMAKE_CURRENT_SOURCE_DIR}/connectionstatus.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/compiletimedigits.h - ${CMAKE_CURRENT_SOURCE_DIR}/compiletimestring.h ${CMAKE_CURRENT_SOURCE_DIR}/credentials.cpp ${CMAKE_CURRENT_SOURCE_DIR}/errorcode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/token.cpp @@ -63,23 +60,23 @@ set(SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/stringutils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/uriparser.cpp # Helper files ( Utillities ) - ${CMAKE_CURRENT_SOURCE_DIR}/bimap.h - ${CMAKE_CURRENT_SOURCE_DIR}/compat-c++14.h - ${CMAKE_CURRENT_SOURCE_DIR}/compat-chrono.h - ${CMAKE_CURRENT_SOURCE_DIR}/histogram.h - ${CMAKE_CURRENT_SOURCE_DIR}/histogramprovider.h - ${CMAKE_CURRENT_SOURCE_DIR}/imqttclient.h - ${CMAKE_CURRENT_SOURCE_DIR}/imqttclientimpl.h - ${CMAKE_CURRENT_SOURCE_DIR}/lockguard.h - ${CMAKE_CURRENT_SOURCE_DIR}/macrodefs.h - ${CMAKE_CURRENT_SOURCE_DIR}/measure.h - ${CMAKE_CURRENT_SOURCE_DIR}/metaprogrammingdefs.h - ${CMAKE_CURRENT_SOURCE_DIR}/mqttstream.h - ${CMAKE_CURRENT_SOURCE_DIR}/stringify.h - ${CMAKE_CURRENT_SOURCE_DIR}/stringutils.h - ${CMAKE_CURRENT_SOURCE_DIR}/synchronizedqueue.h - ${CMAKE_CURRENT_SOURCE_DIR}/utils.h - ${CMAKE_CURRENT_SOURCE_DIR}/uriutils.h + # ${CMAKE_CURRENT_SOURCE_DIR}/bimap.h + # ${CMAKE_CURRENT_SOURCE_DIR}/compat-c++14.h + # ${CMAKE_CURRENT_SOURCE_DIR}/compat-chrono.h + # ${CMAKE_CURRENT_SOURCE_DIR}/histogram.h + # ${CMAKE_CURRENT_SOURCE_DIR}/histogramprovider.h + # ${CMAKE_CURRENT_SOURCE_DIR}/imqttclient.h + # ${CMAKE_CURRENT_SOURCE_DIR}/imqttclientimpl.h + # ${CMAKE_CURRENT_SOURCE_DIR}/lockguard.h + # ${CMAKE_CURRENT_SOURCE_DIR}/macrodefs.h + # ${CMAKE_CURRENT_SOURCE_DIR}/measure.h + # ${CMAKE_CURRENT_SOURCE_DIR}/metaprogrammingdefs.h + # ${CMAKE_CURRENT_SOURCE_DIR}/mqttstream.h + # ${CMAKE_CURRENT_SOURCE_DIR}/stringify.h + # ${CMAKE_CURRENT_SOURCE_DIR}/stringutils.h + # ${CMAKE_CURRENT_SOURCE_DIR}/synchronizedqueue.h + # ${CMAKE_CURRENT_SOURCE_DIR}/utils.h + # ${CMAKE_CURRENT_SOURCE_DIR}/uriutils.h ) include(library) diff --git a/src/bimap.h b/src/bimap.h deleted file mode 100644 index e862792..0000000 --- a/src/bimap.h +++ /dev/null @@ -1,50 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_BIMAP_H -#define OSDEV_COMPONENTS_MQTT_BIMAP_H - -// boost -#include - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Factory function to create boost::bimap from an initializer list - * - * Usage: - * @code - * auto myBimap = makeBimap( { { 1, 2 }, { 2, 3 } } ); - * @endcode - */ -template -boost::bimap makeBimap(std::initializer_list::value_type> list) -{ - return boost::bimap(list.begin(), list.end()); -} - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_BIMAP_H diff --git a/src/clientpaho.h b/src/clientpaho.h deleted file mode 100644 index 2fc83b5..0000000 --- a/src/clientpaho.h +++ /dev/null @@ -1,355 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_CLIENTPAHO_H -#define OSDEV_COMPONENTS_MQTT_CLIENTPAHO_H - -// std -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// boost -#include - -// paho -#include - -// osdev::components::mqtt -#include "synchronizedqueue.h" -#include "imqttclientimpl.h" -#include "mqttfailure.h" -#include "mqttsuccess.h" - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Wrapper class for the paho-c library. - * This implementation uses the clean session flag and recreates subscriptions on reconnect. - * - * The implementation allows multiple subscriptions as long as the subscriptions do not have overlap. For mqtt 3 it is not - * possible to track down the subscription based on the incoming message meta information. By matching the topic against - * the various topic filters a subscription can be identified (but only when there is only one subscription that matches). - */ -class ClientPaho : public IMqttClientImpl -{ -public: - /** - * @brief Construct a ClientPaho instance. - * @param endpoint The endpoint to connect to - * @param id The clientId that is used in the connection. - * @param connectionStatusCallback The callback on which connection status information is communicated. - * @param deliveryCompleteCallback Callback that is called with the publish message tokens for messages that are delivered. - * Being delivered has different meanings depending on the quality of service. - */ - ClientPaho(const std::string& endpoint, - const std::string& id, - const std::function& connectionStatusCallback, - const std::function& deliveryCompleteCallback); - virtual ~ClientPaho() override; - - // Non copyable, non movable. - ClientPaho(const ClientPaho&) = delete; - ClientPaho& operator=(const ClientPaho&) = delete; - ClientPaho(ClientPaho&&) = delete; - ClientPaho& operator=(ClientPaho&&) = delete; - - /** - * @see IMqttClientImpl - */ - virtual std::string clientId() const override; - - /** - * @see IMqttClientImpl - */ - virtual ConnectionStatus connectionStatus() const override; - - /** - * @see IMqttClientImpl - */ - virtual std::int32_t connect(bool wait) override; - - /** - * @see IMqttClientImpl - */ - virtual std::int32_t disconnect(bool wait, int timeoutMs) override; - - /** - * @see IMqttClientImpl - */ - virtual std::int32_t publish(const MqttMessage& message, int qos) override; - - /** - * @see IMqttClientImpl - */ - virtual void publishPending() override; - - /** - * @see IMqttClientImpl - */ - virtual std::int32_t subscribe(const std::string& topic, int qos, const std::function& cb) override; - - /** - * @see IMqttClientImpl - */ - virtual void resubscribe() override; - - /** - * @see IMqttClientImpl - */ - virtual std::int32_t unsubscribe(const std::string& topic, int qos) override; - - /** - * @see IMqttClientImpl - */ - virtual void unsubscribeAll() override; - - /** - * @see IMqttClientImpl - */ - virtual std::chrono::milliseconds waitForCompletion(std::chrono::milliseconds waitFor, const std::set& tokens) const override; - - /** - * @see IMqttClientImpl - */ - virtual bool isOverlapping(const std::string& topic) const override; - - /** - * @see IMqttClientImpl - */ - virtual bool isOverlapping(const std::string& topic, std::string& existingTopic) const override; - - /** - * @see IMqttClientImpl - */ - virtual std::vector pendingOperations() const override; - - /** - * @see IMqttClientImpl - */ - virtual bool hasPendingSubscriptions() const override; - - /** - * @see IMqttClientImpl - */ - virtual boost::optional operationResult(std::int32_t token) const override; - -private: - void parseEndpoint(const std::string& endpoint); - - std::int32_t publishInternal(const MqttMessage& message, int qos); - std::int32_t subscribeInternal(const std::string& topic, int qos); - - void setConnectionStatus(ConnectionStatus status); - bool isOverlappingInternal(const std::string& topic, std::string& existingTopic) const; - - /** - * @brief Internal struct for subscriber information. - * Used to store subscriptions. - */ - struct Subscription - { - int qos; - boost::regex topicRegex; - std::function callback; - }; - - /** - * @brief Internal struct for publisher information. - * Used to store pending publishes during reconnection. - */ - struct Publish - { - int qos; - MqttMessage data; - }; - - /** - * @brief Add an incoming callback event to the synchronized queue. - * @param ev A function object that calls one of the event handlers on a ClientPaho instance, but other types of actions are also possible. - */ - void pushIncomingEvent(std::function ev); - - /** - * @brief Worker method that executes the events. - */ - void callbackEventHandler(); - - /** - * @brief Callback method that is called when a reconnect succeeds. - * @param cause The cause of the original disconnect. - */ - void onConnectOnInstance(const std::string& cause); - - /** - * @brief Callback that is called when a connect call succeeds. - * This callback is also called when a reconnect succeeds because the paho library reuses the initial connect command! - * The connection status is set to Connected. - * @param response A success response with connection data. - */ - void onConnectSuccessOnInstance(const MqttSuccess& response); - - /** - * @brief Callback that is called when a connect call fails after being sent to the endpoint. - * This callback is also called when a reconnect fails because the paho library reuses the initial connect command! - * The connection status is set to Disconnected when the connection state is ConnectInProgress, othwerwise the connection status is left as is. - * @param response A failure response. - */ - void onConnectFailureOnInstance(const MqttFailure& response); - - //void onDisconnectOnInstance(enum MQTTReasonCodes reasonCode); for MQTT V5 which is not supported by centos7 paho-c - - /** - * @brief Callback that is called when a disconnect call succeeds. - * The connection status is set to Disconnected. - * @param response A success response with no specific data. - */ - void onDisconnectSuccessOnInstance(const MqttSuccess& response); - - /** - * @brief Callback that is called when a disconnect call fails after being sent to the endpoint. - * Based on the result returned by the paho library The connection status is set to Disconnected or Connected. - * @param response A failure response. - */ - void onDisconnectFailureOnInstance(const MqttFailure& response); - - /** - * @brief Callback that is called when a publish call succeeds. - * This callback is called before the delivery complete callback. - * @param response A success response with the published message. - */ - void onPublishSuccessOnInstance(const MqttSuccess& response); - - /** - * @brief Callback that is called when a publish call fails after being sent to the endpoint. - * @param response A failure response. - */ - void onPublishFailureOnInstance(const MqttFailure& response); - - /** - * @brief Callback that is called when a subscribe call succeeds. - * @param response A success response with the subscription information. The actual used qos is conveyed in this response. - */ - void onSubscribeSuccessOnInstance(const MqttSuccess& response); - - /** - * @brief Callback that is called when a subscribe call fails after being sent to the endpoint. - * @param response A failure response. - */ - void onSubscribeFailureOnInstance(const MqttFailure& response); - - /** - * @brief Callback that is called when an unsubscribe call succeeds. - * @param response A success response with no specific data. - */ - void onUnsubscribeSuccessOnInstance(const MqttSuccess& response); - - /** - * @brief Callback that is called when an unsubscribe call fails after being sent to the endpoint. - * @param response A failure response. - */ - void onUnsubscribeFailureOnInstance(const MqttFailure& response); - - /** - * @brief Callback that is called when a message is received. - * @param message The message payload and meta data. - */ - int onMessageArrivedOnInstance(const MqttMessage& message); - - /** - * @brief Callback that is called when the delivery of a publish message is considered complete. - * The definition of complete depends on the quality of service used in the publish command. - * @param token The token with the publish command is sent. - */ - void onDeliveryCompleteOnInstance(MQTTAsync_token token); - - /** - * @brief Callback that is called when the connection is broken. - * @param cause The reason string. Always "cause unknown" for mqtt3 endpoints. - */ - void onConnectionLostOnInstance(const std::string& cause); - - // Static callback functions that are registered on the paho library. Functions call their *OnInstance() counterparts. - static void onConnect(void* context, char* cause); - static void onConnectSuccess(void* context, MQTTAsync_successData* response); - static void onConnectFailure(void* context, MQTTAsync_failureData* response); - //static void onDisconnect(void* context, MQTTProperties* properties, enum MQTTReasonCodes reasonCode); for MQTT V5 which is not supported by centos7 paho-c - static void onDisconnectSuccess(void* context, MQTTAsync_successData* response); - static void onDisconnectFailure(void* context, MQTTAsync_failureData* response); - static void onPublishSuccess(void* context, MQTTAsync_successData* response); - static void onPublishFailure(void* context, MQTTAsync_failureData* response); - static void onSubscribeSuccess(void* context, MQTTAsync_successData* response); - static void onSubscribeFailure(void* context, MQTTAsync_failureData* response); - static void onUnsubscribeSuccess(void* context, MQTTAsync_successData* response); - static void onUnsubscribeFailure(void* context, MQTTAsync_failureData* response); - static int onMessageArrived(void* context, char* topicName, int topicLen, MQTTAsync_message* message); - static void onDeliveryComplete(void* context, MQTTAsync_token token); - static void onConnectionLost(void* context, char* cause); - - /** - * @brief Connects the paho logging to the mlogic logging system. - * This callback is registered the first time a ClientPaho instance is constructed. - */ - static void onLogPaho(enum MQTTASYNC_TRACE_LEVELS level, char* message); - - mutable std::mutex m_mutex; - std::string m_endpoint; - std::string m_username; - std::string m_password; - std::string m_clientId; - std::set m_pendingOperations; - std::map m_operationResult; - mutable std::condition_variable m_operationsCompleteCV; - std::map m_subscriptions; - std::map m_pendingSubscriptions; - std::map m_subscribeTokenToTopic; - std::map m_unsubscribeTokenToTopic; - std::deque m_pendingPublishes; - bool m_processPendingPublishes; - mutable std::condition_variable m_pendingPublishesReadyCV; - ::MQTTAsync m_client; - std::atomic m_connectionStatus; - std::function m_connectionStatusCallback; - std::function m_deliveryCompleteCallback; - MQTTAsync_token m_lastUnsubscribe; ///< centos7 workaround - std::unique_ptr> m_connectPromise; - std::unique_ptr> m_disconnectPromise; - - SynchronizedQueue> m_callbackEventQueue; - std::thread m_workerThread; - - static std::atomic_int s_numberOfInstances; -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_CLIENTPAHO_H diff --git a/src/commondefs.h b/src/commondefs.h deleted file mode 100644 index 2f0de8f..0000000 --- a/src/commondefs.h +++ /dev/null @@ -1,129 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_COMMONDEFS_H -#define OSDEV_COMPONENTS_MQTT_COMMONDEFS_H - -// std -#include -#include -#include -#include -#include -#include -#include - -// boost -#include -#include -#include -#include - -#include "utils.h" - - -/// @brief Check if an id is valid -/// @throws InvalidArgumentException if id is invalid -#define OSDEV_COMPONENTS_CHECKMQTTID(id) \ - [&] { \ - if (id.is_nil()) { \ - } \ - }() - - -namespace osdev { -namespace components { -namespace mqtt { - -using MqttId = boost::uuids::uuid; -using OptionalId = MqttId; -using MqttIdSet = std::set; -using MqttIdSetIterator = MqttIdSet::const_iterator; -using MqttIdSetDelta = std::pair; -using StdTime = std::chrono::system_clock::time_point; -using OptionalTime = boost::optional; -using StdTimeVec = std::vector; -using SequenceNumber = std::uint32_t; -using OptionalSequenceNumber = boost::optional; -using CustomField = std::string; -using CustomFieldCollection = std::vector; - -using CountryCodeEnum = std::int32_t; - -/** - * @brief Defines a parsed uri. - */ -using ParsedUri = std::map; - -/** - * @brief Type for the parsed query part of a uri. - */ -using ParsedQuery = std::map; - -/** - * @brief Type for the parsed path part of a uri. - */ -using ParsedPath = std::vector; - -/** - * @brief Defines a duration with the granularity of a day in seconds (24 * 60 * 60 = 86400). - * This duration can be used to create a time_point at midnight of a given DateTime amongst others. - * - * The representation is a signed type so that negative durations are also possible. - */ -using days = std::chrono::duration>; - -/** - * @brief Defines a duration with the granularity of a year in seconds. A year is a bit longer than 365 days (365.2425). If a year is - * subtracted from a date the time part of the new date will therefore differ from the time part of the subtracted from date. - * - * The representation is a signed type so that negative durations are also possible. - */ -using years = std::chrono::duration>; // excactly 365 days would give 31536000 - -/** - * A timepoint type that is printed with millisecond resolution. - */ -struct StdTimeMs -{ - StdTimeMs(const StdTime& tp) - : timePoint(tp) - { - } - - operator StdTime() const { return timePoint; } - - StdTime timePoint; -}; - -std::ostream& operator<<(std::ostream& os, const StdTimeMs& rhs); - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -namespace std { - -std::ostream& operator<<(std::ostream& os, const osdev::components::mqtt::StdTime& rhs); - -} // End namespace std - -#endif // OSDEV_COMPONENTS_MQTT_COMMONDEFS_H diff --git a/src/compat-c++14.h b/src/compat-c++14.h deleted file mode 100644 index df7ffbd..0000000 --- a/src/compat-c++14.h +++ /dev/null @@ -1,75 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_COMPATCXX14 -#define OSDEV_COMPONENTS_COMPATCXX14 - -#include - -// The below code must be skipped if we use a C++14 or newer compiler -#if __cplusplus == 201103L - -namespace std { -/// Copied from libstdc++ 4.9.2 bits/unique_ptr.h - -template -struct _MakeUniq -{ - typedef unique_ptr<_Tp> __single_object; -}; - -template -struct _MakeUniq<_Tp[]> -{ - typedef unique_ptr<_Tp[]> __array; -}; - -template -struct _MakeUniq<_Tp[_Bound]> -{ - struct __invalid_type - { - }; -}; - -/// std::make_unique for single objects -template -inline typename _MakeUniq<_Tp>::__single_object make_unique(_Args&&... __args) -{ - return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); -} - -/// std::make_unique for arrays of unknown bound -template -inline typename _MakeUniq<_Tp>::__array make_unique(size_t __num) -{ - return unique_ptr<_Tp>(new typename remove_extent<_Tp>::type[__num]()); -} - -/// Disable std::make_unique for arrays of known bound -template -inline typename _MakeUniq<_Tp>::__invalid_type make_unique(_Args&&...) = delete; - -} // End namespace std - -#endif // Check for C++14 - -#endif diff --git a/src/compat-chrono.h b/src/compat-chrono.h deleted file mode 100644 index c383852..0000000 --- a/src/compat-chrono.h +++ /dev/null @@ -1,62 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#include -#include - -// function std::chrono::ceil is added in the c++17 standard. -// Added implementation from https://en.cppreference.com/w/cpp/chrono/duration/ceil -// to this compatibility header. The header only defines this implementation when -// the c++ compiler is used with an older standard. - -#if !defined __cpp_lib_chrono || __cpp_lib_chrono < 201611 - -namespace std { -namespace chrono { - -template -struct is_duration : std::false_type -{ -}; -template -struct is_duration< - std::chrono::duration> : std::true_type -{ -}; - -template {}>::type> -To ceil(const std::chrono::duration& d) -{ - To t = std::chrono::duration_cast(d); - if (t < d) - { - return t + To{ 1 }; - } - - // or else... - return t; -} - -} // End namespace chrono -} // End namespace std - -#endif diff --git a/src/compiletimedigits.h b/src/compiletimedigits.h deleted file mode 100644 index 2fd6071..0000000 --- a/src/compiletimedigits.h +++ /dev/null @@ -1,121 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_COMPILETIMEDIGITS_H -#define OSDEV_COMPONENTS_MQTT_COMPILETIMEDIGITS_H - -// std -#include - -#include "compiletimestring.h" - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Calculate the number of digits needed to represent a given value in a given base. - * @tparam B The base to use. - * @param value The value to represent. - * @return The number of digits. - */ -template -constexpr std::size_t numberOfDigits(unsigned value) noexcept -{ - static_assert(B > 0, "base must be larger than zero"); - return (value / B) == 0 ? 1 : (1 + numberOfDigits(value / B)); -} - -/** - * @return stringified digit that represents the given single digit value. - * @note Values can range from 0 to 37 inclusive which maps on 0-9A-Z. - */ -template -constexpr char digit() noexcept -{ - static_assert(value <= 37, "Value must lie between 0 and 37 both inclusive"); - return (value < 10 ? '0' + static_cast(value) : 'A' + static_cast(value) - 10); -} - -/** - * @brief Recursive template definition that is used to determine the value of a given number N in a given base B. - * @tparam N The number to obtain the representation for. - * @tparam B The base to use. - * @tparam Len The length of the representation including the 0 terminator. - * If not provided the length is calculated on first instantiation of this template. - * @tparam remains Empty parameter pack on first instantiation. Will be filled further by every following instantiation. - */ -template (N) + 1, unsigned... remains> -struct Digits -{ - - static constexpr auto create() noexcept -> std::array - { - static_assert(B > 0, "base must be larger than zero"); - return Digits(), remains...>::create(); - } -}; - -/** - * @brief Termination template that will return the actual hex digit array that represents the given value. - * @tparam B The base to use. - * @tparam Len The length of the hexadecimal representation including 0 terminator. - * @tparam remains Parameter pack that contains the hexadecimal character representations. - */ -template -struct Digits<0, B, Len, remains...> -{ - - static constexpr auto create() noexcept -> std::array - { - static_assert(sizeof...(remains) + 1 == Len, "Parameter 'Len' must be equal to the length of the parameter pack 'remains' including the termination zero"); - return std::array{ { remains..., 0 } }; - } -}; - -/** - * @brief Digits builder can be used in combination with the compiletime_string. - * @tparam N The number to obtain the compile time string representation for. - * @tparam B The base to use (up to 37). - * This template struct defines a produce() method and the return type of that method. - */ -template -struct ProduceDigits -{ - /** - * @brief Return type definition of the produce method. - */ - using return_t = std::array(N) + 1>; - - /** - * @brief Produce the actual representation. - */ - static constexpr return_t produce() noexcept - { - return Digits::create(); - } -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_COMPILETIMEDIGITS_H diff --git a/src/compiletimestring.h b/src/compiletimestring.h deleted file mode 100644 index 5a7604b..0000000 --- a/src/compiletimestring.h +++ /dev/null @@ -1,158 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_COMPILETIMESTRING_H -#define OSDEV_COMPONENTS_MQTT_COMPILETIMESTRING_H - -/// @file -/// -/// Support for compiletime string building. -/// -/// In some cases it is necessary to embed string messages as read only string literals -/// so that they can be used in low memory conditions for example. -/// This header defines two macro's that can be used to build managed string literals. -/// These managed string literals can be concatenated and searched at compiletime. -/// When the compiler is done with this code only the embedded string literal that are -/// used by normal expressions remain in the binary. -/// The implementation is a mix of meta template programming and the use of constexpr -/// expressions and is based on an idea found on stackoverflow. -/// This code is meant to handle small strings (up to say 100 characters). Larger strings can trigger the maximum -/// template recursion depth of the compiler (-ftemplate-depth). This depth is set at 900. So in principle strings -/// of 900 characters can be handled. However this is very inefficient and will prolong compiletime severly. -/// -/// Here follows an example of how this code can be used. -/// @code -/// auto str1 = OSDEV_COMPONENTS_CSTRING("This is a test"); -/// auto str2 = OSDEV_COMPONENTS_CSTRING(" with concatenation"); -/// auto str3 = str1 + str2; -/// const char* s = str3.chars; -/// @endcode -/// str3.chars contains the compiletime concatenated string. -/// -/// When examing the binary one will see that it contains the null terminated string literal -/// @code -/// This is a test with concatenation^@ -/// @endcode -/// The str1 and str2 literals are discarded since they are never used in a non compiletime way. -/// @note This code is not meant to handle string literals with internal \0 characters. Beware! - -#include - -#include "metaprogrammingdefs.h" - -/// @brief Build a managed substring string literal from a string literal input -/// The strings are build compile time. -/// The upper bound is one past the last character that the caller is interested in. -#define OSDEV_COMPONENTS_CSTRING_BOUNDED(string_literal, lower, upper) \ - [] { \ - constexpr std::size_t lb = (lower); \ - constexpr std::size_t ub = (upper); \ - static_assert(lb <= ub, "lower bound must be smaller than or equal to upper bound"); \ - static_assert(ub < sizeof(string_literal), \ - "upper bound must be smaller than or equal to the length of the string"); \ - struct constexpr_string_type \ - { \ - const char* chars = string_literal; \ - }; \ - return typename osdev::components::mqtt::apply_bounded_range::template produce>::result{}; \ - }() - -/// @brief Build a managed string literal from a string literal input -#define OSDEV_COMPONENTS_CSTRING(string_literal) \ - OSDEV_COMPONENTS_CSTRING_BOUNDED(string_literal, 0, sizeof(string_literal) - 1) - -namespace osdev { -namespace components { -namespace mqtt { - -/// @brief Managed string literal. -/// This class is used to hold string literals at compile time. -template -struct compiletime_string -{ - /// @brief Declaration of the string - static constexpr const char chars[sizeof...(str) + 1] = { str..., '\0' }; -}; - -/// @brief Definition of the string -template -constexpr const char compiletime_string::chars[sizeof...(str) + 1]; - -/// @brief Managed string literal builder. -/// This class is used to build string literals at compile time. -template -struct string_builder -{ - /// @brief maps indices list on the char array - template - struct produce - { - typedef compiletime_string result; - }; -}; - -/// @brief Adapter for coupling other producers to string_builder. -/// tparam T The type of producer to adapt. -/// @note The adapter must define its return type as return_t and provide a static method produce() which produces the result. -/// The return type must be a kind of array type of chars (char[len], std::array, etc). -template -struct adapt_for_string_builder -{ - static constexpr typename T::return_t chars = T::produce(); -}; - -/// @brief Concatenate two managed string literals -/// The literals are packed in a variadic template. -/// These templates are formed by the MLOGIC_COMMON_CSTRING* macro's -/// The concatenation is done at compiletime. -template -compiletime_string operator+(compiletime_string, compiletime_string) -{ - return {}; -} - -/// @brief Search for a character in a string literal from the right side. -/// The character index is returned relative to the left side. -/// If the character is not found a std::size_t(-1) is returned. -/// The default search starts at the end of the string. The index -/// can be given to start the search at another point in the string. -/// The index is given as nr of characters from the right end side of -/// the string. To proceed with a search see following example. -/// @code -/// constexpr const char s[] = "This is a test"; -/// constexpr std::size_t index = osdev_components::mqtt::rfind(s, 'i'); // gives 5 -/// static_assert(index == 5, ""); -/// constexpr std::size_t index2 = osdev::components::mqtt::rfind(s, 'i', sizeof(s) - index); // gives 2 -/// static_assert(index2 == 2, ""); -/// @endcode -/// This function should only be used at compile time! -template -constexpr std::size_t rfind(const char (&str)[N], char c, std::size_t index = 0) -{ - return index >= N ? std::numeric_limits::max() : str[N - index - 1] == c ? N - index - 1 : rfind(str, c, index + 1); -} - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_COMPILETIMESTRING_H diff --git a/src/connectionstatus.h b/src/connectionstatus.h deleted file mode 100644 index 7eae71a..0000000 --- a/src/connectionstatus.h +++ /dev/null @@ -1,53 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_CONNECTIONSTATUS_H -#define OSDEV_COMPONENTS_MQTT_CONNECTIONSTATUS_H - -// std -#include - -namespace osdev { -namespace components { -namespace mqtt { - -/*! - * \brief Enumeration for MQTT connection Status - */ -enum class ConnectionStatus -{ - Disconnected, ///< Client is disconnected. - DisconnectInProgress, ///< Client is being disconnected. - ConnectInProgress, ///< Client is being connected. - ReconnectInProgress, ///< Client tries to reconnect. - Connected, ///< Client is connected. -}; - -/*! - * \brief Stream operator for the connection status - */ -std::ostream& operator<<(std::ostream &os, ConnectionStatus rhs); - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_CONNECTIONSTATUS_H diff --git a/src/credentials.h b/src/credentials.h deleted file mode 100644 index 3c61c55..0000000 --- a/src/credentials.h +++ /dev/null @@ -1,61 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_CREDENTIALS_H -#define OSDEV_COMPONENTS_MQTT_CREDENTIALS_H - -// std -#include - -namespace osdev { -namespace components { -namespace mqtt { - -/*! - * \brief Class that holds user credentials - */ -class Credentials -{ -public: - /*! - * \brief Default CTor, empty credentials - */ - Credentials(); - - /*! - * \brief Constructor for username/password credentials - * \param username - The username - * \param password - The password - */ - Credentials(const std::string &username, const std::string &password); - - const std::string& username() const { return m_username; } - const std::string& password() const { return m_password; } -private: - std::string m_username; - std::string m_password; -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_CREDENTIALS_H diff --git a/src/date.h b/src/date.h deleted file mode 100644 index 8e4ee81..0000000 --- a/src/date.h +++ /dev/null @@ -1,8235 +0,0 @@ -#ifndef DATE_H -#define DATE_H - -// The MIT License (MIT) -// -// Copyright (c) 2015, 2016, 2017 Howard Hinnant -// Copyright (c) 2016 Adrian Colomitchi -// Copyright (c) 2017 Florian Dang -// Copyright (c) 2017 Paul Thompson -// Copyright (c) 2018, 2019 Tomasz KamiƄski -// Copyright (c) 2019 Jiangang Zhuang -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that would involve another several millennia of evolution). -// We did not mean to shout. - -#ifndef HAS_STRING_VIEW -# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define HAS_STRING_VIEW 1 -# else -# define HAS_STRING_VIEW 0 -# endif -#endif // HAS_STRING_VIEW - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if HAS_STRING_VIEW -# include -#endif -#include -#include - -#ifdef __GNUC__ -# pragma GCC diagnostic push -# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 7) -# pragma GCC diagnostic ignored "-Wpedantic" -# endif -# if __GNUC__ < 5 - // GCC 4.9 Bug 61489 Wrong warning with -Wmissing-field-initializers -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -# endif -#endif - -#ifdef _MSC_VER -# pragma warning(push) -// warning C4127: conditional expression is constant -# pragma warning(disable : 4127) -#endif - -namespace date -{ - -//---------------+ -// Configuration | -//---------------+ - -#ifndef ONLY_C_LOCALE -# define ONLY_C_LOCALE 0 -#endif - -#if defined(_MSC_VER) && (!defined(__clang__) || (_MSC_VER < 1910)) -// MSVC -# ifndef _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING -# define _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING -# endif -# if _MSC_VER < 1910 -// before VS2017 -# define CONSTDATA const -# define CONSTCD11 -# define CONSTCD14 -# define NOEXCEPT _NOEXCEPT -# else -// VS2017 and later -# define CONSTDATA constexpr const -# define CONSTCD11 constexpr -# define CONSTCD14 constexpr -# define NOEXCEPT noexcept -# endif - -#elif defined(__SUNPRO_CC) && __SUNPRO_CC <= 0x5150 -// Oracle Developer Studio 12.6 and earlier -# define CONSTDATA constexpr const -# define CONSTCD11 constexpr -# define CONSTCD14 -# define NOEXCEPT noexcept - -#elif __cplusplus >= 201402 -// C++14 -# define CONSTDATA constexpr const -# define CONSTCD11 constexpr -# define CONSTCD14 constexpr -# define NOEXCEPT noexcept -#else -// C++11 -# define CONSTDATA constexpr const -# define CONSTCD11 constexpr -# define CONSTCD14 -# define NOEXCEPT noexcept -#endif - -#ifndef HAS_UNCAUGHT_EXCEPTIONS -# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define HAS_UNCAUGHT_EXCEPTIONS 1 -# else -# define HAS_UNCAUGHT_EXCEPTIONS 0 -# endif -#endif // HAS_UNCAUGHT_EXCEPTIONS - -#ifndef HAS_VOID_T -# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define HAS_VOID_T 1 -# else -# define HAS_VOID_T 0 -# endif -#endif // HAS_VOID_T - -// Protect from Oracle sun macro -#ifdef sun -# undef sun -#endif - -// Work around for a NVCC compiler bug which causes it to fail -// to compile std::ratio_{multiply,divide} when used directly -// in the std::chrono::duration template instantiations below -namespace detail { -template -using ratio_multiply = decltype(std::ratio_multiply{}); - -template -using ratio_divide = decltype(std::ratio_divide{}); -} // namespace detail - -//-----------+ -// Interface | -//-----------+ - -// durations - -using days = std::chrono::duration - , std::chrono::hours::period>>; - -using weeks = std::chrono::duration - , days::period>>; - -using years = std::chrono::duration - , days::period>>; - -using months = std::chrono::duration - >>; - -// time_point - -template - using sys_time = std::chrono::time_point; - -using sys_days = sys_time; -using sys_seconds = sys_time; - -struct local_t {}; - -template - using local_time = std::chrono::time_point; - -using local_seconds = local_time; -using local_days = local_time; - -// types - -struct last_spec -{ - explicit last_spec() = default; -}; - -class day; -class month; -class year; - -class weekday; -class weekday_indexed; -class weekday_last; - -class month_day; -class month_day_last; -class month_weekday; -class month_weekday_last; - -class year_month; - -class year_month_day; -class year_month_day_last; -class year_month_weekday; -class year_month_weekday_last; - -// date composition operators - -CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; -CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; - -CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; -CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; -CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; -CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; - -CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; -CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; - -CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; -CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; - -CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; -CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; - -CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; -CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; -CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; - -CONSTCD11 - year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; -CONSTCD11 - year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; - -// Detailed interface - -// day - -class day -{ - unsigned char d_; - -public: - day() = default; - explicit CONSTCD11 day(unsigned d) NOEXCEPT; - - CONSTCD14 day& operator++() NOEXCEPT; - CONSTCD14 day operator++(int) NOEXCEPT; - CONSTCD14 day& operator--() NOEXCEPT; - CONSTCD14 day operator--(int) NOEXCEPT; - - CONSTCD14 day& operator+=(const days& d) NOEXCEPT; - CONSTCD14 day& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; - -CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; -CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; -CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; -CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d); - -// month - -class month -{ - unsigned char m_; - -public: - month() = default; - explicit CONSTCD11 month(unsigned m) NOEXCEPT; - - CONSTCD14 month& operator++() NOEXCEPT; - CONSTCD14 month operator++(int) NOEXCEPT; - CONSTCD14 month& operator--() NOEXCEPT; - CONSTCD14 month operator--(int) NOEXCEPT; - - CONSTCD14 month& operator+=(const months& m) NOEXCEPT; - CONSTCD14 month& operator-=(const months& m) NOEXCEPT; - - CONSTCD11 explicit operator unsigned() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; - -CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; -CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; -CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; -CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m); - -// year - -class year -{ - short y_; - -public: - year() = default; - explicit CONSTCD11 year(int y) NOEXCEPT; - - CONSTCD14 year& operator++() NOEXCEPT; - CONSTCD14 year operator++(int) NOEXCEPT; - CONSTCD14 year& operator--() NOEXCEPT; - CONSTCD14 year operator--(int) NOEXCEPT; - - CONSTCD14 year& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 year operator-() const NOEXCEPT; - CONSTCD11 year operator+() const NOEXCEPT; - - CONSTCD11 bool is_leap() const NOEXCEPT; - - CONSTCD11 explicit operator int() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - - static CONSTCD11 year min() NOEXCEPT { return year{-32767}; } - static CONSTCD11 year max() NOEXCEPT { return year{32767}; } -}; - -CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; - -CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; -CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; -CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; -CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y); - -// weekday - -class weekday -{ - unsigned char wd_; -public: - weekday() = default; - explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; - CONSTCD14 weekday(const sys_days& dp) NOEXCEPT; - CONSTCD14 explicit weekday(const local_days& dp) NOEXCEPT; - - CONSTCD14 weekday& operator++() NOEXCEPT; - CONSTCD14 weekday operator++(int) NOEXCEPT; - CONSTCD14 weekday& operator--() NOEXCEPT; - CONSTCD14 weekday operator--(int) NOEXCEPT; - - CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; - CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; - - CONSTCD11 unsigned c_encoding() const NOEXCEPT; - CONSTCD11 unsigned iso_encoding() const NOEXCEPT; - - CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; - CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; - -private: - static CONSTCD14 unsigned char weekday_from_days(int z) NOEXCEPT; - - friend CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; - friend CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; - friend CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; - template - friend std::basic_ostream& - operator<<(std::basic_ostream& os, const weekday& wd); - friend class weekday_indexed; -}; - -CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; - -CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; -CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; -CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd); - -// weekday_indexed - -class weekday_indexed -{ - unsigned char wd_ : 4; - unsigned char index_ : 4; - -public: - weekday_indexed() = default; - CONSTCD11 weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT; - - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi); - -// weekday_last - -class weekday_last -{ - date::weekday wd_; - -public: - explicit CONSTCD11 weekday_last(const date::weekday& wd) NOEXCEPT; - - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl); - -namespace detail -{ - -struct unspecified_month_disambiguator {}; - -} // namespace detail - -// year_month - -class year_month -{ - date::year y_; - date::month m_; - -public: - year_month() = default; - CONSTCD11 year_month(const date::year& y, const date::month& m) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - - template - CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; - template - CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; - CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; - CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; - -template -CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; -template -CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; -template -CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; - -CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; -CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; -CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; -CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym); - -// month_day - -class month_day -{ - date::month m_; - date::day d_; - -public: - month_day() = default; - CONSTCD11 month_day(const date::month& m, const date::day& d) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::day day() const NOEXCEPT; - - CONSTCD14 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md); - -// month_day_last - -class month_day_last -{ - date::month m_; - -public: - CONSTCD11 explicit month_day_last(const date::month& m) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; -CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl); - -// month_weekday - -class month_weekday -{ - date::month m_; - date::weekday_indexed wdi_; -public: - CONSTCD11 month_weekday(const date::month& m, - const date::weekday_indexed& wdi) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; -CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd); - -// month_weekday_last - -class month_weekday_last -{ - date::month m_; - date::weekday_last wdl_; - -public: - CONSTCD11 month_weekday_last(const date::month& m, - const date::weekday_last& wd) NOEXCEPT; - - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); - -// class year_month_day - -class year_month_day -{ - date::year y_; - date::month m_; - date::day d_; - -public: - year_month_day() = default; - CONSTCD11 year_month_day(const date::year& y, const date::month& m, - const date::day& d) NOEXCEPT; - CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; - - CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; - CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; - - template - CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; - template - CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; -CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; - -template -CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; -template -CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; -template -CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; -CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; -CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; -CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd); - -// year_month_day_last - -class year_month_day_last -{ - date::year y_; - date::month_day_last mdl_; - -public: - CONSTCD11 year_month_day_last(const date::year& y, - const date::month_day_last& mdl) NOEXCEPT; - - template - CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; - template - CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::month_day_last month_day_last() const NOEXCEPT; - CONSTCD14 date::day day() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; -CONSTCD11 - bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; - -template -CONSTCD14 -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -template -CONSTCD14 -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; - -template -CONSTCD14 -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); - -// year_month_weekday - -class year_month_weekday -{ - date::year y_; - date::month m_; - date::weekday_indexed wdi_; - -public: - year_month_weekday() = default; - CONSTCD11 year_month_weekday(const date::year& y, const date::month& m, - const date::weekday_indexed& wdi) NOEXCEPT; - CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; - CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; - - template - CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; - template - CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 unsigned index() const NOEXCEPT; - CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD14 bool ok() const NOEXCEPT; - -private: - static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 - bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; -CONSTCD11 - bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); - -// year_month_weekday_last - -class year_month_weekday_last -{ - date::year y_; - date::month m_; - date::weekday_last wdl_; - -public: - CONSTCD11 year_month_weekday_last(const date::year& y, const date::month& m, - const date::weekday_last& wdl) NOEXCEPT; - - template - CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; - template - CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; - CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; - - CONSTCD11 date::year year() const NOEXCEPT; - CONSTCD11 date::month month() const NOEXCEPT; - CONSTCD11 date::weekday weekday() const NOEXCEPT; - CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; - - CONSTCD14 operator sys_days() const NOEXCEPT; - CONSTCD14 explicit operator local_days() const NOEXCEPT; - CONSTCD11 bool ok() const NOEXCEPT; - -private: - CONSTCD14 days to_days() const NOEXCEPT; -}; - -CONSTCD11 -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -CONSTCD11 -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; - -template -CONSTCD14 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; - -CONSTCD11 -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 date::day operator "" _d(unsigned long long d) NOEXCEPT; -CONSTCD11 date::year operator "" _y(unsigned long long y) NOEXCEPT; - -} // inline namespace literals -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -// CONSTDATA date::month January{1}; -// CONSTDATA date::month February{2}; -// CONSTDATA date::month March{3}; -// CONSTDATA date::month April{4}; -// CONSTDATA date::month May{5}; -// CONSTDATA date::month June{6}; -// CONSTDATA date::month July{7}; -// CONSTDATA date::month August{8}; -// CONSTDATA date::month September{9}; -// CONSTDATA date::month October{10}; -// CONSTDATA date::month November{11}; -// CONSTDATA date::month December{12}; -// -// CONSTDATA date::weekday Sunday{0u}; -// CONSTDATA date::weekday Monday{1u}; -// CONSTDATA date::weekday Tuesday{2u}; -// CONSTDATA date::weekday Wednesday{3u}; -// CONSTDATA date::weekday Thursday{4u}; -// CONSTDATA date::weekday Friday{5u}; -// CONSTDATA date::weekday Saturday{6u}; - -#if HAS_VOID_T - -template > -struct is_clock - : std::false_type -{}; - -template -struct is_clock> - : std::true_type -{}; - -template inline constexpr bool is_clock_v = is_clock::value; - -#endif // HAS_VOID_T - -//----------------+ -// Implementation | -//----------------+ - -// utilities -namespace detail { - -template> -class save_istream -{ -protected: - std::basic_ios& is_; - CharT fill_; - std::ios::fmtflags flags_; - std::streamsize precision_; - std::streamsize width_; - std::basic_ostream* tie_; - std::locale loc_; - -public: - ~save_istream() - { - is_.fill(fill_); - is_.flags(flags_); - is_.precision(precision_); - is_.width(width_); - is_.imbue(loc_); - is_.tie(tie_); - } - - save_istream(const save_istream&) = delete; - save_istream& operator=(const save_istream&) = delete; - - explicit save_istream(std::basic_ios& is) - : is_(is) - , fill_(is.fill()) - , flags_(is.flags()) - , precision_(is.precision()) - , width_(is.width(0)) - , tie_(is.tie(nullptr)) - , loc_(is.getloc()) - { - if (tie_ != nullptr) - tie_->flush(); - } -}; - -template> -class save_ostream - : private save_istream -{ -public: - ~save_ostream() - { - if ((this->flags_ & std::ios::unitbuf) && -#if HAS_UNCAUGHT_EXCEPTIONS - std::uncaught_exceptions() == 0 && -#else - !std::uncaught_exception() && -#endif - this->is_.good()) - this->is_.rdbuf()->pubsync(); - } - - save_ostream(const save_ostream&) = delete; - save_ostream& operator=(const save_ostream&) = delete; - - explicit save_ostream(std::basic_ios& os) - : save_istream(os) - { - } -}; - -template -struct choose_trunc_type -{ - static const int digits = std::numeric_limits::digits; - using type = typename std::conditional - < - digits < 32, - std::int32_t, - typename std::conditional - < - digits < 64, - std::int64_t, -#ifdef __SIZEOF_INT128__ - __int128 -#else - std::int64_t -#endif - >::type - >::type; -}; - -template -CONSTCD11 -inline -typename std::enable_if -< - !std::chrono::treat_as_floating_point::value, - T ->::type -trunc(T t) NOEXCEPT -{ - return t; -} - -template -CONSTCD14 -inline -typename std::enable_if -< - std::chrono::treat_as_floating_point::value, - T ->::type -trunc(T t) NOEXCEPT -{ - using std::numeric_limits; - using I = typename choose_trunc_type::type; - CONSTDATA auto digits = numeric_limits::digits; - static_assert(digits < numeric_limits::digits, ""); - CONSTDATA auto max = I{1} << (digits-1); - CONSTDATA auto min = -max; - const auto negative = t < T{0}; - if (min <= t && t <= max && t != 0 && t == t) - { - t = static_cast(static_cast(t)); - if (t == 0 && negative) - t = -t; - } - return t; -} - -template -struct static_gcd -{ - static const std::intmax_t value = static_gcd::value; -}; - -template -struct static_gcd -{ - static const std::intmax_t value = Xp; -}; - -template <> -struct static_gcd<0, 0> -{ - static const std::intmax_t value = 1; -}; - -template -struct no_overflow -{ -private: - static const std::intmax_t gcd_n1_n2 = static_gcd::value; - static const std::intmax_t gcd_d1_d2 = static_gcd::value; - static const std::intmax_t n1 = R1::num / gcd_n1_n2; - static const std::intmax_t d1 = R1::den / gcd_d1_d2; - static const std::intmax_t n2 = R2::num / gcd_n1_n2; - static const std::intmax_t d2 = R2::den / gcd_d1_d2; -#ifdef __cpp_constexpr - static const std::intmax_t max = std::numeric_limits::max(); -#else - static const std::intmax_t max = LLONG_MAX; -#endif - - template - struct mul // overflow == false - { - static const std::intmax_t value = Xp * Yp; - }; - - template - struct mul - { - static const std::intmax_t value = 1; - }; - -public: - static const bool value = (n1 <= max / d2) && (n2 <= max / d1); - typedef std::ratio::value, - mul::value> type; -}; - -} // detail - -// trunc towards zero -template -CONSTCD11 -inline -typename std::enable_if -< - detail::no_overflow::value, - To ->::type -trunc(const std::chrono::duration& d) -{ - return To{detail::trunc(std::chrono::duration_cast(d).count())}; -} - -template -CONSTCD11 -inline -typename std::enable_if -< - !detail::no_overflow::value, - To ->::type -trunc(const std::chrono::duration& d) -{ - using std::chrono::duration_cast; - using std::chrono::duration; - using rep = typename std::common_type::type; - return To{detail::trunc(duration_cast(duration_cast>(d)).count())}; -} - -#ifndef HAS_CHRONO_ROUNDING -# if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 190023918 || (_MSC_FULL_VER >= 190000000 && defined (__clang__))) -# define HAS_CHRONO_ROUNDING 1 -# elif defined(__cpp_lib_chrono) && __cplusplus > 201402 && __cpp_lib_chrono >= 201510 -# define HAS_CHRONO_ROUNDING 1 -# elif defined(_LIBCPP_VERSION) && __cplusplus > 201402 && _LIBCPP_VERSION >= 3800 -# define HAS_CHRONO_ROUNDING 1 -# else -# define HAS_CHRONO_ROUNDING 0 -# endif -#endif // HAS_CHRONO_ROUNDING - -#if HAS_CHRONO_ROUNDING == 0 - -// round down -template -CONSTCD14 -inline -typename std::enable_if -< - detail::no_overflow::value, - To ->::type -floor(const std::chrono::duration& d) -{ - auto t = trunc(d); - if (t > d) - return t - To{1}; - return t; -} - -template -CONSTCD14 -inline -typename std::enable_if -< - !detail::no_overflow::value, - To ->::type -floor(const std::chrono::duration& d) -{ - using rep = typename std::common_type::type; - return floor(floor>(d)); -} - -// round to nearest, to even on tie -template -CONSTCD14 -inline -To -round(const std::chrono::duration& d) -{ - auto t0 = floor(d); - auto t1 = t0 + To{1}; - if (t1 == To{0} && t0 < To{0}) - t1 = -t1; - auto diff0 = d - t0; - auto diff1 = t1 - d; - if (diff0 == diff1) - { - if (t0 - trunc(t0/2)*2 == To{0}) - return t0; - return t1; - } - if (diff0 < diff1) - return t0; - return t1; -} - -// round up -template -CONSTCD14 -inline -To -ceil(const std::chrono::duration& d) -{ - auto t = trunc(d); - if (t < d) - return t + To{1}; - return t; -} - -template ::is_signed - >::type> -CONSTCD11 -std::chrono::duration -abs(std::chrono::duration d) -{ - return d >= d.zero() ? d : -d; -} - -// round down -template -CONSTCD11 -inline -std::chrono::time_point -floor(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{date::floor(tp.time_since_epoch())}; -} - -// round to nearest, to even on tie -template -CONSTCD11 -inline -std::chrono::time_point -round(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{round(tp.time_since_epoch())}; -} - -// round up -template -CONSTCD11 -inline -std::chrono::time_point -ceil(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{ceil(tp.time_since_epoch())}; -} - -#else // HAS_CHRONO_ROUNDING == 1 - -using std::chrono::floor; -using std::chrono::ceil; -using std::chrono::round; -using std::chrono::abs; - -#endif // HAS_CHRONO_ROUNDING - -namespace detail -{ - -template -CONSTCD14 -inline -typename std::enable_if -< - !std::chrono::treat_as_floating_point::value, - To ->::type -round_i(const std::chrono::duration& d) -{ - return round(d); -} - -template -CONSTCD14 -inline -typename std::enable_if -< - std::chrono::treat_as_floating_point::value, - To ->::type -round_i(const std::chrono::duration& d) -{ - return d; -} - -template -CONSTCD11 -inline -std::chrono::time_point -round_i(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{round_i(tp.time_since_epoch())}; -} - -} // detail - -// trunc towards zero -template -CONSTCD11 -inline -std::chrono::time_point -trunc(const std::chrono::time_point& tp) -{ - using std::chrono::time_point; - return time_point{trunc(tp.time_since_epoch())}; -} - -// day - -CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} -CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} -CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} -CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} -CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} -CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} -CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 31;} - -CONSTCD11 -inline -bool -operator==(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const day& x, const day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const day& x, const day& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const day& x, const day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const day& x, const day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const day& x, const day& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -days -operator-(const day& x, const day& y) NOEXCEPT -{ - return days{static_cast(static_cast(x) - - static_cast(y))}; -} - -CONSTCD11 -inline -day -operator+(const day& x, const days& y) NOEXCEPT -{ - return day{static_cast(x) + static_cast(y.count())}; -} - -CONSTCD11 -inline -day -operator+(const days& x, const day& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -day -operator-(const day& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const day& d) -{ - detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(d); - return os; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const day& d) -{ - detail::low_level_fmt(os, d); - if (!d.ok()) - os << " is not a valid day"; - return os; -} - -// month - -CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} -CONSTCD14 inline month& month::operator++() NOEXCEPT {*this += months{1}; return *this;} -CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline month& month::operator--() NOEXCEPT {*this -= months{1}; return *this;} -CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -month& -month::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -CONSTCD14 -inline -month& -month::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} - -CONSTCD11 -inline -bool -operator==(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const month& x, const month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month& x, const month& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const month& x, const month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month& x, const month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month& x, const month& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD14 -inline -months -operator-(const month& x, const month& y) NOEXCEPT -{ - auto const d = static_cast(x) - static_cast(y); - return months(d <= 11 ? d : d + 12); -} - -CONSTCD14 -inline -month -operator+(const month& x, const months& y) NOEXCEPT -{ - auto const mu = static_cast(static_cast(x)) + y.count() - 1; - auto const yr = (mu >= 0 ? mu : mu-11) / 12; - return month{static_cast(mu - yr * 12 + 1)}; -} - -CONSTCD14 -inline -month -operator+(const months& x, const month& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -month -operator-(const month& x, const months& y) NOEXCEPT -{ - return x + -y; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month& m) -{ - if (m.ok()) - { - CharT fmt[] = {'%', 'b', 0}; - os << format(os.getloc(), fmt, m); - } - else - os << static_cast(m); - return os; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month& m) -{ - detail::low_level_fmt(os, m); - if (!m.ok()) - os << " is not a valid month"; - return os; -} - -// year - -CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} -CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} -CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} -CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} -CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} -CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} -CONSTCD11 inline year year::operator-() const NOEXCEPT {return year{-y_};} -CONSTCD11 inline year year::operator+() const NOEXCEPT {return *this;} - -CONSTCD11 -inline -bool -year::is_leap() const NOEXCEPT -{ - return y_ % 4 == 0 && (y_ % 100 != 0 || y_ % 400 == 0); -} - -CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} - -CONSTCD11 -inline -bool -year::ok() const NOEXCEPT -{ - return y_ != std::numeric_limits::min(); -} - -CONSTCD11 -inline -bool -operator==(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) == static_cast(y); -} - -CONSTCD11 -inline -bool -operator!=(const year& x, const year& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year& x, const year& y) NOEXCEPT -{ - return static_cast(x) < static_cast(y); -} - -CONSTCD11 -inline -bool -operator>(const year& x, const year& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year& x, const year& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year& x, const year& y) NOEXCEPT -{ - return !(x < y); -} - -CONSTCD11 -inline -years -operator-(const year& x, const year& y) NOEXCEPT -{ - return years{static_cast(x) - static_cast(y)}; -} - -CONSTCD11 -inline -year -operator+(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) + y.count()}; -} - -CONSTCD11 -inline -year -operator+(const years& x, const year& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD11 -inline -year -operator-(const year& x, const years& y) NOEXCEPT -{ - return year{static_cast(x) - y.count()}; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const year& y) -{ - detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::internal); - os.width(4 + (y < year{0})); - os.imbue(std::locale::classic()); - os << static_cast(y); - return os; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year& y) -{ - detail::low_level_fmt(os, y); - if (!y.ok()) - os << " is not a valid year"; - return os; -} - -// weekday - -CONSTCD14 -inline -unsigned char -weekday::weekday_from_days(int z) NOEXCEPT -{ - auto u = static_cast(z); - return static_cast(z >= -4 ? (u+4) % 7 : u % 7); -} - -CONSTCD11 -inline -weekday::weekday(unsigned wd) NOEXCEPT - : wd_(static_cast(wd != 7 ? wd : 0)) - {} - -CONSTCD14 -inline -weekday::weekday(const sys_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD14 -inline -weekday::weekday(const local_days& dp) NOEXCEPT - : wd_(weekday_from_days(dp.time_since_epoch().count())) - {} - -CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {*this += days{1}; return *this;} -CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} -CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {*this -= days{1}; return *this;} -CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} - -CONSTCD14 -inline -weekday& -weekday::operator+=(const days& d) NOEXCEPT -{ - *this = *this + d; - return *this; -} - -CONSTCD14 -inline -weekday& -weekday::operator-=(const days& d) NOEXCEPT -{ - *this = *this - d; - return *this; -} - -CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} - -CONSTCD11 -inline -unsigned weekday::c_encoding() const NOEXCEPT -{ - return unsigned{wd_}; -} - -CONSTCD11 -inline -unsigned weekday::iso_encoding() const NOEXCEPT -{ - return unsigned{((wd_ == 0u) ? 7u : wd_)}; -} - -CONSTCD11 -inline -bool -operator==(const weekday& x, const weekday& y) NOEXCEPT -{ - return x.wd_ == y.wd_; -} - -CONSTCD11 -inline -bool -operator!=(const weekday& x, const weekday& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD14 -inline -days -operator-(const weekday& x, const weekday& y) NOEXCEPT -{ - auto const wdu = x.wd_ - y.wd_; - auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; - return days{wdu - wk * 7}; -} - -CONSTCD14 -inline -weekday -operator+(const weekday& x, const days& y) NOEXCEPT -{ - auto const wdu = static_cast(static_cast(x.wd_)) + y.count(); - auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; - return weekday{static_cast(wdu - wk * 7)}; -} - -CONSTCD14 -inline -weekday -operator+(const days& x, const weekday& y) NOEXCEPT -{ - return y + x; -} - -CONSTCD14 -inline -weekday -operator-(const weekday& x, const days& y) NOEXCEPT -{ - return x + -y; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const weekday& wd) -{ - if (wd.ok()) - { - CharT fmt[] = {'%', 'a', 0}; - os << format(fmt, wd); - } - else - os << wd.c_encoding(); - return os; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday& wd) -{ - detail::low_level_fmt(os, wd); - if (!wd.ok()) - os << " is not a valid weekday"; - return os; -} - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -inline namespace literals -{ - -CONSTCD11 -inline -date::day -operator "" _d(unsigned long long d) NOEXCEPT -{ - return date::day{static_cast(d)}; -} - -CONSTCD11 -inline -date::year -operator "" _y(unsigned long long y) NOEXCEPT -{ - return date::year(static_cast(y)); -} -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -CONSTDATA date::last_spec last{}; - -CONSTDATA date::month jan{1}; -CONSTDATA date::month feb{2}; -CONSTDATA date::month mar{3}; -CONSTDATA date::month apr{4}; -CONSTDATA date::month may{5}; -CONSTDATA date::month jun{6}; -CONSTDATA date::month jul{7}; -CONSTDATA date::month aug{8}; -CONSTDATA date::month sep{9}; -CONSTDATA date::month oct{10}; -CONSTDATA date::month nov{11}; -CONSTDATA date::month dec{12}; - -CONSTDATA date::weekday sun{0u}; -CONSTDATA date::weekday mon{1u}; -CONSTDATA date::weekday tue{2u}; -CONSTDATA date::weekday wed{3u}; -CONSTDATA date::weekday thu{4u}; -CONSTDATA date::weekday fri{5u}; -CONSTDATA date::weekday sat{6u}; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -} // inline namespace literals -#endif - -CONSTDATA date::month January{1}; -CONSTDATA date::month February{2}; -CONSTDATA date::month March{3}; -CONSTDATA date::month April{4}; -CONSTDATA date::month May{5}; -CONSTDATA date::month June{6}; -CONSTDATA date::month July{7}; -CONSTDATA date::month August{8}; -CONSTDATA date::month September{9}; -CONSTDATA date::month October{10}; -CONSTDATA date::month November{11}; -CONSTDATA date::month December{12}; - -CONSTDATA date::weekday Monday{1}; -CONSTDATA date::weekday Tuesday{2}; -CONSTDATA date::weekday Wednesday{3}; -CONSTDATA date::weekday Thursday{4}; -CONSTDATA date::weekday Friday{5}; -CONSTDATA date::weekday Saturday{6}; -CONSTDATA date::weekday Sunday{7}; - -// weekday_indexed - -CONSTCD11 -inline -weekday -weekday_indexed::weekday() const NOEXCEPT -{ - return date::weekday{static_cast(wd_)}; -} - -CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} - -CONSTCD11 -inline -bool -weekday_indexed::ok() const NOEXCEPT -{ - return weekday().ok() && 1 <= index_ && index_ <= 5; -} - -#ifdef __GNUC__ -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wconversion" -#endif // __GNUC__ - -CONSTCD11 -inline -weekday_indexed::weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT - : wd_(static_cast(static_cast(wd.wd_))) - , index_(static_cast(index)) - {} - -#ifdef __GNUC__ -# pragma GCC diagnostic pop -#endif // __GNUC__ - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const weekday_indexed& wdi) -{ - return low_level_fmt(os, wdi.weekday()) << '[' << wdi.index() << ']'; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_indexed& wdi) -{ - detail::low_level_fmt(os, wdi); - if (!wdi.ok()) - os << " is not a valid weekday_indexed"; - return os; -} - -CONSTCD11 -inline -weekday_indexed -weekday::operator[](unsigned index) const NOEXCEPT -{ - return {*this, index}; -} - -CONSTCD11 -inline -bool -operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return x.weekday() == y.weekday() && x.index() == y.index(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT -{ - return !(x == y); -} - -// weekday_last - -CONSTCD11 inline date::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} -CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} -CONSTCD11 inline weekday_last::weekday_last(const date::weekday& wd) NOEXCEPT : wd_(wd) {} - -CONSTCD11 -inline -bool -operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return x.weekday() == y.weekday(); -} - -CONSTCD11 -inline -bool -operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const weekday_last& wdl) -{ - return low_level_fmt(os, wdl.weekday()) << "[last]"; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const weekday_last& wdl) -{ - detail::low_level_fmt(os, wdl); - if (!wdl.ok()) - os << " is not a valid weekday_last"; - return os; -} - -CONSTCD11 -inline -weekday_last -weekday::operator[](last_spec) const NOEXCEPT -{ - return weekday_last{*this}; -} - -// year_month - -CONSTCD11 -inline -year_month::year_month(const date::year& y, const date::month& m) NOEXCEPT - : y_(y) - , m_(m) - {} - -CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} - -template -CONSTCD14 -inline -year_month& -year_month::operator+=(const months& dm) NOEXCEPT -{ - *this = *this + dm; - return *this; -} - -template -CONSTCD14 -inline -year_month& -year_month::operator-=(const months& dm) NOEXCEPT -{ - *this = *this - dm; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator+=(const years& dy) NOEXCEPT -{ - *this = *this + dy; - return *this; -} - -CONSTCD14 -inline -year_month& -year_month::operator-=(const years& dy) NOEXCEPT -{ - *this = *this - dy; - return *this; -} - -CONSTCD11 -inline -bool -operator==(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month& x, const year_month& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month())); -} - -CONSTCD11 -inline -bool -operator>(const year_month& x, const year_month& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month& x, const year_month& y) NOEXCEPT -{ - return !(x < y); -} - -template -CONSTCD14 -inline -year_month -operator+(const year_month& ym, const months& dm) NOEXCEPT -{ - auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); - auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; - dmi = dmi - dy * 12 + 1; - return (ym.year() + years(dy)) / month(static_cast(dmi)); -} - -template -CONSTCD14 -inline -year_month -operator+(const months& dm, const year_month& ym) NOEXCEPT -{ - return ym + dm; -} - -template -CONSTCD14 -inline -year_month -operator-(const year_month& ym, const months& dm) NOEXCEPT -{ - return ym + -dm; -} - -CONSTCD11 -inline -months -operator-(const year_month& x, const year_month& y) NOEXCEPT -{ - return (x.year() - y.year()) + - months(static_cast(x.month()) - static_cast(y.month())); -} - -CONSTCD11 -inline -year_month -operator+(const year_month& ym, const years& dy) NOEXCEPT -{ - return (ym.year() + dy) / ym.month(); -} - -CONSTCD11 -inline -year_month -operator+(const years& dy, const year_month& ym) NOEXCEPT -{ - return ym + dy; -} - -CONSTCD11 -inline -year_month -operator-(const year_month& ym, const years& dy) NOEXCEPT -{ - return ym + -dy; -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const year_month& ym) -{ - low_level_fmt(os, ym.year()) << '/'; - return low_level_fmt(os, ym.month()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month& ym) -{ - detail::low_level_fmt(os, ym); - if (!ym.ok()) - os << " is not a valid year_month"; - return os; -} - -// month_day - -CONSTCD11 -inline -month_day::month_day(const date::month& m, const date::day& d) NOEXCEPT - : m_(m) - , d_(d) - {} - -CONSTCD11 inline date::month month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline date::day month_day::day() const NOEXCEPT {return d_;} - -CONSTCD14 -inline -bool -month_day::ok() const NOEXCEPT -{ - CONSTDATA date::day d[] = - { - date::day(31), date::day(29), date::day(31), - date::day(30), date::day(31), date::day(30), - date::day(31), date::day(31), date::day(30), - date::day(31), date::day(30), date::day(31) - }; - return m_.ok() && date::day{1} <= d_ && d_ <= d[static_cast(m_)-1]; -} - -CONSTCD11 -inline -bool -operator==(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day& x, const month_day& y) NOEXCEPT -{ - return x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())); -} - -CONSTCD11 -inline -bool -operator>(const month_day& x, const month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day& x, const month_day& y) NOEXCEPT -{ - return !(x < y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month_day& md) -{ - low_level_fmt(os, md.month()) << '/'; - return low_level_fmt(os, md.day()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day& md) -{ - detail::low_level_fmt(os, md); - if (!md.ok()) - os << " is not a valid month_day"; - return os; -} - -// month_day_last - -CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} -CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} -CONSTCD11 inline month_day_last::month_day_last(const date::month& m) NOEXCEPT : m_(m) {} - -CONSTCD11 -inline -bool -operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() == y.month(); -} - -CONSTCD11 -inline -bool -operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return x.month() < y.month(); -} - -CONSTCD11 -inline -bool -operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month_day_last& mdl) -{ - return low_level_fmt(os, mdl.month()) << "/last"; -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_day_last& mdl) -{ - detail::low_level_fmt(os, mdl); - if (!mdl.ok()) - os << " is not a valid month_day_last"; - return os; -} - -// month_weekday - -CONSTCD11 -inline -month_weekday::month_weekday(const date::month& m, - const date::weekday_indexed& wdi) NOEXCEPT - : m_(m) - , wdi_(wdi) - {} - -CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_indexed -month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD11 -inline -bool -month_weekday::ok() const NOEXCEPT -{ - return m_.ok() && wdi_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month_weekday& mwd) -{ - low_level_fmt(os, mwd.month()) << '/'; - return low_level_fmt(os, mwd.weekday_indexed()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday& mwd) -{ - detail::low_level_fmt(os, mwd); - if (!mwd.ok()) - os << " is not a valid month_weekday"; - return os; -} - -// month_weekday_last - -CONSTCD11 -inline -month_weekday_last::month_weekday_last(const date::month& m, - const date::weekday_last& wdl) NOEXCEPT - : m_(m) - , wdl_(wdl) - {} - -CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday_last -month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD11 -inline -bool -month_weekday_last::ok() const NOEXCEPT -{ - return m_.ok() && wdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return x.month() == y.month() && x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const month_weekday_last& mwdl) -{ - low_level_fmt(os, mwdl.month()) << '/'; - return low_level_fmt(os, mwdl.weekday_last()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) -{ - detail::low_level_fmt(os, mwdl); - if (!mwdl.ok()) - os << " is not a valid month_weekday_last"; - return os; -} - -// year_month_day_last - -CONSTCD11 -inline -year_month_day_last::year_month_day_last(const date::year& y, - const date::month_day_last& mdl) NOEXCEPT - : y_(y) - , mdl_(mdl) - {} - -template -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -template -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day_last& -year_month_day_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} - -CONSTCD11 -inline -month_day_last -year_month_day_last::month_day_last() const NOEXCEPT -{ - return mdl_; -} - -CONSTCD14 -inline -day -year_month_day_last::day() const NOEXCEPT -{ - CONSTDATA date::day d[] = - { - date::day(31), date::day(28), date::day(31), - date::day(30), date::day(31), date::day(30), - date::day(31), date::day(31), date::day(30), - date::day(31), date::day(30), date::day(31) - }; - return (month() != February || !y_.is_leap()) && mdl_.ok() ? - d[static_cast(month()) - 1] : date::day{29}; -} - -CONSTCD14 -inline -year_month_day_last::operator sys_days() const NOEXCEPT -{ - return sys_days(year()/month()/day()); -} - -CONSTCD14 -inline -year_month_day_last::operator local_days() const NOEXCEPT -{ - return local_days(year()/month()/day()); -} - -CONSTCD11 -inline -bool -year_month_day_last::ok() const NOEXCEPT -{ - return y_.ok() && mdl_.ok(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month_day_last() == y.month_day_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month_day_last() < y.month_day_last())); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT -{ - return !(x < y); -} - -namespace detail -{ - -template -std::basic_ostream& -low_level_fmt(std::basic_ostream& os, const year_month_day_last& ymdl) -{ - low_level_fmt(os, ymdl.year()) << '/'; - return low_level_fmt(os, ymdl.month_day_last()); -} - -} // namespace detail - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) -{ - detail::low_level_fmt(os, ymdl); - if (!ymdl.ok()) - os << " is not a valid year_month_day_last"; - return os; -} - -template -CONSTCD14 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return (ymdl.year() / ymdl.month() + dm) / last; -} - -template -CONSTCD14 -inline -year_month_day_last -operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dm; -} - -template -CONSTCD14 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT -{ - return ymdl + (-dm); -} - -CONSTCD11 -inline -year_month_day_last -operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return {ymdl.year()+dy, ymdl.month_day_last()}; -} - -CONSTCD11 -inline -year_month_day_last -operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT -{ - return ymdl + dy; -} - -CONSTCD11 -inline -year_month_day_last -operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT -{ - return ymdl + (-dy); -} - -// year_month_day - -CONSTCD11 -inline -year_month_day::year_month_day(const date::year& y, const date::month& m, - const date::day& d) NOEXCEPT - : y_(y) - , m_(m) - , d_(d) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT - : y_(ymdl.year()) - , m_(ymdl.month()) - , d_(ymdl.day()) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(sys_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_day::year_month_day(local_days dp) NOEXCEPT - : year_month_day(from_days(dp.time_since_epoch())) - {} - -CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} -CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} - -template -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -template -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_day& -year_month_day::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD14 -inline -days -year_month_day::to_days() const NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const y = static_cast(y_) - (m_ <= February); - auto const m = static_cast(m_); - auto const d = static_cast(d_); - auto const era = (y >= 0 ? y : y-399) / 400; - auto const yoe = static_cast(y - era * 400); // [0, 399] - auto const doy = (153*(m > 2 ? m-3 : m+9) + 2)/5 + d-1; // [0, 365] - auto const doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096] - return days{era * 146097 + static_cast(doe) - 719468}; -} - -CONSTCD14 -inline -year_month_day::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_day::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_day::ok() const NOEXCEPT -{ - if (!(y_.ok() && m_.ok())) - return false; - return date::day{1} <= d_ && d_ <= (y_ / m_ / last).day(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x == y); -} - -CONSTCD11 -inline -bool -operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return x.year() < y.year() ? true - : (x.year() > y.year() ? false - : (x.month() < y.month() ? true - : (x.month() > y.month() ? false - : (x.day() < y.day())))); -} - -CONSTCD11 -inline -bool -operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return y < x; -} - -CONSTCD11 -inline -bool -operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(y < x); -} - -CONSTCD11 -inline -bool -operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT -{ - return !(x < y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_day& ymd) -{ - detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.imbue(std::locale::classic()); - os << static_cast(ymd.year()) << '-'; - os.width(2); - os << static_cast(ymd.month()) << '-'; - os.width(2); - os << static_cast(ymd.day()); - if (!ymd.ok()) - os << " is not a valid year_month_day"; - return os; -} - -CONSTCD14 -inline -year_month_day -year_month_day::from_days(days dp) NOEXCEPT -{ - static_assert(std::numeric_limits::digits >= 18, - "This algorithm has not been ported to a 16 bit unsigned integer"); - static_assert(std::numeric_limits::digits >= 20, - "This algorithm has not been ported to a 16 bit signed integer"); - auto const z = dp.count() + 719468; - auto const era = (z >= 0 ? z : z - 146096) / 146097; - auto const doe = static_cast(z - era * 146097); // [0, 146096] - auto const yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399] - auto const y = static_cast(yoe) + era * 400; - auto const doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365] - auto const mp = (5*doy + 2)/153; // [0, 11] - auto const d = doy - (153*mp+2)/5 + 1; // [1, 31] - auto const m = mp < 10 ? mp+3 : mp-9; // [1, 12] - return year_month_day{date::year{y + (m <= 2)}, date::month(m), date::day(d)}; -} - -template -CONSTCD14 -inline -year_month_day -operator+(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return (ymd.year() / ymd.month() + dm) / ymd.day(); -} - -template -CONSTCD14 -inline -year_month_day -operator+(const months& dm, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dm; -} - -template -CONSTCD14 -inline -year_month_day -operator-(const year_month_day& ymd, const months& dm) NOEXCEPT -{ - return ymd + (-dm); -} - -CONSTCD11 -inline -year_month_day -operator+(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return (ymd.year() + dy) / ymd.month() / ymd.day(); -} - -CONSTCD11 -inline -year_month_day -operator+(const years& dy, const year_month_day& ymd) NOEXCEPT -{ - return ymd + dy; -} - -CONSTCD11 -inline -year_month_day -operator-(const year_month_day& ymd, const years& dy) NOEXCEPT -{ - return ymd + (-dy); -} - -// year_month_weekday - -CONSTCD11 -inline -year_month_weekday::year_month_weekday(const date::year& y, const date::month& m, - const date::weekday_indexed& wdi) - NOEXCEPT - : y_(y) - , m_(m) - , wdi_(wdi) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -CONSTCD14 -inline -year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT - : year_month_weekday(from_days(dp.time_since_epoch())) - {} - -template -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -template -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday& -year_month_weekday::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday::weekday() const NOEXCEPT -{ - return wdi_.weekday(); -} - -CONSTCD11 -inline -unsigned -year_month_weekday::index() const NOEXCEPT -{ - return wdi_.index(); -} - -CONSTCD11 -inline -weekday_indexed -year_month_weekday::weekday_indexed() const NOEXCEPT -{ - return wdi_; -} - -CONSTCD14 -inline -year_month_weekday::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD14 -inline -bool -year_month_weekday::ok() const NOEXCEPT -{ - if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) - return false; - if (wdi_.index() <= 4) - return true; - auto d2 = wdi_.weekday() - date::weekday(static_cast(y_/m_/1)) + - days((wdi_.index()-1)*7 + 1); - return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); -} - -CONSTCD14 -inline -year_month_weekday -year_month_weekday::from_days(days d) NOEXCEPT -{ - sys_days dp{d}; - auto const wd = date::weekday(dp); - auto const ymd = year_month_day(dp); - return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; -} - -CONSTCD14 -inline -days -year_month_weekday::to_days() const NOEXCEPT -{ - auto d = sys_days(y_/m_/1); - return (d + (wdi_.weekday() - date::weekday(d) + days{(wdi_.index()-1)*7}) - ).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_indexed() == y.weekday_indexed(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) -{ - detail::low_level_fmt(os, ymwdi.year()) << '/'; - detail::low_level_fmt(os, ymwdi.month()) << '/'; - detail::low_level_fmt(os, ymwdi.weekday_indexed()); - if (!ymwdi.ok()) - os << " is not a valid year_month_weekday"; - return os; -} - -template -CONSTCD14 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); -} - -template -CONSTCD14 -inline -year_month_weekday -operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dm; -} - -template -CONSTCD14 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT -{ - return ymwd + (-dm); -} - -CONSTCD11 -inline -year_month_weekday -operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT -{ - return ymwd + dy; -} - -CONSTCD11 -inline -year_month_weekday -operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT -{ - return ymwd + (-dy); -} - -// year_month_weekday_last - -CONSTCD11 -inline -year_month_weekday_last::year_month_weekday_last(const date::year& y, - const date::month& m, - const date::weekday_last& wdl) NOEXCEPT - : y_(y) - , m_(m) - , wdl_(wdl) - {} - -template -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const months& m) NOEXCEPT -{ - *this = *this + m; - return *this; -} - -template -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const months& m) NOEXCEPT -{ - *this = *this - m; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator+=(const years& y) NOEXCEPT -{ - *this = *this + y; - return *this; -} - -CONSTCD14 -inline -year_month_weekday_last& -year_month_weekday_last::operator-=(const years& y) NOEXCEPT -{ - *this = *this - y; - return *this; -} - -CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} -CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} - -CONSTCD11 -inline -weekday -year_month_weekday_last::weekday() const NOEXCEPT -{ - return wdl_.weekday(); -} - -CONSTCD11 -inline -weekday_last -year_month_weekday_last::weekday_last() const NOEXCEPT -{ - return wdl_; -} - -CONSTCD14 -inline -year_month_weekday_last::operator sys_days() const NOEXCEPT -{ - return sys_days{to_days()}; -} - -CONSTCD14 -inline -year_month_weekday_last::operator local_days() const NOEXCEPT -{ - return local_days{to_days()}; -} - -CONSTCD11 -inline -bool -year_month_weekday_last::ok() const NOEXCEPT -{ - return y_.ok() && m_.ok() && wdl_.ok(); -} - -CONSTCD14 -inline -days -year_month_weekday_last::to_days() const NOEXCEPT -{ - auto const d = sys_days(y_/m_/last); - return (d - (date::weekday{d} - wdl_.weekday())).time_since_epoch(); -} - -CONSTCD11 -inline -bool -operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return x.year() == y.year() && x.month() == y.month() && - x.weekday_last() == y.weekday_last(); -} - -CONSTCD11 -inline -bool -operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT -{ - return !(x == y); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) -{ - detail::low_level_fmt(os, ymwdl.year()) << '/'; - detail::low_level_fmt(os, ymwdl.month()) << '/'; - detail::low_level_fmt(os, ymwdl.weekday_last()); - if (!ymwdl.ok()) - os << " is not a valid year_month_weekday_last"; - return os; -} - -template -CONSTCD14 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); -} - -template -CONSTCD14 -inline -year_month_weekday_last -operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dm; -} - -template -CONSTCD14 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT -{ - return ymwdl + (-dm); -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT -{ - return ymwdl + dy; -} - -CONSTCD11 -inline -year_month_weekday_last -operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT -{ - return ymwdl + (-dy); -} - -// year_month from operator/() - -CONSTCD11 -inline -year_month -operator/(const year& y, const month& m) NOEXCEPT -{ - return {y, m}; -} - -CONSTCD11 -inline -year_month -operator/(const year& y, int m) NOEXCEPT -{ - return y / month(static_cast(m)); -} - -// month_day from operator/() - -CONSTCD11 -inline -month_day -operator/(const month& m, const day& d) NOEXCEPT -{ - return {m, d}; -} - -CONSTCD11 -inline -month_day -operator/(const day& d, const month& m) NOEXCEPT -{ - return m / d; -} - -CONSTCD11 -inline -month_day -operator/(const month& m, int d) NOEXCEPT -{ - return m / day(static_cast(d)); -} - -CONSTCD11 -inline -month_day -operator/(int m, const day& d) NOEXCEPT -{ - return month(static_cast(m)) / d; -} - -CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} - -// month_day_last from operator/() - -CONSTCD11 -inline -month_day_last -operator/(const month& m, last_spec) NOEXCEPT -{ - return month_day_last{m}; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, const month& m) NOEXCEPT -{ - return m/last; -} - -CONSTCD11 -inline -month_day_last -operator/(int m, last_spec) NOEXCEPT -{ - return month(static_cast(m))/last; -} - -CONSTCD11 -inline -month_day_last -operator/(last_spec, int m) NOEXCEPT -{ - return m/last; -} - -// month_weekday from operator/() - -CONSTCD11 -inline -month_weekday -operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT -{ - return {m, wdi}; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT -{ - return m / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(int m, const weekday_indexed& wdi) NOEXCEPT -{ - return month(static_cast(m)) / wdi; -} - -CONSTCD11 -inline -month_weekday -operator/(const weekday_indexed& wdi, int m) NOEXCEPT -{ - return m / wdi; -} - -// month_weekday_last from operator/() - -CONSTCD11 -inline -month_weekday_last -operator/(const month& m, const weekday_last& wdl) NOEXCEPT -{ - return {m, wdl}; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, const month& m) NOEXCEPT -{ - return m / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(int m, const weekday_last& wdl) NOEXCEPT -{ - return month(static_cast(m)) / wdl; -} - -CONSTCD11 -inline -month_weekday_last -operator/(const weekday_last& wdl, int m) NOEXCEPT -{ - return m / wdl; -} - -// year_month_day from operator/() - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, const day& d) NOEXCEPT -{ - return {ym.year(), ym.month(), d}; -} - -CONSTCD11 -inline -year_month_day -operator/(const year_month& ym, int d) NOEXCEPT -{ - return ym / day(static_cast(d)); -} - -CONSTCD11 -inline -year_month_day -operator/(const year& y, const month_day& md) NOEXCEPT -{ - return y / md.month() / md.day(); -} - -CONSTCD11 -inline -year_month_day -operator/(int y, const month_day& md) NOEXCEPT -{ - return year(y) / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, const year& y) NOEXCEPT -{ - return y / md; -} - -CONSTCD11 -inline -year_month_day -operator/(const month_day& md, int y) NOEXCEPT -{ - return year(y) / md; -} - -// year_month_day_last from operator/() - -CONSTCD11 -inline -year_month_day_last -operator/(const year_month& ym, last_spec) NOEXCEPT -{ - return {ym.year(), month_day_last{ym.month()}}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const year& y, const month_day_last& mdl) NOEXCEPT -{ - return {y, mdl}; -} - -CONSTCD11 -inline -year_month_day_last -operator/(int y, const month_day_last& mdl) NOEXCEPT -{ - return year(y) / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, const year& y) NOEXCEPT -{ - return y / mdl; -} - -CONSTCD11 -inline -year_month_day_last -operator/(const month_day_last& mdl, int y) NOEXCEPT -{ - return year(y) / mdl; -} - -// year_month_weekday from operator/() - -CONSTCD11 -inline -year_month_weekday -operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT -{ - return {ym.year(), ym.month(), wdi}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const year& y, const month_weekday& mwd) NOEXCEPT -{ - return {y, mwd.month(), mwd.weekday_indexed()}; -} - -CONSTCD11 -inline -year_month_weekday -operator/(int y, const month_weekday& mwd) NOEXCEPT -{ - return year(y) / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, const year& y) NOEXCEPT -{ - return y / mwd; -} - -CONSTCD11 -inline -year_month_weekday -operator/(const month_weekday& mwd, int y) NOEXCEPT -{ - return year(y) / mwd; -} - -// year_month_weekday_last from operator/() - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT -{ - return {ym.year(), ym.month(), wdl}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT -{ - return {y, mwdl.month(), mwdl.weekday_last()}; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(int y, const month_weekday_last& mwdl) NOEXCEPT -{ - return year(y) / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT -{ - return y / mwdl; -} - -CONSTCD11 -inline -year_month_weekday_last -operator/(const month_weekday_last& mwdl, int y) NOEXCEPT -{ - return year(y) / mwdl; -} - -template -struct fields; - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const fields& fds, const std::string* abbrev = nullptr, - const std::chrono::seconds* offset_sec = nullptr); - -template -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - fields& fds, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr); - -// hh_mm_ss - -namespace detail -{ - -struct undocumented {explicit undocumented() = default;}; - -// width::value is the number of fractional decimal digits in 1/n -// width<0>::value and width<1>::value are defined to be 0 -// If 1/n takes more than 18 fractional decimal digits, -// the result is truncated to 19. -// Example: width<2>::value == 1 -// Example: width<3>::value == 19 -// Example: width<4>::value == 2 -// Example: width<10>::value == 1 -// Example: width<1000>::value == 3 -template -struct width -{ - static_assert(d > 0, "width called with zero denominator"); - static CONSTDATA unsigned value = 1 + width::value; -}; - -template -struct width -{ - static CONSTDATA unsigned value = 0; -}; - -template -struct static_pow10 -{ -private: - static CONSTDATA std::uint64_t h = static_pow10::value; -public: - static CONSTDATA std::uint64_t value = h * h * (exp % 2 ? 10 : 1); -}; - -template <> -struct static_pow10<0> -{ - static CONSTDATA std::uint64_t value = 1; -}; - -template -class decimal_format_seconds -{ - using CT = typename std::common_type::type; - using rep = typename CT::rep; - static unsigned CONSTDATA trial_width = - detail::width::value; -public: - static unsigned CONSTDATA width = trial_width < 19 ? trial_width : 6u; - using precision = std::chrono::duration::value>>; - -private: - std::chrono::seconds s_; - precision sub_s_; - -public: - CONSTCD11 decimal_format_seconds() - : s_() - , sub_s_() - {} - - CONSTCD11 explicit decimal_format_seconds(const Duration& d) NOEXCEPT - : s_(std::chrono::duration_cast(d)) - , sub_s_(std::chrono::duration_cast(d - s_)) - {} - - CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;} - CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} - CONSTCD11 precision subseconds() const NOEXCEPT {return sub_s_;} - - CONSTCD14 precision to_duration() const NOEXCEPT - { - return s_ + sub_s_; - } - - CONSTCD11 bool in_conventional_range() const NOEXCEPT - { - return sub_s_ < std::chrono::seconds{1} && s_ < std::chrono::minutes{1}; - } - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, const decimal_format_seconds& x) - { - return x.print(os, std::chrono::treat_as_floating_point{}); - } - - template - std::basic_ostream& - print(std::basic_ostream& os, std::true_type) const - { - date::detail::save_ostream _(os); - std::chrono::duration d = s_ + sub_s_; - if (d < std::chrono::seconds{10}) - os << '0'; - os.precision(width+6); - os << std::fixed << d.count(); - return os; - } - - template - std::basic_ostream& - print(std::basic_ostream& os, std::false_type) const - { - date::detail::save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << s_.count(); - if (width > 0) - { -#if !ONLY_C_LOCALE - os << std::use_facet>(os.getloc()).decimal_point(); -#else - os << '.'; -#endif - date::detail::save_ostream _s(os); - os.imbue(std::locale::classic()); - os.width(width); - os << sub_s_.count(); - } - return os; - } -}; - -template -inline -CONSTCD11 -typename std::enable_if - < - std::numeric_limits::is_signed, - std::chrono::duration - >::type -abs(std::chrono::duration d) -{ - return d >= d.zero() ? +d : -d; -} - -template -inline -CONSTCD11 -typename std::enable_if - < - !std::numeric_limits::is_signed, - std::chrono::duration - >::type -abs(std::chrono::duration d) -{ - return d; -} - -} // namespace detail - -template -class hh_mm_ss -{ - using dfs = detail::decimal_format_seconds::type>; - - std::chrono::hours h_; - std::chrono::minutes m_; - dfs s_; - bool neg_; - -public: - static unsigned CONSTDATA fractional_width = dfs::width; - using precision = typename dfs::precision; - - CONSTCD11 hh_mm_ss() NOEXCEPT - : hh_mm_ss(Duration::zero()) - {} - - CONSTCD11 explicit hh_mm_ss(Duration d) NOEXCEPT - : h_(std::chrono::duration_cast(detail::abs(d))) - , m_(std::chrono::duration_cast(detail::abs(d)) - h_) - , s_(detail::abs(d) - h_ - m_) - , neg_(d < Duration::zero()) - {} - - CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} - CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} - CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_.seconds();} - CONSTCD14 std::chrono::seconds& - seconds(detail::undocumented) NOEXCEPT {return s_.seconds();} - CONSTCD11 precision subseconds() const NOEXCEPT {return s_.subseconds();} - CONSTCD11 bool is_negative() const NOEXCEPT {return neg_;} - - CONSTCD11 explicit operator precision() const NOEXCEPT {return to_duration();} - CONSTCD11 precision to_duration() const NOEXCEPT - {return (s_.to_duration() + m_ + h_) * (1-2*neg_);} - - CONSTCD11 bool in_conventional_range() const NOEXCEPT - { - return !neg_ && h_ < days{1} && m_ < std::chrono::hours{1} && - s_.in_conventional_range(); - } - -private: - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, hh_mm_ss const& tod) - { - if (tod.is_negative()) - os << '-'; - if (tod.h_ < std::chrono::hours{10}) - os << '0'; - os << tod.h_.count() << ':'; - if (tod.m_ < std::chrono::minutes{10}) - os << '0'; - os << tod.m_.count() << ':' << tod.s_; - return os; - } - - template - friend - std::basic_ostream& - date::to_stream(std::basic_ostream& os, const CharT* fmt, - const fields& fds, const std::string* abbrev, - const std::chrono::seconds* offset_sec); - - template - friend - std::basic_istream& - date::from_stream(std::basic_istream& is, const CharT* fmt, - fields& fds, - std::basic_string* abbrev, std::chrono::minutes* offset); -}; - -inline -CONSTCD14 -bool -is_am(std::chrono::hours const& h) NOEXCEPT -{ - using std::chrono::hours; - return hours{0} <= h && h < hours{12}; -} - -inline -CONSTCD14 -bool -is_pm(std::chrono::hours const& h) NOEXCEPT -{ - using std::chrono::hours; - return hours{12} <= h && h < hours{24}; -} - -inline -CONSTCD14 -std::chrono::hours -make12(std::chrono::hours h) NOEXCEPT -{ - using std::chrono::hours; - if (h < hours{12}) - { - if (h == hours{0}) - h = hours{12}; - } - else - { - if (h != hours{12}) - h = h - hours{12}; - } - return h; -} - -inline -CONSTCD14 -std::chrono::hours -make24(std::chrono::hours h, bool is_pm) NOEXCEPT -{ - using std::chrono::hours; - if (is_pm) - { - if (h != hours{12}) - h = h + hours{12}; - } - else if (h == hours{12}) - h = hours{0}; - return h; -} - -template -using time_of_day = hh_mm_ss; - -template -CONSTCD11 -inline -hh_mm_ss> -make_time(const std::chrono::duration& d) -{ - return hh_mm_ss>(d); -} - -template -inline -typename std::enable_if -< - std::ratio_less::value - , std::basic_ostream& ->::type -operator<<(std::basic_ostream& os, const sys_time& tp) -{ - auto const dp = date::floor(tp); - return os << year_month_day(dp) << ' ' << make_time(tp-dp); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const sys_days& dp) -{ - return os << year_month_day(dp); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_time& ut) -{ - return (os << sys_time{ut.time_since_epoch()}); -} - -namespace detail -{ - -template -class string_literal; - -template -inline -CONSTCD14 -string_literal::type, - N1 + N2 - 1> -operator+(const string_literal& x, const string_literal& y) NOEXCEPT; - -template -class string_literal -{ - CharT p_[N]; - - CONSTCD11 string_literal() NOEXCEPT - : p_{} - {} - -public: - using const_iterator = const CharT*; - - string_literal(string_literal const&) = default; - string_literal& operator=(string_literal const&) = delete; - - template ::type> - CONSTCD11 string_literal(CharT c) NOEXCEPT - : p_{c} - { - } - - template ::type> - CONSTCD11 string_literal(CharT c1, CharT c2) NOEXCEPT - : p_{c1, c2} - { - } - - template ::type> - CONSTCD11 string_literal(CharT c1, CharT c2, CharT c3) NOEXCEPT - : p_{c1, c2, c3} - { - } - - CONSTCD14 string_literal(const CharT(&a)[N]) NOEXCEPT - : p_{} - { - for (std::size_t i = 0; i < N; ++i) - p_[i] = a[i]; - } - - template ::type> - CONSTCD14 string_literal(const char(&a)[N]) NOEXCEPT - : p_{} - { - for (std::size_t i = 0; i < N; ++i) - p_[i] = a[i]; - } - - template ::value>::type> - CONSTCD14 string_literal(string_literal const& a) NOEXCEPT - : p_{} - { - for (std::size_t i = 0; i < N; ++i) - p_[i] = a[i]; - } - - CONSTCD11 const CharT* data() const NOEXCEPT {return p_;} - CONSTCD11 std::size_t size() const NOEXCEPT {return N-1;} - - CONSTCD11 const_iterator begin() const NOEXCEPT {return p_;} - CONSTCD11 const_iterator end() const NOEXCEPT {return p_ + N-1;} - - CONSTCD11 CharT const& operator[](std::size_t n) const NOEXCEPT - { - return p_[n]; - } - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, const string_literal& s) - { - return os << s.p_; - } - - template - friend - CONSTCD14 - string_literal::type, - N1 + N2 - 1> - operator+(const string_literal& x, const string_literal& y) NOEXCEPT; -}; - -template -CONSTCD11 -inline -string_literal -operator+(const string_literal& x, const string_literal& y) NOEXCEPT -{ - return string_literal(x[0], y[0]); -} - -template -CONSTCD11 -inline -string_literal -operator+(const string_literal& x, const string_literal& y) NOEXCEPT -{ - return string_literal(x[0], x[1], y[0]); -} - -template -CONSTCD14 -inline -string_literal::type, - N1 + N2 - 1> -operator+(const string_literal& x, const string_literal& y) NOEXCEPT -{ - using CT = typename std::conditional::type; - - string_literal r; - std::size_t i = 0; - for (; i < N1-1; ++i) - r.p_[i] = CT(x.p_[i]); - for (std::size_t j = 0; j < N2; ++j, ++i) - r.p_[i] = CT(y.p_[j]); - - return r; -} - - -template -inline -std::basic_string -operator+(std::basic_string x, const string_literal& y) -{ - x.append(y.data(), y.size()); - return x; -} - -#if __cplusplus >= 201402 && (!defined(__EDG_VERSION__) || __EDG_VERSION__ > 411) \ - && (!defined(__SUNPRO_CC) || __SUNPRO_CC > 0x5150) - -template ::value || - std::is_same::value || - std::is_same::value || - std::is_same::value>> -CONSTCD14 -inline -string_literal -msl(CharT c) NOEXCEPT -{ - return string_literal{c}; -} - -CONSTCD14 -inline -std::size_t -to_string_len(std::intmax_t i) -{ - std::size_t r = 0; - do - { - i /= 10; - ++r; - } while (i > 0); - return r; -} - -template -CONSTCD14 -inline -std::enable_if_t -< - N < 10, - string_literal -> -msl() NOEXCEPT -{ - return msl(char(N % 10 + '0')); -} - -template -CONSTCD14 -inline -std::enable_if_t -< - 10 <= N, - string_literal -> -msl() NOEXCEPT -{ - return msl() + msl(char(N % 10 + '0')); -} - -template -CONSTCD14 -inline -std::enable_if_t -< - std::ratio::type::den != 1, - string_literal::type::num) + - to_string_len(std::ratio::type::den) + 4> -> -msl(std::ratio) NOEXCEPT -{ - using R = typename std::ratio::type; - return msl(CharT{'['}) + msl() + msl(CharT{'/'}) + - msl() + msl(CharT{']'}); -} - -template -CONSTCD14 -inline -std::enable_if_t -< - std::ratio::type::den == 1, - string_literal::type::num) + 3> -> -msl(std::ratio) NOEXCEPT -{ - using R = typename std::ratio::type; - return msl(CharT{'['}) + msl() + msl(CharT{']'}); -} - - -#else // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411) - -inline -std::string -to_string(std::uint64_t x) -{ - return std::to_string(x); -} - -template -inline -std::basic_string -to_string(std::uint64_t x) -{ - auto y = std::to_string(x); - return std::basic_string(y.begin(), y.end()); -} - -template -inline -typename std::enable_if -< - std::ratio::type::den != 1, - std::basic_string ->::type -msl(std::ratio) -{ - using R = typename std::ratio::type; - return std::basic_string(1, '[') + to_string(R::num) + CharT{'/'} + - to_string(R::den) + CharT{']'}; -} - -template -inline -typename std::enable_if -< - std::ratio::type::den == 1, - std::basic_string ->::type -msl(std::ratio) -{ - using R = typename std::ratio::type; - return std::basic_string(1, '[') + to_string(R::num) + CharT{']'}; -} - -#endif // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411) - -template -CONSTCD11 -inline -string_literal -msl(std::atto) NOEXCEPT -{ - return string_literal{'a'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::femto) NOEXCEPT -{ - return string_literal{'f'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::pico) NOEXCEPT -{ - return string_literal{'p'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::nano) NOEXCEPT -{ - return string_literal{'n'}; -} - -template -CONSTCD11 -inline -typename std::enable_if -< - std::is_same::value, - string_literal ->::type -msl(std::micro) NOEXCEPT -{ - return string_literal{'\xC2', '\xB5'}; -} - -template -CONSTCD11 -inline -typename std::enable_if -< - !std::is_same::value, - string_literal ->::type -msl(std::micro) NOEXCEPT -{ - return string_literal{CharT{static_cast('\xB5')}}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::milli) NOEXCEPT -{ - return string_literal{'m'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::centi) NOEXCEPT -{ - return string_literal{'c'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::deca) NOEXCEPT -{ - return string_literal{'d', 'a'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::deci) NOEXCEPT -{ - return string_literal{'d'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::hecto) NOEXCEPT -{ - return string_literal{'h'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::kilo) NOEXCEPT -{ - return string_literal{'k'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::mega) NOEXCEPT -{ - return string_literal{'M'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::giga) NOEXCEPT -{ - return string_literal{'G'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::tera) NOEXCEPT -{ - return string_literal{'T'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::peta) NOEXCEPT -{ - return string_literal{'P'}; -} - -template -CONSTCD11 -inline -string_literal -msl(std::exa) NOEXCEPT -{ - return string_literal{'E'}; -} - -template -CONSTCD11 -inline -auto -get_units(Period p) - -> decltype(msl(p) + string_literal{'s'}) -{ - return msl(p) + string_literal{'s'}; -} - -template -CONSTCD11 -inline -string_literal -get_units(std::ratio<1>) -{ - return string_literal{'s'}; -} - -template -CONSTCD11 -inline -string_literal -get_units(std::ratio<3600>) -{ - return string_literal{'h'}; -} - -template -CONSTCD11 -inline -string_literal -get_units(std::ratio<60>) -{ - return string_literal{'m', 'i', 'n'}; -} - -template -CONSTCD11 -inline -string_literal -get_units(std::ratio<86400>) -{ - return string_literal{'d'}; -} - -template > -struct make_string; - -template <> -struct make_string -{ - template - static - std::string - from(Rep n) - { - return std::to_string(n); - } -}; - -template -struct make_string -{ - template - static - std::basic_string - from(Rep n) - { - auto s = std::to_string(n); - return std::basic_string(s.begin(), s.end()); - } -}; - -template <> -struct make_string -{ - template - static - std::wstring - from(Rep n) - { - return std::to_wstring(n); - } -}; - -template -struct make_string -{ - template - static - std::basic_string - from(Rep n) - { - auto s = std::to_wstring(n); - return std::basic_string(s.begin(), s.end()); - } -}; - -} // namespace detail - -// to_stream - -CONSTDATA year nanyear{-32768}; - -template -struct fields -{ - year_month_day ymd{nanyear/0/0}; - weekday wd{8u}; - hh_mm_ss tod{}; - bool has_tod = false; - -#if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ <= 409) - fields() : ymd{nanyear/0/0}, wd{8u}, tod{}, has_tod{false} {} -#else - fields() = default; -#endif - - fields(year_month_day ymd_) : ymd(ymd_) {} - fields(weekday wd_) : wd(wd_) {} - fields(hh_mm_ss tod_) : tod(tod_), has_tod(true) {} - - fields(year_month_day ymd_, weekday wd_) : ymd(ymd_), wd(wd_) {} - fields(year_month_day ymd_, hh_mm_ss tod_) : ymd(ymd_), tod(tod_), - has_tod(true) {} - - fields(weekday wd_, hh_mm_ss tod_) : wd(wd_), tod(tod_), has_tod(true) {} - - fields(year_month_day ymd_, weekday wd_, hh_mm_ss tod_) - : ymd(ymd_) - , wd(wd_) - , tod(tod_) - , has_tod(true) - {} -}; - -namespace detail -{ - -template -unsigned -extract_weekday(std::basic_ostream& os, const fields& fds) -{ - if (!fds.ymd.ok() && !fds.wd.ok()) - { - // fds does not contain a valid weekday - os.setstate(std::ios::failbit); - return 8; - } - weekday wd; - if (fds.ymd.ok()) - { - wd = weekday{sys_days(fds.ymd)}; - if (fds.wd.ok() && wd != fds.wd) - { - // fds.ymd and fds.wd are inconsistent - os.setstate(std::ios::failbit); - return 8; - } - } - else - wd = fds.wd; - return static_cast((wd - Sunday).count()); -} - -template -unsigned -extract_month(std::basic_ostream& os, const fields& fds) -{ - if (!fds.ymd.month().ok()) - { - // fds does not contain a valid month - os.setstate(std::ios::failbit); - return 0; - } - return static_cast(fds.ymd.month()); -} - -} // namespace detail - -#if ONLY_C_LOCALE - -namespace detail -{ - -inline -std::pair -weekday_names() -{ - static const std::string nm[] = - { - "Sunday", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - "Sun", - "Mon", - "Tue", - "Wed", - "Thu", - "Fri", - "Sat" - }; - return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); -} - -inline -std::pair -month_names() -{ - static const std::string nm[] = - { - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec" - }; - return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); -} - -inline -std::pair -ampm_names() -{ - static const std::string nm[] = - { - "AM", - "PM" - }; - return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); -} - -template -FwdIter -scan_keyword(std::basic_istream& is, FwdIter kb, FwdIter ke) -{ - size_t nkw = static_cast(std::distance(kb, ke)); - const unsigned char doesnt_match = '\0'; - const unsigned char might_match = '\1'; - const unsigned char does_match = '\2'; - unsigned char statbuf[100]; - unsigned char* status = statbuf; - std::unique_ptr stat_hold(0, free); - if (nkw > sizeof(statbuf)) - { - status = (unsigned char*)std::malloc(nkw); - if (status == nullptr) - throw std::bad_alloc(); - stat_hold.reset(status); - } - size_t n_might_match = nkw; // At this point, any keyword might match - size_t n_does_match = 0; // but none of them definitely do - // Initialize all statuses to might_match, except for "" keywords are does_match - unsigned char* st = status; - for (auto ky = kb; ky != ke; ++ky, ++st) - { - if (!ky->empty()) - *st = might_match; - else - { - *st = does_match; - --n_might_match; - ++n_does_match; - } - } - // While there might be a match, test keywords against the next CharT - for (size_t indx = 0; is && n_might_match > 0; ++indx) - { - // Peek at the next CharT but don't consume it - auto ic = is.peek(); - if (ic == EOF) - { - is.setstate(std::ios::eofbit); - break; - } - auto c = static_cast(toupper(static_cast(ic))); - bool consume = false; - // For each keyword which might match, see if the indx character is c - // If a match if found, consume c - // If a match is found, and that is the last character in the keyword, - // then that keyword matches. - // If the keyword doesn't match this character, then change the keyword - // to doesn't match - st = status; - for (auto ky = kb; ky != ke; ++ky, ++st) - { - if (*st == might_match) - { - if (c == static_cast(toupper(static_cast((*ky)[indx])))) - { - consume = true; - if (ky->size() == indx+1) - { - *st = does_match; - --n_might_match; - ++n_does_match; - } - } - else - { - *st = doesnt_match; - --n_might_match; - } - } - } - // consume if we matched a character - if (consume) - { - (void)is.get(); - // If we consumed a character and there might be a matched keyword that - // was marked matched on a previous iteration, then such keywords - // are now marked as not matching. - if (n_might_match + n_does_match > 1) - { - st = status; - for (auto ky = kb; ky != ke; ++ky, ++st) - { - if (*st == does_match && ky->size() != indx+1) - { - *st = doesnt_match; - --n_does_match; - } - } - } - } - } - // We've exited the loop because we hit eof and/or we have no more "might matches". - // Return the first matching result - for (st = status; kb != ke; ++kb, ++st) - if (*st == does_match) - break; - if (kb == ke) - is.setstate(std::ios::failbit); - return kb; -} - -} // namespace detail - -#endif // ONLY_C_LOCALE - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const fields& fds, const std::string* abbrev, - const std::chrono::seconds* offset_sec) -{ -#if ONLY_C_LOCALE - using detail::weekday_names; - using detail::month_names; - using detail::ampm_names; -#endif - using detail::save_ostream; - using detail::get_units; - using detail::extract_weekday; - using detail::extract_month; - using std::ios; - using std::chrono::duration_cast; - using std::chrono::seconds; - using std::chrono::minutes; - using std::chrono::hours; - date::detail::save_ostream ss(os); - os.fill(' '); - os.flags(std::ios::skipws | std::ios::dec); - os.width(0); - tm tm{}; - bool insert_negative = fds.has_tod && fds.tod.to_duration() < Duration::zero(); -#if !ONLY_C_LOCALE - auto& facet = std::use_facet>(os.getloc()); -#endif - const CharT* command = nullptr; - CharT modified = CharT{}; - for (; *fmt; ++fmt) - { - switch (*fmt) - { - case 'a': - case 'A': - if (command) - { - if (modified == CharT{}) - { - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; -#if !ONLY_C_LOCALE - const CharT f[] = {'%', *fmt}; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); -#else // ONLY_C_LOCALE - os << weekday_names().first[tm.tm_wday+7*(*fmt == 'a')]; -#endif // ONLY_C_LOCALE - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'b': - case 'B': - case 'h': - if (command) - { - if (modified == CharT{}) - { - tm.tm_mon = static_cast(extract_month(os, fds)) - 1; -#if !ONLY_C_LOCALE - const CharT f[] = {'%', *fmt}; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); -#else // ONLY_C_LOCALE - os << month_names().first[tm.tm_mon+12*(*fmt != 'B')]; -#endif // ONLY_C_LOCALE - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'c': - case 'x': - if (command) - { - if (modified == CharT{'O'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - if (*fmt == 'c' && !fds.has_tod) - os.setstate(std::ios::failbit); -#if !ONLY_C_LOCALE - tm = std::tm{}; - auto const& ymd = fds.ymd; - auto ld = local_days(ymd); - if (*fmt == 'c') - { - tm.tm_sec = static_cast(fds.tod.seconds().count()); - tm.tm_min = static_cast(fds.tod.minutes().count()); - tm.tm_hour = static_cast(fds.tod.hours().count()); - } - tm.tm_mday = static_cast(static_cast(ymd.day())); - tm.tm_mon = static_cast(extract_month(os, fds) - 1); - tm.tm_year = static_cast(ymd.year()) - 1900; - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; - tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); - CharT f[3] = {'%'}; - auto fe = std::begin(f) + 1; - if (modified == CharT{'E'}) - *fe++ = modified; - *fe++ = *fmt; - facet.put(os, os, os.fill(), &tm, std::begin(f), fe); -#else // ONLY_C_LOCALE - if (*fmt == 'c') - { - auto wd = static_cast(extract_weekday(os, fds)); - os << weekday_names().first[static_cast(wd)+7] - << ' '; - os << month_names().first[extract_month(os, fds)-1+12] << ' '; - auto d = static_cast(static_cast(fds.ymd.day())); - if (d < 10) - os << ' '; - os << d << ' ' - << make_time(duration_cast(fds.tod.to_duration())) - << ' ' << fds.ymd.year(); - - } - else // *fmt == 'x' - { - auto const& ymd = fds.ymd; - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(ymd.month()) << CharT{'/'}; - os.width(2); - os << static_cast(ymd.day()) << CharT{'/'}; - os.width(2); - os << static_cast(ymd.year()) % 100; - } -#endif // ONLY_C_LOCALE - } - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'C': - if (command) - { - if (modified == CharT{'O'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.year().ok()) - os.setstate(std::ios::failbit); - auto y = static_cast(fds.ymd.year()); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - if (y >= 0) - { - os.width(2); - os << y/100; - } - else - { - os << CharT{'-'}; - os.width(2); - os << -(y-99)/100; - } - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'E'}) - { - tm.tm_year = y - 1900; - CharT f[3] = {'%', 'E', 'C'}; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'd': - case 'e': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.day().ok()) - os.setstate(std::ios::failbit); - auto d = static_cast(static_cast(fds.ymd.day())); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - save_ostream _(os); - if (*fmt == CharT{'d'}) - os.fill('0'); - else - os.fill(' '); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << d; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - tm.tm_mday = d; - CharT f[3] = {'%', 'O', *fmt}; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'D': - if (command) - { - if (modified == CharT{}) - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - auto const& ymd = fds.ymd; - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << static_cast(ymd.month()) << CharT{'/'}; - os.width(2); - os << static_cast(ymd.day()) << CharT{'/'}; - os.width(2); - os << static_cast(ymd.year()) % 100; - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'F': - if (command) - { - if (modified == CharT{}) - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - auto const& ymd = fds.ymd; - save_ostream _(os); - os.imbue(std::locale::classic()); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(4); - os << static_cast(ymd.year()) << CharT{'-'}; - os.width(2); - os << static_cast(ymd.month()) << CharT{'-'}; - os.width(2); - os << static_cast(ymd.day()); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'g': - case 'G': - if (command) - { - if (modified == CharT{}) - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - auto ld = local_days(fds.ymd); - auto y = year_month_day{ld + days{3}}.year(); - auto start = local_days((y-years{1})/December/Thursday[last]) + - (Monday-Thursday); - if (ld < start) - --y; - if (*fmt == CharT{'G'}) - os << y; - else - { - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(2); - os << std::abs(static_cast(y)) % 100; - } - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'H': - case 'I': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - if (insert_negative) - { - os << '-'; - insert_negative = false; - } - auto hms = fds.tod; -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - auto h = *fmt == CharT{'I'} ? date::make12(hms.hours()) : hms.hours(); - if (h < hours{10}) - os << CharT{'0'}; - os << h.count(); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_hour = static_cast(hms.hours().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'j': - if (command) - { - if (modified == CharT{}) - { - if (fds.ymd.ok() || fds.has_tod) - { - days doy; - if (fds.ymd.ok()) - { - auto ld = local_days(fds.ymd); - auto y = fds.ymd.year(); - doy = ld - local_days(y/January/1) + days{1}; - } - else - { - doy = duration_cast(fds.tod.to_duration()); - } - save_ostream _(os); - os.fill('0'); - os.flags(std::ios::dec | std::ios::right); - os.width(3); - os << doy.count(); - } - else - { - os.setstate(std::ios::failbit); - } - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'm': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.month().ok()) - os.setstate(std::ios::failbit); - auto m = static_cast(fds.ymd.month()); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - if (m < 10) - os << CharT{'0'}; - os << m; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_mon = static_cast(m-1); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'M': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - if (insert_negative) - { - os << '-'; - insert_negative = false; - } -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - if (fds.tod.minutes() < minutes{10}) - os << CharT{'0'}; - os << fds.tod.minutes().count(); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_min = static_cast(fds.tod.minutes().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'n': - if (command) - { - if (modified == CharT{}) - os << CharT{'\n'}; - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'p': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); -#if !ONLY_C_LOCALE - const CharT f[] = {'%', *fmt}; - tm.tm_hour = static_cast(fds.tod.hours().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); -#else - if (date::is_am(fds.tod.hours())) - os << ampm_names().first[0]; - else - os << ampm_names().first[1]; -#endif - } - else - { - os << CharT{'%'} << modified << *fmt; - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'Q': - case 'q': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - auto d = fds.tod.to_duration(); - if (*fmt == 'q') - os << get_units(typename decltype(d)::period::type{}); - else - os << d.count(); - } - else - { - os << CharT{'%'} << modified << *fmt; - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'r': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); -#if !ONLY_C_LOCALE - const CharT f[] = {'%', *fmt}; - tm.tm_hour = static_cast(fds.tod.hours().count()); - tm.tm_min = static_cast(fds.tod.minutes().count()); - tm.tm_sec = static_cast(fds.tod.seconds().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); -#else - hh_mm_ss tod(duration_cast(fds.tod.to_duration())); - save_ostream _(os); - os.fill('0'); - os.width(2); - os << date::make12(tod.hours()).count() << CharT{':'}; - os.width(2); - os << tod.minutes().count() << CharT{':'}; - os.width(2); - os << tod.seconds().count() << CharT{' '}; - if (date::is_am(tod.hours())) - os << ampm_names().first[0]; - else - os << ampm_names().first[1]; -#endif - } - else - { - os << CharT{'%'} << modified << *fmt; - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'R': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - if (fds.tod.hours() < hours{10}) - os << CharT{'0'}; - os << fds.tod.hours().count() << CharT{':'}; - if (fds.tod.minutes() < minutes{10}) - os << CharT{'0'}; - os << fds.tod.minutes().count(); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'S': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - if (insert_negative) - { - os << '-'; - insert_negative = false; - } -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - os << fds.tod.s_; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_sec = static_cast(fds.tod.s_.seconds().count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 't': - if (command) - { - if (modified == CharT{}) - os << CharT{'\t'}; - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'T': - if (command) - { - if (modified == CharT{}) - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); - os << fds.tod; - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'u': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - auto wd = extract_weekday(os, fds); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - os << (wd != 0 ? wd : 7u); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_wday = static_cast(wd); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'U': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - auto const& ymd = fds.ymd; - if (!ymd.ok()) - os.setstate(std::ios::failbit); - auto ld = local_days(ymd); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - auto st = local_days(Sunday[1]/January/ymd.year()); - if (ld < st) - os << CharT{'0'} << CharT{'0'}; - else - { - auto wn = duration_cast(ld - st).count() + 1; - if (wn < 10) - os << CharT{'0'}; - os << wn; - } - } - #if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_year = static_cast(ymd.year()) - 1900; - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; - tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'V': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.ok()) - os.setstate(std::ios::failbit); - auto ld = local_days(fds.ymd); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - auto y = year_month_day{ld + days{3}}.year(); - auto st = local_days((y-years{1})/12/Thursday[last]) + - (Monday-Thursday); - if (ld < st) - { - --y; - st = local_days((y - years{1})/12/Thursday[last]) + - (Monday-Thursday); - } - auto wn = duration_cast(ld - st).count() + 1; - if (wn < 10) - os << CharT{'0'}; - os << wn; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - auto const& ymd = fds.ymd; - tm.tm_year = static_cast(ymd.year()) - 1900; - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; - tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'w': - if (command) - { - auto wd = extract_weekday(os, fds); - if (os.fail()) - return os; -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - os << wd; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_wday = static_cast(wd); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - else - { - os << CharT{'%'} << modified << *fmt; - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'W': - if (command) - { - if (modified == CharT{'E'}) - os << CharT{'%'} << modified << *fmt; - else - { - auto const& ymd = fds.ymd; - if (!ymd.ok()) - os.setstate(std::ios::failbit); - auto ld = local_days(ymd); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - auto st = local_days(Monday[1]/January/ymd.year()); - if (ld < st) - os << CharT{'0'} << CharT{'0'}; - else - { - auto wn = duration_cast(ld - st).count() + 1; - if (wn < 10) - os << CharT{'0'}; - os << wn; - } - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_year = static_cast(ymd.year()) - 1900; - tm.tm_wday = static_cast(extract_weekday(os, fds)); - if (os.fail()) - return os; - tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'X': - if (command) - { - if (modified == CharT{'O'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.has_tod) - os.setstate(std::ios::failbit); -#if !ONLY_C_LOCALE - tm = std::tm{}; - tm.tm_sec = static_cast(fds.tod.seconds().count()); - tm.tm_min = static_cast(fds.tod.minutes().count()); - tm.tm_hour = static_cast(fds.tod.hours().count()); - CharT f[3] = {'%'}; - auto fe = std::begin(f) + 1; - if (modified == CharT{'E'}) - *fe++ = modified; - *fe++ = *fmt; - facet.put(os, os, os.fill(), &tm, std::begin(f), fe); -#else - os << fds.tod; -#endif - } - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'y': - if (command) - { - if (!fds.ymd.year().ok()) - os.setstate(std::ios::failbit); - auto y = static_cast(fds.ymd.year()); -#if !ONLY_C_LOCALE - if (modified == CharT{}) - { -#endif - y = std::abs(y) % 100; - if (y < 10) - os << CharT{'0'}; - os << y; -#if !ONLY_C_LOCALE - } - else - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_year = y - 1900; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'Y': - if (command) - { - if (modified == CharT{'O'}) - os << CharT{'%'} << modified << *fmt; - else - { - if (!fds.ymd.year().ok()) - os.setstate(std::ios::failbit); - auto y = fds.ymd.year(); -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - save_ostream _(os); - os.imbue(std::locale::classic()); - os << y; - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'E'}) - { - const CharT f[] = {'%', modified, *fmt}; - tm.tm_year = static_cast(y) - 1900; - facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); - } -#endif - } - modified = CharT{}; - command = nullptr; - } - else - os << *fmt; - break; - case 'z': - if (command) - { - if (offset_sec == nullptr) - { - // Can not format %z with unknown offset - os.setstate(ios::failbit); - return os; - } - auto m = duration_cast(*offset_sec); - auto neg = m < minutes{0}; - m = date::abs(m); - auto h = duration_cast(m); - m -= h; - if (neg) - os << CharT{'-'}; - else - os << CharT{'+'}; - if (h < hours{10}) - os << CharT{'0'}; - os << h.count(); - if (modified != CharT{}) - os << CharT{':'}; - if (m < minutes{10}) - os << CharT{'0'}; - os << m.count(); - command = nullptr; - modified = CharT{}; - } - else - os << *fmt; - break; - case 'Z': - if (command) - { - if (modified == CharT{}) - { - if (abbrev == nullptr) - { - // Can not format %Z with unknown time_zone - os.setstate(ios::failbit); - return os; - } - for (auto c : *abbrev) - os << CharT(c); - } - else - { - os << CharT{'%'} << modified << *fmt; - modified = CharT{}; - } - command = nullptr; - } - else - os << *fmt; - break; - case 'E': - case 'O': - if (command) - { - if (modified == CharT{}) - { - modified = *fmt; - } - else - { - os << CharT{'%'} << modified << *fmt; - command = nullptr; - modified = CharT{}; - } - } - else - os << *fmt; - break; - case '%': - if (command) - { - if (modified == CharT{}) - { - os << CharT{'%'}; - command = nullptr; - } - else - { - os << CharT{'%'} << modified << CharT{'%'}; - command = nullptr; - modified = CharT{}; - } - } - else - command = fmt; - break; - default: - if (command) - { - os << CharT{'%'}; - command = nullptr; - } - if (modified != CharT{}) - { - os << modified; - modified = CharT{}; - } - os << *fmt; - break; - } - } - if (command) - os << CharT{'%'}; - if (modified != CharT{}) - os << modified; - return os; -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const year& y) -{ - using CT = std::chrono::seconds; - fields fds{y/0/0}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const month& m) -{ - using CT = std::chrono::seconds; - fields fds{m/0/nanyear}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const day& d) -{ - using CT = std::chrono::seconds; - fields fds{d/0/nanyear}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const weekday& wd) -{ - using CT = std::chrono::seconds; - fields fds{wd}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const year_month& ym) -{ - using CT = std::chrono::seconds; - fields fds{ym/0}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, const month_day& md) -{ - using CT = std::chrono::seconds; - fields fds{md/nanyear}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const year_month_day& ymd) -{ - using CT = std::chrono::seconds; - fields fds{ymd}; - return to_stream(os, fmt, fds); -} - -template -inline -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const std::chrono::duration& d) -{ - using Duration = std::chrono::duration; - using CT = typename std::common_type::type; - fields fds{hh_mm_ss{d}}; - return to_stream(os, fmt, fds); -} - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const local_time& tp, const std::string* abbrev = nullptr, - const std::chrono::seconds* offset_sec = nullptr) -{ - using CT = typename std::common_type::type; - auto ld = std::chrono::time_point_cast(tp); - fields fds; - if (ld <= tp) - fds = fields{year_month_day{ld}, hh_mm_ss{tp-local_seconds{ld}}}; - else - fds = fields{year_month_day{ld - days{1}}, - hh_mm_ss{days{1} - (local_seconds{ld} - tp)}}; - return to_stream(os, fmt, fds, abbrev, offset_sec); -} - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const sys_time& tp) -{ - using std::chrono::seconds; - using CT = typename std::common_type::type; - const std::string abbrev("UTC"); - CONSTDATA seconds offset{0}; - auto sd = std::chrono::time_point_cast(tp); - fields fds; - if (sd <= tp) - fds = fields{year_month_day{sd}, hh_mm_ss{tp-sys_seconds{sd}}}; - else - fds = fields{year_month_day{sd - days{1}}, - hh_mm_ss{days{1} - (sys_seconds{sd} - tp)}}; - return to_stream(os, fmt, fds, &abbrev, &offset); -} - -// format - -template -auto -format(const std::locale& loc, const CharT* fmt, const Streamable& tp) - -> decltype(to_stream(std::declval&>(), fmt, tp), - std::basic_string{}) -{ - std::basic_ostringstream os; - os.exceptions(std::ios::failbit | std::ios::badbit); - os.imbue(loc); - to_stream(os, fmt, tp); - return os.str(); -} - -template -auto -format(const CharT* fmt, const Streamable& tp) - -> decltype(to_stream(std::declval&>(), fmt, tp), - std::basic_string{}) -{ - std::basic_ostringstream os; - os.exceptions(std::ios::failbit | std::ios::badbit); - to_stream(os, fmt, tp); - return os.str(); -} - -template -auto -format(const std::locale& loc, const std::basic_string& fmt, - const Streamable& tp) - -> decltype(to_stream(std::declval&>(), fmt.c_str(), tp), - std::basic_string{}) -{ - std::basic_ostringstream os; - os.exceptions(std::ios::failbit | std::ios::badbit); - os.imbue(loc); - to_stream(os, fmt.c_str(), tp); - return os.str(); -} - -template -auto -format(const std::basic_string& fmt, const Streamable& tp) - -> decltype(to_stream(std::declval&>(), fmt.c_str(), tp), - std::basic_string{}) -{ - std::basic_ostringstream os; - os.exceptions(std::ios::failbit | std::ios::badbit); - to_stream(os, fmt.c_str(), tp); - return os.str(); -} - -// parse - -namespace detail -{ - -template -bool -read_char(std::basic_istream& is, CharT fmt, std::ios::iostate& err) -{ - auto ic = is.get(); - if (Traits::eq_int_type(ic, Traits::eof()) || - !Traits::eq(Traits::to_char_type(ic), fmt)) - { - err |= std::ios::failbit; - is.setstate(std::ios::failbit); - return false; - } - return true; -} - -template -unsigned -read_unsigned(std::basic_istream& is, unsigned m = 1, unsigned M = 10) -{ - unsigned x = 0; - unsigned count = 0; - while (true) - { - auto ic = is.peek(); - if (Traits::eq_int_type(ic, Traits::eof())) - break; - auto c = static_cast(Traits::to_char_type(ic)); - if (!('0' <= c && c <= '9')) - break; - (void)is.get(); - ++count; - x = 10*x + static_cast(c - '0'); - if (count == M) - break; - } - if (count < m) - is.setstate(std::ios::failbit); - return x; -} - -template -int -read_signed(std::basic_istream& is, unsigned m = 1, unsigned M = 10) -{ - auto ic = is.peek(); - if (!Traits::eq_int_type(ic, Traits::eof())) - { - auto c = static_cast(Traits::to_char_type(ic)); - if (('0' <= c && c <= '9') || c == '-' || c == '+') - { - if (c == '-' || c == '+') - (void)is.get(); - auto x = static_cast(read_unsigned(is, std::max(m, 1u), M)); - if (!is.fail()) - { - if (c == '-') - x = -x; - return x; - } - } - } - if (m > 0) - is.setstate(std::ios::failbit); - return 0; -} - -template -long double -read_long_double(std::basic_istream& is, unsigned m = 1, unsigned M = 10) -{ - unsigned count = 0; - unsigned fcount = 0; - unsigned long long i = 0; - unsigned long long f = 0; - bool parsing_fraction = false; -#if ONLY_C_LOCALE - typename Traits::int_type decimal_point = '.'; -#else - auto decimal_point = Traits::to_int_type( - std::use_facet>(is.getloc()).decimal_point()); -#endif - while (true) - { - auto ic = is.peek(); - if (Traits::eq_int_type(ic, Traits::eof())) - break; - if (Traits::eq_int_type(ic, decimal_point)) - { - decimal_point = Traits::eof(); - parsing_fraction = true; - } - else - { - auto c = static_cast(Traits::to_char_type(ic)); - if (!('0' <= c && c <= '9')) - break; - if (!parsing_fraction) - { - i = 10*i + static_cast(c - '0'); - } - else - { - f = 10*f + static_cast(c - '0'); - ++fcount; - } - } - (void)is.get(); - if (++count == M) - break; - } - if (count < m) - { - is.setstate(std::ios::failbit); - return 0; - } - return static_cast(i) + static_cast(f)/std::pow(10.L, fcount); -} - -struct rs -{ - int& i; - unsigned m; - unsigned M; -}; - -struct ru -{ - int& i; - unsigned m; - unsigned M; -}; - -struct rld -{ - long double& i; - unsigned m; - unsigned M; -}; - -template -void -read(std::basic_istream&) -{ -} - -template -void -read(std::basic_istream& is, CharT a0, Args&& ...args); - -template -void -read(std::basic_istream& is, rs a0, Args&& ...args); - -template -void -read(std::basic_istream& is, ru a0, Args&& ...args); - -template -void -read(std::basic_istream& is, int a0, Args&& ...args); - -template -void -read(std::basic_istream& is, rld a0, Args&& ...args); - -template -void -read(std::basic_istream& is, CharT a0, Args&& ...args) -{ - // No-op if a0 == CharT{} - if (a0 != CharT{}) - { - auto ic = is.peek(); - if (Traits::eq_int_type(ic, Traits::eof())) - { - is.setstate(std::ios::failbit | std::ios::eofbit); - return; - } - if (!Traits::eq(Traits::to_char_type(ic), a0)) - { - is.setstate(std::ios::failbit); - return; - } - (void)is.get(); - } - read(is, std::forward(args)...); -} - -template -void -read(std::basic_istream& is, rs a0, Args&& ...args) -{ - auto x = read_signed(is, a0.m, a0.M); - if (is.fail()) - return; - a0.i = x; - read(is, std::forward(args)...); -} - -template -void -read(std::basic_istream& is, ru a0, Args&& ...args) -{ - auto x = read_unsigned(is, a0.m, a0.M); - if (is.fail()) - return; - a0.i = static_cast(x); - read(is, std::forward(args)...); -} - -template -void -read(std::basic_istream& is, int a0, Args&& ...args) -{ - if (a0 != -1) - { - auto u = static_cast(a0); - CharT buf[std::numeric_limits::digits10+2u] = {}; - auto e = buf; - do - { - *e++ = static_cast(CharT(u % 10) + CharT{'0'}); - u /= 10; - } while (u > 0); - std::reverse(buf, e); - for (auto p = buf; p != e && is.rdstate() == std::ios::goodbit; ++p) - read(is, *p); - } - if (is.rdstate() == std::ios::goodbit) - read(is, std::forward(args)...); -} - -template -void -read(std::basic_istream& is, rld a0, Args&& ...args) -{ - auto x = read_long_double(is, a0.m, a0.M); - if (is.fail()) - return; - a0.i = x; - read(is, std::forward(args)...); -} - -template -inline -void -checked_set(T& value, T from, T not_a_value, std::basic_ios& is) -{ - if (!is.fail()) - { - if (value == not_a_value) - value = std::move(from); - else if (value != from) - is.setstate(std::ios::failbit); - } -} - -} // namespace detail; - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - fields& fds, std::basic_string* abbrev, - std::chrono::minutes* offset) -{ - using std::numeric_limits; - using std::ios; - using std::chrono::duration; - using std::chrono::duration_cast; - using std::chrono::seconds; - using std::chrono::minutes; - using std::chrono::hours; - using detail::round_i; - typename std::basic_istream::sentry ok{is, true}; - if (ok) - { - date::detail::save_istream ss(is); - is.fill(' '); - is.flags(std::ios::skipws | std::ios::dec); - is.width(0); -#if !ONLY_C_LOCALE - auto& f = std::use_facet>(is.getloc()); - std::tm tm{}; -#endif - const CharT* command = nullptr; - auto modified = CharT{}; - auto width = -1; - - CONSTDATA int not_a_year = numeric_limits::min(); - CONSTDATA int not_a_2digit_year = 100; - CONSTDATA int not_a_century = not_a_year / 100; - CONSTDATA int not_a_month = 0; - CONSTDATA int not_a_day = 0; - CONSTDATA int not_a_hour = numeric_limits::min(); - CONSTDATA int not_a_hour_12_value = 0; - CONSTDATA int not_a_minute = not_a_hour; - CONSTDATA Duration not_a_second = Duration::min(); - CONSTDATA int not_a_doy = -1; - CONSTDATA int not_a_weekday = 8; - CONSTDATA int not_a_week_num = 100; - CONSTDATA int not_a_ampm = -1; - CONSTDATA minutes not_a_offset = minutes::min(); - - int Y = not_a_year; // c, F, Y * - int y = not_a_2digit_year; // D, x, y * - int g = not_a_2digit_year; // g * - int G = not_a_year; // G * - int C = not_a_century; // C * - int m = not_a_month; // b, B, h, m, c, D, F, x * - int d = not_a_day; // c, d, D, e, F, x * - int j = not_a_doy; // j * - int wd = not_a_weekday; // a, A, u, w * - int H = not_a_hour; // c, H, R, T, X * - int I = not_a_hour_12_value; // I, r * - int p = not_a_ampm; // p, r * - int M = not_a_minute; // c, M, r, R, T, X * - Duration s = not_a_second; // c, r, S, T, X * - int U = not_a_week_num; // U * - int V = not_a_week_num; // V * - int W = not_a_week_num; // W * - std::basic_string temp_abbrev; // Z * - minutes temp_offset = not_a_offset; // z * - - using detail::read; - using detail::rs; - using detail::ru; - using detail::rld; - using detail::checked_set; - for (; *fmt != CharT{} && !is.fail(); ++fmt) - { - switch (*fmt) - { - case 'a': - case 'A': - case 'u': - case 'w': // wd: a, A, u, w - if (command) - { - int trial_wd = not_a_weekday; - if (*fmt == 'a' || *fmt == 'A') - { - if (modified == CharT{}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - is.setstate(err); - if (!is.fail()) - trial_wd = tm.tm_wday; -#else - auto nm = detail::weekday_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - if (!is.fail()) - trial_wd = i % 7; -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - } - else // *fmt == 'u' || *fmt == 'w' - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - read(is, ru{trial_wd, 1, width == -1 ? - 1u : static_cast(width)}); - if (!is.fail()) - { - if (*fmt == 'u') - { - if (!(1 <= trial_wd && trial_wd <= 7)) - { - trial_wd = not_a_weekday; - is.setstate(ios::failbit); - } - else if (trial_wd == 7) - trial_wd = 0; - } - else // *fmt == 'w' - { - if (!(0 <= trial_wd && trial_wd <= 6)) - { - trial_wd = not_a_weekday; - is.setstate(ios::failbit); - } - } - } - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - is.setstate(err); - if (!is.fail()) - trial_wd = tm.tm_wday; - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - } - if (trial_wd != not_a_weekday) - checked_set(wd, trial_wd, not_a_weekday, is); - } - else // !command - read(is, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - break; - case 'b': - case 'B': - case 'h': - if (command) - { - if (modified == CharT{}) - { - int ttm = not_a_month; -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - ttm = tm.tm_mon + 1; - is.setstate(err); -#else - auto nm = detail::month_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - if (!is.fail()) - ttm = i % 12 + 1; -#endif - checked_set(m, ttm, not_a_month, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'c': - if (command) - { - if (modified != CharT{'O'}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - checked_set(Y, tm.tm_year + 1900, not_a_year, is); - checked_set(m, tm.tm_mon + 1, not_a_month, is); - checked_set(d, tm.tm_mday, not_a_day, is); - checked_set(H, tm.tm_hour, not_a_hour, is); - checked_set(M, tm.tm_min, not_a_minute, is); - checked_set(s, duration_cast(seconds{tm.tm_sec}), - not_a_second, is); - } - is.setstate(err); -#else - // "%a %b %e %T %Y" - auto nm = detail::weekday_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - checked_set(wd, static_cast(i % 7), not_a_weekday, is); - ws(is); - nm = detail::month_names(); - i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - checked_set(m, static_cast(i % 12 + 1), not_a_month, is); - ws(is); - int td = not_a_day; - read(is, rs{td, 1, 2}); - checked_set(d, td, not_a_day, is); - ws(is); - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - int tH; - int tM; - long double S{}; - read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, - CharT{':'}, rld{S, 1, w}); - checked_set(H, tH, not_a_hour, is); - checked_set(M, tM, not_a_minute, is); - checked_set(s, round_i(duration{S}), - not_a_second, is); - ws(is); - int tY = not_a_year; - read(is, rs{tY, 1, 4u}); - checked_set(Y, tY, not_a_year, is); -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'x': - if (command) - { - if (modified != CharT{'O'}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - checked_set(Y, tm.tm_year + 1900, not_a_year, is); - checked_set(m, tm.tm_mon + 1, not_a_month, is); - checked_set(d, tm.tm_mday, not_a_day, is); - } - is.setstate(err); -#else - // "%m/%d/%y" - int ty = not_a_2digit_year; - int tm = not_a_month; - int td = not_a_day; - read(is, ru{tm, 1, 2}, CharT{'/'}, ru{td, 1, 2}, CharT{'/'}, - rs{ty, 1, 2}); - checked_set(y, ty, not_a_2digit_year, is); - checked_set(m, tm, not_a_month, is); - checked_set(d, td, not_a_day, is); -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'X': - if (command) - { - if (modified != CharT{'O'}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - checked_set(H, tm.tm_hour, not_a_hour, is); - checked_set(M, tm.tm_min, not_a_minute, is); - checked_set(s, duration_cast(seconds{tm.tm_sec}), - not_a_second, is); - } - is.setstate(err); -#else - // "%T" - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - int tH = not_a_hour; - int tM = not_a_minute; - long double S{}; - read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, - CharT{':'}, rld{S, 1, w}); - checked_set(H, tH, not_a_hour, is); - checked_set(M, tM, not_a_minute, is); - checked_set(s, round_i(duration{S}), - not_a_second, is); -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'C': - if (command) - { - int tC = not_a_century; -#if !ONLY_C_LOCALE - if (modified == CharT{}) - { -#endif - read(is, rs{tC, 1, width == -1 ? 2u : static_cast(width)}); -#if !ONLY_C_LOCALE - } - else - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - auto tY = tm.tm_year + 1900; - tC = (tY >= 0 ? tY : tY-99) / 100; - } - is.setstate(err); - } -#endif - checked_set(C, tC, not_a_century, is); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'D': - if (command) - { - if (modified == CharT{}) - { - int tn = not_a_month; - int td = not_a_day; - int ty = not_a_2digit_year; - read(is, ru{tn, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, - ru{td, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, - rs{ty, 1, 2}); - checked_set(y, ty, not_a_2digit_year, is); - checked_set(m, tn, not_a_month, is); - checked_set(d, td, not_a_day, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'F': - if (command) - { - if (modified == CharT{}) - { - int tY = not_a_year; - int tn = not_a_month; - int td = not_a_day; - read(is, rs{tY, 1, width == -1 ? 4u : static_cast(width)}, - CharT{'-'}, ru{tn, 1, 2}, CharT{'-'}, ru{td, 1, 2}); - checked_set(Y, tY, not_a_year, is); - checked_set(m, tn, not_a_month, is); - checked_set(d, td, not_a_day, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'd': - case 'e': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - int td = not_a_day; - read(is, rs{td, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(d, td, not_a_day, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - command = nullptr; - width = -1; - modified = CharT{}; - if ((err & ios::failbit) == 0) - checked_set(d, tm.tm_mday, not_a_day, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'H': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - int tH = not_a_hour; - read(is, ru{tH, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(H, tH, not_a_hour, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(H, tm.tm_hour, not_a_hour, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'I': - if (command) - { - if (modified == CharT{}) - { - int tI = not_a_hour_12_value; - // reads in an hour into I, but most be in [1, 12] - read(is, rs{tI, 1, width == -1 ? 2u : static_cast(width)}); - if (!(1 <= tI && tI <= 12)) - is.setstate(ios::failbit); - checked_set(I, tI, not_a_hour_12_value, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'j': - if (command) - { - if (modified == CharT{}) - { - int tj = not_a_doy; - read(is, ru{tj, 1, width == -1 ? 3u : static_cast(width)}); - checked_set(j, tj, not_a_doy, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'M': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - int tM = not_a_minute; - read(is, ru{tM, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(M, tM, not_a_minute, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(M, tm.tm_min, not_a_minute, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'm': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - int tn = not_a_month; - read(is, rs{tn, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(m, tn, not_a_month, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(m, tm.tm_mon + 1, not_a_month, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'n': - case 't': - if (command) - { - if (modified == CharT{}) - { - // %n matches a single white space character - // %t matches 0 or 1 white space characters - auto ic = is.peek(); - if (Traits::eq_int_type(ic, Traits::eof())) - { - ios::iostate err = ios::eofbit; - if (*fmt == 'n') - err |= ios::failbit; - is.setstate(err); - break; - } - if (isspace(ic)) - { - (void)is.get(); - } - else if (*fmt == 'n') - is.setstate(ios::failbit); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'p': - if (command) - { - if (modified == CharT{}) - { - int tp = not_a_ampm; -#if !ONLY_C_LOCALE - tm = std::tm{}; - tm.tm_hour = 1; - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - is.setstate(err); - if (tm.tm_hour == 1) - tp = 0; - else if (tm.tm_hour == 13) - tp = 1; - else - is.setstate(err); -#else - auto nm = detail::ampm_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - tp = static_cast(i); -#endif - checked_set(p, tp, not_a_ampm, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - - break; - case 'r': - if (command) - { - if (modified == CharT{}) - { -#if !ONLY_C_LOCALE - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - { - checked_set(H, tm.tm_hour, not_a_hour, is); - checked_set(M, tm.tm_min, not_a_hour, is); - checked_set(s, duration_cast(seconds{tm.tm_sec}), - not_a_second, is); - } - is.setstate(err); -#else - // "%I:%M:%S %p" - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - long double S{}; - int tI = not_a_hour_12_value; - int tM = not_a_minute; - read(is, ru{tI, 1, 2}, CharT{':'}, ru{tM, 1, 2}, - CharT{':'}, rld{S, 1, w}); - checked_set(I, tI, not_a_hour_12_value, is); - checked_set(M, tM, not_a_minute, is); - checked_set(s, round_i(duration{S}), - not_a_second, is); - ws(is); - auto nm = detail::ampm_names(); - auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; - checked_set(p, static_cast(i), not_a_ampm, is); -#endif - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'R': - if (command) - { - if (modified == CharT{}) - { - int tH = not_a_hour; - int tM = not_a_minute; - read(is, ru{tH, 1, 2}, CharT{'\0'}, CharT{':'}, CharT{'\0'}, - ru{tM, 1, 2}, CharT{'\0'}); - checked_set(H, tH, not_a_hour, is); - checked_set(M, tM, not_a_minute, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'S': - if (command) - { - #if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'E'}) -#endif - { - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - long double S{}; - read(is, rld{S, 1, width == -1 ? w : static_cast(width)}); - checked_set(s, round_i(duration{S}), - not_a_second, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'O'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(s, duration_cast(seconds{tm.tm_sec}), - not_a_second, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'T': - if (command) - { - if (modified == CharT{}) - { - using dfs = detail::decimal_format_seconds; - CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; - int tH = not_a_hour; - int tM = not_a_minute; - long double S{}; - read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, - CharT{':'}, rld{S, 1, w}); - checked_set(H, tH, not_a_hour, is); - checked_set(M, tM, not_a_minute, is); - checked_set(s, round_i(duration{S}), - not_a_second, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'Y': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#else - if (modified != CharT{'O'}) -#endif - { - int tY = not_a_year; - read(is, rs{tY, 1, width == -1 ? 4u : static_cast(width)}); - checked_set(Y, tY, not_a_year, is); - } -#if !ONLY_C_LOCALE - else if (modified == CharT{'E'}) - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(Y, tm.tm_year + 1900, not_a_year, is); - is.setstate(err); - } -#endif - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'y': - if (command) - { -#if !ONLY_C_LOCALE - if (modified == CharT{}) -#endif - { - int ty = not_a_2digit_year; - read(is, ru{ty, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(y, ty, not_a_2digit_year, is); - } -#if !ONLY_C_LOCALE - else - { - ios::iostate err = ios::goodbit; - f.get(is, nullptr, is, err, &tm, command, fmt+1); - if ((err & ios::failbit) == 0) - checked_set(Y, tm.tm_year + 1900, not_a_year, is); - is.setstate(err); - } -#endif - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'g': - if (command) - { - if (modified == CharT{}) - { - int tg = not_a_2digit_year; - read(is, ru{tg, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(g, tg, not_a_2digit_year, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'G': - if (command) - { - if (modified == CharT{}) - { - int tG = not_a_year; - read(is, rs{tG, 1, width == -1 ? 4u : static_cast(width)}); - checked_set(G, tG, not_a_year, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'U': - if (command) - { - if (modified == CharT{}) - { - int tU = not_a_week_num; - read(is, ru{tU, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(U, tU, not_a_week_num, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'V': - if (command) - { - if (modified == CharT{}) - { - int tV = not_a_week_num; - read(is, ru{tV, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(V, tV, not_a_week_num, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'W': - if (command) - { - if (modified == CharT{}) - { - int tW = not_a_week_num; - read(is, ru{tW, 1, width == -1 ? 2u : static_cast(width)}); - checked_set(W, tW, not_a_week_num, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'E': - case 'O': - if (command) - { - if (modified == CharT{}) - { - modified = *fmt; - } - else - { - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - } - else - read(is, *fmt); - break; - case '%': - if (command) - { - if (modified == CharT{}) - read(is, *fmt); - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - command = fmt; - break; - case 'z': - if (command) - { - int tH, tM; - minutes toff = not_a_offset; - bool neg = false; - auto ic = is.peek(); - if (!Traits::eq_int_type(ic, Traits::eof())) - { - auto c = static_cast(Traits::to_char_type(ic)); - if (c == '-') - neg = true; - } - if (modified == CharT{}) - { - read(is, rs{tH, 2, 2}); - if (!is.fail()) - toff = hours{std::abs(tH)}; - if (is.good()) - { - ic = is.peek(); - if (!Traits::eq_int_type(ic, Traits::eof())) - { - auto c = static_cast(Traits::to_char_type(ic)); - if ('0' <= c && c <= '9') - { - read(is, ru{tM, 2, 2}); - if (!is.fail()) - toff += minutes{tM}; - } - } - } - } - else - { - read(is, rs{tH, 1, 2}); - if (!is.fail()) - toff = hours{std::abs(tH)}; - if (is.good()) - { - ic = is.peek(); - if (!Traits::eq_int_type(ic, Traits::eof())) - { - auto c = static_cast(Traits::to_char_type(ic)); - if (c == ':') - { - (void)is.get(); - read(is, ru{tM, 2, 2}); - if (!is.fail()) - toff += minutes{tM}; - } - } - } - } - if (neg) - toff = -toff; - checked_set(temp_offset, toff, not_a_offset, is); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - case 'Z': - if (command) - { - if (modified == CharT{}) - { - std::basic_string buf; - while (is.rdstate() == std::ios::goodbit) - { - auto i = is.rdbuf()->sgetc(); - if (Traits::eq_int_type(i, Traits::eof())) - { - is.setstate(ios::eofbit); - break; - } - auto wc = Traits::to_char_type(i); - auto c = static_cast(wc); - // is c a valid time zone name or abbreviation character? - if (!(CharT{1} < wc && wc < CharT{127}) || !(isalnum(c) || - c == '_' || c == '/' || c == '-' || c == '+')) - break; - buf.push_back(c); - is.rdbuf()->sbumpc(); - } - if (buf.empty()) - is.setstate(ios::failbit); - checked_set(temp_abbrev, buf, {}, is); - } - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - else - read(is, *fmt); - break; - default: - if (command) - { - if (width == -1 && modified == CharT{} && '0' <= *fmt && *fmt <= '9') - { - width = static_cast(*fmt) - '0'; - while ('0' <= fmt[1] && fmt[1] <= '9') - width = 10*width + static_cast(*++fmt) - '0'; - } - else - { - if (modified == CharT{}) - read(is, CharT{'%'}, width, *fmt); - else - read(is, CharT{'%'}, width, modified, *fmt); - command = nullptr; - width = -1; - modified = CharT{}; - } - } - else // !command - { - if (isspace(static_cast(*fmt))) - { - // space matches 0 or more white space characters - if (is.good()) - ws(is); - } - else - read(is, *fmt); - } - break; - } - } - // is.fail() || *fmt == CharT{} - if (is.rdstate() == ios::goodbit && command) - { - if (modified == CharT{}) - read(is, CharT{'%'}, width); - else - read(is, CharT{'%'}, width, modified); - } - if (!is.fail()) - { - if (y != not_a_2digit_year) - { - // Convert y and an optional C to Y - if (!(0 <= y && y <= 99)) - goto broken; - if (C == not_a_century) - { - if (Y == not_a_year) - { - if (y >= 69) - C = 19; - else - C = 20; - } - else - { - C = (Y >= 0 ? Y : Y-100) / 100; - } - } - int tY; - if (C >= 0) - tY = 100*C + y; - else - tY = 100*(C+1) - (y == 0 ? 100 : y); - if (Y != not_a_year && Y != tY) - goto broken; - Y = tY; - } - if (g != not_a_2digit_year) - { - // Convert g and an optional C to G - if (!(0 <= g && g <= 99)) - goto broken; - if (C == not_a_century) - { - if (G == not_a_year) - { - if (g >= 69) - C = 19; - else - C = 20; - } - else - { - C = (G >= 0 ? G : G-100) / 100; - } - } - int tG; - if (C >= 0) - tG = 100*C + g; - else - tG = 100*(C+1) - (g == 0 ? 100 : g); - if (G != not_a_year && G != tG) - goto broken; - G = tG; - } - if (Y < static_cast(year::min()) || Y > static_cast(year::max())) - Y = not_a_year; - bool computed = false; - if (G != not_a_year && V != not_a_week_num && wd != not_a_weekday) - { - year_month_day ymd_trial = sys_days(year{G-1}/December/Thursday[last]) + - (Monday-Thursday) + weeks{V-1} + - (weekday{static_cast(wd)}-Monday); - if (Y == not_a_year) - Y = static_cast(ymd_trial.year()); - else if (year{Y} != ymd_trial.year()) - goto broken; - if (m == not_a_month) - m = static_cast(static_cast(ymd_trial.month())); - else if (month(static_cast(m)) != ymd_trial.month()) - goto broken; - if (d == not_a_day) - d = static_cast(static_cast(ymd_trial.day())); - else if (day(static_cast(d)) != ymd_trial.day()) - goto broken; - computed = true; - } - if (Y != not_a_year && U != not_a_week_num && wd != not_a_weekday) - { - year_month_day ymd_trial = sys_days(year{Y}/January/Sunday[1]) + - weeks{U-1} + - (weekday{static_cast(wd)} - Sunday); - if (Y == not_a_year) - Y = static_cast(ymd_trial.year()); - else if (year{Y} != ymd_trial.year()) - goto broken; - if (m == not_a_month) - m = static_cast(static_cast(ymd_trial.month())); - else if (month(static_cast(m)) != ymd_trial.month()) - goto broken; - if (d == not_a_day) - d = static_cast(static_cast(ymd_trial.day())); - else if (day(static_cast(d)) != ymd_trial.day()) - goto broken; - computed = true; - } - if (Y != not_a_year && W != not_a_week_num && wd != not_a_weekday) - { - year_month_day ymd_trial = sys_days(year{Y}/January/Monday[1]) + - weeks{W-1} + - (weekday{static_cast(wd)} - Monday); - if (Y == not_a_year) - Y = static_cast(ymd_trial.year()); - else if (year{Y} != ymd_trial.year()) - goto broken; - if (m == not_a_month) - m = static_cast(static_cast(ymd_trial.month())); - else if (month(static_cast(m)) != ymd_trial.month()) - goto broken; - if (d == not_a_day) - d = static_cast(static_cast(ymd_trial.day())); - else if (day(static_cast(d)) != ymd_trial.day()) - goto broken; - computed = true; - } - if (j != not_a_doy && Y != not_a_year) - { - auto ymd_trial = year_month_day{local_days(year{Y}/1/1) + days{j-1}}; - if (m == not_a_month) - m = static_cast(static_cast(ymd_trial.month())); - else if (month(static_cast(m)) != ymd_trial.month()) - goto broken; - if (d == not_a_day) - d = static_cast(static_cast(ymd_trial.day())); - else if (day(static_cast(d)) != ymd_trial.day()) - goto broken; - j = not_a_doy; - } - auto ymd = year{Y}/m/d; - if (ymd.ok()) - { - if (wd == not_a_weekday) - wd = static_cast((weekday(sys_days(ymd)) - Sunday).count()); - else if (wd != static_cast((weekday(sys_days(ymd)) - Sunday).count())) - goto broken; - if (!computed) - { - if (G != not_a_year || V != not_a_week_num) - { - sys_days sd = ymd; - auto G_trial = year_month_day{sd + days{3}}.year(); - auto start = sys_days((G_trial - years{1})/December/Thursday[last]) + - (Monday - Thursday); - if (sd < start) - { - --G_trial; - if (V != not_a_week_num) - start = sys_days((G_trial - years{1})/December/Thursday[last]) - + (Monday - Thursday); - } - if (G != not_a_year && G != static_cast(G_trial)) - goto broken; - if (V != not_a_week_num) - { - auto V_trial = duration_cast(sd - start).count() + 1; - if (V != V_trial) - goto broken; - } - } - if (U != not_a_week_num) - { - auto start = sys_days(Sunday[1]/January/ymd.year()); - auto U_trial = floor(sys_days(ymd) - start).count() + 1; - if (U != U_trial) - goto broken; - } - if (W != not_a_week_num) - { - auto start = sys_days(Monday[1]/January/ymd.year()); - auto W_trial = floor(sys_days(ymd) - start).count() + 1; - if (W != W_trial) - goto broken; - } - } - } - fds.ymd = ymd; - if (I != not_a_hour_12_value) - { - if (!(1 <= I && I <= 12)) - goto broken; - if (p != not_a_ampm) - { - // p is in [0, 1] == [AM, PM] - // Store trial H in I - if (I == 12) - --p; - I += p*12; - // Either set H from I or make sure H and I are consistent - if (H == not_a_hour) - H = I; - else if (I != H) - goto broken; - } - else // p == not_a_ampm - { - // if H, make sure H and I could be consistent - if (H != not_a_hour) - { - if (I == 12) - { - if (H != 0 && H != 12) - goto broken; - } - else if (!(I == H || I == H+12)) - { - goto broken; - } - } - else // I is ambiguous, AM or PM? - goto broken; - } - } - if (H != not_a_hour) - { - fds.has_tod = true; - fds.tod = hh_mm_ss{hours{H}}; - } - if (M != not_a_minute) - { - fds.has_tod = true; - fds.tod.m_ = minutes{M}; - } - if (s != not_a_second) - { - fds.has_tod = true; - fds.tod.s_ = detail::decimal_format_seconds{s}; - } - if (j != not_a_doy) - { - fds.has_tod = true; - fds.tod.h_ += hours{days{j}}; - } - if (wd != not_a_weekday) - fds.wd = weekday{static_cast(wd)}; - if (abbrev != nullptr) - *abbrev = std::move(temp_abbrev); - if (offset != nullptr && temp_offset != not_a_offset) - *offset = temp_offset; - } - return is; - } -broken: - is.setstate(ios::failbit); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, year& y, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.year().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - y = fds.ymd.year(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, month& m, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.month().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - m = fds.ymd.month(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, day& d, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.day().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - d = fds.ymd.day(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, weekday& wd, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.wd.ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - wd = fds.wd; - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, year_month& ym, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.month().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - ym = fds.ymd.year()/fds.ymd.month(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, month_day& md, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.month().ok() || !fds.ymd.day().ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - md = fds.ymd.month()/fds.ymd.day(); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - year_month_day& ymd, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = std::chrono::seconds; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - ymd = fds.ymd; - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - sys_time& tp, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = typename std::common_type::type; - using detail::round_i; - std::chrono::minutes offset_local{}; - auto offptr = offset ? offset : &offset_local; - fields fds{}; - fds.has_tod = true; - date::from_stream(is, fmt, fds, abbrev, offptr); - if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) - is.setstate(std::ios::failbit); - if (!is.fail()) - tp = round_i(sys_days(fds.ymd) - *offptr + fds.tod.to_duration()); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - local_time& tp, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using CT = typename std::common_type::type; - using detail::round_i; - fields fds{}; - fds.has_tod = true; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) - is.setstate(std::ios::failbit); - if (!is.fail()) - tp = round_i(local_seconds{local_days(fds.ymd)} + fds.tod.to_duration()); - return is; -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - std::chrono::duration& d, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using Duration = std::chrono::duration; - using CT = typename std::common_type::type; - using detail::round_i; - fields fds{}; - date::from_stream(is, fmt, fds, abbrev, offset); - if (!fds.has_tod) - is.setstate(std::ios::failbit); - if (!is.fail()) - d = round_i(fds.tod.to_duration()); - return is; -} - -template , - class Alloc = std::allocator> -struct parse_manip -{ - const std::basic_string format_; - Parsable& tp_; - std::basic_string* abbrev_; - std::chrono::minutes* offset_; - -public: - parse_manip(std::basic_string format, Parsable& tp, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) - : format_(std::move(format)) - , tp_(tp) - , abbrev_(abbrev) - , offset_(offset) - {} - -#if HAS_STRING_VIEW - parse_manip(const CharT* format, Parsable& tp, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) - : format_(format) - , tp_(tp) - , abbrev_(abbrev) - , offset_(offset) - {} - - parse_manip(std::basic_string_view format, Parsable& tp, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) - : format_(format) - , tp_(tp) - , abbrev_(abbrev) - , offset_(offset) - {} -#endif // HAS_STRING_VIEW -}; - -template -std::basic_istream& -operator>>(std::basic_istream& is, - const parse_manip& x) -{ - return date::from_stream(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_); -} - -template -inline -auto -parse(const std::basic_string& format, Parsable& tp) - -> decltype(date::from_stream(std::declval&>(), - format.c_str(), tp), - parse_manip{format, tp}) -{ - return {format, tp}; -} - -template -inline -auto -parse(const std::basic_string& format, Parsable& tp, - std::basic_string& abbrev) - -> decltype(date::from_stream(std::declval&>(), - format.c_str(), tp, &abbrev), - parse_manip{format, tp, &abbrev}) -{ - return {format, tp, &abbrev}; -} - -template -inline -auto -parse(const std::basic_string& format, Parsable& tp, - std::chrono::minutes& offset) - -> decltype(date::from_stream(std::declval&>(), - format.c_str(), tp, - std::declval*>(), - &offset), - parse_manip{format, tp, nullptr, &offset}) -{ - return {format, tp, nullptr, &offset}; -} - -template -inline -auto -parse(const std::basic_string& format, Parsable& tp, - std::basic_string& abbrev, std::chrono::minutes& offset) - -> decltype(date::from_stream(std::declval&>(), - format.c_str(), tp, &abbrev, &offset), - parse_manip{format, tp, &abbrev, &offset}) -{ - return {format, tp, &abbrev, &offset}; -} - -// const CharT* formats - -template -inline -auto -parse(const CharT* format, Parsable& tp) - -> decltype(date::from_stream(std::declval&>(), format, tp), - parse_manip{format, tp}) -{ - return {format, tp}; -} - -template -inline -auto -parse(const CharT* format, Parsable& tp, std::basic_string& abbrev) - -> decltype(date::from_stream(std::declval&>(), format, - tp, &abbrev), - parse_manip{format, tp, &abbrev}) -{ - return {format, tp, &abbrev}; -} - -template -inline -auto -parse(const CharT* format, Parsable& tp, std::chrono::minutes& offset) - -> decltype(date::from_stream(std::declval&>(), format, - tp, std::declval*>(), &offset), - parse_manip{format, tp, nullptr, &offset}) -{ - return {format, tp, nullptr, &offset}; -} - -template -inline -auto -parse(const CharT* format, Parsable& tp, - std::basic_string& abbrev, std::chrono::minutes& offset) - -> decltype(date::from_stream(std::declval&>(), format, - tp, &abbrev, &offset), - parse_manip{format, tp, &abbrev, &offset}) -{ - return {format, tp, &abbrev, &offset}; -} - -// duration streaming - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, - const std::chrono::duration& d) -{ - return os << detail::make_string::from(d.count()) + - detail::get_units(typename Period::type{}); -} - -} // namespace date - -#ifdef _MSC_VER -# pragma warning(pop) -#endif - -#ifdef __GNUC__ -# pragma GCC diagnostic pop -#endif - -#endif // DATE_H - diff --git a/src/errorcode.h b/src/errorcode.h deleted file mode 100644 index 9dac305..0000000 --- a/src/errorcode.h +++ /dev/null @@ -1,42 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_ERRORCODE_H -#define OSDEV_COMPONENTS_MQTT_ERRORCODE_H - -// std -#include - -namespace osdev { -namespace components { -namespace mqtt { - -/*! - * \return A stringified paho error code... - */ -std::string pahoAsyncErrorCodeToString( int errorCode ); - - -} // End namespace mqtt -} // End nnamespace components -} // End namesapce osdev - -#endif // OSDEV_COMPONENTS_MQTT_ERRORCODE_H diff --git a/src/histogram.h b/src/histogram.h deleted file mode 100644 index aa4ed2c..0000000 --- a/src/histogram.h +++ /dev/null @@ -1,355 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAM_H -#define OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAM_H - -#include -#include -#include -#include -#include -#include - -#include "utils.h" - -#include "ihistogram.h" - -namespace osdev { -namespace components { -namespace mqtt { -namespace measurement { - -template -double fabs(T val) -{ - return std::fabs(val); -} - -template -double fabs(const std::chrono::duration& dur) -{ - return std::fabs(dur.count()); -} - -template -double to_double(T val) -{ - return static_cast(val); -} - -template -double to_double(const std::chrono::duration& dur) -{ - return static_cast(dur.count()); -} - -template -std::string to_string(T val) -{ - return std::to_string(val); -} - -template -std::string to_string(const std::chrono::duration& dur) -{ - return std::to_string(dur.count()); -} - -template -T min(T) -{ - return std::numeric_limits::min(); -} - -template -std::chrono::duration min(const std::chrono::duration&) -{ - return std::chrono::duration::min(); -} - -template -T max(T) -{ - return std::numeric_limits::max(); -} - -template -std::chrono::duration max(const std::chrono::duration&) -{ - return std::chrono::duration::max(); -} - -/** - * @brief This class implements a histogram for typed values. - * Outlier values to the left are counted on the first bin and to the right on the last bin. - * @tparam T The value type. T must be default constructable. - * @tparam Buckets The number of buckets to use for this histogram. Default is 10 buckets. - */ -template -class Histogram : public IHistogram -{ -public: - /** - * @brief Construct a histogram. - * @param id The histogram identification - * @param minValue The minimum value of the histogram. - * @param maxValue The maxmim value of the histogram. - * @param unit The unit of the values that are collected in this histogram. - */ - Histogram(const std::string& id, T minValue, T maxValue, const char* unit); - - /** - * @brief Set a value in the histogram. - */ - void setValue(T value); - - /** - * @return true when values are added since the last time a snapshot was taken of the histogram or a reset was performed, false otherwise. - */ - bool isUpdated() const; - - /** - * @brief Reset the histogram. - * All counts are made zero and the smallest and largest recorded values are set to default. - */ - void reset(); - - // IHistogram implementation - virtual const std::string& id() const override; - virtual std::size_t numBuckets() const override; - virtual double bucketWidth() const override; - virtual std::string minValueString() const override; - virtual std::string maxValueString() const override; - virtual const std::string& unit() const override; - virtual HistogramData histogramData() const override; - virtual std::size_t numberOfValuesSinceLastClear() const override; - virtual std::string smallestValueSinceLastClear() const override; - virtual std::string largestValueSinceLastClear() const override; - virtual std::string averageValueSinceLastClear() const override; - virtual void clearRunningValues() override; - virtual std::string toString() const override; - -private: - /** - * @brief Get the index on which to count the given value. - * The outliers to the left are counted on index 0 and to the right on index size() - 1. - * @param value The value to get the index for. - * @return the index in the histogram on which to count the given value. - */ - inline std::size_t index(T value) - { - if (value > m_maxValue) { - return m_histogram.size() - 1; - } - if (value < m_minValue) { - return 0; - } - - return 1 + static_cast(to_double((value - m_minValue) / m_bucketWidth)); - } - - const std::string m_id; - const std::string m_unit; - const T m_minValue; - const T m_maxValue; - const double m_bucketWidth; - T m_smallestValue; - T m_largestValue; - T m_smallestValueSinceLastClear; - T m_largestValueSinceLastClear; - T m_summedValueSinceLastClear; - std::size_t m_numberOfValuesSinceLastClear; - mutable std::mutex m_mutex; - std::vector m_histogram; - mutable std::atomic_bool m_isUpdated; -}; - -template -Histogram::Histogram(const std::string& _id, T minValue, T maxValue, const char* _unit) - : m_id(_id) - , m_unit(_unit) - , m_minValue(minValue) - , m_maxValue(maxValue) - , m_bucketWidth(fabs((m_maxValue - m_minValue)) / Buckets) - , m_smallestValue{ max(maxValue) } - , m_largestValue{ min(maxValue) } - , m_smallestValueSinceLastClear{ max(maxValue) } - , m_largestValueSinceLastClear{ min(maxValue) } - , m_summedValueSinceLastClear{} - , m_numberOfValuesSinceLastClear{} - , m_mutex() - , m_histogram(Buckets + 2) // + 2 for the out of bound buckets - , m_isUpdated(false) -{ -} - -template -void Histogram::setValue(T value) -{ - std::lock_guard lg(m_mutex); - apply_unused_parameters(lg); - - if (value > m_largestValueSinceLastClear) { - m_largestValueSinceLastClear = value; - } - - if (value < m_smallestValueSinceLastClear) { - m_smallestValueSinceLastClear = value; - } - - if (value > m_largestValue) { - m_largestValue = value; - } - - if (value < m_smallestValue) { - m_smallestValue = value; - } - - m_summedValueSinceLastClear += value; - ++m_numberOfValuesSinceLastClear; - - m_histogram[this->index(value)]++; - m_isUpdated = true; -} - -template -bool Histogram::isUpdated() const -{ - return m_isUpdated; -} - -template -void Histogram::reset() -{ - std::lock_guard lg(m_mutex); - apply_unused_parameters(lg); - - m_smallestValue = T{ max(m_maxValue) }; - m_largestValue = T{ min(m_maxValue) }; - m_smallestValueSinceLastClear = T{ max(m_maxValue) }; - m_largestValueSinceLastClear = T{ min(m_maxValue) }; - m_summedValueSinceLastClear = T{}; - m_numberOfValuesSinceLastClear = 0; - - std::fill(m_histogram.begin(), m_histogram.end(), 0); - m_isUpdated = false; -} - -template -const std::string& Histogram::id() const -{ - return m_id; -} - -template -std::size_t Histogram::numBuckets() const -{ - return Buckets; -} - -template -double Histogram::bucketWidth() const -{ - return m_bucketWidth; -} - -template -std::string Histogram::minValueString() const -{ - return to_string(m_minValue); -} - -template -std::string Histogram::maxValueString() const -{ - return to_string(m_maxValue); -} - -template -const std::string& Histogram::unit() const -{ - return m_unit; -} - -template -HistogramData Histogram::histogramData() const -{ - std::lock_guard lg(m_mutex); - apply_unused_parameters(lg); - - m_isUpdated = false; - return HistogramData(to_string(m_smallestValue), to_string(m_largestValue), - to_string(m_smallestValueSinceLastClear), to_string(m_largestValueSinceLastClear), averageValueSinceLastClear(), - m_numberOfValuesSinceLastClear, m_histogram); -} - -template -std::size_t Histogram::numberOfValuesSinceLastClear() const -{ - return m_numberOfValuesSinceLastClear; -} - -template -std::string Histogram::smallestValueSinceLastClear() const -{ - return to_string(m_smallestValueSinceLastClear); -} - -template -std::string Histogram::largestValueSinceLastClear() const -{ - return to_string(m_largestValueSinceLastClear); -} - -template -std::string Histogram::averageValueSinceLastClear() const -{ - if (0 == m_numberOfValuesSinceLastClear) { - return "undefined"; - } - return to_string(to_double(m_summedValueSinceLastClear) / m_numberOfValuesSinceLastClear); -} - -template -void Histogram::clearRunningValues() -{ - std::lock_guard lg(m_mutex); - apply_unused_parameters(lg); - - m_smallestValueSinceLastClear = T{ max(m_maxValue) }; - m_largestValueSinceLastClear = T{ min(m_maxValue) }; - m_summedValueSinceLastClear = T{}; - m_numberOfValuesSinceLastClear = 0; -} - -template -std::string Histogram::toString() const -{ - return visualizeHistogram(*this); -} - -} // End namespace measurement -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAM_H diff --git a/src/histogramprovider.h b/src/histogramprovider.h deleted file mode 100644 index 18360af..0000000 --- a/src/histogramprovider.h +++ /dev/null @@ -1,204 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAMPROVIDER_H -#define OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAMPROVIDER_H - -// std -#include -#include -#include -#include - -#include "compat-c++14.h" - -// mlogic::common -#include "sharedreaderlock.h" - -#include "histogram.h" - -namespace osdev { -namespace components { -namespace mqtt { -namespace measurement { - -/** - * @brief This class provides the user with Entry class for putting measurements in a histogram. - * @tparam TContext A tag type that is used to give the HistogramProvider a user defined context. - * @tparam TValue Defines the value type that the histograms of this provider use. - * @tparam Buckets The number of buckets to use on creation of a Histogram. The default number of buckets is 10. - * - * The TValue type must define the following publically visible members. - * - * value_type - The underlying value type used in the histogram - * const value_type minValue - The minimum value of the histogram - * const value_type maxValue - The maximum value of the histogram - * const char* unit - The value unit. - */ -template -class HistogramProvider -{ -public: - using HistogramType = Histogram; - - /** - * @brief Get the HistogramProvider instance. - */ - static HistogramProvider& instance(); - - /** - * @brief Add a value to the histogram identified by id. - * @param id Identifies the histogram to add the value to. - * @param value The value to add. - */ - void addValue(const std::string& id, typename TValue::value_type value); - - /** - * @brief Log the histogram identified by id via the ILogger framework. - * @param id The histogram to log. If no histogram is found no work is done. - */ - void log(const std::string& id); - - /** - * @brief Log the histograms via the ILogger framework. - * @param onlyChanged When set to true only log histograms that have changed. Default is false. - */ - void logAll(bool onlyChanged = false); - - /** - * @brief Reset all the histograms. - * @param logBeforeReset Flag that indicates whether the histogram needs to be logged before reset. Default is true. - * @note Only changed histograms are logged. - */ - void resetAll(bool logBeforeReset = true); - - /** - * @return The logOnDestroy flag value. - */ - bool logOnDestroy() const { return m_logOnDestroy; } - - /** - * @brief Set the logOnDestroy flag. - * @param logOnDest Log on destruction when true, do not log when set to false. - */ - void setLogOnDestroy(bool logOnDest) { m_logOnDestroy = logOnDest; } - -private: - HistogramProvider(); - - /** - * @brief On destruction the provider will log all its histogram information to stdout. - * Since the destruction will happen as one of the last things in the process, stdout is - * used for logging. - */ - ~HistogramProvider(); - - void log(const HistogramType& hist, bool onlyChanged); - - SharedReaderLock m_sharedLock; - std::unordered_map> m_histograms; - bool m_logOnDestroy; ///< Default is true. -}; - -// static -template -HistogramProvider& HistogramProvider::instance() -{ - static HistogramProvider s_provider; - return s_provider; -} - -template -void HistogramProvider::addValue(const std::string& id, typename TValue::value_type value) -{ - OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(m_sharedLock); - auto it = m_histograms.find(id); - if (m_histograms.end() == it) { - OSDEV_COMPONENTS_EXCLUSIVELOCK_SCOPE(m_sharedLock); - constexpr TValue val; - it = (m_histograms.emplace(std::make_pair(id, std::make_unique(id, val.minValue, val.maxValue, val.unit)))).first; - } - it->second->setValue(value); -} - -template -void HistogramProvider::log(const std::string& id) -{ - OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(m_sharedLock); - auto it = m_histograms.find(id); - if (m_histograms.end() != it) { - log(*(it->second), false); - } -} - -template -void HistogramProvider::logAll(bool onlyChanged) -{ - OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(m_sharedLock); - for (const auto& h : m_histograms) { - log(*h.second, onlyChanged); - } -} - -template -void HistogramProvider::resetAll(bool logBeforeReset) -{ - OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(m_sharedLock); - for (const auto& h : m_histograms) { - if (logBeforeReset) { - log(*h.second, true); // only log the histograms that have changed - } - h.second->reset(); - } -} - -template -HistogramProvider::HistogramProvider() - : m_sharedLock() - , m_histograms() - , m_logOnDestroy(true) -{ -} - -template -HistogramProvider::~HistogramProvider() -{ - if (m_logOnDestroy) { - for (const auto& h : m_histograms) { - std::cout << *h.second << std::endl; - } - } -} - -template -void HistogramProvider::log(const HistogramType& hist, bool onlyChanged) -{ - if ((onlyChanged && hist.isUpdated()) || !onlyChanged) { - MLOGIC_COMMON_INFO("HistogramProvider", "%1", hist); - } -} - -} // End namespace measurement -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAMPROVIDER_H diff --git a/src/ihistogram.h b/src/ihistogram.h deleted file mode 100644 index 18e0b42..0000000 --- a/src/ihistogram.h +++ /dev/null @@ -1,230 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MEASUREMENT_IHISTOGRAM_H -#define OSDEV_COMPONENTS_MQTT_MEASUREMENT_IHISTOGRAM_H - -// std -#include -#include -#include - -namespace osdev { -namespace components { -namespace mqtt { -namespace measurement { - -/** - * @brief Class that holds the dynamic part of a histogram. - * Used to take a snapshot of the histogram. - */ -class HistogramData -{ -public: - /** - * @brief Construct dynamic histrogram data. - * @param smallestValue A stringified version of the smallest recorded value since the last update. - * @param largestValue A stringified version of the largest recorded value since the last update. - * @param smallestValueSinceLastClear A stringified version of the smallest value as string since last clear. - * @param largestValueSinceLastClear A stringified version of the largest value as string since last clear. - * @param averageValueSinceLastClear A stringified version of the average value as string since last clear. - * @param nrOfValuesSinceLastClear Number of values since last clear. - * @param histCounts The histogram counts. - */ - HistogramData(const std::string& smallestValue, const std::string& largestValue, - const std::string& smallestValueSinceLastClear, - const std::string& largestValueSinceLastClear, - const std::string& averageValueSinceLastClear, - std::size_t nrOfValuesSinceLastClear, - const std::vector& histCounts) - : m_smallestValueString(smallestValue) - , m_largestValueString(largestValue) - , m_smallestValueSinceLastClearString(smallestValueSinceLastClear) - , m_largestValueSinceLastClearString(largestValueSinceLastClear) - , m_averageValueSinceLastClearString(averageValueSinceLastClear) - , m_numberOfValuesSinceLastClear(nrOfValuesSinceLastClear) - , m_data(histCounts) - { - } - - /** - * @return The smallest recorded value as a string. - */ - const std::string& smallestValueString() const - { - return m_smallestValueString; - } - - /** - * @return The largest recorded value as a string. - */ - const std::string& largestValueString() const - { - return m_largestValueString; - } - - /** - * @return The number of value since last clear. - */ - std::size_t numberOfValuesSinceLastClear() const - { - return m_numberOfValuesSinceLastClear; - } - - /** - * @return The smallest value since last clear as a string. - */ - const std::string& smallestValueSinceLastClearString() const - { - return m_smallestValueSinceLastClearString; - } - - /** - * @return The largest value since last clear as a string. - */ - const std::string& largestValueSinceLastClearString() const - { - return m_largestValueSinceLastClearString; - } - - /** - * @return The average value since last clear as a string. - */ - const std::string& averageValueSinceLastClearString() const - { - return m_averageValueSinceLastClearString; - } - - /** - * @return The histogram counts. - */ - const std::vector& data() const - { - return m_data; - } - -private: - std::string m_smallestValueString; - std::string m_largestValueString; - std::string m_smallestValueSinceLastClearString; - std::string m_largestValueSinceLastClearString; - std::string m_averageValueSinceLastClearString; - std::size_t m_numberOfValuesSinceLastClear; - std::vector m_data; -}; - -/** - * @brief Interface for histogram classes - * Can be used to store histograms in a container and visualize the histogram. - */ -class IHistogram -{ -public: - virtual ~IHistogram(); - - /** - * @return The histogram identification (title). - */ - virtual const std::string& id() const = 0; - - /** - * @return The number of buckets. - */ - virtual std::size_t numBuckets() const = 0; - - /** - * @return The bucket width as a floating point value. - */ - virtual double bucketWidth() const = 0; - - /** - * @return The minimum value of the histogram definition as a string. - */ - virtual std::string minValueString() const = 0; - - /** - * @return The maximum value of the histogram definition as a string. - */ - virtual std::string maxValueString() const = 0; - - /** - * @return The value unit as a a string. - */ - virtual const std::string& unit() const = 0; - - /** - * @return The histogram data. - * This takes a snapshot of the histogram data. - */ - virtual HistogramData histogramData() const = 0; - - /** - * @return The number of value since last clear. - */ - virtual std::size_t numberOfValuesSinceLastClear() const = 0; - - /** - * @return The smallest value since last clear as a string. - */ - virtual std::string smallestValueSinceLastClear() const = 0; - - /** - * @return The largest value since last clear as a string. - */ - virtual std::string largestValueSinceLastClear() const = 0; - - /** - * @return The average value since last clear as a string. - */ - virtual std::string averageValueSinceLastClear() const = 0; - - /** - * @brief Clears the values that are kept between successive clearRunningValues calls. - * These are: - * smallestValueSinceLastClear - * largestValueSinceLastClear - * averageValueSinceLastClear - */ - virtual void clearRunningValues() = 0; - - /** - * @return The ascii representation of the histogram. - */ - virtual std::string toString() const = 0; -}; - -/** - * @brief Make an ascii visualisation of the given histogram data. - * @param histogram The histogram to visualize. - */ -std::string visualizeHistogram(const IHistogram& histogram); - -/** - * @brief Stream operator for IHistogram instances. - */ -std::ostream& operator<<(std::ostream& os, const IHistogram& rhs); - -} // End namespace measurement -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_MEASUREMENT_IHISTOGRAM_H diff --git a/src/imqttclient.h b/src/imqttclient.h deleted file mode 100644 index d42c8ae..0000000 --- a/src/imqttclient.h +++ /dev/null @@ -1,146 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_IMQTTCLIENT_H -#define OSDEV_COMPONENTS_MQTT_IMQTTCLIENT_H - -// std -#include -#include -#include -#include - -// boost -#include - -// mlogic::client -#include "istatecallback.h" - -// mlogic::mqtt -#include "connectionstatus.h" -#include "credentials.h" -#include "mqttmessage.h" -#include "token.h" - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Interface that describes the minimal interface that a wrapper implementation needs to have in - * order to coorporate with the MqttClient class. - */ -class IMqttClient : public virtual IStateCallback -{ -public: - virtual ~IMqttClient() {} - - /** - * @brief Connect to the endpoint - * @param host The host name or ip address. - * @param port The port to use. - * @param credentials The credentials to use. - */ - virtual void connect(const std::string& host, int port, const Credentials& credentials) = 0; - - /** - * @brief Connect to the endpoint - * @param endpoint an uri endpoint description. - */ - virtual void connect(const std::string& endpoint) = 0; - - /** - * @brief Disconnect the client from the broker - */ - virtual void disconnect() = 0; - - /** - * @brief Publish a message with a given quality of service (0, 1 or 2). - * @param message The message to publish. - * @param qos The quality of service to use. - * @return The token that identifies the publication (used in the deliveryCompleteCallback). - */ - virtual Token publish(const MqttMessage& message, int qos) = 0; - - /** - * @brief Subscribe to a topic(filter). - * The combination of topic and qos makes a subscription unique on a wrapper client. When a topic has overlap - * with an existing subscription it is guaranteed that the given callback is only called once. - * @param topic The topic to subscribe to (can have wildcards). The topic can have overlap with existing topic subscriptions. - * @param qos The quality of service to use (0, 1 or 2). - * @param cb The callback that is called when messages are received for this subscription. - * @return A token that identifies the subscribe command. - */ - virtual Token subscribe(const std::string& topic, int qos, const std::function& cb) = 0; - - /** - * @brief Unsubscribe an existing subscription. - * The combination of topic and qos can occur on multiple wrapper clients. All subscriptions that match are unsubscribed. - * @param topic The topic to unsubscribe. - * @param qos The quality of service of the subscription (0, 1 or 2). - * @return A set of unsubscribe tokens identifying the unsubscribe commands. Usually the set will contain only one item. - */ - virtual std::set unsubscribe(const std::string& topic, int qos) = 0; - - /** - * @brief Wait for all commands to complete. - * @param waitFor The number of milliseconds to wait for completetion of all commands. - * @return True when all commands have completed in time or false if not. - */ - virtual bool waitForCompletion(std::chrono::milliseconds waitFor) const = 0; - - /** - * @brief Wait for a single command to complete. - * @param waitFor The number of milliseconds to wait for completetion of the command. - * @param token The token to wait for. - * @return True when the command has completed in time or false if not. - * @note Non existent tokens are also reported as completed. - */ - virtual bool waitForCompletion(std::chrono::milliseconds waitFor, const Token& token) const = 0; - - /** - * @brief Wait for commands to complete. - * This method enables the user to wait for a set of commands. An empty set means to wait for all commands to complete. - * @param waitFor The number of milliseconds to wait for completetion of all commands. - * @param tokens The tokens to wait for. An empty set means to wait for all commands to complete. - * @return True when the commands have completed in time or false if not. - * @note Non existent tokens are also reported as completed. - */ - virtual bool waitForCompletion(std::chrono::milliseconds waitFor, const std::set& tokens) const = 0; - - /** - * @brief Get the result of a command. - * @param token The token identifying the result. - * @return The command result when available. - */ - virtual boost::optional commandResult(const Token& token) const = 0; - - /** - * @return The endpoint uri. - */ - virtual std::string endpoint() const = 0; -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_IMQTTCLIENT_H diff --git a/src/imqttclientimpl.h b/src/imqttclientimpl.h deleted file mode 100644 index f32f737..0000000 --- a/src/imqttclientimpl.h +++ /dev/null @@ -1,169 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_IMQTTCLIENTIMPL_H -#define OSDEV_COMPONENTS_MQTT_IMQTTCLIENTIMPL_H - -// std -#include -#include -#include -#include -#include -#include - -// boost -#include - -// mlogic::mqtt -#include "connectionstatus.h" -#include "mqttmessage.h" - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Interface that describes the minimal interface that a wrapper implementation needs to have in - * order to coorporate with the MqttClient class. - */ -class IMqttClientImpl -{ -public: - virtual ~IMqttClientImpl(); - - /** - * @return id of this client. - */ - virtual std::string clientId() const = 0; - - /** - * @return The connection status of the wrapper. - */ - virtual ConnectionStatus connectionStatus() const = 0; - - /** - * @brief Connect the wrapper to the endpoint. - * @param wait A flag that indicates if the method should wait for a succesful connection. - * @return the operation token. - */ - virtual std::int32_t connect(bool wait) = 0; - - /** - * @brief Disconnect the wrapper. - * @param wait A flag that indicates if the method should wait for a succesful disconnect. When true the method will wait for the timeoutMs value plus some additional time. - * @param timeoutMs A timeout in milliseconds. The timeout is used to give in flight messages a chance to get delivered. After the timeout the disconnect command is sent. - * @return the operation token. - */ - virtual std::int32_t disconnect(bool wait, int timeoutMs) = 0; - - /** - * @brief Publish a message to an mqtt endpoint with a given quality of service. - * When the connection is in state reconnect the publish is saved so that it can be published later. - * @param message The message to send. - * @param qos The quality of service to use (0, 1 or 2). - * @return The message token. This token identifies the publication. - */ - virtual std::int32_t publish(const MqttMessage& message, int qos) = 0; - - /** - * @brief Publish messages when client is reconnected. - */ - virtual void publishPending() = 0; - - /** - * @brief Subscribe to a topic(filter). - * The combination of topic and qos makes a subscription unique. Subscriptions with the same topic filter - * but different qos are considered different, but they will overlap and cannot exist in the same wrapper. - * @param topic The topic to subscribe to (can have wildcards). The topic cannot have overlap with existing topic subscriptions. - * @param qos The quality of service to use (0, 1 or 2). - * @param cb The callback that is called when messages are received for this subscription. - * @return the operation token. - */ - virtual std::int32_t subscribe(const std::string& topic, int qos, const std::function& cb) = 0; - - /** - * @brief Resubscribe existing topicfilters. - * This method should be called only after a reconnect when subscriptions need to be reinstated. - */ - virtual void resubscribe() = 0; - - /** - * @brief Unsubscribe an existing subscription. - * The combination of topic and qos make a subscription unique. - * @param topic The topic to unsubscribe. - * @param qos The quality of service of the subscription (0, 1 or 2). - * @return the operation token. - */ - virtual std::int32_t unsubscribe(const std::string& topic, int qos) = 0; - - /** - * @brief Unsubscribe all subscriptions. - */ - virtual void unsubscribeAll() = 0; - - /** - * @brief Wait for commands to complete. - * This method enables the user to wait for a set of tokens. An empty set means wait for all operations to complete. - * @param waitFor The number of milliseconds to wait for completetion of all commands. - * @param tokens The set of tokens to wait for. - * @return The number of milliseconds left. - */ - virtual std::chrono::milliseconds waitForCompletion(std::chrono::milliseconds waitFor, const std::set& tokens) const = 0; - - /** - * @brief Check if a topic overlaps with existing topic subscriptions. - * @param topic The topic to test. - * @return true when overlap is detected, false otherwise. - */ - virtual bool isOverlapping(const std::string& topic) const = 0; - - /** - * @brief Check if a topic overlaps with existing topic subscriptions. - * @param topic The topic to test. - * @param[out] existingTopic Contains the topic on which overlap is detected. - * @return true when overlap is detected. The existingTopic contains the topic on which overlap is detected, false otherwise. - */ - virtual bool isOverlapping(const std::string& topic, std::string& existingTopic) const = 0; - - /** - * @return A vector with the pending operation tokens. - */ - virtual std::vector pendingOperations() const = 0; - - /** - * @return true when client wrapper has pending subscriptions, false otherwise. - */ - virtual bool hasPendingSubscriptions() const = 0; - - /** - * @return The operation result. - * @retval true operation has succeeded. - * @retval false operation has failed. - */ - virtual boost::optional operationResult(std::int32_t token) const = 0; -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_IMQTTCLIENTIMPL_H diff --git a/src/istatecallback.h b/src/istatecallback.h deleted file mode 100644 index 7b0db7f..0000000 --- a/src/istatecallback.h +++ /dev/null @@ -1,144 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_ISTATECALLBACK_H -#define OSDEV_COMPONENTS_MQTT_ISTATECALLBACK_H - -// std -#include - -// boost -#include - -namespace osdev { -namespace components { -namespace mqtt { - -/*! - * \brief Struct introduces a typed client identifier. - */ -struct ClientIdentifier -{ - /*! - * \brief Construct a ClientIdentifier - * \param id The client id. Default is empty. - */ - explicit ClientIdentifier(const std::string& id = "") - : m_id(id) - { - } - - const std::string m_id; ///< Client identifier -}; - -/*! - * \brief Enumeration of state codes. - * The states Unknown, CommunicationFailure, GeneralFailure, Good - * and Shutdown are used to communicate state about the server - * that is connected to. The states ConnectionFailure and Unregister - * communicate state about the client. - */ -enum class StateEnum -{ - Unknown, ///< State of underlying data source is unknown. - CommunicationFailure, ///< No communication with underlying data source. - GeneralFailure, ///< Some failure in the underlying data source. - Good, ///< Underlying data source is available. - Shutdown, ///< Underlying data source is shutting down. - ConnectionFailure, ///< Client connection has failed. - Unregister ///< Client is being unregistered. -}; - -/*! - * \brief Stream a StateEnum value - */ -std::ostream& operator<<(std::ostream& os, StateEnum rhs); - -/*! - * \brief Type for identifying state change callbacks. - */ -using StateChangeCallbackHandle = std::uint32_t; - -class IStateCallback; - -/*! - * \brief Create an id for an IStateCallback object. - * \param idStatic - Static part of the id (name of the class). - * \param clientId - Client identifier. Can contain information on how - * the client is used for instance. If the id is empty - * it is not used. - * \param idDynamic - Dynamic part (object address). - * \return identifier as string - */ -std::string createIdentifier(const std::string& idStatic, const ClientIdentifier& clientId, const IStateCallback* idDynamic); - -/*! - * \brief State callback interface. - * When a client implements this interface it can be registered on a - * server in order to handle state changes from the underlying data source. - * \note When the client is destroyed it is expected that it unregisters itself by calling - * clearAllStateChangeCallbacks(). - */ -class IStateCallback -{ -public: - using SigStateChange = boost::signals2::signal; - using SlotStateChange = SigStateChange::slot_type; - - virtual ~IStateCallback(); - - /*! - * \brief Get a string that identifies this interface. - */ - virtual std::string clientId() const = 0; - - /*! - * \brief Register a callback function that is called when the status changes. - * \param cb - The callback function to register. - * \return handle to the registered callback. - * \note Make sure that the callback function lives longer than the IStateCallback object. - * Otherwise unregister callback function before it is destroyed. - */ - virtual StateChangeCallbackHandle registerStateChangeCallback(const SlotStateChange& cb) = 0; - - /*! - * \brief Unregister a previously registered callback function. - * If the callback function was not registered nothing happens. - * \param handle - Identifies the callback function to to unregister. - */ - virtual void unregisterStateChangeCallback(StateChangeCallbackHandle handle) = 0; - - /*! - * \brief Remove all the registered callback functions. - */ - virtual void clearAllStateChangeCallbacks() = 0; - - /*! - * \brief retuns the latest state received. - */ - virtual StateEnum state() const = 0; -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_ISTATECALLBACK_H diff --git a/src/lockguard.h b/src/lockguard.h deleted file mode 100644 index b5ded35..0000000 --- a/src/lockguard.h +++ /dev/null @@ -1,228 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_LOCKGUARD_H -#define OSDEV_COMPONENTS_MQTT_LOCKGUARD_H - -// std -#include - -// osdev::components::mqtt::measurement -#include "measure.h" -#include "utils.h" - -/** - * @brief Enable the lock measurements when macro MEASURE_LOCKS is defined. - * If the macro is not defined the NOP versions of the measure macros are used. - */ -#ifdef MEASURE_LOCKS -#define OSDEV_COMPONENTS_MEASURELOCK OSDEV_COMPONENTS_MEASUREMENT_MEASURE -#define OSDEV_COMPONENTS_MEASURELOCK_SPECIFIC OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC -#else -#define OSDEV_COMPONENTS_MEASURELOCK OSDEV_COMPONENTS_MEASUREMENT_MEASURE_NOP -#define OSDEV_COMPONENTS_MEASURELOCK_SPECIFIC OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC_NOP -#endif - -/** - * @brief Create a lockguard for a given mutex in a specific context. - * @param mutexVariableName The name of the mutex. - * @param mutexObtainer The mutex to lock. This can also be a function that returns a mutex. - * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances. - */ -#define OSDEV_COMPONENTS_LOCKGUARD_OBTAINER(mutexVariableName, mutexObtainer) \ - OSDEV_COMPONENTS_MEASURELOCK(LOCKGUARD, std::lock_guard Lock__Guard__##mutexVariableName##__(mutexObtainer), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) \ - osdev::components::mqtt::apply_unused_parameters(Lock__Guard__##mutexVariableName##__); - -/** - * @brief Create a lockguard for a given mutex in a specific context. - * @param mutexVariableName The name of the mutex. - * @param mutexObtainer The mutex to lock. This can also be a function that returns a mutex. - * @param id The id that identifies this specific lock guard. Used for measuring timings. - * If MEASURE_LOCKS is enabled this method uses a specific histogram instance to record the timing of obtaining this lock. - */ -#define OSDEV_COMPONENTS_LOCKGUARD_SPECIFIC_OBTAINER(mutexVariableName, mutexObtainer, id) \ - OSDEV_COMPONENTS_MEASURELOCK_SPECIFIC(id, LOCKGUARD, std::lock_guard Lock__Guard__##mutexVariableName##__(mutexObtainer), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) \ - osdev::components::mqtt::apply_unused_parameters(Lock__Guard__##mutexVariableName##__); - -/** - * @brief Create a lockguard for a given recursive mutex in a specific context. - * @param mutexVariableName The name of the mutex. - * @param mutexObtainer The mutex to lock. This can also be a function that returns a mutex. - * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances. - */ -#define OSDEV_COMPONENTS_RECURSIVELOCKGUARD_OBTAINER(mutexVariableName, mutexObtainer) \ - OSDEV_COMPONENTS_MEASURELOCK(RECURSIVELOCKGUARD, std::lock_guard Lock__Guard__##mutexVariableName##__(mutexObtainer), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) \ - osdev::components::mqtt::apply_unused_parameters(Lock__Guard__##mutexVariableName##__); - -/** - * @brief Create a lockguard for a given recursive mutex in a specific context. - * @param mutexVariableName The name of the mutex. - * @param mutexObtainer The mutex to lock. This can also be a function that returns a mutex. - * @param id The id that identifies this specific lock guard. Used for measuring timings. - * If MEASURE_LOCKS is enabled this method uses a specific histogram instance to record the timing of obtaining this lock. - */ -#define OSDEV_COMPONENTS_RECURSIVELOCKGUARD_SPECIFIC_OBTAINER(mutexVariableName, mutexObtainer, id) \ - OSDEV_COMPONENTS_MEASURELOCK_SPECIFIC(id, RECURSIVELOCKGUARD, std::lock_guard Lock__Guard__##mutexVariableName##__(mutexVariableName), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) \ - osdev::components::mqtt::apply_unused_parameters(Lock__Guard__##mutexVariableName##__); - -/** - * @brief Create a lockguard for a given bare mutex in a specific context. - * @param mutexVariableName The name of the mutex. - * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances. - */ -#define OSDEV_COMPONENTS_LOCKGUARD(mutexVariableName) \ - OSDEV_COMPONENTS_LOCKGUARD_OBTAINER(mutexVariableName, mutexVariableName) - -/** - * @brief Create a lockguard for a given bare mutex in a specific context. - * @param mutexVariableName The name of the mutex. - * @param id The id that identifies this specific lock guard. Used for measuring timings. - * If MEASURE_LOCKS is enabled this method uses a specific histogram instance to record the timing of obtaining this lock. - */ -#define OSDEV_COMPONENTS_LOCKGUARD_SPECIFIC(mutexVariableName, id) \ - OSDEV_COMPONENTS_LOCKGUARD_SPECIFIC_OBTAINER(mutexVariableName, mutexVariableName, id) - -/** - * @brief Create a lockguard for a given bare recursive mutex in a specific context. - * @param mutexVariableName The name of the mutex. - * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances. - */ -#define OSDEV_COMPONENTS_RECURSIVELOCKGUARD(mutexVariableName) \ - OSDEV_COMPONENTS_RECURSIVELOCKGUARD_OBTAINER(mutexVariableName, mutexVariableName) - -/** - * @brief Create a lockguard for a given bare recursive mutex in a specific context. - * @param mutexVariableName The name of the mutex. - * @param id The id that identifies this specific lock guard. Used for measuring timings. - * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances. - */ -#define OSDEV_COMPONENTS_RECURSIVELOCKGUARD_SPECIFIC(mutexVariableName, id) \ - OSDEV_COMPONENTS_RECURSIVELOCKGUARD_SPECIFIC_OBTAINER(mutexVariableName, mutexVariableName, id) - -/** - * @brief Defines the lock name that is used by the OSDEV_COMPONENTS_UNIQUELOCK_* macros - */ -#define OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName) Unique__Lock__##mutexVariableName##__ - -/** - * @brief Create a uniqeue lock for a given mutex. - * @param mutexVariableName The name of the mutex. - * @param mutexObtainer The mutex to lock. This can also be a function that returns a mutex. - * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances. - */ -#define OSDEV_COMPONENTS_UNIQUELOCK_CREATE_OBTAINER(mutexVariableName, mutexObtainer) \ - OSDEV_COMPONENTS_MEASURELOCK(UNIQUELOCK_CREATE, std::unique_lock OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName)(mutexObtainer), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) \ - osdev::components::mqtt::apply_unused_parameters(OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName)); - -/** - * @brief Create a uniqeue lock for a given mutex. - * @param mutexVariableName The name of the mutex. - * @param mutexObtainer The mutex to lock. This can also be a function that returns mutex. - * @param id The id that identifies this specific lock guard. Used for measuring timings. - * If MEASURE_LOCKS is enabled this method uses a specific histogram instance to record the timing of obtaining this lock. - */ -#define OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC_OBTAINER(mutexVariableName, mutexObtainer, id) \ - OSDEV_COMPONENTS_MEASURELOCK_SPECIFIC(id, UNIQUELOCK_CREATE, std::unique_lock OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName)(mutexObtainer), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) \ - osdev::components::mqtt::apply_unused_parameters(OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName)); - -/** - * @brief Create a uniqeue lock for a given bare mutex. - * @param mutexVariableName The name of the mutex. - * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances. - */ -#define OSDEV_COMPONENTS_UNIQUELOCK_CREATE(mutexVariableName) \ - OSDEV_COMPONENTS_UNIQUELOCK_CREATE_OBTAINER(mutexVariableName, mutexVariableName) - -/** - * @brief Create a uniqeue lock for a given bare mutex. - * @param mutexVariableName The name of the mutex. - * @param id The id that identifies this specific unique lock. Used for measuring timings. - * If MEASURE_LOCKS is enabled this method uses a specific histogram instance to record the timing of obtaining this lock. - */ -#define OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC(mutexVariableName, id) \ - OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC_OBTAINER(mutexVariableName, mutexVariableName, id) - -/** - * @brief Lock a given uniqeue lock. - * @param mutexVariableName The name of the mutex from which the lockname is derived. - * If MEASURE_LOCKS is enabled this method uses a static histogram to record the timing of obtaining the lock for all instances. - */ -#define OSDEV_COMPONENTS_UNIQUELOCK_LOCK(mutexVariableName) \ - OSDEV_COMPONENTS_MEASURELOCK(UNIQUELOCK_LOCK, OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName).lock(), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) - -/** - * @brief Lock a given uniqeue lock. - * @param mutexVariableName The name of the mutex from which the lockname is derived. - * @param id The id that identifies this specific unique lock guard. Used for measuring timings. - * If MEASURE_LOCKS is enabled this method uses a specific histogram instance to record the timing of obtaining this lock. - */ -#define OSDEV_COMPONENTS_UNIQUELOCK_LOCK_SPECIFIC(mutexVariableName, id) \ - OSDEV_COMPONENTS_MEASURELOCK_SPECIFIC(id, UNIQUELOCK_LOCK, OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName).lock(), osdev::components::mqtt::measure_locking_tag, osdev::components::mqtt::MeasureLockingValue, 100) - -/** - * @brief Unlock a given uniqeue lock. - * @param mutexVariableName The name of the mutex from which the lockname is derived. - */ -#define OSDEV_COMPONENTS_UNIQUELOCK_UNLOCK(mutexVariableName) \ - OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName).unlock(); - -/** - * @brief Unlock a given uniqeue lock. - * @param mutexVariableName The name of the mutex from which the lockname is derived. - * @param id The id that identifies this specific unique lock guard. Can be used for measuring timings. - */ -#define OSDEV_COMPONENTS_UNIQUELOCK_UNLOCK_SPECIFIC(mutexVariableName, id) \ - OSDEV_COMPONENTS_UNIQUELOCK(mutexVariableName).unlock(); - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Context tag type. - */ -struct measure_locking_tag -{ -}; - -/** - * @brief Type for measuring lock timings - * The unit is microseconds and the values are expected between 0 and 100 microseconds. - * This type is used to construct a timing histogram. - */ -struct MeasureLockingValue -{ - /** - * @brief The value type of the timing value. - */ - using value_type = std::chrono::microseconds; - - const value_type minValue = value_type(0); ///< Constant mininum value. - const value_type maxValue = value_type(100); ///< Constant maximum value. - - const char* unit = "us"; ///< The value unit. -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_LOCKGUARD_H diff --git a/src/macrodefs.h b/src/macrodefs.h deleted file mode 100644 index 8cd1cdd..0000000 --- a/src/macrodefs.h +++ /dev/null @@ -1,53 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MACRODEFS_H -#define OSDEV_COMPONENTS_MQTT_MACRODEFS_H - -#include - -#include "compiletimestring.h" - -/// @brief Helper macro to stringify a symbol -#define OSDEV_COMPONENTS_STRINGIFY(x) #x - -/// @brief Use this macro to safely stringify a symbol -/// This will also work for nested macro's -#define OSDEV_COMPONENTS_TOSTRING(x) OSDEV_COMPONENTS_STRINGIFY(x) - -/// @brief Helper macro to combine two symbols -#define OSDEV_COMPONENTS_COMBINER(x, y) x##y - -/// @brief Use this macro to safely combine two symbols -/// This will also work for nested macro's -#define OSDEV_COMPONENTS_COMBINE(x, y) OSDEV_COMPONENTS_COMBINER(x, y) - -/// @brief Macro that reduces a path to the basename -#define OSDEV_COMPONENTS_MANAGEDBASEFILENAME \ - OSDEV_COMPONENTS_CSTRING_BOUNDED(__FILE__, osdev::components::rfind(__FILE__, '/') + 1, sizeof(__FILE__) - 1) - -/// @brief Compiletime test if an instance derives from a certain base class -#define OSDEV_COMPONENTS_DERIVESFROM(derived, baseType) \ - static_assert(std::is_base_of::type>::type>::value, \ - OSDEV_COMPONENTS_TOSTRING(derived) " must be derived from " OSDEV_COMPONENTS_TOSTRING(baseType)) - -#endif //OSDEV_COMPONENTS_MQTT_MACRODEFS_H diff --git a/src/measure.h b/src/measure.h deleted file mode 100644 index f2520af..0000000 --- a/src/measure.h +++ /dev/null @@ -1,158 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MEASUREMENT_MEASURE_H -#define OSDEV_COMPONENTS_MQTT_MEASUREMENT_MEASURE_H - -#include "macrodefs.h" -#include "histogramprovider.h" -#include "timemeasurement.h" - -namespace osdev { -namespace components { -namespace mqtt { -namespace measurement { - -inline std::string createId(const std::string& operationName, const std::string& dynamicId) -{ - return operationName + (dynamicId.empty() ? std::string{} : std::string(" ") + dynamicId); -} - -} // End namespace measurement -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#define OSDEV_COMPONENTS_MEASUREMENT_LOCATION \ - (OSDEV_COMPONENTS_MANAGEDBASEFILENAME + OSDEV_COMPONENTS_CSTRING(":" OSDEV_COMPONENTS_TOSTRING(__LINE__))).chars - -/** - * @brief Macro helper that sets up a TimeMeasurement on an operation. - * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements. - * @param operationName The name of the operation. This is part of the id under which the measurements are registered. - * @param dynamicId The dynamic identification of this measurement. This id makes different instances of the same operation unique. - * The dynamicId must be an expression that yields a string like object. The dynamic id is part of the id under which the - * measurements are registered. - * @param operation The operation to perform. - * @param context The context tag used to identify the HistogramProvider. - * @param valueType A datatype that can be used by HistogramProvider. - * @param numBuckets The number of buckets to use for the histogram. - * @param boolOnDestroy Boolean value that makes the measurement measure on destruction. - */ -#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_HELPER(operationName, dynamicId, operation, context, valueType, numBuckets, boolOnDestroy) \ - static const std::string OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(ID__, __LINE__), __) = \ - std::string(OSDEV_COMPONENTS_CSTRING(#operationName).chars); \ - osdev::components::mqtt::measurement::TimeMeasurement OSDEV_COMPONENTS_COMBINE( \ - OSDEV_COMPONENTS_COMBINE(TM__, __LINE__), __)( \ - osdev::components::mqtt::measurement::createId(OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(ID__, __LINE__), __), dynamicId), \ - [](const std::string& id, std::chrono::steady_clock::time_point, std::chrono::microseconds, std::chrono::microseconds sinceLast) { \ - osdev::components::mqtt::measurement::HistogramProvider::instance().addValue(id, sinceLast); \ - }, \ - boolOnDestroy); \ - operation; - -/** - * @brief Make a measurement before and after an operation. - * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements. - * @param operationName The name under which the measurements are registered. - * @param operation The operation to perform. - * @param context The context tag used to identify the HistogramProvider. - * @param valueType A datatype that can be used by HistogramProvider. - * @param numBuckets The number of buckets to use for the histogram. - */ -#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE(operationName, operation, context, valueType, numBuckets) \ - OSDEV_COMPONENTS_MEASUREMENT_MEASURE_HELPER(operationName, OSDEV_COMPONENTS_MEASUREMENT_LOCATION, operation, context, valueType, numBuckets, false) \ - OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(TM__, __LINE__), __).measure(); - -/** - * @brief Make a measurement on a return operation. - * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements. - * @param operationName The name under which the measurements are registered. - * @param operation The operation to perform. - * @param context The context tag used to identify the HistogramProvider. - * @param valueType A datatype that can be used by HistogramProvider. - * @param numBuckets The number of buckets to use for the histogram. - */ -#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_ONDESTROY(operationName, operation, context, valueType, numBuckets) \ - OSDEV_COMPONENTS_MEASUREMENT_MEASURE_HELPER(operationName, OSDEV_COMPONENTS_MEASUREMENT_LOCATION, operation, context, valueType, numBuckets, true) - -/** - * @brief Make a measurement on a return operation. - * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements. - * @param operationName The name under which the measurements are registered. - * @param dynamicId An extra identification to discern between instances of the same operation. The dynamicId must be an expression that - * yields a string like object. - * @param operation The operation to perform. - * @param context The context tag used to identify the HistogramProvider. - * @param valueType A datatype that can be used by HistogramProvider. - * @param numBuckets The number of buckets to use for the histogram. - */ -#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC_ONDESTROY(operationName, dynamicId, operation, context, valueType, numBuckets) \ - OSDEV_COMPONENTS_MEASUREMENT_MEASURE_HELPER(operationName, dynamicId, operation, context, valueType, numBuckets, true) - -/** - * @brief Nop version that only performs the operation. - */ -#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_ONDESTROY_NOP(operationName, operation, context, valueType, numBuckets) operation; - -/** - * @brief Nop version that only performs the operation. - */ -#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_NOP(operationName, operation, context, valueType, numBuckets) operation; - -/** - * @brief Nop version that only performs the operation. - */ -#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC_ONDESTROY_NOP(operationName, dynamicId, operation, context, valueType, numBuckets) operation; - -/** - * @brief Make a measurement before and after an operation for a specific instance of the operation. - * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements. - * @param dynamicId An extra identification to discern between instances of the same operation. - * @param operationName The name under which the measurements are registered. - * @param operation The operation to perform. - * @param context The context tag used to identify the HistogramProvider. - * @param valueType A datatype that can be used by HistogramProvider. - * @param numBuckets The number of buckets to use for the histogram. - */ -#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC(dynamicId, operationName, operation, context, valueType, numBuckets) \ - auto OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(IDPREFIX__, __LINE__), __) = \ - OSDEV_COMPONENTS_MANAGEDBASEFILENAME + OSDEV_COMPONENTS_CSTRING(":" OSDEV_COMPONENTS_TOSTRING(__LINE__) ", function "); \ - auto OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(IDPOSTFIX__, __LINE__), __) = OSDEV_COMPONENTS_CSTRING(", " #operationName " "); \ - static const std::string OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(ID__, __LINE__), __) = \ - std::string(OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(IDPREFIX__, __LINE__), __).chars) + \ - std::string(__func__) + \ - OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(IDPOSTFIX__, __LINE__), __).chars; \ - osdev::components::mqtt::measurement::TimeMeasurement OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(TM__, __LINE__), __)( \ - OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(ID__, __LINE__), __) + dynamicId, \ - [](const std::string& id, std::chrono::steady_clock::time_point, std::chrono::microseconds, std::chrono::microseconds sinceLast) { \ - osdev::components::mqtt::measurement::HistogramProvider::instance().addValue(id, sinceLast); \ - }, \ - false); \ - operation; \ - OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(TM__, __LINE__), __).measure(); - -/** - * @brief Nop version that only performs the operation. - */ -#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC_NOP(dynamicId, operationName, operation, context, valueType, numBuckets) operation; - -#endif // OSDEV_COMPONENTS_MEASUREMENT_MEASUREMENT_H diff --git a/src/metaprogrammingdefs.h b/src/metaprogrammingdefs.h deleted file mode 100644 index 57346ac..0000000 --- a/src/metaprogrammingdefs.h +++ /dev/null @@ -1,186 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_METAPROGRAMMINGDEFS_H -#define OSDEV_COMPONENTS_MQTT_METAPROGRAMMINGDEFS_H - -#include // for std::size_t -#include -#include - -#include "utils.h" - -/** - * @brief Create a type trait that checks for the existence of a member in a struct. - * @param memberName The member name - * - * A call such as OSDEV_COMPONENTS_HASMEMBER_TRAIT(onSuccess) leads to a type trait has_onSuccess. - */ -#define OSDEV_COMPONENTS_HASMEMBER_TRAIT(memberName) \ - template \ - struct has_##memberName : public std::false_type \ - { \ - }; \ - \ - template \ - struct has_##memberName().memberName)>> : public std::true_type \ - { \ - }; - -/** - * @brief Create a type trait that checks for the existence of a member method with no parameters. - * @param methodName The method name - * @param postfix An extra postfix that is added to the type trait struct. - * This makes it possible create type traits that check for - * overloaded methods. - * - * A call such as OSDEV_COMPONENTS_HASMETHOD_TRAIT(capacity,1) leads to a type trait has_capacity1. - */ -#define OSDEV_COMPONENTS_HASMETHOD_TRAIT(methodName, postfix) \ - template \ - struct has_##methodName##postfix : public std::false_type \ - { \ - }; \ - \ - template \ - struct has_##methodName##postfix().methodName())>> : public std::true_type \ - { \ - }; - -/** - * @brief Create a type trait that checks for the existence of a member method with 1 or more parameters. - * @param methodName The method name - * @param postfix An extra postfix that is added to the type trait struct. - * This makes it possible create type traits that check for - * overloaded methods. - * @param ... Variable number of arguments. These can be values, but also declval constructs such as std::declval(). - * - * A call such as OSDEV_COMPONENTS_HASMETHODWP_TRAIT(capacity,,"string") leads to a type trait has_capacity that - * checks for the existence of a method capacity that accepts a const char pointer. - */ -#define OSDEV_COMPONENTS_HASMETHODWP_TRAIT(methodName, postfix, ...) \ - template \ - struct has_##methodName##postfix : public std::false_type \ - { \ - }; \ - \ - template \ - struct has_##methodName##postfix().methodName(__VA_ARGS__))>> : public std::true_type \ - { \ - }; - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Used to detect ill formed types in a SFINAE context. - * If the parameter pack Ts contains an invalid type, struct make_void cannot be expanded. - */ -template -struct make_void -{ - using type = void; -}; - -/** - * @brief Introduced in c++17 (but will also work in c++11) - */ -template -using void_t = typename make_void::type; - -/// @brief Build an index list that can be used to traverse another list (f.i. a char array). -/// Recursive definition. -/// Start with an empty indices list. Build the indices list recursively. -/// upper-1, indices... becomes the indices... in the next recursive call. -template class meta_functor, std::size_t... Is> -struct apply_bounded_range -{ - typedef typename apply_bounded_range::result result; -}; - -/// @brief Terminator of apply_bounded_range. -/// Terminates when the upper bound (which runs) is equal to the lower bound. -template class meta_functor, std::size_t... Is> -struct apply_bounded_range -{ - typedef typename meta_functor::result result; -}; - -/** - * @brief Helper method to perform static_assert on the expected count of a parameter pack. - */ -template -void static_assert_count() -{ - constexpr std::size_t actualCount = sizeof...(Args); - static_assert(expectedCount == actualCount, "Unexpected parameter count."); -} - -/** - * @brief Helper method to convert a type to std::string. - * @param d_first The output iterator to write the converted string to. - * @param arg The argument to convert to std::string. - */ -template -int apply_to_string_one(OutputIt d_first, const T& arg) -{ - std::ostringstream ss; - ss << arg; - *d_first++ = ss.str(); - return 0; -} - -/** - * @brief Converts all parameters to std::string. - * @param d_first The output iterator to write the converted strings to. - * @param args The arguments to convert to std::string. - */ -template -void apply_to_string(OutputIt d_first, Args&&... args) -{ - // d_first is not used when args parameter pack is empty. - apply_unused_parameters(d_first); - - // We need to use the expand_type trick in order to be able to use an initializer list - // so that we can call the method for every variadic argument - using expand_type = int[]; - // Array must be initialized with values. Add a single value so that the array can be initialized when the parameter pack is empty. - expand_type et{ 0, apply_to_string_one(d_first, std::forward(args))... }; - apply_unused_parameters(et); -} - -/** - * @brief Helper that removes const, volatile and reference from a type. - * @note Defined in std C++20. - */ -template -struct remove_cvref -{ - using type = typename std::remove_cv::type>::type; -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_METAPROGRAMMINGDEFS_H diff --git a/src/mqttclient.h b/src/mqttclient.h deleted file mode 100644 index c160b02..0000000 --- a/src/mqttclient.h +++ /dev/null @@ -1,233 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MQTTCLIENT_H -#define OSDEV_COMPONENTS_MQTT_MQTTCLIENT_H - -// std -#include -#include -#include -#include -#include -#include - -// osdev::components::mqtt -#include "synchronizedqueue.h" -#include "istatecallback.h" -#include "serverstate.h" - -#include "imqttclient.h" - -namespace osdev { -namespace components { -namespace mqtt { - -// Forward definition -class IMqttClientImpl; - -class MqttClient : public virtual IMqttClient -{ -public: - /*! - * \brief Construct an instance of the MqttClient. - * \param clientId The client identification used in the connection to the mqtt broker. - * \param deliveryCompleteCallback Optional callback used to signal completion of a publication. - */ - MqttClient( const std::string& clientId, const std::function& deliveryCompleteCallback = std::function{}); - virtual ~MqttClient() override; - - // Non copyable, non movable - MqttClient(const MqttClient&) = delete; - MqttClient& operator=(const MqttClient&) = delete; - MqttClient(MqttClient&&) = delete; - MqttClient& operator=(MqttClient&&) = delete; - - /** - * @see IStateCallback - */ - virtual std::string clientId() const override; - - /** - * @see IStateCallback - */ - virtual StateChangeCallbackHandle registerStateChangeCallback(const SlotStateChange& cb) override; - - /** - * @see IStateCallback - */ - virtual void unregisterStateChangeCallback(StateChangeCallbackHandle handle) override; - - /** - * @see IStateCallback - */ - virtual void clearAllStateChangeCallbacks() override; - - /** - * @see IStateCallback - */ - virtual StateEnum state() const override; - - // MqttClient interface - - /** - * @see IMqttClient - */ - virtual void connect(const std::string& host, int port, const Credentials& credentials) override; - - /** - * @see IMqttClient - */ - virtual void connect(const std::string& endpoint) override; - - /** - * @see IMqttClient - */ - virtual void disconnect() override; - - /** - * @see IMqttClient - */ - virtual Token publish(const MqttMessage& message, int qos) override; - - /** - * @see IMqttClient - * When an overlapping subscription is detected a new connection has to be created. This is a synchronous action - * and this method will wait for the connection to be up. - */ - virtual Token subscribe(const std::string& topic, int qos, const std::function& cb) override; - - /** - * @see IMqttClient - */ - virtual std::set unsubscribe(const std::string& topic, int qos) override; - - /** - * @see IMqttClient - */ - virtual bool waitForCompletion(std::chrono::milliseconds waitFor) const override; - - /** - * @see IMqttClient - */ - virtual bool waitForCompletion(std::chrono::milliseconds waitFor, const Token& token) const override; - - /** - * @see IMqttClient - */ - virtual bool waitForCompletion(std::chrono::milliseconds waitFor, const std::set& tokens) const override; - - /** - * @see IMqttClient - */ - virtual boost::optional commandResult(const Token& token) const override; - - /** - * @see IMqttClient - */ - virtual std::string endpoint() const override; - -private: - /*! - * \brief Callback used to pick up the connection status of the wrappers. - * \param id The client id. - * \param cs The connection status. - */ - void connectionStatusChanged( const std::string& id, ConnectionStatus cs ); - - /*! - * \brief Callback for handling delivery complete. - * \param clientId The identifier of the client on which the publish command is executed. - * \param token The token identifies the publish command. - */ - void deliveryComplete( const std::string& clientId, std::int32_t token ); - - /** - * \brief Wait for commands to complete including active tokens in this client. - * The interface mutex is not locked by this method. - * First wait for client commands to complete (use method waitForCompletionInternalClients) - * and then wait for publish delivery callbacks to complete. - * \param clients - Vector with client wrapper pointers that need to be waited on. - * \param[in,out] waitFor - The number of milliseconds to wait for completetion of all commands and delivery callbacks. - * \param tokens - The tokens to wait for. An empty set means to wait for all commands on all clients to complete - * including all publish delivery callbacks. - * \return True when commands have completed in time including delivery callbacks or false if not. - */ - bool waitForCompletionInternal(const std::vector& clients, std::chrono::milliseconds waitFor, const std::set& tokens) const; - - /** - * \brief Wait for commands on the wrapper clients to complete. - * The interface mutex is not locked by this method. - * \param clients - Vector with client wrapper pointers that need to be waited on. - * \param[in,out] waitFor - The number of milliseconds to wait for completetion of all commands. - * On return waitFor contains the time left. - * \param tokens - The tokens to wait for. An empty set means to wait for all commands - * on all clients to complete. - * \return True when all commands have completed in time or false if not. - */ - bool waitForCompletionInternalClients(const std::vector& clients, std::chrono::milliseconds& waitFor, const std::set& tokens) const; - - /** - * @brief Determine the state of this client based on the connection statusses of its client wrappers. - * The states this client can communicate are: - * Unknown : When at least one wrapper is in a different state then Connected or ReconnectInProgress or when no wrappers are available. - * Good : When all wrappers are connected. - * ConnectionFailure : When at least one wrapper attempts reconnection. - * Unregister : When the serverstate instance is destroyed. - * - * The other states are about the information providers to the mqtt broker (the publishers) and we cannot say anything about them here. - * The state "Good" is the exception. This state means in this case that this clients connection is ok and not that the underlying data - * source (publisher) is ok. - * - * @param connectionStates A vector with the connection statusses of all client wrappers. - */ - StateEnum determineState(const std::vector& connectionStates); - - /** - * @brief Add an event to the synchronized queue. - * @param ev A function object that performs work in the context of this class. - */ - void pushEvent(std::function ev); - - /** - * @brief Worker method that executes the events. - */ - void eventHandler(); - - mutable std::mutex m_interfaceMutex; ///< Makes the interface mutual exclusive - mutable std::mutex m_internalMutex; ///< Protect the internal state. - std::string m_endpoint; ///< The endpoint uri. - std::string m_clientId; ///< The main client identification. - std::set m_activeTokens; ///< Set with active command tokens. Callbacks still need to be made for these tokens. - mutable std::condition_variable m_activeTokensCV; ///< Wait on a condition to become true w.r.t. the active token set. - std::function m_deliveryCompleteCallback; ///< Optional callback for publish completion. - ServerState m_serverState; ///< Records the state of the connection to the broker that this client is connected to. - std::unique_ptr m_principalClient; ///< The main wrapper client. - std::vector> m_additionalClients; ///< A vector of additional wrapper clients. - SynchronizedQueue> m_eventQueue; ///< Synchronized queue for scheduling additional work. - std::thread m_workerThread; ///< A worker thread that is used to perform actions that cannot be done on the callback threads. -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_MQTTCLIENT_H diff --git a/src/mqttfailure.h b/src/mqttfailure.h deleted file mode 100644 index 4f48a84..0000000 --- a/src/mqttfailure.h +++ /dev/null @@ -1,79 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MQTTFAILURE_H -#define OSDEV_COMPONENTS_MQTT_MQTTFAILURE_H - -// std -#include - -// paho -#include - -namespace osdev { -namespace components { -namespace mqtt { - -/*! - * \brief Class for paho mqtt failure response data. - */ -class MqttFailure -{ -public: - /*! - * \brief Construct MqttFailure instance by copying information from the paho failure struct. - * \param data Paho response failure data. - */ - explicit MqttFailure(const MQTTAsync_failureData* data); - - /*! - * \return The command token - */ - MQTTAsync_token token() const { return m_token; } - - /*! - * \return The failure code. - */ - int code() const { return m_code; } - - /*! - * \return The failure message. - * \retval "no message" when no message is available. - */ - const std::string& message() const { return m_message; } - - /*! - * \return string interpretation of the code. - * \note negative codes are interpreted as paho error codes. - */ - std::string codeToString() const; - -private: - MQTTAsync_token m_token; ///< Command token. - int m_code; ///< Failure code. - std::string m_message; ///< Optional message. Equal to "no message" when message is not available. -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_MQTTFAILURE_H diff --git a/src/mqttidgenerator.h b/src/mqttidgenerator.h deleted file mode 100644 index 8baf591..0000000 --- a/src/mqttidgenerator.h +++ /dev/null @@ -1,68 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MLOGICIDGENERATOR_H -#define OSDEV_COMPONENTS_MQTT_MLOGICIDGENERATOR_H - -// std -#include - -// osdev::components::mqtt -#include "commondefs.h" - -namespace osdev { -namespace components { -namespace mqtt { - -class MqttIdGenerator -{ -public: - /** - * @brief Generates a new MqttId, which is guaranteed to be unique. - * @return A new unique MqttId. - */ - static MqttId generate(); - - /** - * @brief Returns an MqttId that represents null. - * @return An MqttId that represents null. - */ - static MqttId nullId(); - - /** - * @brief Returns an MqttId based on a namespace uuid and a given string. - * @param namespaceUuid The namespace in which the MqttId is generated. - * @param name The name for which an MqttId is generated. - */ - static MqttId nameId(MqttId namespaceUuid, const std::string& name); - - /** - * @brief Returns an MqttId in the MQTT namespace for a given string. - * @param name The name for which an MqttId is generated. - */ - static MqttId nameId(const std::string& name); -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_MLOGICIDGENERATOR_H diff --git a/src/mqttmessage.h b/src/mqttmessage.h deleted file mode 100644 index 1a6e9c3..0000000 --- a/src/mqttmessage.h +++ /dev/null @@ -1,89 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MQTTMESSAGE_H -#define OSDEV_COMPONENTS_MQTT_MQTTMESSAGE_H - -// std -#include - -// paho-c -#include - -namespace osdev { -namespace components { -namespace mqtt { - -/*! - * @brief Class for paho mqtt message data - */ -class MqttMessage -{ -public: - /*! - * @brief Construct empty MqttMessage instance - */ - MqttMessage(); - - /*! - * @brief Construct MqttMessage instance by copying information gfrom the paho message struct - * @param topic - Paho topic data (copied) - * @param msg - Paho message data (copied) - */ - MqttMessage( const std::string &topic, const MQTTAsync_message &msg ); - - /*! - * @brief Construct MqttMessage instance. - * @param topic - Topic String - * @param retainedFlag - Flag that indicates if message is retained - * @param duplicateFlag - Flag that indicates if message is duplicate. - * @param thePayload - The message itself. - */ - MqttMessage( const std::string &topic, bool retainedFlag, bool duplicateFlag, std::string thePayload ); - - /*! @return The retained flag value. */ - bool retained() const { return m_retained; } - - /*! @return The duplicate flag value */ - bool duplicate() const { return m_duplicate; } - - /*! @return The topic on which the message is received. */ - const std::string& topic() const { return m_topic; } - - /*! @return The message payload. */ - const std::string& payload() const { return m_payload; } - - /*! @return This instance as a paho message */ - MQTTAsync_message toAsyncMessage() const; - -private: - bool m_retained; ///< Retained flag. Not all brokers communicate this flag correctly. (emqx does not, mosquitto does.) - bool m_duplicate; ///< Duplicate flag ( for qos 1? ) - std::string m_topic; ///< The topic on which the message is recieved. - std::string m_payload; ///< The actual message data. -}; - - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_MQTTMESSAGE_H diff --git a/src/mqttstream.h b/src/mqttstream.h deleted file mode 100644 index e9d9a94..0000000 --- a/src/mqttstream.h +++ /dev/null @@ -1,130 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MQTTSTREAM_H -#define OSDEV_COMPONENTS_MQTT_MQTTSTREAM_H - -// This header is used in conjunction with mlogic/commmon/stringify.h to get output streaming of stl container types. -// The streaming operators are not suitable for marshalling because type information is lost! - -// std -#include -#include -#include -#include -#include -#include -#include - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Streams a container with content for which stream operators are available. - * @note This function is meant for printing and not for marshalling! - * @tparam Open The container opening character. - * @tparam Close The container closing character. - * @tparam T The container type. - * @param os The stream to use. - * @param rhs The container that is to be streamed. - * @param sep The field separator. Default is ", " - * @return reference to the stream object. - */ -template -std::ostream& streamContainer(std::ostream& os, const T& rhs, const std::string& sep = ", ") -{ - os << Open; - for (auto cit = rhs.cbegin(); rhs.cend() != cit; ++cit) { - os << *cit; - if (std::next(cit) != rhs.end()) { - os << sep; - } - } - os << Close; - - return os; -} - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -namespace std { - -/** - * @brief Streams a list that contains values for which an output stream operator is available. - */ -template -std::ostream& operator<<(std::ostream& os, const std::list& rhs) -{ - return osdev::components::mqtt::streamContainer<'<', '>'>(os, rhs); -} - -/** - * @brief Streams an array that contains values for which an output stream operator is available. - */ -template -std::ostream& operator<<(std::ostream& os, const std::array& rhs) -{ - return osdev::components::mqtt::streamContainer<'[', ']'>(os, rhs); -} - -/** - * @brief Streams a vector that contains values for which an output stream operator is available. - */ -template -std::ostream& operator<<(std::ostream& os, const std::vector& rhs) -{ - return osdev::components::mqtt::streamContainer<'[', ']'>(os, rhs); -} - -/** - * @brief Streams a set that contains values for which an output stream operator is available. - */ -template -std::ostream& operator<<(std::ostream& os, const std::set& rhs) -{ - return osdev::components::mqtt::streamContainer<'{', '}'>(os, rhs); -} - -/** - * @brief Streams a map that contains keys and values for which an output stream operator is available. - */ -template -std::ostream& operator<<(std::ostream& os, const std::map& rhs) -{ - return osdev::components::mqtt::streamContainer<'{', '}'>(os, rhs); -} - -/** - * @brief Streams a pair that contains values for which an output stream operator is available. - */ -template -std::ostream& operator<<(std::ostream& os, const std::pair& rhs) -{ - os << "{" << rhs.first << " : " << rhs.second << "}"; - return os; -} - -} // End namespace std - -#endif // OSDEV_COMPONENTS_MQTT_MQTTSTREAM_H diff --git a/src/mqttsubscriberbase.cpp b/src/mqttsubscriberbase.cpp index e69de29..48afb64 100644 --- a/src/mqttsubscriberbase.cpp +++ b/src/mqttsubscriberbase.cpp @@ -0,0 +1,55 @@ +/* **************************************************************************** + * Copyright 2019 Open Systems Development BV * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the "Software"), * + * to deal in the Software without restriction, including without limitation * + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * + * and/or sell copies of the Software, and to permit persons to whom the * + * Software is furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in * + * all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * + * DEALINGS IN THE SOFTWARE. * + * ***************************************************************************/ +#include "mqttclient.h" +#include "mqttsubscriberbase.h" +#include "mqttmessage.h" +#include "credentials.h" + +MqttSubscriberBase::MqttSubscriberBase( const std::string &client_id ) + : m_mqtt_client( client_id ) +{ + +} + +std::string MqttSubscriberBase::getClientId() const +{ + return m_mqtt_client.clientId(); +} + +void MqttSubscriberBase::connect(const std::string &hostname, int portnumber, + const std::string &username, const std::string &password) +{ + m_mqtt_client.connect( hostname, portnumber, osdev::components::mqtt::Credentials( username, password ) ); +} + +void MqttSubscriberBase::subscribe( const std::string &message_topic ) +{ + m_mqtt_client.subscribe( message_topic, 1, [this]( const osdev::components::mqtt::MqttMessage &message ) + { + this->receive_data( message.topic(), message.payload() ); + }); +} + +void MqttSubscriberBase::disconnect() +{ + m_mqtt_client.disconnect(); +} diff --git a/src/mqttsuccess.h b/src/mqttsuccess.h deleted file mode 100644 index 34c7940..0000000 --- a/src/mqttsuccess.h +++ /dev/null @@ -1,165 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MQTTSUCCESS_H -#define OSDEV_COMPONENTS_MQTT_MQTTSUCCESS_H - -// std -#include -#include - -// boost -#include - -// paho -#include - -// osdev::components::mqtt -#include "mqttmessage.h" - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Class that holds paho connection data which is returned in the connect response. - */ -class ConnectionData -{ -public: - /*! - * \brief Construct an empty ConnectData instance. - */ - ConnectionData(); - - /*! - * \brief Construct ConnectData based on incoming values from paho. - * \param serverUri - The serverUri to which the connection is made (needs to be copied). - * \param mqttVersion - The mqtt version used by the broker. - * \param sessionPresent - Flag that indicates if a session was present for the given clientId. - */ - ConnectionData(char* serverUri, int mqttVersion, int sessionPresent); - - /*! - * \return The server uri. - */ - const std::string& serverUri() const { return m_serverUri; } - - /*! - * \return The mqtt version. - */ - int mqttVersion() const { return m_mqttVersion; } - - /*! - * \return if a session was present for the given clientId. - */ - bool sessionPresent() const { return m_sessionPresent; } - -private: - std::string m_serverUri; ///< The broker server uri. - int m_mqttVersion; ///< The mqtt version used by the broker. - bool m_sessionPresent; ///< Flag that indicates whether a session was present for the client id used in the connect. -}; - -struct Unspecified -{ -}; - -/*! - * \brief Class for paho mqtt success response data. - * The paho success response data uses a union and can have different information depending on the command. - */ -class MqttSuccess -{ -public: - /*! - * \brief Response data for commands without specific data. - * \param token The token that identifies to which command this response belongs. - */ - explicit MqttSuccess(MQTTAsync_token token); - - /*! - * \brief Response data for a subscribe command. - * \param token The token that identifies to which command this response belongs. - * \param qos Actual quality of service of the subscription. - */ - MqttSuccess(MQTTAsync_token token, int qos); - - /*! - * \brief Response data for a subscribe many command. - * \param token The token that identifies to which command this response belongs. - * \param qosMany Actual quality of service of the subscription for each topic filter. - */ - MqttSuccess(MQTTAsync_token token, const std::vector& qosMany); - - /*! - * \brief Response data for a publish command. - * \param token The token that identifies to which command this response belongs. - * \param pubMsg The message that was published. - */ - MqttSuccess(MQTTAsync_token token, const MqttMessage& pubMsg); - - /*! - * \brief Response data for a connect command. - * \param token The token that identifies to which command this response belongs. - * \param connData The connection data. - */ - MqttSuccess(MQTTAsync_token token, const ConnectionData& connData); - - /*! - * \return the command token. - */ - MQTTAsync_token token() const { return m_token; } - - /*! - * \return the qos - * \throw exception when command is not a subscribe command. - */ - int qos() const; - - /*! - * \return a vector of qos values (matching the topics in the subscribe many command). - * \throw exception when command is not a subscribe many command. - */ - std::vector qosMany() const; - - /*! - * \return Message that has been published. - * \throw exception when command is not a publish command. - */ - MqttMessage publishData() const; - - /*! - * return Connection data. - * throw exception when command is not a connect command. - */ - ConnectionData connectionData() const; - -private: - MQTTAsync_token m_token; ///< Command token. - boost::variant, MqttMessage, ConnectionData, Unspecified> m_data; ///< Data for the various commands. -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_MQTTSUCCESS_H diff --git a/src/mqtttypeconverter.h b/src/mqtttypeconverter.h deleted file mode 100644 index 2b7b983..0000000 --- a/src/mqtttypeconverter.h +++ /dev/null @@ -1,110 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MLOGICTYPECONVERTER_H -#define OSDEV_COMPONENTS_MQTT_MLOGICTYPECONVERTER_H - -// std -#include -#include -#include "date.h" -#include - -// boost -#include - -#include "commondefs.h" - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Utility namespace to convert between mqtt common types and other frequently used types. - */ -namespace MqttTypeConverter { - -/** - * @brief Converts from MqttId to std::string. - * @param mqttId The mqttId to convert. - * @return The std::string with contents of the provided mqttId. Format is 12345678-9abc-def0-1234-56789abcdef0. - */ -std::string toStdString(const MqttId& mqttId); - -/** - * @brief Converts from system clock timepoint to std::string. - * @tparam Duration std::chrono::duration instance. - * Duration::Period is used to determine the precision - * of the subsecond part of the returned ISO8601 string. - * Uses the duration of the StdTime type by default. - * @param tp The timepoint to converter. - * @return ISO8601 string representation of stdTime. - */ -template -std::string toStdString(const std::chrono::time_point& tp) -{ - return date::format("%FT%T%Ez", tp); -} - -/** - * @brief Converts from std::string to MqttId. - * @param mqttId The MqttId string to convert. - * @return the converted string to MqttId. - */ -MqttId toMqttId(const std::string& mqttId); - -/** - * @brief Creates a descriptive string based on the specified input parameters. - * @param str The prefix of the string. - * @param mqttId The id of which to use the first 4 characters. - * @return str + "-" + - * Example: "Unassigned-a2c4". - */ -std::string toShortGuidAppendedString(const std::string& str, const MqttId& mqttId); - -/** - * @brief Converts from PosixTime (time_t) to StdTime. - * @param posixTime The Posix Time (time_t) to convert. - * @return The StdTime with same value as the provided posixTime. - */ -StdTime toStdTime(const std::time_t& posixTime); - -/** - * @brief Converts from StdTime to PosixTime (time_t). - * @param stdTime The StdTime to convert. - * @return The PosixTime with the same value as the provided stdTime. - */ -time_t toPosixTime(const osdev::components::mqtt::StdTime& stdTime); - -/** - * @brief Converts the specified posixTimeString to an OptionalTime. - * @param posixTimeString A posix time as string. - * @return The converted posixTimeString. - * @retval boost::none if the specified posixTimeString could not be converted to a StdTime. - */ -osdev::components::mqtt::OptionalTime toOptionalTime(const std::string& posixTimeString); - -} // End namespace MqttTypeConverter -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_MQTTTYPECONVERTER_H diff --git a/src/mqttutil.h b/src/mqttutil.h deleted file mode 100644 index ae7e945..0000000 --- a/src/mqttutil.h +++ /dev/null @@ -1,67 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MQTTUTIL_H -#define OSDEV_COMPONENTS_MQTT_MQTTUTIL_H - -// std -#include - -namespace osdev { -namespace components { -namespace mqtt { - -/*! - * \brief Determine if topic is a valid mqtt topic filter. - * \param topic - The topic to test. - * \return True when topic is valid, false otherwise. - */ -bool isValidTopic( const std::string &topic ); - -/*! - * \brief Test a topic against another topicfilter for overlap. - * \param existingTopic - The topic to test against - * \param newTopic - The topic to test. - * \return True when topics overlap, false otherwise - */ -bool testForOverlap( const std::string & existingTopic, const std::string &newTopic ); - -/*! - * \brief Test a topic for occurence of wildcards - * \param topic - The topic to test - * \return True if topics contains wildcards, false otherwise - */ -bool hasWildcard( const std::string &topic ); - -/*! - * \brief Create a regular expression string based on a topicfilter that can be used - * to match topic strings against topics with no wildcards. - * \pre The topic filter is valid. - * \return The regular expression string. If the topic filter is not valid then the - * returned string is also not valid. - */ -std::string convertTopicToRegex( const std::string &topic ); - -} // End namespace mqtt -} // End namespace components -} // osdev - -#endif // OSDEV_COMPONENTS_MQTT_MQTTUTIL_H diff --git a/src/scopeguard.h b/src/scopeguard.h deleted file mode 100644 index 7c34847..0000000 --- a/src/scopeguard.h +++ /dev/null @@ -1,76 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_SCOPEGUARD_H -#define OSDEV_COMPONENTS_MQTT_SCOPEGUARD_H - -// std -#include - -#include "macrodefs.h" -#include "utils.h" - -#define OSDEV_COMPONENTS_SCOPEGUARD(variableName, ...) \ - osdev::components::mqtt::ScopeGuard OSDEV_COMPONENTS_COMBINE(Scope__Guard__##variableName##__, __LINE__)(__VA_ARGS__); \ - osdev::components::mqtt::apply_unused_parameters(OSDEV_COMPONENTS_COMBINE(Scope__Guard__##variableName##__, __LINE__)); - -namespace osdev { -namespace components { -namespace mqtt { - -using CleanUpFunction = std::function; - -/** - * @brief Ensures that a cleanup function is called at the end of the current scope. - */ -class ScopeGuard -{ -public: - /** - * @brief Constructs an empty scopeguard. - * The scopeguard can be set by moving another ScopeGuard into this one. - */ - ScopeGuard(); - - /** - * @brief Constructs a RAII instance that will call cleanupFunc in it's destructor. - * @param cleanupFunc The cleanup function to call at the end of the current scope. - * This cleanup function must not throw exceptions. If it does, the behavior is undefined. - */ - ScopeGuard(const CleanUpFunction& cleanupFunc); - - // Movable, not copyable - ScopeGuard(const ScopeGuard&) = delete; - ScopeGuard& operator=(const ScopeGuard&) = delete; - ScopeGuard(ScopeGuard&&) = default; - ScopeGuard& operator=(ScopeGuard&&) = default; - - ~ScopeGuard() noexcept; - -private: - CleanUpFunction m_cleanupFunc; -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_SCOPEGUARD_H diff --git a/src/serverstate.h b/src/serverstate.h deleted file mode 100644 index d54b39a..0000000 --- a/src/serverstate.h +++ /dev/null @@ -1,131 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_SERVERSTATE_H -#define OSDEV_COMPONENTS_MQTT_SERVERSTATE_H - -// std -#include -#include - -// boost -#include - -// osdev::components::mqtt -#include "istatecallback.h" - -namespace osdev { -namespace components { -namespace mqtt { - -/*! - * \brief Class for administrating ServerState callbacks. - * ServiceClientBase uses this object to notify the server state listeners - * of changes in the serverstate of the server that the client is connected to. - */ -class ServerState -{ -public: - /*! - * \brief Constructs a ServerState object. This object has a one to one relation with an IStateCallback object. - * \param stateCallbackIf identification of the interface that generates the signal. - */ - explicit ServerState(const IStateCallback* stateCallbackIf); - - /*! - * \brief Destroy the ServerState. - * Calls clearAllStateChangeCallbacks() to unregister itself from the listeners. - */ - virtual ~ServerState(); - - // non copyable, non movable - ServerState(const ServerState&) = delete; - ServerState& operator=(ServerState&) = delete; - ServerState(ServerState&&) = delete; - ServerState& operator=(ServerState&&) = delete; - - /*! - * \brief Registers a statechange callback method. - * \param cb - The callback method. - * \return handle that identifies the callback method. - */ - StateChangeCallbackHandle registerStateChangeCallback(const IStateCallback::SlotStateChange& cb); - - /*! - * \brief Unregisters a state change callback method. - * \param handle Handle that identifies the callback method. - */ - void unregisterStateChangeCallback(StateChangeCallbackHandle handle); - - /*! - * \brief Removes all callback methods. - * An Unregister state is signalled to the listeners. - */ - void clearAllStateChangeCallbacks(); - - /*! - * \brief Emit the State changed signal. - * \param newState - The new state. - */ - void emitStateChanged(StateEnum newState); - - /*! - * \brief Return the last state received from server. - * \return state of server - */ - StateEnum state() const; - - /*! - * \brief Returns the handle that will be given to the next callback that is registered. - */ - static StateChangeCallbackHandle nextHandle() - { - return s_nextServerStateCallbackHandle; - } - -private: - /*! - * Type for holding connections to server state callback functions. - */ - using ServerStateCallbackMap = std::map; - - IStateCallback::SigStateChange sig_serverStateChanged; ///< Signal emitted when server state has changed. - - const IStateCallback* m_stateCallbackIf; ///< Identification of the the interface that generates the signal (not owned) - ServerStateCallbackMap m_serverStateCallbackMap; ///< Map with serverstate callback connections. - - static std::atomic s_nextServerStateCallbackHandle; ///< Handle given to next serverstate callback registration (0 is not valid). - - StateEnum m_state; ///< Store the last state received from the server. - - /*! - * \return The clientId of the IStateCallback interface, or "null" if it is nullptr. - */ - std::string stateCallbackClientId() const; - - static const std::string s_identifier; -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_SERVERSTATE_H diff --git a/src/sharedreaderlock.h b/src/sharedreaderlock.h deleted file mode 100644 index d91be15..0000000 --- a/src/sharedreaderlock.h +++ /dev/null @@ -1,204 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_SHAREDREADERLOCK_H -#define OSDEV_COMPONENTS_MQTT_SHAREDREADERLOCK_H - -// std -#include -#include -#include -#include - -// mlogic::common -#include "scopeguard.h" - -namespace osdev { -namespace components { -namespace mqtt { - -#define OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(lockvar) \ - lockvar.lockShared(); \ - OSDEV_COMPONENTS_SCOPEGUARD(lockvar, [&]() { lockvar.unlock(); }); - -#define OSDEV_COMPONENTS_EXCLUSIVELOCK_SCOPE(lockvar) \ - lockvar.lockExclusive(); \ - OSDEV_COMPONENTS_SCOPEGUARD(lockvar, [&]() { lockvar.unlock(); }); - -/** - * @brief Class is used to administrate the lock data. - */ -class LockData -{ -public: - /** - * @brief Default constructable. Lock is not active and the count is 0. - */ - LockData() - : m_count(0) - , m_active(false) - { - } - - // Copyable, movable - LockData(const LockData&) = default; - LockData& operator=(const LockData&) = default; - LockData(LockData&&) = default; - LockData& operator=(LockData&&) = default; - - /** - * @return true when the lock is active, false otherwise. - * @note A lock becomes active the first time that increase() is called. - */ - inline bool active() const - { - return m_active; - } - - /** - * @brief Increases the lock count by one. - * An inactive lock becomes active by this call. - */ - inline void increase() - { - m_active = true; - ++m_count; - } - - /** - * @brief Decreases the lock count by one. - * The count is only decreased for active locks. When the lock count becomes 0 the lock - * is deactivated. - * @return true when the lock is still active after decrease and false when it is deactivated. - */ - inline bool decrease() - { - if (m_active) { - --m_count; - m_active = (0 != m_count); - } - return m_active; - } - - /** - * @brief Conversion operator that returns the lock count. - */ - inline operator std::size_t() const - { - return m_count; - } - - /** - * @brief Static method for initializing a lock data based on already existing lock data. - * The new lock data is not active. - * @note This is used to promote a shared lock to an exclusive lock. - */ - inline static LockData initialize(const LockData& other) - { - auto newLockData(other); - newLockData.m_active = false; - return newLockData; - } - -private: - std::size_t m_count; ///< The lock count. - - /** - * @brief Flag to indicate whether the lock is active. - * This flag is necessary because when the lock is promoted - * the lock count is not zero but the lock still should be activated again. - */ - bool m_active; -}; - -/** - * @brief Lock class that allows multiple readers to own the lock in a shared way. - * A writer will want exclusive ownership so that it can mutate the content that - * is protected by this lock. - * - * Reader and writer should be interpreted as to how threads interact with the content that this lock protects. It is up - * to the caller to enforce the correct behaviour. In other words don't take a shared lock and change the content! - * - * The administration of this class uses the std::thread::id to register which thread holds what kind of lock. - * This id is reused, so be really careful to pair each lock with an unlock, otherwise newly spawned threads might - * end up having a lock without taking one. - */ -class SharedReaderLock -{ -public: - /** - * Default constructable. - * The lock is not locked. - */ - SharedReaderLock(); - - /** - * Destructor will throw when there are threads registered. - */ - ~SharedReaderLock(); - - // Non copyable, non movable - SharedReaderLock(const SharedReaderLock&) = delete; - SharedReaderLock& operator=(const SharedReaderLock&) = delete; - SharedReaderLock(SharedReaderLock&&) = delete; - SharedReaderLock& operator=(SharedReaderLock&&) = delete; - - /** - * @brief Lock in a shared way. For read only operations. - * Multiple threads can have shared ownership on this lock. - * It is guaranteed that a call to lockExclusive will wait until all read locks are unlocked. - * When a call to lockExclusive is made and is waiting, no new reader locks are accepted. - * A thread that owns a shared lock can lock again. The lock will be unlocked for this thread when as many unlock calls are made. - * A thread that owns a shared lock can upgrade the lock to an exclusive lock by calling lockExclusive. The thread has to wait - * for exclusive ownership and has the exclusive lock until all unlocks are made (if it had done multiple shared locks before an exclusive lock). - */ - void lockShared(); - - /** - * @brief Lock in an exclusive way. For write operations. - * Only one thread can have exclusive ownership of this lock. - * While a thread waits for exlusive ownership shared locks are denied. This lock is unfair in the - * sense that it favours write locks. - * A thread that owns exclusive ownership can make another exclusive lock or a even a shared lock. Both are - * treated as an exclusive lock that updates the lock count. As many unlocks need to be called to unlock the exclusive lock. - */ - void lockExclusive(); - - /** - * @brief Unlock the lock. The thread id is used to determine which lock needs to be unlocked. - * If a thread does not own this lock at all then nothing happens. - */ - void unlock(); - -private: - std::mutex m_mutex; ///< Mutex that protects the lock administration. - std::map m_readLockMap; ///< Map with read lock data. - std::map m_writeLockMap; ///< Map with write lock data. - - std::condition_variable m_readersCV; ///< lockShared waits on this condition variable. - std::condition_variable m_writersCV; ///< lockExclusive waits on this condition variable. -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_SHAREDREADERLOCK_H diff --git a/src/stringify.h b/src/stringify.h deleted file mode 100644 index 4f55f25..0000000 --- a/src/stringify.h +++ /dev/null @@ -1,54 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDSEV_COMPONENTS_MQTT_STRINGIFY_H -#define OSDSEV_COMPONENTS_MQTT_STRINGIFY_H - -// std -#include - -// osdev::components::mqtt -#include "mqttstream.h" - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Stringifies all objects for which an output stream operator is available. - * @note This method is meant to be used for printing and not for marshalling! - * @tparam T The object type. - * @param value The value to stringify. - * @return stringified value. - */ -template -std::string toString(const T& value) -{ - std::ostringstream oss; - oss << value; - return oss.str(); -} - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_STRINGIFY_H diff --git a/src/stringutils.h b/src/stringutils.h deleted file mode 100644 index 44b7df0..0000000 --- a/src/stringutils.h +++ /dev/null @@ -1,54 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_STRINGUTILS_H -#define OSDEV_COMPONENTS_MQTT_STRINGUTILS_H - -// See boost/algorithm/string.hpp for more string utility functions - -// std -#include -#include -#include - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Removes characters from the specified string. Modifies the specified string in place. - * @param str The string to modify. - * @param charsToRemove The characters to remove from str. - */ -void removeCharsFromStringInPlace(std::string& str, const std::string& charsToRemove); - -/** - * @brief Determines whether the specified string is all digits. - * @param str The string for which to determine if it's numeric. - * @return True if the specified string is numeric; otherwise, false. - */ -bool is_numeric(const std::string& str); - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_STRINGUTILS_H diff --git a/src/synchronizedqueue.h b/src/synchronizedqueue.h deleted file mode 100644 index 9b71987..0000000 --- a/src/synchronizedqueue.h +++ /dev/null @@ -1,319 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_SYNCHRONIZEDQUEUE_H -#define OSDEV_COMPONENTS_MQTT_SYNCHRONIZEDQUEUE_H - -// std -#include -#include -#include -#include -#include -#include -#include - -// osdev::components::mqtt -#include "lockguard.h" -#include "metaprogrammingdefs.h" - -namespace osdev { -namespace components { -namespace mqtt { - -OSDEV_COMPONENTS_HASMETHOD_TRAIT(capacity, ) - -/*! - * \brief Generic Queue template for defining a thin - * wrapper around std::queue to make overflow detection possible. - * This template has no definition and will lead to a compile - * time error when it is chosen. - */ -template -class Queue; - -/*! - * \brief A specialization when the underlying container has a capacity method. - * To detect overflow the capacity of the underlying container is needed. - * Not all containers have a capacity. - * When the capacity method is not available SFINAE will discard this template. - */ -template -class Queue::value>::type> : public std::queue -{ -public: - using size_type = typename std::queue::size_type; - - typename C::size_type capacity() const - { - return this->c.capacity(); - } -}; - -/*! - * \brief A specialization for when the underlying container does not support a capacity - * In this case max_size is returned which results in overflow not being detected. - */ -template -class Queue::value>::type> : public std::queue -{ -public: - using size_type = typename std::queue::size_type; - typename C::size_type capacity() const - { - return this->c.max_size(); - } -}; - -/*! - * \brief Represents a synchronized queue - * @tparam T The type of the items in the queue - * @tparam C The underlying character. The container must satisfy the - * requirements of a SequenceContainer. - * Addittionally container must supply a pop_front and a max_size method. - * - * The underlying container determines the overflow behaviour. - * A circular buffer leads to a lossy queue that drops items when the - * queue is full while a std::deque will never overflow. - * - * The queue has the following states: started, paused, stopped. - * In the started state the queue acceptsincoming items and it allows items - * to be popped when data is available. - * In the paused state incoming items are allowed. The pop method will - * block until the queue is unpaused or stopped. - * In the stopped state incoming items are not allowed and dropped. - * The pop method will return false. - */ -template > -class SynchronizedQueue -{ -public: - using QueueType = Queue; - - /*! - * \brief Constructs an empty queue - * \param id - Identification string for this queue ( used in logging ). - * \param paused - The state in which to setup the queue ( pause or active), - * ( Default is active ) - */ - explicit SynchronizedQueue( const std::string &id, bool paused = false ) - : m_id( id ) - , m_queueMutex() - , m_dataAvailableOrStopCV() - , m_queue() - , m_stop( false ) - , m_pause( paused ) - , m_numOverflows( 0 ) - {} - - /*! - * \brief Stops the queue on destruction - */ - ~SynchronizedQueue() - { - this->stop(); - } - - /*! - * @brief Pushes the item in the queue - * @tparam TItem - The cv qualified type of the value to push. - * In a stopped state the queue drops incoming data. - * In a paused / active state the queue accepts incoming data. - * @tparam item - The item to push to the queue. - */ - template - void push(TItem &&item) - { - OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC( m_queueMutex, m_id ); - if( m_stop ) - { - return; - } - - if( m_queue.capacity() == m_queue.size() ) - { - if( m_numOverflows++ % 100 == 0 ) - { - // Log a warning that there is a number of overflows. - } - } - m_queue.push( std::forward(item) ); - OSDEV_COMPONENTS_UNIQUELOCK_UNLOCK_SPECIFIC(m_queueMutex, m_id); - m_dataAvailableOrStopCV.notify_one(); - } - - /*! - * @brief pop - Pops an item from the queue. This method blocks when te state is paused or when there is no data available. - * @param item - The item to which to copy the popped item. - * @return True if an item was popped: otherwise false - */ - bool pop(T& item) - { - OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC( m_queueMutex, m_id ); - m_dataAvailableOrStopCV.wait(OSDEV_COMPONENTS_UNIQUELOCK(m_queueMutex), [this]() - { return ((this->m_queue.size() > 0 && !m_pause) || this->m_stop); }); - if( m_stop ) - { - return false; - } - item = std::move(m_queue.front()); - m_queue.pop(); - return true; - } - - bool pop(std::vector &items) - { - OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC( m_queueMutex, m_id ); - m_dataAvailableOrStopCV.wait(OSDEV_COMPONENTS_UNIQUELOCK(m_queueMutex), [this]() - { return ((this->m_queue.size() > 0 && !m_pause) || this->m_stop); }); - if( m_stop ) - { - return false; - } - items.clear(); - items.reserve(m_queue.size()); - while( m_queue.size() > 0 ) - { - items.emplace_back(std::move(m_queue.front() ) ); - m_queue.pop(); - } - return true; - } - - /*! - * \return The current size of the queue - */ - typename QueueType::size_type size() const - { - OSDEV_COMPONENTS_LOCKGUARD_SPECIFIC(m_queueMutex, m_id); - return m_queue.size(); - } - - - /*! - * \brief Start the Queue - * The queue is only started when it is in a stopped state. - * \param paused - If true, the queue will be started in a paused - * state which means that no items will be popped. - */ - void start(bool paused) - { - // Reason that a lock is used: See documentation of std::condition_variable - // - // Even is the shared variable is atomic (m_stop in this case), it must be modified under the mutex - // in order to correctly publish the modification to the waiting thread. - // - OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC(m_queueMutex, m_id); - if( !m_stop ) - { - // already started - return; - } - m_stop = false; - m_pause = paused; - OSDEV_COMPONENTS_UNIQUELOCK_UNLOCK_SPECIFIC(m_queueMutex, m_id); - if( !paused ) - { - m_dataAvailableOrStopCV.notify_all(); - } - } - - /*! - * \brief Pause or unpause the queue. - * When the queue is paused no items will be popped. - * The state is not altered when the queue is stopped. - * \param value - Flag that indicates whether the queue is paused or unpaused. - */ - void pause(bool value) - { - // Reason that a lock is used: see documentation of std::condition_variable - // - // Even if the shared variable is atomic (m_stop in this case), it must be modified under the mutex - // in order to correctly publish the modification to the waiting thread. - // - OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC(m_queueMutex, m_id); - if (m_stop) { - return; - } - m_pause = value; - OSDEV_COMPONENTS_UNIQUELOCK_UNLOCK_SPECIFIC(m_queueMutex, m_id); - if (!value) { - m_dataAvailableOrStopCV.notify_all(); - } - } - - /*! - * \brief Stop the queue. - * The pop method will return a false after calling this method. - * The queue can be restarted with the start method. - */ - void stop() - { - // Reason that a lock is used: see documentation of std::condition_variable - // - // Even if the shared variable is atomic (m_stop in this case), it must be modified under the mutex - // in order to correctly publish the modification to the waiting thread. - // - OSDEV_COMPONENTS_UNIQUELOCK_CREATE_SPECIFIC(m_queueMutex, m_id); - m_stop = true; - m_pause = false; - OSDEV_COMPONENTS_UNIQUELOCK_UNLOCK_SPECIFIC(m_queueMutex, m_id); - m_dataAvailableOrStopCV.notify_all(); - } - - /*! - * \brief Clears the queue. - * This method also resets the overflow counter. - */ - void clear() - { - OSDEV_COMPONENTS_LOCKGUARD_SPECIFIC(m_queueMutex, m_id); - QueueType emptyQueue; - std::swap(m_queue, emptyQueue); - m_numOverflows = 0; - } - -private: - const std::string m_id; ///< Queue identification string - mutable std::mutex m_queueMutex; ///< Protects access to the queue - std::condition_variable m_dataAvailableOrStopCV; ///< Provides wait functionality for the queue becoming empty - QueueType m_queue; ///< Holds the items - std::atomic_bool m_stop; ///< Flag that indicates whether the queue needs to stop. - std::atomic_bool m_pause; ///< Flag that indicates whether the queue is paused. - - /*! - * \brief Counts the number of items that the buffer overflows. - * If the underlying buffer is a ring buffer an overflow - * means that an item will be overwritten. For a normal - * sequence container it means that the it is enlarged. - */ - std::uint32_t m_numOverflows; -}; - - - - - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_SYNCHRONIZEDQUEUE_H diff --git a/src/timemeasurement.h b/src/timemeasurement.h deleted file mode 100644 index 850c1fe..0000000 --- a/src/timemeasurement.h +++ /dev/null @@ -1,72 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_MEASUREMENT_TIMEMEASUREMENT_H -#define OSDEV_COMPONENTS_MQTT_MEASUREMENT_TIMEMEASUREMENT_H - -#include -#include -#include -#include - -namespace osdev { -namespace components { -namespace mqtt { -namespace measurement { - -using TimeMeasurementCallback = std::function; - -class TimeMeasurement -{ -public: - TimeMeasurement(const std::string& id, const TimeMeasurementCallback& callback, bool measureOnDestruction = true); - ~TimeMeasurement(); - - TimeMeasurement(const TimeMeasurement&) = delete; - TimeMeasurement& operator=(const TimeMeasurement&) = delete; - TimeMeasurement(TimeMeasurement&&) = default; - TimeMeasurement& operator=(TimeMeasurement&&) = default; - - void set(); - void measure(); - -private: - std::string m_id; - - std::chrono::steady_clock::time_point m_start; - std::chrono::steady_clock::time_point m_last; - TimeMeasurementCallback m_callback; - bool m_measureOnDestruction; -}; - -template -std::ostream& operator<<(std::ostream& os, const std::chrono::duration& rhs) -{ - os << rhs.count(); - return os; -} - -} // End namespace measurement -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_MEASUREMENT_TIMEMEASUREMENT_H diff --git a/src/token.h b/src/token.h deleted file mode 100644 index 7db2917..0000000 --- a/src/token.h +++ /dev/null @@ -1,127 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_TOKEN_H -#define OSDEV_COMPONENTS_MQTT_TOKEN_H - -// std -#include -#include - -// paho -#include - -namespace osdev { -namespace components { -namespace mqtt { - -/*! - * \brief The Token class defines an operation token - */ -class Token -{ -public: - /*! @brief Construct an invalid token. - * The token number is -1 in that case. The client is undefined, in this case empty. - */ - Token(); - - /*! @brief Constructs token for an operation originating from specific client wrapper. - * @param clientId - Identifies the client wrapper - * @param tokenNr - Identifies the operation done on that client. - */ - Token( const std::string &clientId, std::int32_t tokenNr ); - - /*! @return True when token has a valid token number, false otherwise. */ - bool isValid() const { return -1 == m_token; } - - /*! @return The operation token */ - const std::string& clientId() const { return m_clientId; } - - /*! @return The operation token */ - std::int32_t token() const { return m_token; } - - /*! @return True if Tokens have the same clientId and token number, false otherwise. */ - bool equals( const Token &rhs ) const; - - /*! - * @brief Token is ordered. - * First on lexical test of clientId and with same clientId on token number. - */ - bool smallerThan( const Token &rhs ) const; - -private: - std::string m_clientId; ///< Identified the client - std::int32_t m_token; ///< Identifies the operation on that client. -}; - -/** - * @return True if Tokens have the same clientId and token number, false otherwise. - */ -inline bool operator==( const Token &lhs, const Token &rhs ) -{ - return lhs.equals( rhs ); -} - -inline bool operator==( const Token &lhs, std::int32_t rhs ) -{ - return lhs.token() == rhs; -} - -inline bool operator==( std::int32_t lhs, const Token &rhs ) -{ - return lhs == rhs; -} - -template -inline bool operator!=( const TLeft &lhs, const TRight &rhs ) -{ - return !( lhs == rhs ); -} - -/*! - * @return True if Token lhs is smaller than token rhs - */ -inline bool operator<( const Token &lhs, std::int32_t rhs ) -{ - return lhs.token() < rhs; -} - -inline bool operator<( std::int32_t lhs, const Token &rhs ) -{ - return lhs < rhs.token(); -} - -inline bool operator<( const Token &lhs, const Token &rhs ) -{ - return lhs.smallerThan( rhs ); -} - -/*! - * @brief Stream operator for a Token - */ -std::ostream& operator<<( std::ostream &os, const Token &rhs ); - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_TOKEN_H diff --git a/src/uriparser.h b/src/uriparser.h deleted file mode 100644 index b628aae..0000000 --- a/src/uriparser.h +++ /dev/null @@ -1,108 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_URIPARSER_H -#define OSDEV_COMPONENTS_MQTT_URIPARSER_H - -// std -#include - -#include "commondefs.h" - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Helper class to parse, normalize, and replace the port service name by portnumber in a uri. - * example: - * opc.tcp://user:secret\@processmodel:processmodel/path1/path2?query3=value4#fragment5 - * will result in - * opc.tcp://user:secret\@processmodel:12345/path1/path2?query3=value4#fragment5 - * @note This assumes that the port service name was registered in the etc/services file under protocol tcp. - * Lookup of a portnumber for protocols other than tcp are not supported. - * - * IPv6 addresses in a uri are only parsable when they are wrapped in brackets ([]) (RFC 3986, section 3.2.2) - * - * @note This class is designed to handle a subset of all possible uris. The scheme and the authority part are - * expected not to be empty. The authority part can contain user and password information. - * - * @note Only scheme, hostname and port are converted to lowercase (see RFC 3986 6.2.2.1. Case Normalization) - * - * @note Special characters are escaped with percent encoding. This applies to user, password, path, query and fragment parts. - * The path and query part is escaped on a per element basis. This means that ParsedUri contains the raw query and path strings. - * The decoding is done when the methods parseQuery and parsePath are called. - */ -class UriParser -{ -public: - /** - * @brief Parses the specified uri string. - * @param uri The uri string to parse. - * @return A map containing the parsed uri. - * @note The path and query parts can still contain percent encoded special characters. - */ - static ParsedUri parse(const std::string& uri); - - /** - * @brief Parse the query part of a parsed uri. Percent encoded special characters are decoded. - * @param parsedUri A ParsedUri object. - * @return key/value map. - */ - static ParsedQuery parseQuery(const ParsedUri& parsedUri); - - /** - * @brief Parse the path part of a parsed uri. Percent encoded special characters are decoded. - * @param parsedUri A ParsedUri object. - * @return vector of path elements. - */ - static ParsedPath parsePath(const ParsedUri& parsedUri); - - /** - * @brief Parses, normalizes, and replaces the port service name by portnumber in the specified uri string. - * @param uri The uri string to parse and normalize. - * @return The normalized uri string, with the port service name replaced by the portnumber. - */ - static std::string normalize(const std::string& uri); - - /** - * @brief Converts a parsed uri back to a string. - * @param parsedUri The parsed uri to convert to string. - * @return The uri as string. - */ - static std::string toString(const ParsedUri& parsedUri); - - /** - * @brief Get portnumber associated with a service. - * @param serviceName Name of the service for which a portnumber is searched. - * @param protocolName Name of the protocol for which the portname was registered. - * @return portnumber. - * @throws InvalidArgumentException when service is unknown. - * @throws SystemException when underlying systemcall fails. - */ - static int getPortnumber(const std::string& serviceName, const std::string& protocolName = "tcp"); -}; - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_URIPARSER_H diff --git a/src/uriutils.h b/src/uriutils.h deleted file mode 100644 index 0bd97e4..0000000 --- a/src/uriutils.h +++ /dev/null @@ -1,57 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_URIUTILS_H -#define OSDEV_COMPONENTS_MQTT_URIUTILS_H - -// std -#include - -#include "compiletimedigits.h" - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Get the percent encoded string for a given character. - * @tparam N The character to encode. - * @return pointer to the encoded character string. - */ -template -inline const char* percentEncode() -{ - static const auto* s_code = - (compiletime_string<'%'>{} + typename apply_bounded_range< // - 0, // - numberOfDigits<16>(N), // - string_builder< // - adapt_for_string_builder< // - ProduceDigits>>::template produce>::result{}) - .chars; - return s_code; -} - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_URIUTILS_H diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index f630e60..0000000 --- a/src/utils.h +++ /dev/null @@ -1,231 +0,0 @@ -/* **************************************************************************** - * Copyright 2019 Open Systems Development BV * - * * - * Permission is hereby granted, free of charge, to any person obtaining a * - * copy of this software and associated documentation files (the "Software"), * - * to deal in the Software without restriction, including without limitation * - * the rights to use, copy, modify, merge, publish, distribute, sublicense, * - * and/or sell copies of the Software, and to permit persons to whom the * - * Software is furnished to do so, subject to the following conditions: * - * * - * The above copyright notice and this permission notice shall be included in * - * all copies or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * - * DEALINGS IN THE SOFTWARE. * - * ***************************************************************************/ -#ifndef OSDEV_COMPONENTS_MQTT_UTILS_H -#define OSDEV_COMPONENTS_MQTT_UTILS_H - -// std -#include -#include -#include -#include - -namespace osdev { -namespace components { -namespace mqtt { - -/** - * @brief Does nothing. - * Utility template function to explicitly use parameters, that would otherwise be unused. - * This helps to prevent the -Wunused-parameter warning. - */ -template -void apply_unused_parameters(const Args&...) -{ -} - -/** - * @brief Converts (dynamic_cast) a std::unique_ptr from TFrom to TTo. - * @param from The std::unique_ptr to convert from TFrom to TTo. - * @return The converted std::unique_ptr. - */ -template -std::unique_ptr dynamic_unique_ptr_cast(std::unique_ptr&& from) -{ - auto to = dynamic_cast(from.get()); - if (nullptr == to) { - return std::unique_ptr(nullptr); - } - - from.release(); - return std::unique_ptr(to); -} - -/** - * @brief Converts (dynamic_cast) a std::unique_ptr from TFrom to TTo. - * @param from The std::unique_ptr to convert from TFrom to TTo. - * @return The converted std::unique_ptr. - */ -template -std::unique_ptr dynamic_unique_ptr_cast(std::unique_ptr&& from) -{ - auto to = dynamic_cast(from.get()); - if (nullptr == to) { - return std::unique_ptr(nullptr, from.get_deleter()); - } - - from.release(); - return std::unique_ptr(to, std::move(from.get_deleter())); -} - -/** - * @brief Helper class for iteration of keys of a map. - */ -template -class KeyIterator : public TMap::iterator -{ -public: - typedef typename TMap::iterator MapIterator; - typedef typename MapIterator::value_type::first_type KeyType; - - KeyIterator(const MapIterator& other) - : TMap::iterator(other) - { - } - - KeyType& operator*() - { - return TMap::iterator::operator*().first; - } -}; - -/** - * @brief Helper function to get the begin KeyIterator from a map. - */ -template -KeyIterator KeyBegin(MapType& map) -{ - return KeyIterator(map.begin()); -} - -/** - * @brief Helper function to get the end KeyIterator from a map. - */ -template -KeyIterator KeyEnd(MapType& map) -{ - return KeyIterator(map.end()); -} - -/** - * @brief Helper class for iteration of keys of a const map. - */ -template -class KeyConstIterator : public TMap::const_iterator -{ - typedef typename TMap::const_iterator TMapIterator; - typedef typename TMapIterator::value_type::first_type TKeyType; - -public: - KeyConstIterator(const TMapIterator& other) - : TMapIterator(other) - { - } - - const TKeyType& operator*() - { - return TMapIterator::operator*().first; - } -}; - -/** - * @brief Helper function to get the cbegin KeyConstIterator from a const map. - */ -template -KeyConstIterator KeyBegin(const TMap& map) -{ - return KeyConstIterator(map.cbegin()); -} - -/** - * @brief Helper function to get the cend KeyConstIterator from a const map. - */ -template -KeyConstIterator KeyEnd(const TMap& map) -{ - return KeyConstIterator(map.cend()); -} - -/** - * @brief Helper function to get the difference of the keys in two maps. - * @param map1 The first map for which to examine the keys. - * @param map2 The second map for which to examine the keys. - * @return Collection of keys present in map1, but not in map2. - * @note The types of the keys in the maps must be identical. - */ -template -std::vector keyDifference( - const TMap1& map1, - const TMap2& map2, - const std::function& keyCompare = std::less()) -{ - static_assert(std::is_same< - typename TMap1::key_type, - typename TMap2::key_type>::value, - "Inconsistent key types."); - - typedef typename TMap1::key_type Key; - std::vector onlyInMap1; - std::set_difference( - KeyBegin(map1), - KeyEnd(map1), - KeyBegin(map2), - KeyEnd(map2), - std::inserter(onlyInMap1, onlyInMap1.begin()), - keyCompare); - return onlyInMap1; -} - -/** - * @brief Helper function to get the intersection of the keys in two maps. - * @param map1 The first map for which to examine the keys. - * @param map2 The second map for which to examine the keys. - * @return Collection of keys present in both maps. - * @note The types of the keys in the maps must be identical. - */ -template -std::vector keyIntersection( - const TMap1& map1, - const TMap2& map2, - const std::function& keyCompare = std::less()) -{ - static_assert(std::is_same< - typename TMap1::key_type, - typename TMap2::key_type>::value, - "Inconsistent key types."); - - typedef typename TMap1::key_type Key; - std::vector inBothMaps; - std::set_intersection( - KeyBegin(map1), - KeyEnd(map1), - KeyBegin(map2), - KeyEnd(map2), - std::inserter(inBothMaps, inBothMaps.begin()), - keyCompare); - return inBothMaps; -} - -/** - * @brief Determine the absolute path of the binary that belongs to a process. - * @param pid Process Id ifor which to determine the path to the binary. If pid equals -1 then the pid of the current process is used. - * @return Absolute path to the binary. - * @throw SystemException if call to readlink fails. - * @throw PathException if path is to long. - * @note Works only for processes owned by the user that is calling this function. - */ -std::string getPathToBinary(int pid = -1); - -} // End namespace mqtt -} // End namespace components -} // End namespace osdev - -#endif // OSDEV_COMPONENTS_MQTT_UTILS_H