Native Language Considerations

FSMLang provides for including user code in the generated files through the use of native blocks. These are available to be exploited by any output language generator. This section explores the use of these blocks, and, since the current “native language” output is C, some comments are made on the convenience and debugging macros available in the generated code.

Native Blocks

Native blocks come in two flavors: one which will be placed in the machine’s private header file; and another which will be placed in the source file.

The following block is placed at the beginning of the generated headers:

native
{
   #ifdef SILLY_MACHINE_DEBUG
   #define DBG_PRINTF(...) printf(__VA_ARGS__); printf("\n");
   #else
   #define DBG_PRINTF
   #endif

}
machine sillyMachine
{
   event e1;
   state s1;

   action a1[e1,s1];
}

Here it is in the private header:

/**
   sillyMachine_priv.h

   This file automatically generated by FSMLang
*/

#ifndef _SILLYMACHINE_PRIV_H_
#define _SILLYMACHINE_PRIV_H_

#include "sillyMachine.h"
#ifndef SILLY_MACHINE_NATIVE_PROLOG
#define SILLY_MACHINE_NATIVE_PROLOG

#ifdef SILLY_MACHINE_DEBUG
#define DBG_PRINTF(...) printf(__VA_ARGS__); printf("\n");
#else
#define DBG_PRINTF
#endif

#endif

Native material such as this, of course, is not needed in every header, and, indeed, must be protected against multiple inclusions. However, there are situations (such as the use of pragmas to silence undesired warnings (better would be to log a bug with the FSMLang maintainers, though!)), which require the block to be placed in all headers.

Alert readers noticed the multiple-inclusion protection macro incorporated the word, PROLOG. Indeed, the keyword prologue can be used with native (though, it is the default), as can the keyword epilogue. native epilogue blocks are placed at the end of the headers. Both blocks can be specified; both come before the machine declaration.

native
{
#ifdef SILLY_MACHINE_DEBUG
#define DBG_PRINTF(...) printf(__VA_ARGS__); printf("\n");
#else
#define DBG_PRINTF
#endif

#pragma stop warnings!
}

native epilogue
{
#pragma resume warnings!
}

machine sillyMachine
{
   event e1;
   state s1;

   action a1[e1,s1];
}
/**
        sillyMachine.h

        This file automatically generated by FSMLang
*/

#ifndef _SILLYMACHINE_H_
#define _SILLYMACHINE_H_

#include "sillyMachine_events.h"
#ifndef SILLY_MACHINE_NATIVE_PROLOG
#define SILLY_MACHINE_NATIVE_PROLOG

#ifdef SILLY_MACHINE_DEBUG
#define DBG_PRINTF(...) printf(__VA_ARGS__); printf("\n");
#else
#define DBG_PRINTF
#endif

#pragma stop warnings!

#endif
#define FSM_VERSION "1.45.1"

#ifndef NO_CONVENIENCE_MACROS
#undef UFMN
#define UFMN(A) sillyMachine_##A
#undef THIS
#define THIS(A) sillyMachine_##A
#endif
#undef STATE
#define STATE(A) sillyMachine_##A

#undef ACTION_RETURN_TYPE
#define ACTION_RETURN_TYPE SILLY_MACHINE_EVENT

void run_sillyMachine(SILLY_MACHINE_EVENT);

typedef struct _sillyMachine_struct_ *pSILLY_MACHINE;
extern pSILLY_MACHINE psillyMachine;

#ifndef SILLY_MACHINE_NATIVE_EPILOG
#define SILLY_MACHINE_NATIVE_EPILOG

#pragma resume warnings!

#endif

#endif

Native implementation (both prologue and epilogue) blocks are placed in the machine’s generated source file. As with the native header blocks, any legal language construct may be placed here. The default prologue may be omitted, and implementation may be shortened to impl.

For example, this fsmlang:

native
{
#ifdef SILLY_MACHINE_DEBUG
#define DBG_PRINTF(...) printf(__VA_ARGS__); printf("\n");
#else
#define DBG_PRINTF
#endif

#pragma stop warnings!
}

native epilogue
{
#pragma resume warnings!
}

machine sillyMachine
native implementation
{
#pragma stop warnings!
}
native impl epilogue
{
#pragma resume warnings!
}
{
   event e1;
   state s1;

   action a1[e1,s1];
}

Yields (the middle has been cut out):

/**
        sillyMachine.c

        This file automatically generated by FSMLang
*/

#include "sillyMachine_priv.h"
#include <stddef.h>

/* Begin Native Implementation Prolog */


#pragma stop warnings!

/* End Native Implementation Prolog */

/*
 Lines omitted for brevity.
*/

/* Begin Native Implementation Epilog */


#pragma resume warnings!

/* End Native Implementation Epilog */

Note that native implementation blocks come after the machine name, but before the machine’s opening brace.

Macros: User Functions and Debugging

Some of the macros are most valuable in a hierarchical machine setting, and others are most valuable in settings where events have data. Neither of these topics has yet been addressed, but placing the table here, in the context of things done to help C programmers seemed good.

Macros

Name

Definition

ACTION_RETURN_TYPE

Gives the type returned by the machine’s action functions. When actions return events, this macro is set to the event enumeration, even when events have data. This is because within the state machine only the enumeration is returned.

Using this macro in the definition of action functions obviates the need to adjust function return type should the design of events (having/not having data) change.

#define ACTION_RETURN_TYPE HSM_COMMUNICATOR_EVENT

ACTION_RETURN_TYPE hsmCommunicator_startSessionEstablishment(FSM_TYPE_PTR);

FSM_TYPE_PTR

Gives the type of the pointer to the current machine’s structure.

Using this macro, along with UFMN, ACTION_RETURN_TYPE, and THIS facilitates machine redesign. Adding or removing sub-machines, with the concommitant re-shuffling of action functions is much easier, since the macro definitions will change appropriately in the function’s new setting.

#define FSM_TYPE_PTR pHSM_COMMUNICATOR

ACTION_RETURN_TYPE UFMN(startSessionEstablishment)(FSM_TYPE_PTR);

THIS(A)

THIS prepends the machine name prefix to the given argument.

return THIS(SEND_MESSAGE);

will always return the correct event enumeration.

PARENT(A)

Similar to THIS, PARENT prepends the name of the machine’s parent to the given argument.

return PARENT(SESSION_ESTABLISHED)

will always return the correct event enumeration.

UFMN(A)

UFMN is to user function names as THIS is to event names. That is,

The name of the this function:

ACTION_RETURN_TYPE UFMN(startSessionEstablishment)(FSM_TYPE_PTR);

will always expand correctly, even when the function is moved from one machine to another, and when the use of the CL argument --short-user-fn-names is changed.

State machine design, as all code design, is iterative. The macros above facilitate this activity. The two debugging macros below help identify the pain points which may prompt the redesign.

DBG_PRINTF is used to output useful debug information. It must be defined to accept variadic arguments. If it is not defined by the time compilation gets to the source file, it will be defined to be vacuous.

<UPPER_CASE_MACHINE_NAME>_DEBUG must be defined during compilation in order to see the debug output.