| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- #ifndef FUNCTOR_SEQUENCER_H
- #define FUNCTOR_SEQUENCER_H
- #include <stdint.h>
- #include <stddef.h>
- //
- // Functor Sequencer v1.0
- //
- // Sychov A., jul/2019
- //
- // The functor sequencer.
- // This module implements the model of abstract programming (behaviour) that
- // implying a set of abstract states are switching sequently using so called
- // functors. The object that involves an user-defined context, user-defined
- // set of abstract states is called "sequencer". Each functor represents either
- // the only user-defined function with user context, or a set of such functions.
- // The set (@fFSeqEntry_t) consist of the user-defined function with user
- // context and two special user-defined functions - "enter" and "leave" -
- // that are called on the state changes. The state can be changed in two ways:
- // internally by the function itself and externally by calling module function
- // "Startup". The first way is the main and the only correct method of chaning
- // state of the running sequencer. The other method is should only used to start
- // up the sequencer with entering the specified state after constructing of a
- // sequencer object. To switch of running sequencer the "Shutdown" method is used.
- // The method "Dispatch" provides the sequencer operation and must be called
- // externally to make the sequencer to perform it's function.
- // The "entry" and "exit" functions are called automatically by the sequencer
- // if the functions are enabled by setting @FSEQ_ENTRY_EXIT_SUPPORT to 1.
- // The "entry" function has a prototype @fSeqEnter_t.
- // The "leave" function has a prototype @fSeqLeave_t.
- #define FSEQ_ENTRY_EXIT_SUPPORT 1
- // @FSEQ_ALT_INHERIT_METHOD_SUPPORT
- // Each state may have it's own private context. This macro chooses the method
- // of accessing to the private context.
- // If @FSEQ_ALT_INHERIT_METHOD_SUPPORT is zero (Method #0):
- // the state function body can get access to the state private context using casting
- // the pointer @fFSeqEntry_t* to the pointer to container:
- // const struct { fFSeqEntry_t state; void * ctx_ref_ven; } stateContainer;
- // This method uses the @ctx_ref_ven constant pointer a.k.a veneer, that refers
- // to the actual private context:
- // const fFSeqEntry_t * stateFunction( const fFSeqEntry_t * this,
- // tFSeqCtx_t ctx, const fFSeqEntry_t * * pnext )
- // {
- // // get access to the @ctx_ref_ven field from @stateContainer structure
- // const void * const_ctx_ref_ven = (const void*)(&this[1]);
- // ...
- // void * private_ctx = (void*)const_ctx_ref; // cast the pointer
- // ... use @private_ctx
- // }
- // Advantage: the @ctx_ref_ven is constant and refers to the private context directly,
- // easy to manage the private context for each state, easy code support,
- // you do not need to keep the state pointer into the RAM.
- // Disadvantage: the same, the @ctx_ref_ven is constant, and if you need multiple instances
- // of the the same state, you need to create multiply copies of the state entry,
- // due to you need to change @ctx_ref_ven field.
- //
- // If @FSEQ_ALT_INHERIT_METHOD_SUPPORT is non-zero (Method #1):
- // Alternative method. The state function body can get access to the state private context
- // the same way, using casting the pointer @fFSeqEntry_t* to the pointer to container, but
- // the container must be declared in the RAM:
- // typedef struct { ... } tPrivateContextData_t;
- // struct { fFSeqEntry_t state; tPrivateContextData_t data; } stateContainer;
- // // ...
- // const fFSeqEntry_t * stateFunction( const fFSeqEntry_t * this,
- // tFSeqCtx_t ctx, const fFSeqEntry_t * * pnext )
- // {
- // // get access to the actual private context @private_ctx (field from @stateContainer structure)
- // tPrivateContextData_t * private_ctx_data = (void*)(&this[1]);
- // ...
- // ... use @private_ctx_data
- // }
- //
- // Advantage: since the @stateContainer is located in the RAM, you don't need a veneer,
- // the private fields are located directly "after" the state entry in the memory,
- // you can easily to add new instances of the state by creating new container.
- // Disadvantage: each container for state instance must contain the state entry pointer, that
- // is located in the RAM, also the code support is more complicated due to
- // you need to link the dynamic container and the static state entry.
- //
- // Internally, the method #0 is quite differ than the method #1, because the state entry actually
- // does not contain any state functions in the method #1 but only the virtual table pointer,
- // and in the method #0 the state entry consist of the state functions itself. It is a important
- // point to understand how it works and how to inherit and embedd the state entry in your application.
- // But if you do not need a private context, it is does not matter which method you choose due to
- // the interface of the module does not depend on this issue.
- #define FSEQ_ALT_INHERIT_METHOD_SUPPORT 1
- // User is allowed to pass some user-dependent context via special parameter
- // of type "tFSeqCtx_t". This argument must be set on constructing the
- // sequencer object during the constructing function call (@fSeqConstruct).
- typedef void * tFSeqCtx_t; // user context
-
- // @fFSeqEntry_t (forward declaration)
- // The set of user-defined functions (see below)
- struct fFSeqEntry_t;
- typedef const struct fFSeqEntry_t * ( * fSeq_t)( const struct fFSeqEntry_t * this,
- tFSeqCtx_t ctx,
- const struct fFSeqEntry_t * * pnext );
- #if FSEQ_ENTRY_EXIT_SUPPORT
- typedef void ( * fSeqEnter_t )( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
- typedef void ( * fSeqLeave_t )( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
- #endif
- #if FSEQ_ALT_INHERIT_METHOD_SUPPORT
- // The user-defined state methods.
- // Is only used when FSEQ_ALT_INHERIT_METHOD_SUPPORT is enabled.
- // The structure represents the state virtual table methods.
- typedef struct fFSeqVTable_t
- {
- fSeq_t f; // function body
- #if FSEQ_ENTRY_EXIT_SUPPORT //
- fSeqEnter_t enter; // entry-function
- fSeqLeave_t leave; // exit-function
- #endif
- }
- fFSeqVTable_t;
- #endif
- // @fFSeqEntry_t
- // The user-defined state entry.
- // Can either contain the user-defined methods itself, or
- // contain only reference to the methods table (see @FSEQ_ALT_INHERIT_METHOD_SUPPORT)
- // User can use "inheritance" method to declare some
- // additional state-dependent context, see the example below.
- typedef struct fFSeqEntry_t
- {
- #if FSEQ_ALT_INHERIT_METHOD_SUPPORT
- const fFSeqVTable_t * vtbl; // state virtual methods table reference
- #else
- fSeq_t f; // function body
- #if FSEQ_ENTRY_EXIT_SUPPORT //
- fSeqEnter_t enter; // entry-function
- fSeqLeave_t leave; // exit-function
- #endif
- #endif
- }
- fFSeqEntry_t;
- #if FSEQ_ALT_INHERIT_METHOD_SUPPORT == 0
- // @DECLARE_FSEQ_STATE_ENTRY_WITH_CONTEXT
- // Declares user-defined state for sequencer
- // @name - the name of the state;
- // @ctx_var - user context variable (structure)
- // @... - va_arg arguments: state functions list (body, entry, exit)
- #define DECLARE_FSEQ_STATE_ENTRY_WITH_CONTEXT(statename, ctx_var, ...)\
- const struct {\
- fFSeqEntry_t stateEntry;\
- void * ctx_ref;\
- } statename = {\
- .stateEntry = { __VA_ARGS__ },\
- .ctx_ref = &ctx_var\
- };
- #define DECLARE_FSEQ_STATE_CONTEXT_BEGIN( statename, ctx_var )\
- struct {
- #define DECLARE_FSEQ_STATE_CONTEXT_END( statename, ctx_var )\
- } ctx_var;
- #define DECLARE_FSEQ_STATE_CONTEXT_END_INIT_BEGIN( statename, ctx_var )\
- } ctx_var = {
- #define DECLARE_FSEQ_STATE_CONTEXT_END_INIT_END( statename, ctx_var )\
- };
- #else
- #define DECLARE_FSEQ_STATE_ENTRY_WITH_CONTEXT(statename, ctx_var, ...)\
- const fFSeqVTable_t statename##_vtbl = { __VA_ARGS__ };
- #define DECLARE_FSEQ_STATE_CONTEXT_BEGIN( statename, ctx_var )\
- extern const fFSeqVTable_t statename##_vtbl;\
- const struct {\
- fFSeqEntry_t stateEntry;\
- #define DECLARE_FSEQ_STATE_CONTEXT_END( statename, ctx_var )\
- } ctx_var = { \
- .stateEntry = { .vtbl = &statename##_vtbl },\
- };
- #define DECLARE_FSEQ_STATE_CONTEXT_END_INIT_BEGIN( statename, ctx_var )\
- } ctx_var = { \
- .stateEntry = { .vtbl = &statename##_vtbl },
- #define DECLARE_FSEQ_STATE_CONTEXT_END_INIT_END( statename, ctx_var )\
- };
- #endif
- /*
- // ------------------------------------------------------------------------------------------------
- // Inheritance example (Method #0, 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;
-
- 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
- }
- // ------------------------------------------------------------------------------------------------
- */
- /*
- // ------------------------------------------------------------------------------------------------
- // 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
- }
- // ------------------------------------------------------------------------------------------------
- */
- // @xFSeqObj_t
- // The sequencer object, contains some private fields
- // that must not be accessed by user.
- typedef struct
- {
- const void * private[2];
- }
- xFSeqObj_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 * fSeqObj, const fFSeqEntry_t * first, tFSeqCtx_t 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 * fSeqObj, const fFSeqEntry_t * first );
- // @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 * fSeqObj );
- // @fSeqShutdown
- // Shutdowns the running sequencer
- // @fSeqObj - the sequencer object to shutdown
- void fSeqShutdown( xFSeqObj_t * fSeqObj );
- /* Macro using example:
- const fFSeqEntry_t * state1Function( const fFSeqEntry_t *, tFSeqCtx_t, const fFSeqEntry_t * * );
- void state1Enter( const fFSeqEntry_t *, tFSeqCtx_t );
- void state1Leave( const fFSeqEntry_t *, tFSeqCtx_t );
- DECLARE_FSEQ_STATE_CONTEXT_BEGIN(seqState1, seqState1Context)
- struct {
- int state1_var;
- };
- DECLARE_FSEQ_STATE_CONTEXT_END(seqState1, seqState1Context)
- DECLARE_FSEQ_STATE_ENTRY_WITH_CONTEXT( seqState1, seqState1Context, state1Function, state1Enter, state1Leave );
- */
- #endif
|