QP is an event-driven, RTOS-like, active object framework for microcontrollers, such as mbed. The QP framework provides thread-safe execution of active objects (concurrent state machines) and support both manual and automatic coding of UML statecharts in readable, production-quality C or C++. Automatic code generation of QP code is supported by the free QM modeling tool.

Dependents:   qp_hangman qp_dpp qp_blinky

QP/C++ (Quantum Platform in C++) is a lightweight, open source active object (actor) framework for building responsive and modular real-time embedded applications as systems of asynchronous event-driven active objects (actors). The QP/C++ framework is a member of a larger family consisting of QP/C++, QP/C, and QP-nano frameworks, which are all strictly quality controlled, thoroughly documented, and available under GPLv3 with a special Exception for mbed (see http://www.state-machine.com/licensing/QP-mbed_GPL_Exception.txt).

The behavior of active objects is specified in QP/C++ by means of hierarchical state machines (UML statecharts). The framework supports manual coding of UML state machines in C++ as well as automatic code generation by means of the free QM modeling tool (http://www.state-machine.com/qm).

Please see the "QP/C++ Reference Manual" (http://www.state-machine.com/qpcpp) for more information.

Revision:
6:01d57c81e96a
Parent:
5:949864ba515c
Child:
7:bf92d3a6625e
--- a/qp.h	Sun Sep 25 18:10:41 2011 +0000
+++ b/qp.h	Mon Sep 26 01:42:32 2011 +0000
@@ -1,7 +1,7 @@
 //////////////////////////////////////////////////////////////////////////////
 // Product: QP/C++
-// Last Updated for QP ver: 4.1.06 (modified to fit in one file)
-// Date of the Last Update: Jan 26, 2011
+// Last Updated for QP ver: 4.2.04 (modified to fit in one file)
+// Date of the Last Update: Sep 25, 2011
 //
 //                    Q u a n t u m     L e a P s
 //                    ---------------------------
@@ -28,6 +28,10 @@
 #ifndef qp_h
 #define qp_h
 
+#ifdef Q_USE_NAMESPACE
+namespace QP {
+#endif
+
 // "qevent.h" ================================================================
 /// \brief QEvent class and basic macros used by all QP components.
 ///
@@ -40,7 +44,7 @@
 /// \return version of the QP as a hex constant constant 0xXYZZ, where X is
 /// a 1-digit major version number, Y is a 1-digit minor version number, and
 /// ZZ is a 2-digit release number.
-#define QP_VERSION      0x4106
+#define QP_VERSION      0x4204U
 
 #ifndef Q_ROM
     /// \brief Macro to specify compiler-specific directive for placing a
@@ -97,9 +101,11 @@
     /// This macro can be defined in the QEP port file (qep_port.h) to
     /// configure the ::QSignal type. When the macro is not defined, the
     /// default of 1 byte is chosen.
-    #define Q_SIGNAL_SIZE 1
+    #define Q_SIGNAL_SIZE 2
 #endif
 #if (Q_SIGNAL_SIZE == 1)
+    typedef uint8_t QSignal;
+#elif (Q_SIGNAL_SIZE == 2)
     /// \brief QSignal represents the signal of an event.
     ///
     /// The relationship between an event and a signal is as follows. A signal
@@ -109,12 +115,6 @@
     /// event. (The signal conveys the type of the occurrence-what happened?)
     /// However, an event can also contain additional quantitative information
     /// about the occurrence in form of event parameters. Please refer to the
-    /// document
-    /// <A HREF="http://www.quantum-leaps.com/devzone/Recipe_IntroHSM.pdf">
-    /// Brief Introduction to UML State Machines</A>) for more information
-    /// about state machine concepts.
-    typedef uint8_t QSignal;
-#elif (Q_SIGNAL_SIZE == 2)
     typedef uint16_t QSignal;
 #elif (Q_SIGNAL_SIZE == 4)
     typedef uint32_t QSignal;
@@ -137,7 +137,13 @@
 /// \include qep_qevent.cpp
 struct QEvent {
     QSignal sig;                             ///< signal of the event instance
-    uint8_t dynamic_;  ///< attributes of a dynamic event (0 for static event)
+    uint8_t poolId_;                         ///< pool ID (0 for static event)
+    uint8_t refCtr_;                                    ///< reference counter
+
+#ifdef Q_EVT_CTOR
+    QEvent(QSignal s) : sig(s) {}
+    virtual ~QEvent() {}                                 // virtual destructor
+#endif
 };
 
 //////////////////////////////////////////////////////////////////////////////
@@ -1179,6 +1185,11 @@
     #error "QF_EVENT_SIZ_SIZE defined incorrectly, expected 1, 2, or 4"
 #endif
 
+//////////////////////////////////////////////////////////////////////////////
+#ifndef QF_MAX_EPOOL
+    /// \brief Default value of the macro configurable value in qf_port.h
+    #define QF_MAX_EPOOL 3
+#endif
 
 //////////////////////////////////////////////////////////////////////////////
 #ifndef QF_ACTIVE_SUPER_
@@ -1306,7 +1317,7 @@
     /// \include qf_start.cpp
     void start(uint8_t prio,
                QEvent const *qSto[], uint32_t qLen,
-               void *stkSto = (void *)0, uint32_t stkSize = 0,
+               void *stkSto, uint32_t stkSize,
                QEvent const *ie = (QEvent *)0);
 
     /// \brief Posts an event \a e directly to the event queue of the acitve
@@ -1327,13 +1338,18 @@
     /// queues, direct event dispatching is synchronous. Direct event
     /// dispatching occurs when you call QHsm::dispatch(), or QFsm::dispatch()
     /// function.
+#ifndef Q_SPY
     void postFIFO(QEvent const *e);
+#else
+    void postFIFO(QEvent const *e, void const *sender);
+#endif
 
     /// \brief Posts an event directly to the event queue of the active object
     /// \a me using the Last-In-First-Out (LIFO) policy.
     ///
-    /// \note The LIFO policy should be used only with great caution because
-    /// it alters order of events in the queue.
+    /// \note The LIFO policy should be used only for self-posting and with
+    /// great caution because it alters order of events in the queue.
+    ///
     /// \sa QActive::postFIFO()
     void postLIFO(QEvent const *e);
 
@@ -1451,14 +1467,14 @@
     /// deferred event queue \a eq and posted (LIFO) to the event queue of
     /// the active object.
     ///
-    /// QActive::recall() returns the pointer to the recalled event to the
-    /// caller. The function returns NULL if no event has been recalled.
+    /// QActive::recall() returns 1 (TRUE) if an event has been recalled.
+    /// Otherwise the function returns 0.
     ///
     /// An active object can use multiple event queues to defer events of
     /// different kinds.
     ///
     /// \sa QActive::defer(), QEQueue, QActive::postLIFO()
-    QEvent const *recall(QEQueue *eq);
+    uint8_t recall(QEQueue *eq);
 
 public:
     /// \brief Un-subscribes from the delivery of all signals to the active
@@ -1667,21 +1683,14 @@
     /// expected in the active object's state machine.
     uint8_t rearm(QTimeEvtCtr nTicks);
 
-    // for backwards compatibility
-
-    /// \brief Arm a one-shot time event for direct event posting (obsolete).
+    /// \brief Get the current value of the down-counter of a time event.
     ///
-    /// This facility is now obsolete, please use \sa QTimeEvt::postIn().
-    void fireIn(QActive *act, QTimeEvtCtr nTicks) {
-        postIn(act, nTicks);
-    }
-
-    /// \brief Arm a periodic time event for direct event posting (obsolete).
+    /// If the time event is armed, the function returns the current value of
+    /// the down-counter of the given time event. If the time event is not
+    /// armed, the function returns 0.
     ///
-    /// This facility is now obsolete, please use \sa QTimeEvt::postEvery().
-    void fireEvery(QActive *act, QTimeEvtCtr nTicks) {
-        postEvery(act, nTicks);
-    }
+    /// /note The function is thread-safe.
+    QTimeEvtCtr ctr(void);
 
 private:
 
@@ -1905,7 +1914,11 @@
     /// event to multiple subscribers. This happens in the caller's thread
     /// with the scheduler locked to prevent preemptions during the multi-
     /// casting process. (Please note that the interrupts are not locked.)
+#ifndef Q_SPY
     static void publish(QEvent const *e);
+#else
+    static void publish(QEvent const *e, void const *sender);
+#endif
 
     /// \brief Processes all armed time events at every clock tick.
     ///
@@ -1921,7 +1934,11 @@
     ///
     /// The following example illustrates the call to QF::tick():
     /// \include qf_tick.cpp
+#ifndef Q_SPY
     static void tick(void);
+#else
+    static void tick(void const *sender);
+#endif
 
     /// \brief Returns the QF version.
     ///
@@ -1977,6 +1994,10 @@
     /// Please use the macro #Q_NEW.
     static QEvent *new_(uint16_t evtSize, QSignal sig);
 
+#ifdef Q_EVT_CTOR
+    #define Q_NEW(evtT_, sig_, ...) \
+        (new(QF::new_(sizeof(evtT_), sig_)) evtT_((sig_), ##__VA_ARGS__))
+#else
     /// \brief Allocate a dynamic event.
     ///
     /// This macro returns an event pointer cast to the type \a evtT_. The
@@ -1991,6 +2012,7 @@
     /// The following example illustrates dynamic allocation of an event:
     /// \include qf_post.cpp
     #define Q_NEW(evtT_, sig_) ((evtT_ *)QF::new_(sizeof(evtT_), (sig_)))
+#endif
 
     /// \brief Recycle a dynamic event.
     ///
@@ -2086,6 +2108,157 @@
 extern uint8_t const Q_ROM Q_ROM_VAR QF_div8Lkup[65];
 
 //////////////////////////////////////////////////////////////////////////////
+#ifdef Q_EVT_CTOR
+#include <new>                                            // for placement new
+#endif
+
+// from qf.h -----------------------------------------------------------------
+//////////////////////////////////////////////////////////////////////////////
+// QS software tracing integration, only if enabled
+#ifdef Q_SPY                                   // QS software tracing enabled?
+    #define QS_TIME_SIZE            4
+    #define QS_OBJ_PTR_SIZE         4
+    #define QS_FUN_PTR_SIZE         4
+
+    /// \brief Invoke the system clock tick processing QF::tick(). This macro
+    /// is the recommended way of invoking clock tick processing, because it
+    /// provides the vital information for software tracing and avoids any
+    /// overhead when the tracing is disabled.
+    ///
+    /// This macro takes the argument \a sender_, which is a pointer to the
+    /// sender object. This argument is actually only used when QS software
+    /// tracing is enabled (macro #Q_SPY is defined). When QS software
+    /// tracing is disabled, the macro calls QF::tick() without any
+    /// arguments, so the overhead of passing this extra argument is
+    /// entirely avoided.
+    ///
+    /// \note the pointer to the sender object is not necessarily a poiner
+    /// to an active object. In fact, typically QF::TICK() will be called from
+    /// an interrupt, in which case you would create a unique object just to
+    /// unambiguously identify the ISR as the sender of the time events.
+    ///
+    /// \sa QF::tick()
+    #define TICK(sender_)                  tick(sender_)
+
+    /// \brief Invoke the event publishing facility QF::publish(). This macro
+    /// is the recommended way of publishing events, because it provides the
+    /// vital information for software tracing and avoids any overhead when the
+    /// tracing is disabled.
+    ///
+    ///
+    /// This macro takes the last argument \a sender_, which is a pointer to
+    /// the sender object. This argument is actually only used when QS software
+    /// tracing is enabled (macro #Q_SPY is defined). When QS software
+    /// tracing is disabled, the macro calls QF::publish() without the
+    /// \a sender_ argument, so the overhead of passing this extra argument
+    /// is entirely avoided.
+    ///
+    /// \note the pointer to the sender object is not necessarily a poiner
+    /// to an active object. In fact, if QF::PUBLISH() is called from an
+    /// interrupt or other context, you can create a unique object just to
+    /// unambiguously identify the publisher of the event.
+    ///
+    /// \sa QF::publish()
+    #define PUBLISH(e_, sender_)           publish((e_), (sender_))
+
+    /// \brief Invoke the direct event posting facility QActive::postFIFO().
+    /// This macro is the recommended way of posting events, because it provides
+    /// the vital information for software tracing and avoids any overhead when
+    /// the tracing is disabled.
+    ///
+    ///
+    /// This macro takes the last argument \a sender_, which is a pointer to
+    /// the sender object. This argument is actually only used when QS software
+    /// tracing is disabled (macro #Q_SPY is defined). When QS software
+    /// tracing is not enabled, the macro calls QF_publish() without the
+    /// \a sender_ argument, so the overhead of passing this extra argument
+    /// is entirely avoided.
+    ///
+    /// \note the pointer to the sender object is not necessarily a poiner
+    /// to an active object. In fact, if ao->POST() is called from an
+    /// interrupt or other context, you can create a unique object just to
+    /// unambiguously identify the publisher of the event.
+    ///
+    /// \sa QActive::postFIFO()
+    #define POST(e_, sender_)              postFIFO((e_), (sender_))
+
+    #if (QF_EQUEUE_CTR_SIZE == 1)
+
+        /// \brief Internal QS macro to output an unformatted event queue
+        /// counter data element
+        /// \note the counter size depends on the macro #QF_EQUEUE_CTR_SIZE.
+        #define QS_EQC_(ctr_)       QS::u8_(ctr_)
+    #elif (QF_EQUEUE_CTR_SIZE == 2)
+        #define QS_EQC_(ctr_)       QS::u16_(ctr_)
+    #elif (QF_EQUEUE_CTR_SIZE == 4)
+        #define QS_EQC_(ctr_)       QS::u32_(ctr_)
+    #else
+        #error "QF_EQUEUE_CTR_SIZE not defined"
+    #endif
+
+
+    #if (QF_EVENT_SIZ_SIZE == 1)
+
+        /// \brief Internal QS macro to output an unformatted event size
+        /// data element
+        /// \note the event size depends on the macro #QF_EVENT_SIZ_SIZE.
+        #define QS_EVS_(size_)      QS::u8_(size_)
+    #elif (QF_EVENT_SIZ_SIZE == 2)
+        #define QS_EVS_(size_)      QS::u16_(size_)
+    #elif (QF_EVENT_SIZ_SIZE == 4)
+        #define QS_EVS_(size_)      QS::u32_(size_)
+    #endif
+
+
+    #if (QF_MPOOL_SIZ_SIZE == 1)
+
+        /// \brief Internal QS macro to output an unformatted memory pool
+        /// block-size data element
+        /// \note the block-size depends on the macro #QF_MPOOL_SIZ_SIZE.
+        #define QS_MPS_(size_)      QS::u8_(size_)
+    #elif (QF_MPOOL_SIZ_SIZE == 2)
+        #define QS_MPS_(size_)      QS::u16_(size_)
+    #elif (QF_MPOOL_SIZ_SIZE == 4)
+        #define QS_MPS_(size_)      QS::u32_(size_)
+    #endif
+
+    #if (QF_MPOOL_CTR_SIZE == 1)
+
+        /// \brief Internal QS macro to output an unformatted memory pool
+        /// block-counter data element
+        /// \note the counter size depends on the macro #QF_MPOOL_CTR_SIZE.
+        #define QS_MPC_(ctr_)       QS::u8_(ctr_)
+    #elif (QF_MPOOL_CTR_SIZE == 2)
+        #define QS_MPC_(ctr_)       QS::u16_(ctr_)
+    #elif (QF_MPOOL_CTR_SIZE == 4)
+        #define QS_MPC_(ctr_)       QS::u32_(ctr_)
+    #endif
+
+
+    #if (QF_TIMEEVT_CTR_SIZE == 1)
+
+        /// \brief Internal QS macro to output an unformatted time event
+        /// tick-counter data element
+        /// \note the counter size depends on the macro #QF_TIMEEVT_CTR_SIZE.
+        #define QS_TEC_(ctr_)       QS::u8_(ctr_)
+    #elif (QF_TIMEEVT_CTR_SIZE == 2)
+        #define QS_TEC_(ctr_)       QS::u16_(ctr_)
+    #elif (QF_TIMEEVT_CTR_SIZE == 4)
+        #define QS_TEC_(ctr_)       QS::u32_(ctr_)
+    #endif
+
+#else
+    #ifndef qs_dummy_h
+    #include "qs_dummy.h"                   // disable the QS software tracing
+    #endif
+
+    #define TICK(dummy_)                   tick()
+    #define PUBLISH(e_, dummy_)            publish((e_))
+    #define POST(e_, dummy_)               postFIFO((e_))
+
+#endif                                                                // Q_SPY
+
+//////////////////////////////////////////////////////////////////////////////
 // QS software tracing
 #ifdef Q_SPY
 
@@ -2140,7 +2313,7 @@
     QS_QF_TIMEEVT_DISARM,           ///< true disarming of an armed time event
     QS_QF_TIMEEVT_REARM,                         ///< rearming of a time event
     QS_QF_TIMEEVT_POST,      ///< a time event posted itself directly to an AO
-    QS_QF_RESERVED7,
+    QS_QF_TIMEEVT_CTR,                 ///< a time event counter was requested
     QS_QF_INT_LOCK,                                ///< interrupts were locked
     QS_QF_INT_UNLOCK,                            ///< interrupts were unlocked
     QS_QF_ISR_ENTRY,                                   ///< an ISR was entered
@@ -2384,6 +2557,18 @@
     /// client code directly.
     static void mem(uint8_t const *blk, uint8_t size);
 
+#if (QS_OBJ_PTR_SIZE == 8) || (QS_FUN_PTR_SIZE == 8)
+    /// \brief Output uint64_t data element without format information
+    /// \note This function is only to be used through macros, never in the
+    /// client code directly.
+    static void u64_(uint64_t d);
+
+    /// \brief Output uint64_t data element with format information
+    /// \note This function is only to be used through macros, never in the
+    /// client code directly.
+    static void u64(uint8_t format, uint64_t d);
+#endif
+
     // QS buffer access ......................................................
 
     /// \brief Byte-oriented interface to the QS data buffer.
@@ -2633,7 +2818,7 @@
 /// to NULL.
 ///
 /// \sa Example of using QS filters in #QS_FILTER_ON documentation
-#define QS_FILTER_AP_OBJ(obj_)  (QS_apObj_ = (obj_))
+#define QS_FILTER_AP_OBJ(obj_)  (QS::apObj_ = (obj_))
 
 
 //////////////////////////////////////////////////////////////////////////////
@@ -2769,6 +2954,8 @@
     #define QS_OBJ_(obj_)       QS::u16_((uint16_t)(obj_))
 #elif (QS_OBJ_PTR_SIZE == 4)
     #define QS_OBJ_(obj_)       QS::u32_((uint32_t)(obj_))
+#elif (QS_OBJ_PTR_SIZE == 8)
+    #define QS_OBJ_(obj_)       QS::u64_((uint64_t)(obj_))
 #else
 
     /// \brief Internal QS macro to output an unformatted object pointer
@@ -2785,6 +2972,8 @@
     #define QS_FUN_(fun_)       QS::u16_((uint16_t)(fun_))
 #elif (QS_FUN_PTR_SIZE == 4)
     #define QS_FUN_(fun_)       QS::u32_((uint32_t)(fun_))
+#elif (QS_FUN_PTR_SIZE == 8)
+    #define QS_FUN_(fun_)       QS::u64_((uint64_t)(fun_))
 #else
 
     /// \brief Internal QS macro to output an unformatted function pointer
@@ -2822,7 +3011,10 @@
     QS_MEM_T,                         ///< up to 255-bytes memory block format
     QS_SIG_T,                                         ///< event signal format
     QS_OBJ_T,                                       ///< object pointer format
-    QS_FUN_T                                      ///< function pointer format
+    QS_FUN_T,                                     ///< function pointer format
+    QS_I64_T,                                ///< signed 64-bit integer format
+    QS_U64_T,                              ///< unsigned 64-bit integer format
+    QS_U32_HEX_T                    ///< unsigned 32-bit integer in hex format
 };
 
 /// \brief Output formatted int8_t to the QS record
@@ -2857,6 +3049,18 @@
 #define QS_F64(width_, data_) \
     QS::f64((uint8_t)(((width_) << 4)) | QS_F64_T, (data_))
 
+/// \brief Output formatted int64_t to the QS record
+#define QS_I64(width_, data_) \
+    QS::u64((uint8_t)(((width_) << 4)) | QS_I64_T, (data_))
+
+/// \brief Output formatted uint64_t to the QS record
+#define QS_U64(width_, data_) \
+    QS::u64((uint8_t)(((width_) << 4)) | QS_U64_T, (data_))
+
+/// \brief Output formatted uint32_t to the QS record
+#define QS_U32_HEX(width_, data_) \
+    QS::u32((uint8_t)(((width_) << 4)) | QS_U32_HEX_T, (data_))
+
 /// \brief Output formatted zero-terminated ASCII string to the QS record
 #define QS_STR(str_)            QS::str(str_)
 
@@ -2875,6 +3079,8 @@
     #define QS_OBJ(obj_)        QS::u16(QS_OBJ_T, (uint16_t)(obj_))
 #elif (QS_OBJ_PTR_SIZE == 4)
     #define QS_OBJ(obj_)        QS::u32(QS_OBJ_T, (uint32_t)(obj_))
+#elif (QS_OBJ_PTR_SIZE == 8)
+    #define QS_OBJ(obj_)        QS::u64(QS_OBJ_T, (uint64_t)(obj_))
 #else
     /// \brief Output formatted object pointer to the QS record
     #define QS_OBJ(obj_)        QS::u32(QS_OBJ_T, (uint32_t)(obj_))
@@ -2887,6 +3093,8 @@
     #define QS_FUN(fun_)        QS::u16(QS_FUN_T, (uint16_t)(fun_))
 #elif (QS_FUN_PTR_SIZE == 4)
     #define QS_FUN(fun_)        QS::u32(QS_FUN_T, (uint32_t)(fun_))
+#elif (QS_FUN_PTR_SIZE == 8)
+    #define QS_FUN(fun_)        QS::u64(QS_FUN_T, (uint64_t)(fun_))
 #else
     /// \brief Output formatted function pointer to the QS record
     #define QS_FUN(fun_)        QS::u32(QS_FUN_T, (uint32_t)(fun_))
@@ -3159,7 +3367,9 @@
 #define QS_U32(width_, data_)           ((void)0)
 #define QS_F32(width_, data_)           ((void)0)
 #define QS_F64(width_, data_)           ((void)0)
+#define QS_U64(width_, data_)           ((void)0)
 #define QS_STR(str_)                    ((void)0)
+#define QS_U32_HEX(width_, data_)       ((void)0)
 #define QS_STR_ROM(str_)                ((void)0)
 #define QS_MEM(mem_, size_)             ((void)0)
 #define QS_SIG(sig_, obj_)              ((void)0)
@@ -3180,6 +3390,7 @@
 #define QS_U8_(data_)                   ((void)0)
 #define QS_U16_(data_)                  ((void)0)
 #define QS_U32_(data_)                  ((void)0)
+#define QS_U64_(data_)                  ((void)0)
 #define QS_TIME_()                      ((void)0)
 #define QS_SIG_(sig_)                   ((void)0)
 #define QS_EVS_(size_)                  ((void)0)
@@ -3198,6 +3409,10 @@
 
 #endif                                                                // Q_SPY
 
+#ifdef Q_USE_NAMESPACE
+}                                                              // namespace QP
+#endif
+
 //////////////////////////////////////////////////////////////////////////////
 /**
 * \brief Customizable QP assertions.
@@ -3207,19 +3422,23 @@
 * programs.
 *
 * \note The preprocessor switch Q_NASSERT disables checking assertions.
-* In particular macros \ref Q_ASSERT, \ref Q_REQUIRE, \ref Q_ENSURE,
-* \ref Q_INVARIANT, and \ref Q_ERROR do NOT evaluate the test condition
+* In particular macros #Q_ASSERT, #Q_REQUIRE, #Q_ENSURE, #Q_INVARIANT,
+* #Q_ERROR as well as  #Q_ASSERT_ID, #Q_REQUIRE_ID, #Q_ENSURE_ID,
+* #Q_INVARIANT_ID, and #Q_ERROR_ID do NOT evaluate the test condition
 * passed as the argument to these macros. One notable exception is the
-* macro \ref Q_ALLEGE, that still evaluates the test condition, but does
+* macro #Q_ALLEGE, that still evaluates the test condition, but does
 * not report assertion failures when the switch Q_NASSERT is defined.
 */
 #ifdef Q_NASSERT          /* Q_NASSERT defined--assertion checking disabled */
 
     #define Q_DEFINE_THIS_FILE
     #define Q_DEFINE_THIS_MODULE(name_)
-    #define Q_ASSERT(test_)    ((void)0)
-    #define Q_ALLEGE(test_)    ((void)(test_))
-    #define Q_ERROR()          ((void)0)
+    #define Q_ASSERT(test_)             ((void)0)
+    #define Q_ASSERT_ID(id_, test_)     ((void)0)
+    #define Q_ALLEGE(test_)             ((void)(test_))
+    #define Q_ALLEGE_ID(id_, test_)     ((void)(test_))
+    #define Q_ERROR()                   ((void)0)
+    #define Q_ERROR_ID(id_)             ((void)0)
 
 #else                  /* Q_NASSERT not defined--assertion checking enabled */
 
@@ -3227,9 +3446,10 @@
         extern "C" {
     #endif
 
-    /** callback invoked in case the condition passed to \ref Q_ASSERT,
-    * \ref Q_REQUIRE, \ref Q_ENSURE, \ref Q_ERROR, or \ref Q_ALLEGE
-    * evaluates to FALSE.
+    /** callback invoked in case the condition passed to #Q_ASSERT,
+    * #Q_REQUIRE, #Q_ENSURE, #Q_ERROR, #Q_ALLEGE as well as #Q_ASSERT_ID,
+    * #Q_REQUIRE_ID, #Q_ENSURE_ID, #Q_ERROR_ID, and #Q_ALLEGE_ID evaluates
+    * to FALSE.
     *
     * \param file file name where the assertion failed
     * \param line line number at which the assertion failed
@@ -3258,50 +3478,116 @@
     /** General purpose assertion that makes sure the \a test_ argument is
     * TRUE. Calls the Q_onAssert() callback if the \a test_ evaluates
     * to FALSE.
-    * \note the \a test_ is NOT evaluated if assertions are
-    * disabled with the Q_NASSERT switch.
+    * \note the \a test_ is NOT evaluated if assertions are disabled with
+    * the Q_NASSERT switch.
+    * \sa #Q_ASSERT_ID
     */
     #define Q_ASSERT(test_) \
         if (test_) { \
         } \
-        else (Q_onAssert(l_this_file, __LINE__))
+        else (Q_onAssert(&l_this_file[0], __LINE__))
+
+    /** General purpose assertion that makes sure the \a test_ argument is
+    * TRUE. Calls the Q_onAssert() callback if the \a test_ evaluates
+    * to FALSE. The argument \a id_ is the ID number (unique within
+    * the file) of the assertion. This assertion style is better suited
+    * for unit testig, because it avoids the volatility of line numbers
+    * for indentifying assertions.
+    * \note the \a test_ is NOT evaluated if assertions are disabled with
+    * the Q_NASSERT switch.
+    * \sa #Q_ASSERT
+    */
+    #define Q_ASSERT_ID(id_, test_) \
+        if (test_) { \
+        } \
+        else (Q_onAssert(&l_this_file[0], (id_)))
 
     /** General purpose assertion that ALWAYS evaluates the \a test_
     * argument and calls the Q_onAssert() callback if the \a test_
     * evaluates to FALSE.
-    * \note the \a test_ argument IS always evaluated even when assertions are
-    * disabled with the Q_NASSERT macro. When the Q_NASSERT macro is
+    * \note the \a test_ argument IS always evaluated even when assertions
+    * are disabled with the Q_NASSERT macro. When the Q_NASSERT macro is
     * defined, the Q_onAssert() callback is NOT called, even if the
     * \a test_ evaluates to FALSE.
+    * \sa #Q_ALLEGE_ID
     */
     #define Q_ALLEGE(test_)    Q_ASSERT(test_)
 
+    /** General purpose assertion that ALWAYS evaluates the \a test_
+    * argument and calls the Q_onAssert() callback if the \a test_
+    * evaluates to FALSE. This assertion style is better suited
+    * for unit testig, because it avoids the volatility of line numbers
+    * for indentifying assertions.
+    * \note the \a test_ argument IS always evaluated even when assertions
+    * are disabled with the Q_NASSERT macro. When the Q_NASSERT macro is
+    * defined, the Q_onAssert() callback is NOT called, even if the
+    * \a test_ evaluates to FALSE.
+    * \sa #Q_ALLEGE
+    */
+    #define Q_ALLEGE_ID(id_, test_) Q_ASSERT_ID(id_, test_)
+
     /** Assertion that always calls the Q_onAssert() callback if
     * ever executed.
     * \note can be disabled with the Q_NASSERT switch.
+    * \sa #Q_ERROR_ID
     */
     #define Q_ERROR() \
         (Q_onAssert(l_this_file, __LINE__))
 
+    /** Assertion that always calls the Q_onAssert() callback if
+    * ever executed. This assertion style is better suited for unit
+    * testig, because it avoids the volatility of line numbers for
+    * indentifying assertions.
+    * \note can be disabled with the Q_NASSERT switch.
+    * \sa #Q_ERROR
+    */
+    #define Q_ERROR_ID(id_) \
+        (Q_onAssert(l_this_file, (id_)))
+
+
 #endif                                                         /* Q_NASSERT */
 
 /** Assertion that checks for a precondition. This macro is equivalent to
-* \ref Q_ASSERT, except the name provides a better documentation of the
+* #Q_ASSERT, except the name provides a better documentation of the
 * intention of this assertion.
+* \sa #Q_REQUIRE_ID
 */
-#define Q_REQUIRE(test_)   Q_ASSERT(test_)
+#define Q_REQUIRE(test_)         Q_ASSERT(test_)
+
+/** Assertion that checks for a precondition. This macro is equivalent to
+* #Q_ASSERT_ID, except the name provides a better documentation of the
+* intention of this assertion.
+* \sa #Q_REQUIRE
+*/
+#define Q_REQUIRE_ID(id_, test_) Q_ASSERT_ID(id_, test_)
 
 /** Assertion that checks for a postcondition. This macro is equivalent to
-* \ref Q_ASSERT, except the name provides a better documentation of the
+* #Q_ASSERT, except the name provides a better documentation of the
 * intention of this assertion.
+* \sa #Q_ENSURE_ID
 */
-#define Q_ENSURE(test_)    Q_ASSERT(test_)
+#define Q_ENSURE(test_)          Q_ASSERT(test_)
+
+/** Assertion that checks for a postcondition. This macro is equivalent to
+* #Q_ASSERT_ID, except the name provides a better documentation of the
+* intention of this assertion.
+* \sa #Q_ENSURE
+*/
+#define Q_ENSURE_ID(id_, test_)  Q_ASSERT_ID(id_, test_)
 
 /** Assertion that checks for an invariant. This macro is equivalent to
-* \ref Q_ASSERT, except the name provides a better documentation of the
+* #Q_ASSERT, except the name provides a better documentation of the
 * intention of this assertion.
+* \sa #Q_INVARIANT_ID
 */
-#define Q_INVARIANT(test_) Q_ASSERT(test_)
+#define Q_INVARIANT(test_)       Q_ASSERT(test_)
+
+/** Assertion that checks for an invariant. This macro is equivalent to
+* #Q_ASSERT_ID, except the name provides a better documentation of the
+* intention of this assertion.
+* \sa #Q_INVARIANT
+*/
+#define Q_INVARIANT_ID(id_, test_) Q_ASSERT_ID(id_, test_)
 
 /** Compile-time assertion exploits the fact that in C/C++ a dimension of
 * an array cannot be negative. The following declaration causes a compilation