diff --git a/CMakeLists.txt b/CMakeLists.txt index e437a02..54b96fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ option(HSMBUILD_EXAMPLES "Build examples" ON) option(HSMBUILD_CODECOVERAGE "Build with code coverage" OFF) option(HSMBUILD_CLANGTIDY "Enable validation with clang-tidy-15 (if available)" OFF) -set(HSMBUILD_PLATFORM "posix" CACHE STRING "Specifies target platform. Possible values: posix, windows, freertos, arduino") +set(HSMBUILD_PLATFORM "posix" CACHE STRING "Specifies target platform. Possible values: posix, windows, freertos, arduino, qnx") set(HSMBUILD_FREERTOS_ROOT "" CACHE STRING "FreeRTOS sources root folder") set(HSMBUILD_FREERTOS_CONFIG_FILE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/hsmcpp/os/freertos" CACHE STRING "Path to folder with FreeRTOSConfig.h") option(HSMBUILD_DISPATCHER_FREERTOS "Enable FreeRTOS based dispatcher" OFF) @@ -142,7 +142,7 @@ elseif (HSMBUILD_PLATFORM STREQUAL "arduino") ${HSM_INCLUDES_ROOT}/os/arduino/AtomicFlag.hpp ${HSM_INCLUDES_ROOT}/os/arduino/Mutex.hpp ${HSM_INCLUDES_ROOT}/HsmEventDispatcherArduino.hpp) -elseif (HSMBUILD_PLATFORM STREQUAL "posix") +elseif (HSMBUILD_PLATFORM STREQUAL "posix" OR HSMBUILD_PLATFORM STREQUAL "qnx") set (LIBRARY_SRC ${LIBRARY_SRC} ${HSM_SRC_ROOT}/os/posix/InterruptsFreeSection.cpp ${HSM_SRC_ROOT}/os/stl/ConditionVariable.cpp ${HSM_SRC_ROOT}/os/stl/AtomicFlag.cpp) @@ -211,7 +211,8 @@ elseif (HSMBUILD_TARGET STREQUAL "library") add_definitions(${HSM_DEFINITIONS_BASE}) add_library(${HSM_LIBRARY_NAME} STATIC ${LIBRARY_SRC}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) - target_include_directories(${HSM_LIBRARY_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) + target_include_directories(${HSM_LIBRARY_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/include/hsmcpp/os) if (NOT WIN32) target_compile_options(${HSM_LIBRARY_NAME} PUBLIC "-fPIC") endif() diff --git a/cmake/dispatchers/std.cmake b/cmake/dispatchers/std.cmake index 3394942..b91520e 100644 --- a/cmake/dispatchers/std.cmake +++ b/cmake/dispatchers/std.cmake @@ -8,7 +8,7 @@ if (HSMBUILD_DISPATCHER_STD) endif() # Export variables - if (NOT WIN32) + if (NOT WIN32 AND NOT HSMBUILD_PLATFORM STREQUAL "qnx") set(HSMCPP_STD_CXX_FLAGS ${HSMCPP_CXX_FLAGS} -pthread CACHE STRING "" FORCE) else() set(HSMCPP_STD_CXX_FLAGS ${HSMCPP_CXX_FLAGS} "" CACHE STRING "" FORCE) diff --git a/cmake/platforms/qnx.cmake b/cmake/platforms/qnx.cmake new file mode 100644 index 0000000..de2e25b --- /dev/null +++ b/cmake/platforms/qnx.cmake @@ -0,0 +1,2 @@ +add_definitions(-DPLATFORM_POSIX=1) +add_definitions(-DPLATFORM_QNX=1) \ No newline at end of file diff --git a/examples/01_blink/01_blink.ino b/examples/01_blink/01_blink.ino deleted file mode 100644 index cb35e74..0000000 --- a/examples/01_blink/01_blink.ino +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2023 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details - -#include -#include -#include - -#include "BlinkHsmBase.hpp" - -/* - This example demonstrates basic usage of hsmcpp library by implementing a LED - blinking. - - The main idea is to separate behavior of your device from code and define it - in a form of a state machine. - - State machine is defined in blink.scxml file and looks like this: - - --------- --------- SWITCH --------- - | | | LED |--------->| LED | - [*]-->| setup |-------->| On | | Off | - | | | |<---------| | - --------- --------- SWITCH --------- - - SWITCH transition is repeatedly triggered on 1 second timeout, which is - started when we transition from [setup] to [LED On] state. A corresponding - state callback will be called from BlinkHSM When transition is executed and - HSM moved to a new state. - - ============================== - # Building with PlatformIO - ============================== - blink.scxml will be automatically generated into BlinkHsmBase C++ class during - build. Generation is controlled by "custom_hsm_files" and "custom_hsm_gendir" - settings in platformio.ini. - - ============================== - # Building with ArduinoIDE - ============================== - You will need to manually generate HSM class before compiling this sketch. - Generator is installed as part of the hsmcpp library and is located in: - /libraries/hsmcpp/tools/scxml2gen.py - - is path to your sketchbook location (see File > Preferences > Sketchbook location). - - To generate BlinkHsmBase class run this command from inside your sketch folder: - python3 /libraries/hsmcpp/tools/scxml2gen.py -code -scxml ./blink.scxml -class_name BlinkHsm -class_suffix Base -template_hpp /libraries/hsmcpp/tools/template.hpp -template_cpp /libraries/hsmcpp/tools/template.cpp -dest_dir ./ -*/ - -// Implementation of state machine callbacks. -class BlinkHSM : public BlinkHsmBase { -protected: - void onOff(const hsmcpp::VariantVector_t &args) override { - // turn the LED off by making the voltage LOW - digitalWrite(LED_BUILTIN, LOW); - } - - void onOn(const hsmcpp::VariantVector_t &args) override { - // turn the LED on (HIGH is the voltage level) - digitalWrite(LED_BUILTIN, HIGH); - } -}; - -// global instances of state machine and dispatcher -BlinkHSM *gHSM = nullptr; -std::shared_ptr gDispatcher; - -void setup() { - // initialize digital pin LED_BUILTIN as an output. - pinMode(LED_BUILTIN, OUTPUT); - - // Create instance of the state machine - // Usually you would want the instance of your state machine to live for - // signifficant period of time (or forever). Because of this, it should be - // created dynamically to use heap memory instead of stack. - gHSM = new BlinkHSM(); - // Create event dispatcher - gDispatcher = hsmcpp::HsmEventDispatcherArduino::create(); - // initialize state machine - gHSM->initialize(gDispatcher); - - // call HSM transition to start blinking - gHSM->transition(BlinkHsmEvents::START); -} - -void loop() { - // tell dispatcher to process pending events and transitions - gDispatcher->dispatchEvents(); -} \ No newline at end of file diff --git a/examples/01_blink/blink.scxml b/examples/01_blink/blink.scxml deleted file mode 100644 index bb92816..0000000 --- a/examples/01_blink/blink.scxml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/examples/02_blink_button/02_blink_button.ino b/examples/02_blink_button/02_blink_button.ino deleted file mode 100644 index fe6d72a..0000000 --- a/examples/02_blink_button/02_blink_button.ino +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (C) 2023 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details - -#include -#include -#include - -#include "BlinkButtonHsmBase.hpp" - -/* - This example demonstrates basic usage of hsmcpp library by controlling LED on - button press. - - Note: This code assumes the button is connected in a way that when it's - pressed, the input on the button pin becomes HIGH. - - The main idea is to separate behavior of your device from code and define it - in a form of a state machine. - - State machine is defined in blink_button.scxml file and looks like this: - - +-------+ BUTTON_PRESSED +-------+ - | LED |------------------>| LED | - [*]-->| Off | | On | - | |<------------------| | - +-------+ BUTTON_RELEASED +-------+ - - BUTTON_PRESSED and BUTTON_RELEASED events are sent from loop() based on - current button state. A corresponding state callback will be called from - BlinkHSM When transition is executed and HSM moved to a new state. - - ============================== - # Building with PlatformIO - ============================== - blink_button.scxml will be automatically generated into BlinkButtonHsmBase C++ - class during build. Generation is controlled by "custom_hsm_files" and - "custom_hsm_gendir" settings in platformio.ini. - - ============================== - # Building with ArduinoIDE - ============================== - You will need to manually generate HSM class before compiling this sketch. - Generator is installed as part of the hsmcpp library and is located in: - /libraries/hsmcpp/tools/scxml2gen.py - - is path to your sketchbook location (see File > Preferences > Sketchbook location). - - To generate BlinkHsmBase class run this command from inside your sketch folder: - python3 /libraries/hsmcpp/tools/scxml2gen.py -code -scxml ./blink.scxml -class_name BlinkHsm -class_suffix Base -template_hpp /libraries/hsmcpp/tools/template.hpp -template_cpp /libraries/hsmcpp/tools/template.cpp -dest_dir ./ -*/ - -#define PIN_BUTTON (14) - -// Implementation of state machine callbacks. -class BlinkHSM : public BlinkButtonHsmBase { -protected: - void onOff(const hsmcpp::VariantVector_t &args) override { - // turn the LED off by making the voltage LOW - digitalWrite(LED_BUILTIN, LOW); - } - - void onOn(const hsmcpp::VariantVector_t &args) override { - // turn the LED on (HIGH is the voltage level) - digitalWrite(LED_BUILTIN, HIGH); - } -}; - -// global instances of state machine and dispatcher -BlinkHSM *gHSM = nullptr; -std::shared_ptr gDispatcher; - -void setup() { - // initialize digital pin LED_BUILTIN as an output - pinMode(LED_BUILTIN, OUTPUT); - // initialize button pin as an input - pinMode(PIN_BUTTON, INPUT); - - // Create instance of the state machine - // Usually you would want the instance of your state machine to live for - // signifficant period of time (or forever). Because of this, it should be - // created dynamically to use heap memory instead of stack. - gHSM = new BlinkHSM(); - // Create event dispatcher - gDispatcher = hsmcpp::HsmEventDispatcherArduino::create(); - // initialize state machine - gHSM->initialize(gDispatcher); -} - -void loop() { - // store previous state to be able to detect only changes in button states - // (pressed/released) - static int prevButtonState = LOW; - const int buttonState = digitalRead(PIN_BUTTON); - - if (buttonState != prevButtonState) { - prevButtonState = buttonState; - - if (buttonState == HIGH) { - gHSM->transition(BlinkButtonHsmEvents::BUTTON_PRESSED); - } else { - gHSM->transition(BlinkButtonHsmEvents::BUTTON_RELEASED); - } - } - - // tell dispatcher to process pending events and transitions - gDispatcher->dispatchEvents(); -} \ No newline at end of file diff --git a/examples/02_blink_button/blink_button.scxml b/examples/02_blink_button/blink_button.scxml deleted file mode 100644 index 55573e8..0000000 --- a/examples/02_blink_button/blink_button.scxml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/library.properties b/library.properties deleted file mode 100644 index d3bb30e..0000000 --- a/library.properties +++ /dev/null @@ -1,10 +0,0 @@ -name=hsmcpp -version=1.0.4 -author=Ihor Krechetov -maintainer=Ihor Krechetov -sentence=C++ library for Hierarchical State Machines (HSM, FSM) -paragraph=C++ library for hierarchical state machines / finite state machines. Provides a code-free visual approach for defining state machine logic using GUI editors with automatic code and diagram generation. Check out https://hsmcpp.readthedocs.io for detailed documentation. -category=Other -url=https://hsmcpp.readthedocs.io -architectures=* -includes=hsmcpp.hpp diff --git a/src/hsmcpp.hpp b/src/hsmcpp.hpp deleted file mode 100644 index 99ca225..0000000 --- a/src/hsmcpp.hpp +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (C) 2023 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details - -#ifndef HSMCPP_ARDUINO_HPP -#define HSMCPP_ARDUINO_HPP - -#include -#include - -#endif // HSMCPP_ARDUINO_HPP diff --git a/src/hsmcpp/HsmEventDispatcherArduino.hpp b/src/hsmcpp/HsmEventDispatcherArduino.hpp deleted file mode 100644 index 44eb2e3..0000000 --- a/src/hsmcpp/HsmEventDispatcherArduino.hpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (C) 2022 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details - -#ifndef HSMCPP_HSMEVENTDISPATCHERARDUINO_HPP -#define HSMCPP_HSMEVENTDISPATCHERARDUINO_HPP - -#include -#include - -#include "HsmEventDispatcherBase.hpp" - -namespace hsmcpp { - -/** - * @brief HsmEventDispatcherArduino provides dispatcher for Arduino platform. - * @details Clients are supposed to call HsmEventDispatcherArduino::dispatchEvents() in loop() method of the application. - * See @rstref{platforms-dispatcher-arduino} for details. - */ -class HsmEventDispatcherArduino : public HsmEventDispatcherBase { -private: - struct RunningTimerInfo { - unsigned long startedAt; // monotonic time when timer was started (ms) - unsigned long elapseAfter; // monotonic time when timer should elapse next time (ms) - }; - -public: - /** - * @brief Create dispatcher instance. - * @param eventsCacheSize size of the queue preallocated for delayed events - * @return New dispatcher instance. - * - * @threadsafe{Instance can be safely created and destroyed from any thread.} - */ - // cppcheck-suppress misra-c2012-17.8 ; false positive. setting default parameter value is not parameter modification - static std::shared_ptr create( - const size_t eventsCacheSize = DISPATCHER_DEFAULT_EVENTS_CACHESIZE); - - /** - * @brief See IHsmEventDispatcher::start() - * @concurrencysafe{ } - */ - bool start() override; - - /** - * @brief See IHsmEventDispatcher::emitEvent() - * @threadsafe{ } - */ - void emitEvent(const HandlerID_t handlerID) override; - - /** - * @brief Dispatch pending events. - * @details Applications must call this method in Android's loop() function as often as possible. It does nothing and - * returns quickly if there are no pending events. - * - * @threadsafe{ } - */ - void dispatchEvents(); - -protected: - /** - * @copydoc HsmEventDispatcherBase::HsmEventDispatcherBase() - */ - explicit HsmEventDispatcherArduino(const size_t eventsCacheSize); - - /** - * Destructor. - */ - virtual ~HsmEventDispatcherArduino(); - - /** - * @copydoc HsmEventDispatcherBase::deleteSafe() - */ - bool deleteSafe() override; - - void notifyDispatcherAboutEvent() override; - - /** - * @brief See HsmEventDispatcherBase::startTimerImpl() - * @interruptsafe{ } - */ - void startTimerImpl(const TimerID_t timerID, const unsigned int intervalMs, const bool isSingleShot) override; - /** - * @brief See HsmEventDispatcherBase::stopTimerImpl() - * @interruptsafe{ } - */ - void stopTimerImpl(const TimerID_t timerID) override; - - void handleTimers(); - -private: - std::map mRunningTimers; -}; - -} // namespace hsmcpp - -#endif // HSMCPP_HSMEVENTDISPATCHERARDUINO_HPP diff --git a/src/hsmcpp/HsmEventDispatcherBase.hpp b/src/hsmcpp/HsmEventDispatcherBase.hpp deleted file mode 100644 index 07d95f9..0000000 --- a/src/hsmcpp/HsmEventDispatcherBase.hpp +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright (C) 2021 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details - -#ifndef HSMCPP_HSMEVENTDISPATCHERBASE_HPP -#define HSMCPP_HSMEVENTDISPATCHERBASE_HPP - -#include -#include -#include -#include - -#include "IHsmEventDispatcher.hpp" -#include "os/Mutex.hpp" - -/** - * @details Default queue size used for enqueued events. - */ -#define DISPATCHER_DEFAULT_EVENTS_CACHESIZE (10) - -namespace hsmcpp { - -/** - * @brief HsmEventDispatcherBase provides common implementation for all dispatchers. - * @details Class contains all platform/framework independent logic for dispatchers. Even though it's not mandatory to use - * HsmEventDispatcherBase when implementing your own dispatcher, it's highly probable that this class will be a better choice - * than directly subclassing from IHsmEventDispatcher. - */ -class HsmEventDispatcherBase : public IHsmEventDispatcher { -protected: - struct TimerInfo { - HandlerID_t handlerID = INVALID_HSM_DISPATCHER_HANDLER_ID; - unsigned int intervalMs = 0; - bool isSingleShot = false; - }; - - struct EnqueuedEventInfo { - HandlerID_t handlerID = INVALID_HSM_DISPATCHER_HANDLER_ID; - EventID_t eventID = INVALID_HSM_EVENT_ID; - - EnqueuedEventInfo(const HandlerID_t newHandlerID, const EventID_t newEventID); - }; - -public: - /** - * @brief See IHsmEventDispatcher::stop() - * @threadsafe{ } - */ - void stop() override; - - /** - * @brief See IHsmEventDispatcher::registerEventHandler() - * @threadsafe{ } - */ - HandlerID_t registerEventHandler(const EventHandlerFunc_t& handler) override; - - /** - * @brief See IHsmEventDispatcher::unregisterEventHandler() - * @threadsafe{ } - */ - void unregisterEventHandler(const HandlerID_t handlerID) override; - - /** - * @brief See IHsmEventDispatcher::emitEvent() - * @threadsafe{ } - */ - void emitEvent(const HandlerID_t handlerID) override = 0; - - /** - * @brief See IHsmEventDispatcher::enqueueEvent() - * @concurrencysafe{ } - */ - bool enqueueEvent(const HandlerID_t handlerID, const EventID_t event) override; - - /** - * @brief See IHsmEventDispatcher::enqueueAction() - * @threadsafe{ } - */ - void enqueueAction(ActionHandlerFunc_t actionCallback) override; - - /** - * @brief See IHsmEventDispatcher::registerEnqueuedEventHandler() - * @threadsafe{ } - */ - HandlerID_t registerEnqueuedEventHandler(const EnqueuedEventHandlerFunc_t& handler) override; - - /** - * @brief See IHsmEventDispatcher::unregisterEnqueuedEventHandler() - * @threadsafe{ } - */ - void unregisterEnqueuedEventHandler(const HandlerID_t handlerID) override; - - /** - * @brief See IHsmEventDispatcher::registerTimerHandler() - * @threadsafe{ } - */ - HandlerID_t registerTimerHandler(const TimerHandlerFunc_t& handler) override; - - /** - * @brief See IHsmEventDispatcher::unregisterTimerHandler() - * @threadsafe{ } - */ - void unregisterTimerHandler(const HandlerID_t handlerID) override; - - /** - * @brief See IHsmEventDispatcher::startTimer() - * @threadsafe{ } - */ - void startTimer(const HandlerID_t handlerID, - const TimerID_t timerID, - const unsigned int intervalMs, - const bool isSingleShot) override; - - /** - * @brief See IHsmEventDispatcher::restartTimer() - * @threadsafe{ } - */ - void restartTimer(const TimerID_t timerID) override; - - /** - * @brief See IHsmEventDispatcher::stopTimer() - * @threadsafe{ } - */ - void stopTimer(const TimerID_t timerID) override; - - /** - * @brief See IHsmEventDispatcher::isTimerRunning() - * @threadsafe{ } - */ - bool isTimerRunning(const TimerID_t timerID) override; - -protected: - /** - * @brief Default constructor. - * @param eventsCacheSize size of the queue preallocated for delayed events - */ - // cppcheck-suppress misra-c2012-17.8 ; false positive. setting default parameter value is not parameter modification - explicit HsmEventDispatcherBase(const size_t eventsCacheSize = DISPATCHER_DEFAULT_EVENTS_CACHESIZE); - - /** - * Destructor. - */ - virtual ~HsmEventDispatcherBase() = default; - - static void handleDelete(HsmEventDispatcherBase* dispatcher); - - /** - * @brief Schedule instance for deletion. - * @retval false instance will be deleted later automatically - * @retval true instance can be deleted now - * - * @notthreadsafe{Should not be called multiple times} - */ - virtual bool deleteSafe() = 0; - - /** - * @brief Generate new unique ID for handler. - * @details To avoid unintended errors, even different type of handlers have unique IDs. Current implementation just returns - * an incremended ID. - * - * @return new unique handler ID - * - * @notthreadsafe{Used in registerXxxxxHandler() API which are not required to be thread-safe.} - */ - virtual HandlerID_t getNextHandlerID(); - - /** - * @brief Unregister all event handlers. - * - * @threadsafe{ } - */ - void unregisterAllEventHandlers(); - - /** - * @brief Find enqueued events handler callback. - * - * @param handlerID enqueued events handler id - * - * @return Enqueued events handler callback or nullptr if handlerID is invalid. - * - * @notthreadsafe{Used only in dispatchEnqueuedEvents(), but data race can happen if registerEnqueuedEventHandler() or - * unregisterEnqueuedEventHandler() are called from a different thread.} - */ - EnqueuedEventHandlerFunc_t getEnqueuedEventHandlerFunc(const HandlerID_t handlerID) const; - - /** - * @brief Find timer handler callback. - * - * @param handlerID timer handler id - * - * @return Timer handler callback or nullptr if handlerID is invalid. - * - * @notthreadsafe{Used only in handleTimerEvent(), but data race can happen if registerTimerHandler() or - * unregisterTimerHandler() are called from a different thread.} - */ - TimerHandlerFunc_t getTimerHandlerFunc(const HandlerID_t handlerID) const; - - /** - * @brief Platform specific implementation to start a timer. - * @details Must be implemented by derived classes. Default implementation does nothing. - * - * @param timerID unique timer id - * @param intervalMs timer interval in milliseconds - * @param isSingleShot true - timer will run only once and then will stop - * false - timer will keep running until stopTimer() is called or dispatcher is destroyed - */ - virtual void startTimerImpl(const TimerID_t timerID, const unsigned int intervalMs, const bool isSingleShot); - - /** - * @brief Platform specific implementation to stop a timer. - * @details Must be implemented by derived classes. Default implementation does nothing. - * - * @param timerID id of running or expired timer - */ - virtual void stopTimerImpl(const TimerID_t timerID); - - /** - * @brief Contains common logic for handling timer event - * - * @param timerID id of the expired timer - * - * @return interval in milliseconds to use for restarting the timer or zero if timer can be deleted - * (usually because it's a singleshot timer) - */ - unsigned int handleTimerEvent(const TimerID_t timerID); - - /** - * @brief Wakeup dispatching thread to process pending events. - * @details Must be implemented by derived classes. - */ - virtual void notifyDispatcherAboutEvent() = 0; - - /** - * @brief Dispatch currently enqueued events. - * @details Should be called by derived classes in dedicated dispatcher thread. - * - * @threadsafe{ } - */ - void dispatchEnqueuedEvents(); - - /** - * @brief Dispatch currently enqueued actions. - * @details Should be called by derived classes in dedicated dispatcher thread. - * - * @threadsafe{ } - */ - void dispatchPendingActions(); - - /** - * @brief Dispatch all pending events. - * @details Should be called by derived classes in dedicated dispatcher thread. - * - * @threadsafe{ } - */ - void dispatchPendingEvents(); - - /** - * @brief Dispatch pending events based on provided list. - * - * @param events list of events to dispatch - * - * @threadsafe{ } - */ - void dispatchPendingEventsImpl(const std::list& events); - -protected: - HandlerID_t mNextHandlerId = 1; - std::map mActiveTimers; // protected by mHandlersSync - std::map mEventHandlers; // protected by mHandlersSync - std::map mEnqueuedEventHandlers; // protected by mHandlersSync - std::map mTimerHandlers; // protected by mHandlersSync - std::list mPendingActions; // protected by mEmitSync - std::list mPendingEvents; // protected by mEmitSync - std::vector mEnqueuedEvents; // protected by mEnqueuedEventsSync - Mutex mEmitSync; - Mutex mHandlersSync; - Mutex mEnqueuedEventsSync; - Mutex mRunningTimersSync; - bool mStopDispatcher = false; -}; - -} // namespace hsmcpp -#endif // HSMCPP_HSMEVENTDISPATCHERBASE_HPP diff --git a/src/hsmcpp/HsmTypes.hpp b/src/hsmcpp/HsmTypes.hpp deleted file mode 100644 index 44cf4f5..0000000 --- a/src/hsmcpp/HsmTypes.hpp +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (C) 2023 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -/** - * @file - * Contains definitions of global types used by HSM. -*/ - -#ifndef HSMCPP_HSMTYPES_HPP -#define HSMCPP_HSMTYPES_HPP - -#include - -#include "variant.hpp" - -namespace hsmcpp { - -using HandlerID_t = int32_t; ///< Handler ID returned by dispatchers for events, timers and enqueued events handlers -/** Type representing timer ID. Used to identify timer when calling timer related API of HierarchicalStateMachine class. */ -using TimerID_t = int32_t; -using EventID_t = int32_t; ///< Type representing HSM event ID. Used when working with HierarchicalStateMachine class. -using StateID_t = int32_t; ///< Type representing HSM state ID. Used when working with HierarchicalStateMachine class. - -/** This macro can be used to indicate to sync API of HierarchicalStateMachine to wait indefinitely for an operation to finish. */ -constexpr int HSM_WAIT_INDEFINITELY = 0; - -/** Generic value for an invalid ID. Not supposed to be used directly. */ -constexpr int INVALID_ID = -1000; -/** Invalid ID for HSM event. Used in relation with EventID_t type. */ -constexpr hsmcpp::EventID_t INVALID_HSM_EVENT_ID = static_cast(INVALID_ID); -/** Invalid ID for HSM state. Used in relation with StateID_t type. */ -constexpr hsmcpp::StateID_t INVALID_HSM_STATE_ID = static_cast(INVALID_ID); -/** Invalid ID for HSM timer. Used in relation with TimerID_t type. */ -constexpr hsmcpp::TimerID_t INVALID_HSM_TIMER_ID = static_cast(INVALID_ID); - -/** Invalid dispatcher handler ID. Used in relation with HandlerID_t type. */ -constexpr hsmcpp::HandlerID_t INVALID_HSM_DISPATCHER_HANDLER_ID = 0; - -/** - * Function type for HierarchicalStateMachine transition callbacks. - * - * @param VariantVector_t \c args value provided in HierarchicalStateMachine::transition() or similar API - */ -using HsmTransitionCallback_t = std::function; -/** - * Function type for HierarchicalStateMachine condition callbacks. - * - * @param VariantVector_t \c args value provided in HierarchicalStateMachine::transition() or similar API - */ -using HsmTransitionConditionCallback_t = std::function; -/** - * Function type for HierarchicalStateMachine state changed callbacks. - * - * @param VariantVector_t \c args value provided in HierarchicalStateMachine::transition() or similar API - */ -using HsmStateChangedCallback_t = std::function; -/** - * Function type for HierarchicalStateMachine state entering callbacks. - * - * @param VariantVector_t \c args value provided in HierarchicalStateMachine::transition() or similar API - * - * @return Callback should return TRUE to allow current transition. Returning FALSE will cause ongoing transition to be canceled. - */ -using HsmStateEnterCallback_t = std::function; -/** - * Function type for HierarchicalStateMachine state exiting callbacks. - * @return Callback should return TRUE to allow current transition. Returning FALSE will cause ongoing transition to be canceled. - */ -using HsmStateExitCallback_t = std::function; -/** - * Function type for HierarchicalStateMachine failed transition callbacks. Callback is called whenever HSM failed to process new - * event (due to no registered transition, failed conditions or transition being canceled by a enter/exit callback). - * - * @param std::list list of active states which didn't have a matching transition - * @param EventID_t id of the event which was not processed - * @param VariantVector_t \c args value provided in HierarchicalStateMachine::transition() or similar API - */ -using HsmTransitionFailedCallback_t = std::function&, const EventID_t, const VariantVector_t&)>; - -// cppcheck-suppress misra-c2012-20.7 ; enclosing input expressions in parentheses is not needed (and will not compile) -#define HsmTransitionCallbackPtr_t(_class, _func) void (_class::*_func)(const VariantVector_t&) -// cppcheck-suppress misra-c2012-20.7 -#define HsmTransitionConditionCallbackPtr_t(_class, _func) bool (_class::*_func)(const VariantVector_t&) -// cppcheck-suppress misra-c2012-20.7 -#define HsmStateChangedCallbackPtr_t(_class, _func) void (_class::*_func)(const VariantVector_t&) -// cppcheck-suppress misra-c2012-20.7 -#define HsmStateEnterCallbackPtr_t(_class, _func) bool (_class::*_func)(const VariantVector_t&) -// cppcheck-suppress misra-c2012-20.7 -#define HsmStateExitCallbackPtr_t(_class, _func) bool (_class::*_func)() -// cppcheck-suppress misra-c2012-20.7 -#define HsmTransitionFailedCallbackPtr_t(_class, _func) void (_class::*_func)(const std::list&, const EventID_t, const VariantVector_t&) - -/** - * @enum HistoryType - * @brief Defines the type of history state. - * @details The history state can be shallow or deep (see @rstref{features-history} for details). - */ -enum class HistoryType { - SHALLOW, ///< remember only the immediate substate of the parent state - DEEP ///< remember the last active state of the substate hierarchy -}; - -/** - * @enum TransitionType - * Defines the type of a self transition (see @rstref{features-transitions-selftransitions} for - * details). - */ -enum class TransitionType { - INTERNAL_TRANSITION, ///< do not cause a state change during self transition - EXTERNAL_TRANSITION ///< exit current state during self transition -}; - -/** - * @enum StateActionTrigger - * Defines the trigger for a state action (see @rstref{features-states-actions} for details). - */ -enum class StateActionTrigger { - ON_STATE_ENTRY, ///< trigger action on state entry - ON_STATE_EXIT ///< trigger action on state exit -}; - -/** - * @enum StateAction - * @brief Defines the type of state action (see @rstref{features-states-actions} for details). - * - * @details The state action can start, stop, or restart a timer, or cause a transition to another state. - * Depending on the action type, you need to provide specific arguments when calling registerStateAction() - */ -enum class StateAction { - START_TIMER, ///< **Arguments**: TimerID_t timerID, int32_t intervalMs, bool singleshot - STOP_TIMER, ///< **Arguments**: TimerID_t timerID - RESTART_TIMER, ///< **Arguments**: TimerID_t timerID - TRANSITION, ///< **Arguments**: EventID_t eventID -}; - -} // namespace hsmcpp - -#endif // HSMCPP_HSMTYPES_HPP \ No newline at end of file diff --git a/src/hsmcpp/IHsmEventDispatcher.hpp b/src/hsmcpp/IHsmEventDispatcher.hpp deleted file mode 100644 index 28470ce..0000000 --- a/src/hsmcpp/IHsmEventDispatcher.hpp +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright (C) 2021 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details - -#ifndef HSMCPP_IHSMEVENTDISPATCHER_HPP -#define HSMCPP_IHSMEVENTDISPATCHER_HPP - -#include "HsmTypes.hpp" - -namespace hsmcpp { -/** - * @brief Event handler callback. - * @details When using class members, developers are responsible to track corresponding object's lifetime. Clients can notify - * dispatcher that callback became invalid by returning FALSE. - * @retval true handler's callback can be used further - * @retval false handler's callback is invalid and should not be reused - */ -using EventHandlerFunc_t = std::function; -/** - * @brief Timer event handler callback. - * @details When using class members, developers are responsible to track corresponding object's lifetime. Clients can notify - * dispatcher that callback became invalid by returning FALSE. - * - * @param timerID id of the timer event - * @retval true handler's callback can be used further - * @retval false handler's callback is invalid and should not be reused - */ -using TimerHandlerFunc_t = std::function; -/** - * @brief Handler callback for enqueued events. - * @details When using class members, developers are responsible to track corresponding object's lifetime. Clients can notify - * dispatcher that callback became invalid by returning FALSE. - * - * @param eventID id of the event - * @retval true handler's callback can be used further - * @retval false handler's callback is invalid and should not be reused - */ -using EnqueuedEventHandlerFunc_t = std::function; - -/** - * @brief Handler callback for enqueued actions. - * @details See IHsmEventDispatcher::enqueueAction() for details. - */ -using ActionHandlerFunc_t = std::function; - -/** - * @brief IHsmEventDispatcher provides an interface for events dispatcher implementations. - * @details The IHsmEventDispatcher class defines the standard dispatcher interface that HSM uses to process internal events. It - * is not supposed to be instantiated directly. Instead, you should subclass it to create platform or framework specific - * dispatchers. - * When subclassing IHsmEventDispatcher, at the very least you must implement: - * \li registerEventHandler() - * \li unregisterEventHandler() - * \li emitEvent() - * - * For other API you can make empty implementation. This will be sufficient for basic HierarchicalStateMachine functionality. - * For timers support following API must be implemented: - * \li registerTimerHandler() - * \li unregisterTimerHandler() - * \li startTimer() - * \li restartTimer() - * \li stopTimer() - * \li isTimerRunning() - * - * For interrupt-safe transitions you need to implement: - * \li registerEnqueuedEventHandler() - * \li unregisterEnqueuedEventHandler() - * \li enqueueEvent() - */ -class IHsmEventDispatcher { -public: - /** - * @brief Destructor. - * - * @warning Make sure to release/delete all HSM instances which are using this dispatcher before deleting it. Failing to do - * so will result in undefined behavior and (usually) a crash. - */ - virtual ~IHsmEventDispatcher() = default; - - /** - * @brief Start events dispatching. - * @details Is called by HierarchicalStateMachine::initialize(). Implementation of this method is optional and depends on - * individual dispatcher design. Calling this function multiple times should have no effect. In case there is no special - * start up logic dispatcher implementation must return true. - * - * @remark Implementation should be non blocking and doesn't have to be threadsafe. - * - * @retval true dispatching was successfully started or it is already running - * @retval false failed to start events dispatching - */ - virtual bool start() = 0; - - /** - * @brief Stop dispatching events. - * @details Future calls to dispatchEvents() will have no effect. - * @remark Operation is performed asynchronously. It does not interrupt currently handled event, but will cancel all other - * pending events. - */ - virtual void stop() = 0; - - /** - * @brief Register a new event handler. - * @details Dispatcher must support registering multiple handlers. Order in which these handlers are triggered is not - * important. - * @remark As a general rule registerEventHandler() and unregisterEventHandler() are not expected to be thread-safe and - * should be used from the same thread. But this depends on specific Dispatcher implementation. - * - * @param handler handler callback - * - * @return unique handler ID - */ - virtual HandlerID_t registerEventHandler(const EventHandlerFunc_t& handler) = 0; - - /** - * @brief Unregister events handler. - * @param handlerID handler ID received from registerEventHandler() - */ - virtual void unregisterEventHandler(const HandlerID_t handlerID) = 0; - - /** - * @brief Register a new handler for enqueued events. - * @details Dispatcher must support registering multiple handlers. Order in which these handlers are triggered is not - * important. - * @remark As a general rule registerEnqueuedEventHandler() and unregisterEnqueuedEventHandler() are not expected to be - * thread-safe. - * - * @param handler handler callback - * - * @return unique handler ID - */ - virtual HandlerID_t registerEnqueuedEventHandler(const EnqueuedEventHandlerFunc_t& handler) = 0; - - /** - * @brief Unregister events handler. - * @param handlerID handler ID received from registerEnqueuedEventHandler() - */ - virtual void unregisterEnqueuedEventHandler(const HandlerID_t handlerID) = 0; - - /** - * @brief Add a new event to the queue for later dispatching. - * @details Dispatcher should initiate processing of the new event as soon as possible. - * - * @param handlerID id of the handler that should process the event - * - * @threadsafe{Dispatcher implementation **must guarantee** that this call is thread-safe.} - */ - virtual void emitEvent(const HandlerID_t handlerID) = 0; - - /** - * @brief Add a new event to the queue for later dispatching. - * @details Behaves same way as emitEvent(), but is intended to be used only from inside signals/interrupts. - * Unlike emitEvent(), there is usually a limit of how many events can be added to the queue - * at the same time (depends on individual implementation). - * - * @warning Implementation of this method **SHOULD NOT** use dynamic memory allocations. - * - * @param handlerID id of the handler that should be called - * @param event id of the hsm event - * - * @retval true event was successfully added - * @retval false failed to add event because internal queue is full - * - * @concurrencysafe{Dispatcher implementation **must guarantee** that this call is thread-safe and signals/interrupts safe.} - */ - virtual bool enqueueEvent(const HandlerID_t handlerID, const EventID_t event) = 0; - - /** - * @brief Enqueue action to be executed on dispatcher's thread. - * @details Enqueued actions have highest priority compared to events and will be executed as soon as possible. If there are - * any events being processed then dispatcher will first finish their execution. - * - * @param actionCallback functor to be called by dispatcher - */ - virtual void enqueueAction(ActionHandlerFunc_t actionCallback) = 0; - - /** - * @brief Register a new handler for timers. - * @details Dispatcher must support registering multiple handlers. Order in which these handlers are triggered is not - * important. Used by HierarchicalStateMachine to receive notifications about timer events. - * - * @param handler handler callback - * - * @return unique handler ID which must be used when starting a new timer with startTimer() - */ - virtual HandlerID_t registerTimerHandler(const TimerHandlerFunc_t& handler) = 0; - - /** - * @brief Unregister timer handler. - * @details This will automatically stop all timers registered with this handler. - * - * @param handlerID handler ID received from registerTimerHandler() - */ - virtual void unregisterTimerHandler(const HandlerID_t handlerID) = 0; - - /** - * @brief Start a timer. - * @details If timer with this ID is already running it will be restarted with new settings. - * - * @param handlerID handler id for wich to start the timer (returned from registerTimerHandler()) - * @param timerID unique timer id - * @param intervalMs timer interval in milliseconds - * @param isSingleShot true - timer will run only once and then will stop - * false - timer will keep running until stopTimer() is called or dispatcher is destroyed - * - * @threadsafe{Dispatcher implementation **must guarantee** that this call is thread-safe.} - */ - virtual void startTimer(const HandlerID_t handlerID, - const TimerID_t timerID, - const unsigned int intervalMs, - const bool isSingleShot) = 0; - - /** - * @brief Restart running or expired timer. - * @details Timer is restarted with the same arguments which were provided to startTimer(). Only currently running or - * expired timers (with isSingleShot set to true) will be restarted. Has no effect if called for a timer which was not - * started. - * - * @param timerID id of running timer - * - * @threadsafe{Dispatcher implementation **must guarantee** that this call is thread-safe.} - */ - virtual void restartTimer(const TimerID_t timerID) = 0; - - /** - * @brief Stop active timer. - * @details Function stops an active timer without triggering any notifications and unregisters it. Further calls to - * restartTimer() will have no effects untill it's started again with startTimer(). - * - * @remark For expired timers (which have isSingleShot property set to true), funtion simply unregisters them. - * - * @param timerID id of running or expired timer - * - * @threadsafe{Dispatcher implementation **must guarantee** that this call is thread-safe.} - */ - virtual void stopTimer(const TimerID_t timerID) = 0; - - /** - * @brief Check if timer is currently running. - * - * @param timerID id of the timer to check - * - * @retval true timer is running - * @retval false timer is not running - * - * @threadsafe{Dispatcher implementation **must guarantee** that this call is thread-safe.} - */ - virtual bool isTimerRunning(const TimerID_t timerID) = 0; -}; - -} // namespace hsmcpp - -#endif // HSMCPP_IHSMEVENTDISPATCHER_HPP diff --git a/src/hsmcpp/hsm.hpp b/src/hsmcpp/hsm.hpp deleted file mode 100644 index 62717ab..0000000 --- a/src/hsmcpp/hsm.hpp +++ /dev/null @@ -1,908 +0,0 @@ -// Copyright (C) 2021 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details - -#ifndef HSMCPP_HSM_HPP -#define HSMCPP_HSM_HPP - -#include -#include -#include -#include - -#include "HsmTypes.hpp" -#include "variant.hpp" - -namespace hsmcpp { - -class IHsmEventDispatcher; - -/** - * @brief Implements a Hierarchical State Machine (HSM) for event-driven systems. - * - * @details Represents a hierarchical state machine (HSM) with a set of states and state transitions. - * This class allows for the creation and manipulation of a state machine, where the HSM can be in one - * of a finite set of states, and can transition between states based on a set of defined rules. - * The HSM can have multiple levels of nested states and transitions can be defined between any two states, - * including self-transitions. - * The class provides public functions to: - * \li define state machine structure - * \li define transition rules - * \li register state and transition callbacks - * \li trigger state transitions - * \li interact with HSM timers - */ -class HierarchicalStateMachine { -public: - /** - * @brief Constructor that sets the initial state of the HSM. - * - * @details Initial state can be modified later with setInitialState(). - * - * @param initialState The initial state of the HSM. - */ - explicit HierarchicalStateMachine(const StateID_t initialState); - - /** - * @brief Destructor. - - * @notthreadsafe{Internally uses release().} - */ - virtual ~HierarchicalStateMachine(); - - /** - * @brief Sets the initial state of the HSM. - * @remark Has no effect when called after initialize(). - * - * @param initialState The initial state of the HSM. - * - * @concurrencysafe{ } - */ - void setInitialState(const StateID_t initialState); - - /** - * @brief Initializes the HSM. - * @details Registers HSM with provided event dispatcher and transitions state machine into it's initial state. HSM - * structure must be registered **BEFORE** calling it. Changing structure after this call can result in undefined behavior - * and is not advised. - * - * @remark If initial state has registered callbacks or actions they will be executed synchronously during - * initialize() call. - * - * @warning HSM does not take ownership of the dispatcher. User is responsible for keeping dispatcher instance alive as long - * as HSM object exists or until call to release(). - * - * @param dispatcher An event dispatcher that can be used to receive events and dispatch them to the HSM. - * @return true if initialization succeeds, false otherwise. - * - * @notthreadsafe{Internally uses IHsmEventDispatcher::registerEventHandler() and IHsmEventDispatcher::start(). Usually must - * be called from the same thread where dispatcher was created.} - */ - virtual bool initialize(const std::weak_ptr& dispatcher); - - /** - * @brief Returns dispatcher that was passed to initialize() method. - * - * @return dispatcher used by HSM or nullptr (if HSM was not initialized or release() was called). - * - * @threadsafe{ } - */ - std::weak_ptr dispatcher() const; - - /** - * @brief Checks initialization status of HSM. - * - * @retval true HSM is initialized - * @retval false HSM is not initialized - * - * @concurrencysafe{ } - */ - bool isInitialized() const; - - /** - * @brief Releases dispatcher instance and frees any allocated internal resources. - * @details Internally calls IHsmEventDispatcher::unregisterEventHandler(). Usually, HSM has to be released on the same - * thread it was initialized. - * - * @warning HSM can't be reused after calling this API. - * - * @note Usually you don't need to call this function directly. The only scenario when it's needed is for multithreaded - * environment where it's impossible to delete HSM instance on the same thread where it was initialized. In this case you - * call release() on the dispatcher's thread before deleting HSM instance on another thread. - * - * @notthreadsafe{Usually must be called on the same thread as initialize(). Releases reference to dispatcher. In case - * HierarchicalStateMachine object is the only one owning IHsmEventDispatcher reference, then you need to check the - * limitations for deleting dispatcher instance. See the documentation for the used dispatcher.} - */ - void release(); - - /** - * @brief Registers a callback function to be called when a transition fails. - * @details Transition failure is usually caused by: - * \li no defined transition from the current active state for triggered event - * \li false conditions for all matching transitions - * \li transition was blocked by exit or enter callback returning false - * - * @param onFailedTransition The callback function to be called when transition fails. - * - * @concurrencysafe{ } - */ - void registerFailedTransitionCallback(HsmTransitionFailedCallback_t onFailedTransition); - - /** - * @brief Registers a class member as a callback function to be called when a transition fails. - * @copydetails registerFailedTransitionCallback() - * - * @param handler Pointer to an object whose class members will be used as callbacks. - */ - template - void registerFailedTransitionCallback(HsmHandlerClass* handler, - HsmTransitionFailedCallbackPtr_t(HsmHandlerClass, onFailedTransition)); - - /** - * @brief Registers a new state and optional state callbacks. - * - * @param state unique ID of the state to be registered. - * @param onStateChanged (optional) callback function to be called when the state became active. - * @param onEntering (optional) callback function to be called when entering the state. - * @param onExiting (optional) callback function to be called before exiting the state. - * - * @notthreadsafe{Calling thing API from multiple threads can cause data races and will result in undefined behavior} - */ - void registerState(const StateID_t state, - HsmStateChangedCallback_t onStateChanged = nullptr, - HsmStateEnterCallback_t onEntering = nullptr, - HsmStateExitCallback_t onExiting = nullptr); - - /** - * @brief Registers a new state and optional state callbacks (using class members). - * @copydetails registerState() - * @tparam handler Pointer to an object whose class members will be used as callbacks. - * - * @warning If handler object is destroyed while HSM instance is still running it will result in a crash. - */ - template - void registerState(const StateID_t state, - HsmHandlerClass* handler = nullptr, - HsmStateChangedCallbackPtr_t(HsmHandlerClass, onStateChanged) = nullptr, - HsmStateEnterCallbackPtr_t(HsmHandlerClass, onEntering) = nullptr, - HsmStateExitCallbackPtr_t(HsmHandlerClass, onExiting) = nullptr); - - /** - * @brief Registers state as final. - * - * @details See @rstref{features-substates-final_state} for details. - * - * @param state unique ID of the state to be registered as final. - * @param event (optional) event ID to automatically trigger when entering a final state. If not set (INVALID_HSM_EVENT_ID - * value is used) then HSM will trigger same event which was used to transition into this final state. - * @param onStateChanged (optional) callback function to be called when the state became active. - * @param onEntering (optional) callback function to be called when entering the state. - * @param onExiting (optional) callback function to be called before exiting the state. - * - * @notthreadsafe{Calling thing API from multiple threads can cause data races and will result in undefined behavior} - */ - void registerFinalState(const StateID_t state, - const EventID_t event = INVALID_HSM_EVENT_ID, - HsmStateChangedCallback_t onStateChanged = nullptr, - HsmStateEnterCallback_t onEntering = nullptr, - HsmStateExitCallback_t onExiting = nullptr); - - /** - * @brief Registers a state as final using class members as callbacks - * @copydetails registerFinalState() - * - * @warning If handler object is destroyed while HSM instance is still running it will result in a crash. - * - * @param handler Pointer to an object whose class members will be used as callbacks. - */ - template - void registerFinalState(const StateID_t state, - const EventID_t event = INVALID_HSM_EVENT_ID, - HsmHandlerClass* handler = nullptr, - HsmStateChangedCallbackPtr_t(HsmHandlerClass, onStateChanged) = nullptr, - HsmStateEnterCallbackPtr_t(HsmHandlerClass, onEntering) = nullptr, - HsmStateExitCallbackPtr_t(HsmHandlerClass, onExiting) = nullptr); - - /** - * @brief Registers a history state with the state machine. - * - * @details See @rstref{features-history} for details. - * - * @param parent ID of the parent state. - * @param historyState ID of the state to be registered as history state. - * @param type type of history to be used. - * @param defaultTarget ID of the default target state to be used when transitioning into empty history state. - * @param transitionCallback transition callback function to be called when the history state is entered. - * - * @notthreadsafe{Calling thing API from multiple threads can cause data races and will result in undefined behavior} - */ - // TODO: check structure and return FALSE? - void registerHistory(const StateID_t parent, - const StateID_t historyState, - const HistoryType type = HistoryType::SHALLOW, - const StateID_t defaultTarget = INVALID_HSM_STATE_ID, - HsmTransitionCallback_t transitionCallback = nullptr); - - /** - * @brief Registers a history state with the state machine (using class member as a callback) - * @copydetails registerHistory() - * - * @warning If handler object is destroyed while HSM instance is still running it will result in a crash. - * - * @param handler Pointer to an object whose class members will be used as callbacks. - */ - template - void registerHistory(const StateID_t parent, - const StateID_t historyState, - const HistoryType type = HistoryType::SHALLOW, - const StateID_t defaultTarget = INVALID_HSM_STATE_ID, - HsmHandlerClass* handler = nullptr, - HsmTransitionCallbackPtr_t(HsmHandlerClass, transitionCallback) = nullptr); - - /** - * @brief Registers state as a substate. - * @details Substate must be first registered with a call to registerState(). - * HSM will make a sanity structure check if HSM_ENABLE_SAFE_STRUCTURE. Following cases are not allowed: - * \li parent and substate can't be same - * \li substate can't belong to multiple parents - * \li circular dependencies are not allowed (A -> B -> A -> ...) - * See @rstref{features-substates} for details. - * - * @param parent ID of the parent state. - * @param substate ID of the state to be registered as substate - * @retval true substate was successfully registered - * @retval false registering substate is not allowed - * - * @notthreadsafe{Calling thing API from multiple threads can cause data races and will result in undefined behavior} - */ - bool registerSubstate(const StateID_t parent, const StateID_t substate); - - /** - * @brief Registers an entry point for a parent state. - * @details Entry point state must be first registered with a call to registerState(). - * HSM will make a sanity structure check if HSM_ENABLE_SAFE_STRUCTURE. Following cases are not allowed: - * \li parent and entry point can't be same - * \li entry point can't belong to multiple parents - * \li circular dependencies are not allowed (A -> B -> A -> ...) - * See @rstref{features-substates-entry_points} for details. - * - * @param parent ID of the parent state. - * @param substate ID of the substate to be registered as an entry point. - * @param onEvent (optional) ID of the event that should match event that caused activation of the parent state. - * @param conditionCallback (optional) callback function that will be called to determine if the transition to the - * entry point is allowed or not. - * @param expectedConditionValue (optional) expected value from the condition callback to allow transition to the entry - * state. - * @retval true substate was successfully registered - * @retval false registering substate is not allowed - * - * @notthreadsafe{Calling thing API from multiple threads can cause data races and will result in undefined behavior} - */ - bool registerSubstateEntryPoint(const StateID_t parent, - const StateID_t substate, - const EventID_t onEvent = INVALID_HSM_EVENT_ID, - HsmTransitionConditionCallback_t conditionCallback = nullptr, - const bool expectedConditionValue = true); - - /** - * @brief Registers an entry point for a parent state with class member as a callback. - * @copydetails registerSubstateEntryPoint() - * - * @warning If handler object is destroyed while HSM instance is still running it will result in a crash. - * - * @param handler Pointer to an object whose class members will be used as callbacks. - */ - template - bool registerSubstateEntryPoint(const StateID_t parent, - const StateID_t substate, - const EventID_t onEvent = INVALID_HSM_EVENT_ID, - HsmHandlerClass* handler = nullptr, - HsmTransitionConditionCallbackPtr_t(HsmHandlerClass, conditionCallback) = nullptr, - const bool expectedConditionValue = true); - - /** - * @brief Registers a timer to be used inside HSM. - * @details This function registers new timer and it's event. When the timer expires, the event - * is sent to the state machine. - * - * @param timerID unique ID of the timer. - * @param event ID of the event to send when timer expires. - * - * @notthreadsafe{Calling thing API from multiple threads can cause data races and will result in undefined behavior} - */ - void registerTimer(const TimerID_t timerID, const EventID_t event); - - // TODO: add support for transition actions - /** - * @brief Registers a state action with optional arguments. - * @details The action will be triggered depending on the specified action trigger when the state is activated. - * See @rstref{features-states-actions} for details. - * - * @param state ID of the state for actions are being registered. - * @param actionTrigger trigger for the state action. - * @param action action type to register. - * @param args optional arguments for the state action (see StateAction enum for details). - * @retval true state action was registered - * @retval false action registration failed due to invalid arguments - * - * @notthreadsafe{Calling thing API from multiple threads can cause data races and will result in undefined behavior} - */ - template - bool registerStateAction(const StateID_t state, - const StateActionTrigger actionTrigger, - const StateAction action, - Args&&... args); - - /** - * @brief Registers a transition from one state to another. - * @details The transition will be triggered by the specified event when the **from** state is active. - * See @rstref{features-transitions} for details. - * - * @param from ID of the state to transition from. - * @param to ID of the state to transition to. - * @param onEvent ID of the event that triggers the transition. - * @param transitionCallback (optional) callback function that will be called when transition occurs. - * @param conditionCallback (optional) callback function that will be called to determine if the transition is allowed. - * @param expectedConditionValue (optional) expected value from the condition callback function to allow transition. - * - * @notthreadsafe{Calling thing API from multiple threads can cause data races and will result in undefined behavior} - */ - void registerTransition(const StateID_t fromState, - const StateID_t toState, - const EventID_t onEvent, - HsmTransitionCallback_t transitionCallback = nullptr, - HsmTransitionConditionCallback_t conditionCallback = nullptr, - const bool expectedConditionValue = true); - - /** - * @brief Registers a transition from one state to another. - * @copydetails registerTransition() - * - * @warning If handler object is destroyed while HSM instance is still running it will result in a crash. - * - * @param handler Pointer to an object whose class members will be used as callbacks. - */ - template - void registerTransition(const StateID_t fromState, - const StateID_t toState, - const EventID_t onEvent, - HsmHandlerClass* handler = nullptr, - HsmTransitionCallbackPtr_t(HsmHandlerClass, transitionCallback) = nullptr, - HsmTransitionConditionCallbackPtr_t(HsmHandlerClass, conditionCallback) = nullptr, - const bool expectedConditionValue = true); - - /** - * @brief Register a self-transition for a state. - * @details This function registers a self-transition for a given state. A self-transition is a transition from a state - * to itself, triggered by a specific event. The transition can be either internal or external. The user can also register a - * transition callback and a transition condition callback. - * See @rstref{features-transitions-selftransitions} for details. - * - * @param state ID of the state to register self-transition for - * @param onEvent ID of event that triggers self-transition - * @param type type of self transition - * @param transitionCallback A function that is called when the transition occurs (default: nullptr). - * @param conditionCallback A function that is called to determine if the transition is allowed (default: nullptr). - * @param expectedConditionValue The expected value returned by the condition callback (default: true). - * - * @notthreadsafe{Calling thing API from multiple threads can cause data races and will result in undefined behavior} - */ - void registerSelfTransition(const StateID_t state, - const EventID_t onEvent, - const TransitionType type = TransitionType::EXTERNAL_TRANSITION, - HsmTransitionCallback_t transitionCallback = nullptr, - HsmTransitionConditionCallback_t conditionCallback = nullptr, - const bool expectedConditionValue = true); - - /** - * @brief Register a self-transition for a state (using class members as callback). - * @copydetails registerSelfTransition() - * - * @warning If handler object is destroyed while HSM instance is still running it will result in a crash. - * - * @param handler Pointer to an object whose class members will be used as callbacks. - */ - template - void registerSelfTransition(const StateID_t state, - const EventID_t onEvent, - const TransitionType type = TransitionType::EXTERNAL_TRANSITION, - HsmHandlerClass* handler = nullptr, - HsmTransitionCallbackPtr_t(HsmHandlerClass, transitionCallback) = nullptr, - HsmTransitionConditionCallbackPtr_t(HsmHandlerClass, conditionCallback) = nullptr, - const bool expectedConditionValue = true); - - /** - * @brief Get the ID of the last activated state. - * @details Returns current active state if HSM doesn't contain any parallel states. Otherwise returns most recently - * activated state. - * - * @return ID of the last active state. - * - * @notthreadsafe{Calling thing API from multiple threads can cause data races and will result in undefined behavior} - */ - StateID_t getLastActiveState() const; - - /** - * @brief Get the list of currently active states. - * - * @return list of currently active states. - */ - const std::list& getActiveStates() const; - - /** - * @brief Check if a state is active. - * @details This function checks if a specific state is currently active in the HSM. - * - * @param state ID of the state to check - * @return True if the state is active, false otherwise. - */ - bool isStateActive(const StateID_t state) const; - - /** - * @brief Trigger a transition in the HSM. - * @details This function sends event to HSM to trigger a potential transition. The transition is executed asynchronously, - * and any registered callbacks are called in the order they were registered. The user can optionally provide arguments - * which will be passed to all callbacks triggered by transition. - * - * @param event ID of event to send to HSM - * @param args (optional) arguments to pass to the callbacks - * - * @threadsafe{ } - */ - template - void transition(const EventID_t event, Args&&... args); - - /** - * @brief Trigger a transition in the HSM. - * @details This is an extended version of transition() function. It also allows to sends an event to HSM to trigger a - * potential transition, but provides a bit more capabilities. - * - * @warning setting sync=true when calling this function from HSM callback will result in blocking HSM events processing and - * will result in a deadlock if timeoutMs is set to HSM_WAIT_INDEFINITELY. - * - * @param event ID of event to send to HSM - * @param clearQueue indicates whether to clear the pending events queue before adding a new event - * @param sync indicates whether to wait for the transition to complete before returning. Keep in mind that this **does not - * cancel** transition if it couldn't finish before timeoutMs. If you need to guarantee that transition was fully processed - * make sure to set timeoutMs to HSM_WAIT_INDEFINITELY. - * @param timeoutMs maximum time in milliseconds to wait for the transition to complete if sync is true. Use - * HSM_WAIT_INDEFINITELY to wait indefinitely. - * @param args (optional) arguments to pass to the callbacks - * - * @return always returns true if sync=false. - * @retval true (if sync=true) event was accepted and transition successfully finished - * @retval false (if sync=true) no matching transitions were found, transition was canceled or timeoutMs expired - * - * @threadsafe{ } - */ - template - bool transitionEx(const EventID_t event, const bool clearQueue, const bool sync, const int timeoutMs, Args&&... args); - - /** - * @brief Trigger a transition in the HSM with arguments passed as a vector. - * @copydetails transition() - */ - void transitionWithArgsArray(const EventID_t event, VariantVector_t&& args); - - /** - * @brief Trigger a transition in the HSM with arguments passed as a vector. - * @copydetails transitionEx() - */ - bool transitionExWithArgsArray(const EventID_t event, - const bool clearQueue, - const bool sync, - const int timeoutMs, - VariantVector_t&& args); - - /** - * @brief Trigger a transition in the HSM and process it synchronously. - * @details Convenience wrapper for transitionEx() which tries to execute transition synchronously. Please see - * transitionEx() for detailed description. - * - * @warning calling this function from HSM callback might result in a deadlock (see transitionEx() for details). - * - * @param event ID of event to send to HSM - * @param timeoutMs maximum time in milliseconds to wait for the transition to complete if sync is true. Use - * HSM_WAIT_INDEFINITELY to wait indefinitely. - * @param args (optional) arguments to pass to the callbacks - * - * @retval true event was accepted and transition successfully finished - * @retval false no matching transitions were found, transition was canceled or timeoutMs expired - * - * @threadsafe{ } - */ - template - bool transitionSync(const EventID_t event, const int timeoutMs, Args&&... args); - - /** - * @brief Trigger a transition in the HSM and clear all pending events. - * @details Convenience wrapper for transitionEx() which clears the pending events queue before sending a new event. - * Transition is executed asynchronously. - * - * @param event ID of event to send to HSM - * @param args (optional) arguments to pass to the callbacks - * - * @threadsafe{ } - */ - template - void transitionWithQueueClear(const EventID_t event, Args&&... args); - - /** - * @brief Interrupt/signal safe version of transition - * @details This is a simplified version of transition that can be safely used from an interrupt/signal. Event is processed - * asynchronously. - * - * @remark There are no restrictions to use other transition APIs inside an interrupt, but all of them use dynamic - * heap memory allocation (which can cause heap corruption on some platfroms). This version of the transition relies on - * dispatcher implementation and might not be available everywhere (please check dispatcher's description). It also might - * fail if internal dispatcher events queue is full. - * - * @param event ID of event to send to HSM - * - * @retval true event was added to queue - * @retval false failed to add event to queue because it's not supported by dispatcher or queue limit was reached - * - * @concurrencysafe{ } - */ - bool transitionInterruptSafe(const EventID_t event); - - /** - * @brief Check if a transition is possible. - * @details This function checks if a transition can be triggered by the given event and current state. - * This function takes a variable number of arguments, which will be passed to the condition callbacks of the relevant - * states and transitions. - * - * @remark It's recommended to avoid using this API unless really needed. It might confuse in a multithreaded - * environment since it only can check possibility of transition in the **current** HSM state, but it can't prevent this - * state from changing after returning from isTransitionPossible(). You would have to use additional synchronization - * mechanisms to guarantee that state doesn't change between calls to isTransitionPossible() and transition(). - * - * @param event ID of event to send to HSM - * @param args (optional) arguments to pass to the condition callbacks - * @return True if a transition is possible, false otherwise. - * - * @notthreadsafe{Calling thing API from multiple threads can cause data races and will result in undefined behavior} - */ - template - bool isTransitionPossible(const EventID_t event, Args&&... args); - - /** - * @brief Start a timer. - * @details If timer with this ID is already running it will be restarted with new settings. - * - * @param timerID unique timer id - * @param intervalMs timer interval in milliseconds - * @param isSingleShot true - timer will run only once and then will stop - * false - timer will keep running until stopTimer() is called or dispatcher is destroyed - * - * @threadsafe{ } - */ - void startTimer(const TimerID_t timerID, const unsigned int intervalMs, const bool isSingleShot); - - /** - * @brief Restart running timer. - * @details Timer is restarted with the same arguments which were provided to startTimer(). Only currently running or - * expired timers (with isSingleShot set to true) will be restarted. Has no effect if called for a timer which was not - * started. - * - * @param timerID id of running timer - * - * @threadsafe{ } - */ - void restartTimer(const TimerID_t timerID); - - /** - * @brief Stop active timer. - * @details Function stops an active timer without triggering any notifications and unregisters it. Further calls to - * restartTimer() will have no effects untill it's started again with startTimer(). - * - * @remark For expired timers (which have isSingleShot property set to true), funtion simply unregisters them. - * - * @param timerID id of running or expired timer - * - * @threadsafe{ } - */ - void stopTimer(const TimerID_t timerID); - - /** - * @brief Check if timer is currently running. - * - * @param timerID id of the timer to check - * - * @retval true timer is running - * @retval false timer is not running - * - * @threadsafe{ } - */ - bool isTimerRunning(const TimerID_t timerID); - - /** - * @brief Enable debugging for HSM instance. - * @details Enables creation of the log file that can be later analyzed with hsmdebugger. By default log will be written to - * ./dump.hsmlog file. This location can be overwritten by setting ENV_DUMPPATH environment variable with desired path. - * - * @remark HSMBUILD_DEBUGGING build option must be enabled for this functionality to work. It's recommended to keep this - * feature disabled in production code to avoid performance overhead. - * - * @retval true debugging was successfully enabled - * @retval false failed to open log file - * - * @notthreadsafe{Calling thing API from multiple threads can cause data races and will result in undefined behavior} - */ - bool enableHsmDebugging(); - - /** - * @brief Enable debugging for HSM instance with specific path for the log file. - * Enables creation of the log file that can be later analyzed with hsmdebugger. - * - * @remark HSMBUILD_DEBUGGING build option must be enabled for this functionality to work. It's recommended to keep this - * feature disabled in production code to avoid performance overhead. - * - * @param dumpPath The path for the dump files. - * @retval true debugging was successfully enabled (always returns true if HSMBUILD_DEBUGGING was not set) - * @retval false failed to open log file - * - * @notthreadsafe{Calling thing API from multiple threads can cause data races and will result in undefined behavior} - */ - bool enableHsmDebugging(const std::string& dumpPath); - - /** - * @brief Disable HSM debugging. - * @details This function disables debugging for the Hierarchical State Machine and closes the log file. - * Does nothing if enableHsmDebugging() was not called. - * - * @threadsafe{ Internally just calls std::filebuf::close(). } - */ - void disableHsmDebugging(); - -protected: - /** - * @brief Convert state ID to a text name. - * @details Default implementation only converts numeric state ID to a string. When scxml code generation is used, method - * implementation of getStateName() will be generated. If HSM structure is registered manually it's developer's - * responsibility to provide implementation of this method. - * - * @remark This method must be implemented for debugging to work. State names should match with the names in scxml file. - * - * @param state ID of the state - * @return name of the state with requested ID or an ID converted to string - * - * @threadsafe{ } - */ - virtual std::string getStateName(const StateID_t state) const; - - /** - * @brief Convert event ID to a text name. - * @details Default implementation only converts numeric event ID to a string. When scxml code generation is used, method - * implementation of getEventName() will be generated. If HSM structure is registered manually it's developer's - * responsibility to provide implementation of this method. - * - * @remark This method must be implemented for debugging to work. Event names should match with the names in scxml file. - * - * @param event ID of the event - * @return name of the event with requested ID or an ID converted to string - * - * @threadsafe{ } - */ - virtual std::string getEventName(const EventID_t event) const; - -private: - template - void makeVariantList(VariantVector_t& vList, Args&&... args); - - bool registerStateActionImpl(const StateID_t state, - const StateActionTrigger actionTrigger, - const StateAction action, - const VariantVector_t& args); - bool isTransitionPossibleImpl(const EventID_t event, const VariantVector_t& args); - -private: - class Impl; - std::shared_ptr mImpl; -}; - -// ================================================================================================================= -// Template Functions -// ================================================================================================================= -template -void HierarchicalStateMachine::registerFailedTransitionCallback(HsmHandlerClass* handler, - HsmTransitionFailedCallbackPtr_t(HsmHandlerClass, - onFailedTransition)) { - registerFailedTransitionCallback(std::bind(onFailedTransition, handler, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); -} - -template -void HierarchicalStateMachine::registerState(const StateID_t state, - HsmHandlerClass* handler, - HsmStateChangedCallbackPtr_t(HsmHandlerClass, onStateChanged), - HsmStateEnterCallbackPtr_t(HsmHandlerClass, onEntering), - HsmStateExitCallbackPtr_t(HsmHandlerClass, onExiting)) { - HsmStateChangedCallback_t funcStateChanged; - HsmStateEnterCallback_t funcEntering; - HsmStateExitCallback_t funcExiting; - - if (nullptr != handler) { - if (nullptr != onStateChanged) { - funcStateChanged = std::bind(onStateChanged, handler, std::placeholders::_1); - } - - if (nullptr != onEntering) { - funcEntering = std::bind(onEntering, handler, std::placeholders::_1); - } - - if (nullptr != onExiting) { - funcExiting = std::bind(onExiting, handler); - } - } - - registerState(state, std::move(funcStateChanged), std::move(funcEntering), std::move(funcExiting)); -} - -template -void HierarchicalStateMachine::registerFinalState(const StateID_t state, - const EventID_t event, - HsmHandlerClass* handler, - HsmStateChangedCallbackPtr_t(HsmHandlerClass, onStateChanged), - HsmStateEnterCallbackPtr_t(HsmHandlerClass, onEntering), - HsmStateExitCallbackPtr_t(HsmHandlerClass, onExiting)) { - HsmStateChangedCallback_t funcStateChanged; - HsmStateEnterCallback_t funcEntering; - HsmStateExitCallback_t funcExiting; - - if (nullptr != handler) { - if (nullptr != onStateChanged) { - funcStateChanged = std::bind(onStateChanged, handler, std::placeholders::_1); - } - - if (nullptr != onEntering) { - funcEntering = std::bind(onEntering, handler, std::placeholders::_1); - } - - if (nullptr != onExiting) { - funcExiting = std::bind(onExiting, handler); - } - } - - registerFinalState(state, event, std::move(funcStateChanged), std::move(funcEntering), std::move(funcExiting)); -} - -template -void HierarchicalStateMachine::registerHistory(const StateID_t parent, - const StateID_t historyState, - const HistoryType type, - const StateID_t defaultTarget, - HsmHandlerClass* handler, - HsmTransitionCallbackPtr_t(HsmHandlerClass, transitionCallback)) { - HsmTransitionCallback_t funcTransitionCallback; - - if (nullptr != handler) { - if (nullptr != transitionCallback) { - funcTransitionCallback = std::bind(transitionCallback, handler, std::placeholders::_1); - } - } - - registerHistory(parent, historyState, type, defaultTarget, std::move(funcTransitionCallback)); -} - -template -bool HierarchicalStateMachine::registerSubstateEntryPoint(const StateID_t parent, - const StateID_t substate, - const EventID_t onEvent, - HsmHandlerClass* handler, - HsmTransitionConditionCallbackPtr_t(HsmHandlerClass, - conditionCallback), - const bool expectedConditionValue) { - HsmTransitionConditionCallback_t condition; - - if ((nullptr != handler) && (nullptr != conditionCallback)) { - condition = std::bind(conditionCallback, handler, std::placeholders::_1); - } - - return registerSubstateEntryPoint(parent, substate, onEvent, std::move(condition), expectedConditionValue); -} - -template -bool HierarchicalStateMachine::registerStateAction(const StateID_t state, - const StateActionTrigger actionTrigger, - const StateAction action, - Args&&... args) { - VariantVector_t eventArgs; - - makeVariantList(eventArgs, std::forward(args)...); - return registerStateActionImpl(state, actionTrigger, action, eventArgs); -} - -template -void HierarchicalStateMachine::registerTransition(const StateID_t fromState, - const StateID_t toState, - const EventID_t onEvent, - HsmHandlerClass* handler, - HsmTransitionCallbackPtr_t(HsmHandlerClass, transitionCallback), - HsmTransitionConditionCallbackPtr_t(HsmHandlerClass, conditionCallback), - const bool expectedConditionValue) { - HsmTransitionCallback_t funcTransitionCallback; - HsmTransitionConditionCallback_t funcConditionCallback; - - if (nullptr != handler) { - if (nullptr != transitionCallback) { - funcTransitionCallback = std::bind(transitionCallback, handler, std::placeholders::_1); - } - - if (nullptr != conditionCallback) { - funcConditionCallback = std::bind(conditionCallback, handler, std::placeholders::_1); - } - } - - registerTransition(fromState, toState, onEvent, std::move(funcTransitionCallback), std::move(funcConditionCallback), expectedConditionValue); -} - -template -void HierarchicalStateMachine::registerSelfTransition(const StateID_t state, - const EventID_t onEvent, - const TransitionType type, - HsmHandlerClass* handler, - HsmTransitionCallbackPtr_t(HsmHandlerClass, transitionCallback), - HsmTransitionConditionCallbackPtr_t(HsmHandlerClass, conditionCallback), - const bool expectedConditionValue) { - HsmTransitionCallback_t funcTransitionCallback; - HsmTransitionConditionCallback_t funcConditionCallback; - - if (nullptr != handler) { - if (nullptr != transitionCallback) { - funcTransitionCallback = std::bind(transitionCallback, handler, std::placeholders::_1); - } - - if (nullptr != conditionCallback) { - funcConditionCallback = std::bind(conditionCallback, handler, std::placeholders::_1); - } - } - - registerSelfTransition(state, onEvent, type, std::move(funcTransitionCallback), std::move(funcConditionCallback), expectedConditionValue); -} - -template -void HierarchicalStateMachine::transition(const EventID_t event, Args&&... args) { - (void)transitionEx(event, false, false, 0, std::forward(args)...); -} - -template -bool HierarchicalStateMachine::transitionEx(const EventID_t event, - const bool clearQueue, - const bool sync, - const int timeoutMs, - Args&&... args) { - VariantVector_t eventArgs; - - makeVariantList(eventArgs, std::forward(args)...); - return transitionExWithArgsArray(event, clearQueue, sync, timeoutMs, std::move(eventArgs)); -} - -template -bool HierarchicalStateMachine::transitionSync(const EventID_t event, const int timeoutMs, Args&&... args) { - return transitionEx(event, false, true, timeoutMs, std::forward(args)...); -} - -template -void HierarchicalStateMachine::transitionWithQueueClear(const EventID_t event, Args&&... args) { - // NOTE: async transitions always return true - (void)transitionEx(event, true, false, 0, std::forward(args)...); -} - -template -bool HierarchicalStateMachine::isTransitionPossible(const EventID_t event, Args&&... args) { - VariantVector_t eventArgs; - - makeVariantList(eventArgs, std::forward(args)...); - - return isTransitionPossibleImpl(event, eventArgs); -} - -template -void HierarchicalStateMachine::makeVariantList(VariantVector_t& vList, Args&&... args) { - volatile int make_variant[] = {0, (vList.emplace_back(std::forward(args)), 0)...}; - (void)make_variant; -} - -} // namespace hsmcpp - -#endif // HSMCPP_HSM_HPP diff --git a/src/hsmcpp/logging.hpp b/src/hsmcpp/logging.hpp deleted file mode 100644 index d3dc468..0000000 --- a/src/hsmcpp/logging.hpp +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (C) 2021 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details - -#ifndef HSMCPP_LOGGING_HPP -#define HSMCPP_LOGGING_HPP - -#if defined(HSM_LOGGING_MODE_NICE) -#define HSM_USE_CONSOLE_ERRORS -#elif defined(HSM_LOGGING_MODE_DEBUG_OFF) -#define HSM_DISABLE_DEBUG_TRACES -#define HSM_USE_CONSOLE_TRACES -#elif defined(HSM_LOGGING_MODE_STRICT_VERBOSE) -#define HSM_USE_CONSOLE_TRACES -#define HSM_EXIT_ON_FATAL -#elif defined(HSM_LOGGING_MODE_STRICT) -#define HSM_USE_CONSOLE_ERRORS -#define HSM_EXIT_ON_FATAL -#else // HSM_LOGGING_MODE_OFF -#define HSM_DISABLE_TRACES -#define HSM_DISABLE_DEBUG_TRACES -#endif - -// --------------------------------------------------------------------------------- -// This macroses needs to de defined in each CPP files -// #undef HSM_TRACE_CLASS -// #define HSM_TRACE_CLASS "default" - -#ifndef HSM_DISABLE_TRACES - #ifdef PLATFORM_ARDUINO - #include - #elif !defined(PLATFORM_FREERTOS) - #include - #include - #else - // NOTE: used only for logging in debug mode. in release mode HSM_DISABLE_TRACES is defined so this include is ignored - // cppcheck-suppress misra-c2012-21.6 - #include - #endif - - // --------------------------------------------------------------------------------- - // PRIVATE MACROSES - // Don't use this in the code. It's for internal usage only - extern int g_hsm_traces_pid; - - #ifdef PLATFORM_ARDUINO - #define HSM_TRACE_CALL_COMMON() - #define HSM_TRACE_INIT() - #elif !defined(PLATFORM_FREERTOS) - #define HSM_TRACE_CALL_COMMON() const int _tid = syscall(__NR_gettid); (void)_tid - #define HSM_TRACE_INIT() if (0 == g_hsm_traces_pid){ g_hsm_traces_pid = getpid(); } - #else - #define HSM_TRACE_CALL_COMMON() const int _tid = 0; (void)_tid - #define HSM_TRACE_INIT() - #endif - - #ifdef PLATFORM_ARDUINO - void serialPrintf(const char* fmt, ...); - - #define HSM_TRACE_CONSOLE_FORCE(msg, ...) \ - serialPrintf((const char*)F("[HSM] %s::%s: " msg), HSM_TRACE_CLASS, __func__,## __VA_ARGS__) - #else - #define HSM_TRACE_CONSOLE_FORCE(msg, ...) \ - printf("[PID:%d, TID:%d] %s::%s: " msg "\n", g_hsm_traces_pid, _tid, HSM_TRACE_CLASS, __func__,## __VA_ARGS__) - #endif - - #ifdef HSM_USE_CONSOLE_TRACES - #define HSM_TRACE_CONSOLE(msg, ...) HSM_TRACE_CONSOLE_FORCE(msg,## __VA_ARGS__) - #define HSM_TRACE_ERROR_CONSOLE(msg, ...) - #else - #define HSM_TRACE_CONSOLE(msg, ...) - #ifdef HSM_USE_CONSOLE_ERRORS - #define HSM_TRACE_ERROR_CONSOLE(msg, ...) HSM_TRACE_CONSOLE_FORCE(msg,## __VA_ARGS__) - #else - #define HSM_TRACE_ERROR_CONSOLE(msg, ...) - #endif - #endif - - #define HSM_TRACE_COMMON(msg, ...) HSM_TRACE_CONSOLE(msg,## __VA_ARGS__) - // --------------------------------------------------------------------------------- - - #define HSM_TRACE_PREINIT() int g_hsm_traces_pid = (0); - #define HSM_TRACE(msg, ...) HSM_TRACE_COMMON(msg,## __VA_ARGS__) - #define HSM_TRACE_WARNING(msg, ...) HSM_TRACE_COMMON("[WARNING] " msg,## __VA_ARGS__) - #define HSM_TRACE_ERROR(msg, ...) HSM_TRACE_COMMON("[ERROR] " msg,## __VA_ARGS__); \ - HSM_TRACE_ERROR_CONSOLE("[ERROR] " msg,## __VA_ARGS__) - #if defined(HSM_EXIT_ON_FATAL) && !defined(PLATFORM_FREERTOS) - #define HSM_TRACE_FATAL(msg, ...) HSM_TRACE_COMMON("[FATAL] " msg,## __VA_ARGS__); \ - HSM_TRACE_ERROR_CONSOLE("[FATAL] " msg,## __VA_ARGS__); \ - exit(1) - #else - #define HSM_TRACE_FATAL(msg, ...) HSM_TRACE_COMMON("[FATAL] " msg,## __VA_ARGS__); \ - HSM_TRACE_ERROR_CONSOLE("[FATAL] " msg,## __VA_ARGS__) - #endif // HSM_EXIT_ON_FATAL - #define HSM_TRACE_CALL() HSM_TRACE_CALL_COMMON(); \ - HSM_TRACE(" was called") - #define HSM_TRACE_CALL_ARGS(msg, ...) HSM_TRACE_CALL_COMMON(); \ - HSM_TRACE(msg,## __VA_ARGS__) - #define HSM_TRACE_DEF() HSM_TRACE_CALL_COMMON() -#else // PLATFROM_FREERTOS - #define HSM_TRACE_PREINIT() int g_hsm_traces_pid = (0); - #define HSM_TRACE_INIT() - #define HSM_TRACE(msg, ...) - #define HSM_TRACE_WARNING(msg, ...) - #define HSM_TRACE_ERROR(msg, ...) - #define HSM_TRACE_FATAL(msg, ...) - #define HSM_TRACE_CALL() - #define HSM_TRACE_CALL_ARGS(msg, ...) - #define HSM_TRACE_DEF() -#endif // HSM_DISABLE_TRACES - -#if !defined(HSM_DISABLE_DEBUG_TRACES) && !defined(HSM_DISABLE_TRACES) - #define HSM_TRACE_DEBUG(msg, ...) HSM_TRACE_COMMON("[DEBUG] " msg,## __VA_ARGS__) - // Should be a first line in any function that needs traces - #define HSM_TRACE_CALL_DEBUG() HSM_TRACE_CALL_COMMON(); \ - HSM_TRACE_DEBUG(" was called") - - #define HSM_TRACE_CALL_DEBUG_ARGS(msg, ...) HSM_TRACE_CALL_COMMON(); \ - HSM_TRACE_DEBUG(msg,## __VA_ARGS__) - - #define HSM_TRACE_CALL_RESULT(msg, ...) HSM_TRACE_DEBUG(" => " msg,## __VA_ARGS__) - #define HSM_TRACE_LINE() HSM_TRACE_DEBUG("line:%d", __LINE__) -#else // !defined(HSM_DISABLE_DEBUG_TRACES) && !defined(HSM_DISABLE_TRACES) - #define HSM_TRACE_DEBUG(msg, ...) - - #ifndef HSM_DISABLE_TRACES - #define HSM_TRACE_CALL_DEBUG() HSM_TRACE_CALL_COMMON() - #define HSM_TRACE_CALL_DEBUG_ARGS(msg, ...) HSM_TRACE_CALL_COMMON() - #else - #define HSM_TRACE_CALL_DEBUG() - #define HSM_TRACE_CALL_DEBUG_ARGS(msg, ...) - #endif - - #define HSM_TRACE_CALL_RESULT(msg, ...) - #define HSM_TRACE_LINE() -#endif // !defined(HSM_DISABLE_DEBUG_TRACES) && !defined(HSM_DISABLE_TRACES) - -// --------------------------------------------------------------------------------- -// HELPERS -#ifndef SC2INT - #define SC2INT(v) static_cast(v) -#endif -#ifndef BOOL2INT - #define BOOL2INT(v) static_cast(v) -#endif -#ifndef BOOL2STR - #define BOOL2STR(v) ((v) ? "true" : "false") -#endif - -#ifndef DEF2STR - // NOTE: convenience macros for logging. logs are not used in relase mode - // cppcheck-suppress misra-c2012-20.10 - #define DEF2STR_INTERNAL(s) #s - #define DEF2STR(s) DEF2STR_INTERNAL(s) -#endif - -#endif // HSMCPP_LOGGING_HPP diff --git a/src/hsmcpp/os/AtomicFlag.hpp b/src/hsmcpp/os/AtomicFlag.hpp deleted file mode 100644 index 532f677..0000000 --- a/src/hsmcpp/os/AtomicFlag.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2023 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#ifndef HSMCPP_OS_ATOMICFLAG_HPP -#define HSMCPP_OS_ATOMICFLAG_HPP - -#include "os.hpp" - -#if defined(FREERTOS_AVAILABLE) - #include "freertos/AtomicFlag.hpp" -#elif defined(PLATFORM_ARDUINO) - #include "arduino/AtomicFlag.hpp" -#elif defined(STL_AVAILABLE) - #include "stl/AtomicFlag.hpp" -#else - #error PLATFORM not supported -#endif - -#endif // HSMCPP_OS_ATOMICFLAG_HPP diff --git a/src/hsmcpp/os/ConditionVariable.hpp b/src/hsmcpp/os/ConditionVariable.hpp deleted file mode 100644 index 4ac4123..0000000 --- a/src/hsmcpp/os/ConditionVariable.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2022 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#ifndef HSMCPP_OS_CONDITIONVARIABLE_HPP -#define HSMCPP_OS_CONDITIONVARIABLE_HPP - -#include "os.hpp" - -#if defined(FREERTOS_AVAILABLE) - #include "freertos/ConditionVariable.hpp" -#elif defined(PLATFORM_ARDUINO) - #include "arduino/ConditionVariable.hpp" -#elif defined(STL_AVAILABLE) - #include "stl/ConditionVariable.hpp" -#else - #error PLATFORM not supported -#endif - -#endif // HSMCPP_OS_CONDITIONVARIABLE_HPP diff --git a/src/hsmcpp/os/CriticalSection.hpp b/src/hsmcpp/os/CriticalSection.hpp deleted file mode 100644 index 2676d28..0000000 --- a/src/hsmcpp/os/CriticalSection.hpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (C) 2022 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#include "common/CriticalSection.hpp" diff --git a/src/hsmcpp/os/InterruptsFreeSection.hpp b/src/hsmcpp/os/InterruptsFreeSection.hpp deleted file mode 100644 index cde7af8..0000000 --- a/src/hsmcpp/os/InterruptsFreeSection.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (C) 2022 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#ifndef HSMCPP_OS_INTERRUPTSFREESECTION_HPP -#define HSMCPP_OS_INTERRUPTSFREESECTION_HPP - -#include "os.hpp" - -#if defined(FREERTOS_AVAILABLE) - #include "freertos/InterruptsFreeSection.hpp" -#elif defined(PLATFORM_ARDUINO) - #include "common/InterruptsFreeSection.hpp" -#elif defined(POSIX_AVAILABLE) - #include "posix/InterruptsFreeSection.hpp" -#elif defined(PLATFORM_WINDOWS) - #include "common/InterruptsFreeSection.hpp" -#else - #error PLATFORM not supported -#endif - -#endif // HSMCPP_OS_INTERRUPTSFREESECTION_HPP diff --git a/src/hsmcpp/os/LockGuard.hpp b/src/hsmcpp/os/LockGuard.hpp deleted file mode 100644 index 0781820..0000000 --- a/src/hsmcpp/os/LockGuard.hpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (C) 2022 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#include "common/LockGuard.hpp" diff --git a/src/hsmcpp/os/Mutex.hpp b/src/hsmcpp/os/Mutex.hpp deleted file mode 100644 index 79afd2d..0000000 --- a/src/hsmcpp/os/Mutex.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2022 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#ifndef HSMCPP_OS_MUTEX_HPP -#define HSMCPP_OS_MUTEX_HPP - -#include "os.hpp" - -#if defined(FREERTOS_AVAILABLE) - #include "freertos/Mutex.hpp" -#elif defined(PLATFORM_ARDUINO) - #include "arduino/Mutex.hpp" -#elif defined(STL_AVAILABLE) - #include "stl/Mutex.hpp" -#else - #error PLATFORM not supported -#endif - -#endif // HSMCPP_OS_MUTEX_HPP diff --git a/src/hsmcpp/os/UniqueLock.hpp b/src/hsmcpp/os/UniqueLock.hpp deleted file mode 100644 index aa7793e..0000000 --- a/src/hsmcpp/os/UniqueLock.hpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (C) 2022 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#include "common/UniqueLock.hpp" diff --git a/src/hsmcpp/os/arduino/AtomicFlag.hpp b/src/hsmcpp/os/arduino/AtomicFlag.hpp deleted file mode 100644 index a9d77bb..0000000 --- a/src/hsmcpp/os/arduino/AtomicFlag.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2023 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#ifndef HSMCPP_OS_ARDUINO_ATOMICFLAG_HPP -#define HSMCPP_OS_ARDUINO_ATOMICFLAG_HPP - -#include "hsmcpp/os/UniqueLock.hpp" - -namespace hsmcpp { - -/// Arduino is single threaded so this is just a simple wrapper for a bool flag -class AtomicFlag { -public: - AtomicFlag() = default; - ~AtomicFlag() = default; - AtomicFlag(const AtomicFlag&) = delete; - AtomicFlag& operator=(const AtomicFlag&) = delete; - AtomicFlag& operator=(const AtomicFlag&) volatile = delete; - - bool test_and_set() noexcept; - void clear() noexcept; - bool test() const noexcept; - UniqueLock lock() noexcept; - void wait(const bool old) const noexcept; - void notify() noexcept; - -private: - volatile bool mValue = false; -}; - -} // namespace hsmcpp - -#endif // HSMCPP_OS_ARDUINO_ATOMICFLAG_HPP diff --git a/src/hsmcpp/os/arduino/ConditionVariable.hpp b/src/hsmcpp/os/arduino/ConditionVariable.hpp deleted file mode 100644 index 6d302b4..0000000 --- a/src/hsmcpp/os/arduino/ConditionVariable.hpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2022 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#ifndef HSMCPP_OS_STL_CONDITIONVARIABLE_HPP -#define HSMCPP_OS_STL_CONDITIONVARIABLE_HPP - -#include "hsmcpp/os/common/UniqueLock.hpp" -#include - -namespace hsmcpp -{ - -class ConditionVariable { -public: - ConditionVariable() = default; - ~ConditionVariable(); - - void wait(UniqueLock& sync, std::function stopWaiting = nullptr); - bool wait_for(UniqueLock& sync, const int timeoutMs, std::function stopWaiting); - inline void notify(); - -private: - ConditionVariable(const ConditionVariable&) = delete; - ConditionVariable& operator=(const ConditionVariable&) = delete; - -private: -}; - -inline void ConditionVariable::notify() -{ - // mVariable.notify_all(); -} - -} // namespace hsmcpp - -#endif // HSMCPP_OS_STL_CONDITIONVARIABLE_HPP diff --git a/src/hsmcpp/os/arduino/Mutex.hpp b/src/hsmcpp/os/arduino/Mutex.hpp deleted file mode 100644 index a192159..0000000 --- a/src/hsmcpp/os/arduino/Mutex.hpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2022 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#ifndef HSMCPP_OS_STL_MUTEX_HPP -#define HSMCPP_OS_STL_MUTEX_HPP - -namespace hsmcpp -{ - -class Mutex -{ -public: - Mutex() = default; - ~Mutex() = default; - - inline void lock() - { - } - - inline void unlock() - { - } - -private: - Mutex(const Mutex&) = delete; - Mutex& operator=(const Mutex&) = delete; -}; - -} // namespace hsmcpp - -#endif // HSMCPP_OS_STL_MUTEX_HPP diff --git a/src/hsmcpp/os/common/CriticalSection.hpp b/src/hsmcpp/os/common/CriticalSection.hpp deleted file mode 100644 index 2f57053..0000000 --- a/src/hsmcpp/os/common/CriticalSection.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2023 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#ifndef HSMCPP_OS_COMMON_CRITICALSECTION_HPP -#define HSMCPP_OS_COMMON_CRITICALSECTION_HPP - -#include - -namespace hsmcpp { - -class Mutex; -class InterruptsFreeSection; - -class CriticalSection { -public: - explicit CriticalSection(Mutex& sync); - ~CriticalSection(); - -private: - CriticalSection(const CriticalSection&) = delete; - CriticalSection& operator=(const CriticalSection&) = delete; - CriticalSection(CriticalSection&&) = delete; - CriticalSection& operator=(CriticalSection&&) = delete; - -private: - Mutex& mSync; - std::unique_ptr mInterruptsBlock; -}; - -} // namespace hsmcpp - -#endif // HSMCPP_OS_COMMON_CRITICALSECTION_HPP diff --git a/src/hsmcpp/os/common/InterruptsFreeSection.hpp b/src/hsmcpp/os/common/InterruptsFreeSection.hpp deleted file mode 100644 index 4c3d857..0000000 --- a/src/hsmcpp/os/common/InterruptsFreeSection.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2023 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#ifndef HSMCPP_OS_COMMON_INTERRUPTSFREESECTION_HPP -#define HSMCPP_OS_COMMON_INTERRUPTSFREESECTION_HPP - -namespace hsmcpp -{ - -class InterruptsFreeSection -{ -public: - InterruptsFreeSection(); - ~InterruptsFreeSection(); - -private: - InterruptsFreeSection(const InterruptsFreeSection&) = delete; - InterruptsFreeSection& operator=(const InterruptsFreeSection&) = delete; - InterruptsFreeSection(InterruptsFreeSection&&) = delete; - InterruptsFreeSection& operator=(InterruptsFreeSection&&) = delete; -}; - -} // namespace hsmcpp - -#endif // HSMCPP_OS_COMMON_INTERRUPTSFREESECTION_HPP diff --git a/src/hsmcpp/os/common/LockGuard.hpp b/src/hsmcpp/os/common/LockGuard.hpp deleted file mode 100644 index 8945f3a..0000000 --- a/src/hsmcpp/os/common/LockGuard.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2022 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#ifndef HSMCPP_OS_COMMON_LOCKGUARD_HPP -#define HSMCPP_OS_COMMON_LOCKGUARD_HPP - -namespace hsmcpp -{ - -class Mutex; - -class LockGuard -{ -public: - explicit LockGuard(Mutex& sync); - ~LockGuard(); - -private: - LockGuard(const LockGuard&) = delete; - LockGuard& operator=(const LockGuard&) = delete; - LockGuard(LockGuard&&) = delete; - LockGuard& operator=(LockGuard&&) = delete; - -private: - Mutex& mSync; -}; - -} // namespace hsmcpp - -#endif // HSMCPP_OS_COMMON_LOCKGUARD_HPP diff --git a/src/hsmcpp/os/common/UniqueLock.hpp b/src/hsmcpp/os/common/UniqueLock.hpp deleted file mode 100644 index 0c237bd..0000000 --- a/src/hsmcpp/os/common/UniqueLock.hpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) 2022 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#ifndef HSMCPP_OS_COMMON_UNIQUELOCK_HPP -#define HSMCPP_OS_COMMON_UNIQUELOCK_HPP - -#include - -namespace hsmcpp -{ - -class Mutex; - -class UniqueLock -{ -public: - UniqueLock() = default; - explicit UniqueLock(Mutex& sync); - ~UniqueLock(); - - UniqueLock(UniqueLock&& src) noexcept; - - UniqueLock& operator=(UniqueLock&& src) noexcept; - - void lock(); - void unlock(); - - inline bool owns_lock() const noexcept - { - const bool isLocked = const_cast(mOwnsLock).test_and_set(std::memory_order_acquire); - - if (false == isLocked) { - const_cast(mOwnsLock).clear(std::memory_order_release); - } - - return isLocked; - } - - inline explicit operator bool() const noexcept - { - return owns_lock(); - } - - Mutex* release() noexcept; - inline Mutex* mutex() const noexcept - { - return mSync; - } - -private: - UniqueLock(const UniqueLock& src) = delete; - UniqueLock& operator=(const UniqueLock& src) = delete; - -private: - Mutex* mSync = nullptr; - mutable std::atomic_flag mOwnsLock = ATOMIC_FLAG_INIT; -}; - -} // namespace hsmcpp - -#endif // HSMCPP_OS_COMMON_UNIQUELOCK_HPP diff --git a/src/hsmcpp/os/os.hpp b/src/hsmcpp/os/os.hpp deleted file mode 100644 index 9b7fae6..0000000 --- a/src/hsmcpp/os/os.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2022 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details -#ifndef HSMCPP_OS_OS_HPP -#define HSMCPP_OS_OS_HPP - -// TODO: check if there are better ways to detect FreeRTOS -#if defined(PLATFORM_FREERTOS) || defined(INC_FREERTOS_H) - #define FREERTOS_AVAILABLE (1) -#elif defined(PLATFORM_POSIX) || defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) || defined (__linux__) - #define POSIX_AVAILABLE (1) - #define STL_AVAILABLE (1) -#elif defined(PLATFORM_WINDOWS) || defined(WIN32) - #define STL_AVAILABLE (1) -#elif defined(ARDUINO) && !defined(PLATFORM_ARDUINO) - #define PLATFORM_ARDUINO (1) -#endif - -#if defined(__cpp_exceptions) || defined(_CPPUNWIND) || defined(__EXCEPTIONS) - #define EXCEPTIONS_ENABLED (1) -#endif - -// NOTE: needed for embedded platforms where exceptions are disabled -#ifdef EXCEPTIONS_ENABLED - #define HSM_TRY try - #define HSM_CATCH(_x) catch(_x) -#else - #define HSM_TRY if(true) - #define HSM_CATCH(_x) if(false) -#endif - -#endif // HSMCPP_OS_OS_HPP \ No newline at end of file diff --git a/src/hsmcpp/variant.hpp b/src/hsmcpp/variant.hpp deleted file mode 100644 index 41697c8..0000000 --- a/src/hsmcpp/variant.hpp +++ /dev/null @@ -1,907 +0,0 @@ -// Copyright (C) 2021 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details - -#ifndef HSMCPP_VARIANT_HPP -#define HSMCPP_VARIANT_HPP - -#include -#include -#include -#include -#include -#include -#include - -namespace hsmcpp { - -class Variant; - -using ByteArray_t = std::vector; -using VariantVector_t = std::vector; -using VariantList_t = std::list; -using VariantMap_t = std::map; -using VariantPair_t = std::pair; ///< Provides a way to store two values as a single unit. - -// cppcheck-suppress misra-c2012-20.7 ; parentheses are not needed -#define DEF_CONSTRUCTOR(_val_type, _internal_type) \ - /** @brief Constructs a new variant with an _val_type value */ \ - explicit Variant(const _val_type v); - -// cppcheck-suppress misra-c2012-20.7 ; parentheses are not needed -#define DEF_OPERATOR_ASSIGN(_val_type, _internal_type) \ - /** @brief Assigns _val_type value to current variant object and changing it's type to _internal_type. */ \ - Variant& operator=(const _val_type v); - -#define DEF_MAKE_DOC(_internal_type) \ - /** @brief Creates a Variant object with a value of type _internal_type. */ \ - /** @param v The value to assign to the Variant object. */ \ - /** @return Newly constructed Variant object. */ - -// cppcheck-suppress misra-c2012-20.7 ; parentheses are not needed -#define DEF_MAKE(_val_type, _internal_type) \ - DEF_MAKE_DOC(_internal_type) \ - static Variant make(const _val_type v); - -/** - * @brief Variant class represents a type-safe union. - * @details Variant class is supposed to be a substitute for std::variant which is not available prior to C++17. - * - * The Variant class is a generic container that can hold various types of data (similar to union). It is designed to be - * flexible and efficient by allowing the user to store data of different types and sizes, and retrieve them in a type-safe - * manner. The class definition provides an Variant::Type enumeration of different data types that the Variant object can hold, - * including numeric types, boolean values, strings, and more complex types like vectors, lists, maps and pairs. - * - * A Variant object holds a single value of a single type at a time or none if it's empty. Value can be retrieved as a copy - * using toX() methods (for example toString(), toInt64(), etc.). When asked for a type that can be generated from the stored - * type, toX() copies and converts and leaves the object itself unchanged. When asked for a type that cannot be generated from - * the stored type, the result depends on the type (see each the function's documentation for details). - * - * Here is some example code to demonstrate the use of Variant: - * - * \code{.cpp} - * Variant v; // v is empty and has Type::UNKNOWN - * - * if (!v) { - * v = 42; // v now contains numeric value (usually Type::BYTE_4, but depends on compiler) - * std::cout << "v is an int: " << v.isNumeric() << std::endl; // output: v is an int: 1 - * std::cout << "v == 42: " << (v == Variant(42)) << std::endl; // output: v == 42: 1 - * std::cout << "v == \"42\": " << (v.toString() == "42") << std::endl; // output: v == "42": 1 - * - * v = "Hello, world!"; // v now contains a Type::STRING value - * std::cout << "v is a string: " << v.isString() << std::endl; // output: v is a string: 1 - * std::cout << "v == \"Hello, world!\": " << (v == "Hello, world!") << std::endl; // output: v == "Hello, world!": 1 - * std::cout << "v.toInt64(): " << v.toInt64() << std::endl; // output: v.toInt64(): 0 - * - * v = 3.14; // v now contains a Type::DOUBLE value - * std::cout << "v is a double: " << v.isDouble() << std::endl; // output: v is a double: 1 - * std::cout << "v > 2.0: " << (v > 2.0) << std::endl; // output: v > 2.0: 1 - * } - * \endcode - */ -class Variant { -private: - /** - * @brief Handles memory allocation/deallocation for custom types. - * - * @param void* memory pointer - * @param bool true - release memory; false - copy memory - * @return new memory pointer if copy was requested or nullptr - */ - // using MemoryHandlerFunc_t = std::function; - using MemoryAllocatorFunc_t = std::function(const void*)>; - using CompareFunc_t = std::function; - -public: - /** - * @brief Describes types that can be stored inside Variant container. - */ - enum class Type { - UNKNOWN, ///< empty object - - BYTE_1, ///< A 1-byte signed integer - BYTE_2, ///< A 2-byte signed integer - BYTE_4, ///< A 4-byte signed integer - BYTE_8, ///< An 8-byte signed integer - - UBYTE_1, ///< A 1-byte unsigned integer - UBYTE_2, ///< A 2-byte unsigned integer - UBYTE_4, ///< A 4-byte unsigned integer - UBYTE_8, ///< An 8-byte unsigned integer - - DOUBLE, ///< A double-precision floating-point number - BOOL, ///< A boolean value - - STRING, ///< std::string - BYTEARRAY, ///< ByteArray_t - - LIST, ///< VariantList_t - VECTOR, ///< VariantVector_t - - MAP, ///< VariantMap_t - PAIR, ///< VariantPair_t - - CUSTOM ///< any type - }; - -public: - DEF_MAKE(int8_t, Type::BYTE_1); - DEF_MAKE(int16_t, Type::BYTE_2); - DEF_MAKE(int32_t, Type::BYTE_4); - DEF_MAKE(int64_t, Type::BYTE_8); - DEF_MAKE(uint8_t, Type::UBYTE_1); - DEF_MAKE(uint16_t, Type::UBYTE_2); - DEF_MAKE(uint32_t, Type::UBYTE_4); - DEF_MAKE(uint64_t, Type::UBYTE_8); - DEF_MAKE(double, Type::DOUBLE); - DEF_MAKE(bool, Type::BOOL); - DEF_MAKE(std::string&, Type::STRING); - DEF_MAKE(char*, Type::STRING); - DEF_MAKE(ByteArray_t&, Type::VECTOR); - - /** - * @brief Creates a Variant object with a value of type Type::VECTOR. - * @param binaryData The binary data to store. - * @param bytesCount The size of the binaryData in bytes. - * @return Newly constructed Variant object. - */ - static Variant make(const char* binaryData, const size_t bytesCount); - - DEF_MAKE(VariantVector_t&, Type::VECTOR); - template - DEF_MAKE(std::vector&, Type::VECTOR); - DEF_MAKE(VariantList_t&, Type::LIST); - template - DEF_MAKE(std::list&, Type::LIST); - DEF_MAKE(VariantMap_t&, Type::MAP); - - DEF_MAKE_DOC(Type::MAP) - template - static Variant make(const std::map& v); - - DEF_MAKE(VariantPair_t&, Type::PAIR); - - /** - * @brief Creates a Variant object with a value of type Type::PAIR. - * @param first stored as first value of a pair - * @param second stored as second value of a pair - * @return Newly constructed Variant object. - */ - template - static Variant make(const TFirst& first, const TSecond& second); - - DEF_MAKE_DOC(Type::CUSTOM) - template - static Variant make(const T& v); - - DEF_MAKE_DOC(Type::CUSTOM) - /** - * @note Normally if value type is supported (for example int, std::string, std::list, etc.) it will be properly identified - * and stored internally. This method is usefull if you want to explicitly create Variant with CUSTOM type (even when value - * type is supported by Variant, like std::string). - */ - template - static Variant makeCustom(const T& v); - - /** - * @brief Create a new Variant object from an existing Variant object. - * @param v The Variant object to copy. - * @return Newly constructed Variant object. - */ - static Variant make(const Variant& v); - -public: - /** - * @brief Default constructor. - * @details Constructs a new empty Variant object with Type::UNKNOWN and null data. - */ - Variant() = default; - - /** - * @brief Copy constructor for Variant object. - * @param v Another Variant object to copy from. - */ - Variant(const Variant& v); - - /** - * @brief Move constructor for Variant object. - * @param v Another Variant object to move from. - */ - Variant(Variant&& v) noexcept; - - /** - * @brief Destructor for Variant object. - */ - ~Variant(); - - DEF_CONSTRUCTOR(int8_t, Type::BYTE_1) - DEF_CONSTRUCTOR(int16_t, Type::BYTE_2) - DEF_CONSTRUCTOR(int32_t, Type::BYTE_4) - DEF_CONSTRUCTOR(int64_t, Type::BYTE_8) - DEF_CONSTRUCTOR(uint8_t, Type::UBYTE_1) - DEF_CONSTRUCTOR(uint16_t, Type::UBYTE_2) - DEF_CONSTRUCTOR(uint32_t, Type::UBYTE_4) - DEF_CONSTRUCTOR(uint64_t, Type::UBYTE_8) - DEF_CONSTRUCTOR(double, Type::DOUBLE) - DEF_CONSTRUCTOR(bool, Type::BOOL) - DEF_CONSTRUCTOR(std::string&, Type::STRING) - /** @brief Constructs a new variant object of type Type::STRING with an const char* value. */ - explicit Variant(const char* v); - - DEF_CONSTRUCTOR(ByteArray_t&, Type::BYTEARRAY) - /** @brief Constructs a new variant of type Type::BYTEARRAY. Copies bytesCount bytes from binaryData buffer. */ - explicit Variant(const char* binaryData, const size_t bytesCount); - - DEF_CONSTRUCTOR(VariantVector_t&, Type::VECTOR) - DEF_CONSTRUCTOR(VariantList_t&, Type::LIST) - DEF_CONSTRUCTOR(VariantMap_t&, Type::MAP) - DEF_CONSTRUCTOR(VariantPair_t&, Type::PAIR) - - template - DEF_CONSTRUCTOR(std::vector&, Type::VECTOR); - template - DEF_CONSTRUCTOR(std::list&, Type::LIST); - template - explicit Variant(const std::map& v); - - /** - * @brief Constructs a new variant object of type Type::PAIR. - * @param first stored as first value of a pair - * @param second stored as second value of a pair - */ - template - explicit Variant(const TFirst& first, const TSecond& second); - - /** @brief Constructs a new variant object of type Type::CUSTOM using custom types. */ - template - explicit Variant(const T& v); - - /** @brief Assigns the value of the \c v to this variant. */ - Variant& operator=(const Variant& v); - /** @brief Move-assigns the value of the \c v to this variant. */ - Variant& operator=(Variant&& v) noexcept; - - DEF_OPERATOR_ASSIGN(int8_t, Type::BYTE_1) - DEF_OPERATOR_ASSIGN(int16_t, Type::BYTE_2) - DEF_OPERATOR_ASSIGN(int32_t, Type::BYTE_4) - DEF_OPERATOR_ASSIGN(int64_t, Type::BYTE_8) - DEF_OPERATOR_ASSIGN(uint8_t, Type::UBYTE_1) - DEF_OPERATOR_ASSIGN(uint16_t, Type::UBYTE_2) - DEF_OPERATOR_ASSIGN(uint32_t, Type::UBYTE_4) - DEF_OPERATOR_ASSIGN(uint64_t, Type::UBYTE_8) - DEF_OPERATOR_ASSIGN(double, Type::DOUBLE) - DEF_OPERATOR_ASSIGN(bool, Type::BOOL) - DEF_OPERATOR_ASSIGN(std::string&, Type::STRING) - DEF_OPERATOR_ASSIGN(char*, Type::STRING) - DEF_OPERATOR_ASSIGN(ByteArray_t&, Type::BYTEARRAY) - DEF_OPERATOR_ASSIGN(VariantVector_t&, Type::VECTOR) - DEF_OPERATOR_ASSIGN(VariantList_t&, Type::LIST) - DEF_OPERATOR_ASSIGN(VariantMap_t&, Type::MAP) - DEF_OPERATOR_ASSIGN(VariantPair_t&, Type::PAIR) - - template - DEF_OPERATOR_ASSIGN(T&, Type::CUSTOM) - - /** - * @brief Resets value of the Varaint object and frees all allocated memory. - * After calling this method internal type will become Type::UNKNOWN. - */ - void clear(); - - /** - * @brief Gets the type of data stored in the Variant object - * @return type of the stored data - */ - inline Type getType() const; - - /** - * @brief Checks if variant contains value or not - * @retval true variant was initialzied with a value and it's type is not Type::UNKNOWN - * @retval false no value was set for the variant and it's type is Type::UNKNOWN - */ - operator bool() const; - - /** - * @brief Check if the Variant object doesn't contain any value - * @return true if the Variant object doesn't contain any value, false otherwise - */ - bool isEmpty() const; - - /** - * @brief Check if the Variant object contains a numeric value - * @return true if the Variant object contains a numeric value, false otherwise - */ - bool isNumeric() const; - - /** - * @brief Check if the Variant object contains a signed numeric value - * @return true if the Variant object contains a signed numeric value, false otherwise - */ - bool isSignedNumeric() const; - - /** - * @brief Check if the Variant object contains an unsigned numeric value - * @return true if the Variant object contains an unsigned numeric value, false otherwise - */ - bool isUnsignedNumeric() const; - - /** - * @brief Check if the Variant object contains a boolean value - * @return true if the Variant object contains a boolean value, false otherwise - */ - bool isBool() const; - - /** - * @brief Check if the Variant object contains a string - * @return true if the Variant object contains a string, false otherwise - */ - bool isString() const; - - /** - * @brief Check if the Variant object contains a byte array - * @return true if the Variant object contains a byte array, false otherwise - */ - bool isByteArray() const; - - /** - * @brief Check if the Variant object contains a vector - * @return true if the Variant object contains a vector, false otherwise - */ - bool isVector() const; - /** - * @brief Check if the Variant object contains a list - * @return true if the Variant object contains a list, false otherwise - */ - bool isList() const; - - /** - * @brief Check if the Variant object contains a map - * @return true if the Variant object contains a map, false otherwise - */ - bool isMap() const; - - /** - * @brief Check if the Variant object contains a pair - * @return true if the Variant object contains a pair, false otherwise - */ - bool isPair() const; - - /** - * @brief Check if the Variant object contains a custom type - * @return true if the Variant object contains a custom type, false otherwise - */ - bool isCustomType() const; - - /** - * @brief Returns the variant value represented as int64 - * @details Supported data types are: - * \li Type::BYTE_1 - * \li Type::BYTE_2 - * \li Type::BYTE_4 - * \li Type::BYTE_8 - * \li Type::UBYTE_1 - * \li Type::UBYTE_2 - * \li Type::UBYTE_4 - * \li Type::UBYTE_8 - * \li Type::DOUBLE - * \li Type::BOOL - returns 0 if the value is false and 1 if it's true - * \li Type::STRING - tries to convert string to number or returns 0 - * - * @return numeric representation of the Variant object (calling this method on an unsupported variant type returns 0). - */ - int64_t toInt64() const; - - /** - * @brief Returns the variant value represented as uint64 - * @copydetails toInt64() - */ - uint64_t toUInt64() const; - - /** - * @brief Returns the variant value represented as double - * @copydetails toInt64() - */ - double toDouble() const; - - /** - * @brief Returns the variant value represented as bool - * @details Supported data types are: - * \li Type::BYTE_1 - * \li Type::BYTE_2 - * \li Type::BYTE_4 - * \li Type::BYTE_8 - * \li Type::UBYTE_1 - * \li Type::UBYTE_2 - * \li Type::UBYTE_4 - * \li Type::UBYTE_8 - * \li Type::DOUBLE - * \li Type::BOOL - * \li Type::STRING - returns true if string value is "true" or it represents a number different from 0 - * - * @return boolean representation of the Variant object (calling this method on an unsupported variant type returns false). - */ - bool toBool() const; - - /** - * @brief Returns the variant value represented as string - * @details If stored value is not a string, method will try to convert it. Supported data types are: - * \li Type::BYTE_1 - * \li Type::BYTE_2 - * \li Type::BYTE_4 - * \li Type::BYTE_8 - * \li Type::UBYTE_1 - * \li Type::UBYTE_2 - * \li Type::UBYTE_4 - * \li Type::UBYTE_8 - * \li Type::DOUBLE - * \li Type::BOOL - * \li Type::STRING - * \li Type::BYTEARRAY - * \li Type::LIST - * \li Type::VECTOR - * \li Type::MAP - * \li Type::PAIR - * - * @return string representation of the Variant object (calling this method on an unsupported variant type returns an empty - * string). - */ - std::string toString() const; - - /** - * @brief Returns the variant value represented as byte array - * @details If stored value is not a byte array, method will try to convert it. Supported data types are: - * \li Type::BYTE_1 - * \li Type::BYTE_2 - * \li Type::BYTE_4 - * \li Type::BYTE_8 - * \li Type::UBYTE_1 - * \li Type::UBYTE_2 - * \li Type::UBYTE_4 - * \li Type::UBYTE_8 - * \li Type::DOUBLE - * \li Type::BOOL - * \li Type::STRING - * \li Type::BYTEARRAY - * \li Type::MAP - * \li Type::PAIR - * - * @remark This function always returns a copy of data. If you know that internal type is Type::BYTEARRAY and just want to - * access data it's better to use toByteArrayPtr() method. - * - * @return byte array representation of the Variant object (calling this method on an unsupported variant type returns an - * empty vector). - */ - ByteArray_t toByteArray() const; - - /** - * @brief Returns pointer to internal byte array data. - * @return pointer to internal data or nullptr if data type is not Type::BYTEARRAY - */ - std::shared_ptr getByteArray() const; - - /** - * @brief Returns pointer to internal vector data. - * @return pointer to internal data or nullptr if data type is not Type::VECTOR - */ - std::shared_ptr getVector() const; - - /** - * @brief Copies internal data to a requested std::vector type. - * Usage example: - * - * \code{.cpp} - * std::vector intData = {1, 2, 3}; - * Variant v(intData); - * - * std::vector intDataConverted = v.toVector([](const Variant& v){ return v.toInt64(); })); - * // intDataConverted = {1, 2, 3} - * - * std::vector strDataConverted = v.toVector([](const Variant& v){ return v.toString(); })); - * // strDataConverted = {"1", "2", "3"} - * \endcode - * - * @param converter functor which will be called for each vector item to convert it from Variant to requested type - * @return std::vector filled with data (empty container if data type is not Type::VECTOR) - */ - template - std::vector toVector(const std::function& converter) const; - - /** - * @brief Returns pointer to internal list data. - * @return pointer to internal data or nullptr if data type is not Type::LIST - */ - std::shared_ptr getList() const; - - /** - * @brief Copies internal data to a requested std::list type. - * Usage example: - * - * \code{.cpp} - * std::list intData = {1, 2, 3}; - * Variant v(intData); - * - * std::list intDataConverted = v.toList([](const Variant& v){ return v.toInt64(); })); - * // intDataConverted = {1, 2, 3} - * - * std::list strDataConverted = v.toList([](const Variant& v){ return v.toString(); })); - * // strDataConverted = {"1", "2", "3"} - * \endcode - * - * @param converter functor which will be called for each list item to convert it from Variant to requested type - * @return std::list filled with data (empty container if data type is not Type::LIST) - */ - template - std::list toList(const std::function& converter) const; - - /** - * @brief Returns pointer to internal map data. - * @return pointer to internal data or nullptr if data type is not Type::MAP - */ - std::shared_ptr getMap() const; - - /** - * @brief Copies internal data to a requested std::map type. - * Usage example: - * - * \code{.cpp} - * std::map intStrData = {{1, "aa"}, {2, "bb"}, {3, "cc"}}; - * Variant v = Variant::make(intStrData); - * - * std::map intStrDataConverted = v.toMap([](const Variant& key){ return key.toInt64(); }, - * [](const Variant& value){ return value.toString(); })); - * \endcode - * - * @param converterKey functor which will be called for each map key to convert it from Variant to requested type - * @param converterValue functor which will be called for each map value to convert it from Variant to requested type - * @return std::map filled with data (empty container if data type is not Type::MAP) - */ - template - std::map toMap(const std::function& converterKey, - const std::function& converterValue) const; - - /** - * @brief Returns pointer to internal pair data. - * @return pointer to internal data or nullptr if data type is not Type::PAIR - */ - std::shared_ptr getPair() const; - - /** - * @brief Copies internal data to a requested std::pair type. - * @details Usage example: - * - * \code{.cpp} - * std::pair intStrData = {1, "aa"}; - * Variant v = Variant::make(intStrData); - * - * std::pair intStrDataConverted = v.toPair([](const Variant& first){ return first.toInt64(); }, - * [](const Variant& second){ return second.toString(); })); - * \endcode - * - * @param converterFirst functor which will be called for first pair element to convert it from Variant to requested type - * @param converterSecond functor which will be called for second pair element to convert it from Variant to requested type - * @return std::pair filled with data (empty container if data type is not Type::PAIR) - */ - template - std::pair toPair(const std::function& converterFirst, - const std::function& converterSecond) const; - - /** - * @brief Returns pointer to custom type data. - * - * @warning This function is not type-safe and simply does static_cast to requested type. User is responsible for specifying - * same type that was used for initialization of a variant object. - * - * @return pointer to internal data or nullptr if data type is not Type::CUSTOM - */ - template - std::shared_ptr getCustomType() const; - - /** - * @brief Compares this Variant object to another Variant object for equality. - * @details Variant uses the == operator of the stored type to check for equality. Variants of different types will always - * compare as not equal. - * @param val The Variant object to compare to. - * @return true if this object is equal to val, false otherwise. - */ - bool operator==(const Variant& val) const; - - /** - * @brief Compares this Variant object to another Variant object for inequality. - * @details Impelemented using operator==(). - * @param val The Variant object to compare to. - * @return true if this object is not equal to val, false otherwise. - */ - bool operator!=(const Variant& val) const; - - /** - * @brief Checks if this Variant object is greater than another Variant object. - * @details Operator only compares variants of the same type. Comparing logic depends on the underlying type: - * \li numeric, bool or string values are compared using standard operator > - * \li vector or list values are compared by size (A.size() > B.size()) - * \li other types are not supported and false is always returned - * - * @param val The Variant object to compare to. - * @return true if this object is greater than val, false otherwise. - */ - bool operator>(const Variant& val) const; - - /** - * @brief Checks if this Variant object is greater than or equal to another Variant object. - * @details Impelemented using operator==() and operator>(). - * - * @param val The Variant object to compare to. - * @return true if this object is greater than or equal to val, false otherwise. - */ - bool operator>=(const Variant& val) const; - - /** - * @brief Checks if this Variant object is less than another Variant object. - * @details Impelemented using operator!=() and operator>(). - * - * @param val The Variant object to compare to. - * @return true if this object is less than val, false otherwise. - */ - bool operator<(const Variant& val) const; - - /** - * @brief Checks if this Variant object is less than or equal to another Variant object. - * @details Impelemented using operator==() and operator>(). - * - * @param val The Variant object to compare to. - * @return true if this object is less than or equal to val, false otherwise. - */ - bool operator<=(const Variant& val) const; - -private: - Variant(std::shared_ptr d, const Type t); - - template - Variant(const T& v, const Type t); - - bool isSameObject(const Variant& val) const; - - template - void assign(const T& v, const Type t); - - template - inline std::shared_ptr value() const; - - template - void createMemoryAllocator(); - - void freeMemory(); - -private: - Type type = Type::UNKNOWN; - std::shared_ptr data; - MemoryAllocatorFunc_t memoryAllocator; - CompareFunc_t compareOperator; -}; - -template -Variant Variant::make(const std::vector& v) { - return Variant(v); -} - -template -Variant Variant::make(const std::list& v) { - return Variant(v); -} - -template -Variant Variant::make(const std::map& v) { - return Variant(v); -} - -template -Variant Variant::make(const TFirst& first, const TSecond& second) { - return Variant(first, second); -} - -template -Variant Variant::make(const T& v) { - return makeCustom(v); -} - -template -Variant Variant::makeCustom(const T& v) { - return Variant(v, Type::CUSTOM); -} - -template -Variant::Variant(const std::vector& v) { - std::shared_ptr dest = std::shared_ptr(new VariantVector_t(), [](void* ptr) { - delete reinterpret_cast(ptr); - }); - - dest->reserve(v.size()); - - for (auto it = v.begin(); it != v.end(); ++it) { - dest->emplace_back(*it); - } - - data = std::static_pointer_cast(dest); - type = Type::VECTOR; - createMemoryAllocator(); -} - -template -Variant::Variant(const std::list& v) { - std::shared_ptr dest = - std::shared_ptr(new VariantList_t(), [](void* ptr) { delete reinterpret_cast(ptr); }); - - for (auto it = v.begin(); it != v.end(); ++it) { - dest->emplace_back(*it); - } - - data = std::static_pointer_cast(dest); - type = Type::LIST; - createMemoryAllocator(); -} - -template -Variant::Variant(const std::map& v) { - std::shared_ptr dest = - std::shared_ptr(new VariantMap_t(), [](void* ptr) { delete reinterpret_cast(ptr); }); - - for (auto it = v.begin(); it != v.end(); ++it) { - dest->emplace(it->first, it->second); - } - - data = std::static_pointer_cast(dest); - type = Type::MAP; - createMemoryAllocator(); -} - -template -Variant::Variant(const TFirst& first, const TSecond& second) - // cppcheck-suppress misra-c2012-10.4 : false-positive. thinks that ':' is arithmetic operation - : Variant(std::make_pair(Variant(first), Variant(second))) {} - -template -Variant::Variant(const T& v) { - assign(v, Type::CUSTOM); -} - -template -Variant& Variant::operator=(const T& v) { - assign(v, Type::CUSTOM); - return *this; -} - -Variant::Type Variant::getType() const { - return type; -} - -template -std::shared_ptr Variant::getCustomType() const { - std::shared_ptr result; - - if (true == isCustomType()) { - result = value(); - } - - return result; -} - -template -std::vector Variant::toVector(const std::function& converter) const { - std::vector res; - - if (isVector()) { - std::shared_ptr vectorData = getVector(); - - // cppcheck-suppress misra-c2012-14.4 ; false-positive. std::shared_ptr has a bool() operator - if (vectorData) { - res.reserve(vectorData->size()); - - for (const Variant& curItem : *vectorData) { - res.emplace_back(converter(curItem)); - } - } - } - - return res; -} - -template -std::list Variant::toList(const std::function& converter) const { - std::list res; - - if (isList()) { - std::shared_ptr listData = getList(); - - // cppcheck-suppress misra-c2012-14.4 ; false-positive. std::shared_ptr has a bool() operator - if (listData) { - for (const Variant& curItem : *listData) { - res.emplace_back(converter(curItem)); - } - } - } - - return res; -} - -template -std::map Variant::toMap(const std::function& converterKey, - const std::function& converterValue) const { - std::map res; - - if (isMap()) { - std::shared_ptr mapData = getMap(); - - // cppcheck-suppress misra-c2012-14.4 ; false-positive. std::shared_ptr has a bool() operator - if (mapData) { - for (const auto& curItem : *mapData) { - res.insert(std::make_pair(converterKey(curItem.first), converterValue(curItem.second))); - } - } - } - - return res; -} - -template -std::pair Variant::toPair(const std::function& converterFirst, - const std::function& converterSecond) const { - std::pair res; - - if (isPair()) { - std::shared_ptr pairData = getPair(); - - // cppcheck-suppress misra-c2012-14.4 ; false-positive. std::shared_ptr has a bool() operator - if (pairData) { - res = std::make_pair(converterFirst(pairData->first), converterSecond(pairData->second)); - } - } - - return res; -} - -template -Variant::Variant(const T& v, const Type t) { - assign(v, t); -} - -template -void Variant::assign(const T& v, const Type t) { - freeMemory(); - createMemoryAllocator(); - type = t; - data = memoryAllocator(&v); -} - -template -inline std::shared_ptr Variant::value() const { - return std::static_pointer_cast(data); -} - -template -void Variant::createMemoryAllocator() { - memoryAllocator = [](const void* ptr) { - // NOTE: false-positive. "return" statement belongs to lambda function, not parent function - // cppcheck-suppress misra-c2012-15.5 - return ((nullptr != ptr) ? (std::shared_ptr(new T(*reinterpret_cast(ptr)), - [](void* ptr) { delete reinterpret_cast(ptr); })) - : nullptr); - }; - - // cppcheck-suppress misra-c2012-13.1 ; false-positive. this is a functor, not initializer list - compareOperator = [](const void* left, const void* right) { - int res = -1; - if (*reinterpret_cast(left) == *reinterpret_cast(right)) { - res = 0; - } else if (*reinterpret_cast(left) > *reinterpret_cast(right)) { - res = 1; - } else { - // do nothing - } - - // NOTE: false-positive. "return" statement belongs to lambda function, not parent function - // cppcheck-suppress misra-c2012-15.5 - return res; - }; -} - -} // namespace hsmcpp - -#endif // HSMCPP_VARIANT_HPP diff --git a/src/hsmcpp/version.hpp b/src/hsmcpp/version.hpp deleted file mode 100644 index d9587d7..0000000 --- a/src/hsmcpp/version.hpp +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (C) 2023 Igor Krechetov -// Distributed under MIT license. See file LICENSE for details - -#ifndef HSMCPP_VERSION_HPP -#define HSMCPP_VERSION_HPP - -#define HSMCPP_MAJOR_VERSION (1) -#define HSMCPP_MINOR_VERSION (0) -#define HSMCPP_MICRO_VERSION (4) - -#endif // HSMCPP_VERSION_HPP diff --git a/src/variant.cpp b/src/variant.cpp index a249742..6291863 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -4,6 +4,7 @@ #include "hsmcpp/variant.hpp" #include +#include #include "hsmcpp/os/os.hpp" @@ -209,7 +210,7 @@ bool Variant::operator==(const Variant& val) const { if ((Type::DOUBLE == type) || (Type::DOUBLE == val.type)) { // compare with precision for double // cppcheck-suppress misra-c2012-10.4 : false positive. both operands have type double - equal = (std::abs(toDouble() - val.toDouble()) < comparePrecision); + equal = (fabs(toDouble() - val.toDouble()) < comparePrecision); } else if (isUnsignedNumeric() || val.isUnsignedNumeric()) { equal = (toUInt64() == val.toUInt64()); } else {