#include "app/fseq/fseq.h" #include "my_assert.h" #include // @sFSeqObj_t // The sequencer private structure typedef struct { const fFSeqEntry_t * fEntry; // current "function object" pointer tFSeqCtx_t ctx; // user context } sFSeqObj_t; // @fSeqConstruct // Construct the sequencer object, but not run it. // @fSeqObj - the object to construct // @first - the main state of the object, can not be NULL // @ctx - user-defined context, can be NULL void fSeqConstruct( xFSeqObj_t * fSeq, const fFSeqEntry_t * first, tFSeqCtx_t ctx ) { my_assert( fSeq ); my_assert( first ); my_assert( first->vtbl ); my_assert( first->vtbl->f ); if( NULL != fSeq && NULL != first ) { ((sFSeqObj_t*)fSeq)->fEntry = first; ((sFSeqObj_t*)fSeq)->ctx = ctx; } } // @fSeqStartup // Starts the constructed sequencer. // @fSeqObj - the sequencer object to start // @first - the state to start from, can be NULL // If @first is NULL, the value @first from the @fSeqConstruct() call will be used. void fSeqStartup( xFSeqObj_t * fSeq, const fFSeqEntry_t * first ) { if( NULL != first ) { ((sFSeqObj_t*)fSeq)->fEntry = first; } #if FSEQ_ENTRY_EXIT_SUPPORT #if FSEQ_ALT_INHERIT_METHOD_SUPPORT ((sFSeqObj_t*)fSeq)->fEntry->vtbl->enter( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx ); #else ((sFSeqObj_t*)fSeq)->fEntry->enter( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx ); #endif #endif } // @fSeqShutdown // Shutdowns the running sequencer // @fSeqObj - the sequencer object to shutdown void fSeqShutdown( xFSeqObj_t * fSeq ) { #if FSEQ_ENTRY_EXIT_SUPPORT #if FSEQ_ALT_INHERIT_METHOD_SUPPORT ((sFSeqObj_t*)fSeq)->fEntry->vtbl->leave( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx ); #else ((sFSeqObj_t*)fSeq)->fEntry->leave( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx ); #endif #endif } // @fSeqDispatch // Provides the sequencer work // Must be called to make the sequencer call the state functions // @fSeqObj - the sequencer object to operate with void fSeqDispatch( xFSeqObj_t * fSeq ) { const fFSeqEntry_t * next; #if FSEQ_ENTRY_EXIT_SUPPORT const fFSeqEntry_t * next_deferred = NULL; #if FSEQ_ALT_INHERIT_METHOD_SUPPORT while( (next = ((sFSeqObj_t*)fSeq)->fEntry->vtbl->f( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx, &next_deferred ), next ) ) { if( ((sFSeqObj_t*)fSeq)->fEntry != next ) // provide leave/enter events only if the operator has been changed { ((sFSeqObj_t*)fSeq)->fEntry->vtbl->leave( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx ); ((sFSeqObj_t*)fSeq)->fEntry = next; ((sFSeqObj_t*)fSeq)->fEntry->vtbl->enter( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx ); } } if( NULL != next_deferred ) { // provide leave/enter events even if the operator has been changed (deffered feature) ((sFSeqObj_t*)fSeq)->fEntry->vtbl->leave( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx ); ((sFSeqObj_t*)fSeq)->fEntry = next_deferred; ((sFSeqObj_t*)fSeq)->fEntry->vtbl->enter( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx ); } #else while( (next = ((sFSeqObj_t*)fSeq)->fEntry->f( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx, &next_deferred ), next ) ) { if( ((sFSeqObj_t*)fSeq)->fEntry != next ) // provide leave/enter events only if the operator has been changed { ((sFSeqObj_t*)fSeq)->fEntry->leave( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx ); ((sFSeqObj_t*)fSeq)->fEntry = next; ((sFSeqObj_t*)fSeq)->fEntry->enter( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx ); } } if( NULL != next_deferred ) { // provide leave/enter events even if the operator has been changed (deffered feature) ((sFSeqObj_t*)fSeq)->fEntry->leave( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx ); ((sFSeqObj_t*)fSeq)->fEntry = next_deferred; ((sFSeqObj_t*)fSeq)->fEntry->enter( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx ); } #endif #else #if FSEQ_ALT_INHERIT_METHOD_SUPPORT while( NULL != (next = ((sFSeqObj_t*)fSeq)->fEntry->vtbl->f( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx, &((sFSeqObj_t*)fSeq)->fEntry ), next) ) { ((sFSeqObj_t*)fSeq)->fEntry = next; } #else while( NULL != (next = ((sFSeqObj_t*)fSeq)->fEntry->f( ((sFSeqObj_t*)fSeq)->fEntry, ((sFSeqObj_t*)fSeq)->ctx, &((sFSeqObj_t*)fSeq)->fEntry ), next) ) { ((sFSeqObj_t*)fSeq)->fEntry = next; } #endif #endif } #if 0 #if FSEQ_ALT_INHERIT_METHOD_SUPPORT == 0 // ---------------- // Inheritance example: xFSeqObj_t seq; // sequencer // ... // first state vtable method prototypes: const fFSeqEntry_t * firstStateFunction( const fFSeqEntry_t *, tFSeqCtx_t, const fFSeqEntry_t * * ); void firstStateEnter( const fFSeqEntry_t *, tFSeqCtx_t ); void firstStateLeave( const fFSeqEntry_t *, tFSeqCtx_t ); // ... struct { int publicVar; } GlobalContext; // no comments // ... typedef struct { int privateVar; } tFirstStatePrivateContext; tFirstStatePrivateContext firstStatePrivateDynamicContext; // the dynamic private context of first state (RAM) // ... // The state user-structure, incuding the state entry itself (firstState) and the const pointer to the another object. const struct { fFSeqEntry_t firstState; void * ctx_ref; } firstStateExtended = { .firstState = { firstStateFunction, firstStateEnter, firstStateLeave }, .ctx_ref = &firstStatePrivateDynamicContext // link the const pointer with the dynamic object }; // ... void foo() // some user function { fSeqConstruct( &seq, &firstStateExtended.firstState, &GlobalContext ); // consturct sequencer and link the global context } // ... // @firstStateFunction // firstState function body const fFSeqEntry_t * firstStateFunction( const fFSeqEntry_t * this, tFSeqCtx_t ctx, const fFSeqEntry_t * * pnext ) { const void * const_ctx_ref = (const void*)(&this[1]); // get access to the @ctx_ref field from @firstStateExtended structure // ... tFirstStatePrivateContext * private_ctx = (void*)const_ctx_ref; // cast the pointer // ... private_ctx->privateVar = 1; // use private context } #endif #if FSEQ_ALT_INHERIT_METHOD_SUPPORT // ------------------------------------------------------------------------------------------------ // Inheritance example (Method #1, see @FSEQ_ALT_INHERIT_METHOD_SUPPORT): xFSeqObj_t seq; // sequencer // ... // first state vtable method prototypes: const fFSeqEntry_t * firstStateFunction( const fFSeqEntry_t *, tFSeqCtx_t, const fFSeqEntry_t * * ); void firstStateEnter( const fFSeqEntry_t *, tFSeqCtx_t ); void firstStateLeave( const fFSeqEntry_t *, tFSeqCtx_t ); // ... struct { int publicVar; } GlobalContext; // no comments // ... typedef struct { int privateVar; } tFirstStatePrivateContext; // ... typedef struct { fFSeqEntry_t firstState; tFirstStatePrivateContext privateContext; } tFirstStateAndPrivateContext; // the structure of state methods: const fFSeqVTable_t firstStateVTable = { firstStateFunction, firstStateEnter, firstStateLeave }; // The state user-structure, incuding the state entry, and the private context itself: tFirstStateAndPrivateContext firstStateExtended = { // the dynamic private context of first state (RAM) .firstState.vtbl = &firstStateVTable, .privateContext = { .privateVar = 0, // some values... } }; // ... // ... void foo() // some user function { fSeqConstruct( &seq, &firstStateExtended.firstState, &GlobalContext ); // consturct sequencer and link the global context } // ... // @firstStateFunction // firstState function body const fFSeqEntry_t * firstStateFunction( const fFSeqEntry_t * this, tFSeqCtx_t ctx, const fFSeqEntry_t * * pnext ) { tFirstStatePrivateContext * private_ctx = (tFirstStatePrivateContext*)(&this[1]); // get access to the private context // ... private_ctx->privateVar = 1; // use private context } // ------------------------------------------------------------------------------------------------ #endif #endif