|
Extending libFAUDES in general involves
implementation of additional data-types and algorithms;
introduction of documentation via the run-time interface;
introduction of corresponding luabindings;
integration via the libFAUDES build-system mechanisms.
Note that you may very well extend the library by data-types and functions only, further integration is optional. However, the remaining tasks are recommended in order to advertise your add-on to the user.
We give a walk-through to the above steps by the example plug-in.
The standard library distribution comes with a number of plug-ins, including the example plug-in. The latter integrates an alternative accessibility function AlternativeAccessible() into libFAUDES and is meant as a template for your own plug-in. The example plug-in directory is organized as follows:
example -+- README short overview on purpose of this plug-in | |- Makefile.plugin makefile to integrate this plug-in with the build system | |- src --+- pex_altaccess.h declaration of function AlternativeAccessible() | |- pex_altaccess.cpp definition of function AlternativeAccessible() | |- pex_include.h header to include all other headers of this plug-in | +- doxygen/faudes_images images for doxygen documentation | +- registry/pex_*.rti run-time interface definitions, see below | +- registry/example_*.gtml run-time interface documentation, see below | +- registry/pex_interface.i SWIG interface, see below | +- Makefile.tutorial extra dependencies for this plug-in's tutorial +- tutorial --- pex_tutorial.cpp mini application to illustrate intended usage +- data --- g_notacc.gen data to run tutorial with
The directory name implicitly defines the libFAUDES plug-in name i.e. example. Furthermore, all source files start with a common prefix, i.e. pex_, in order to avoid confusion with other plug-ins.
The example algorithm to implement converts a given generator to an accessible generator without affecting the closed and marked languages. This is done by removing all states that cannot be reached from any initial state. For the below generator G, the states to remove are labelled 7, 8 and 9.
The proposed implementation in src/pex_altaccess.cpp is organized in two stages
starting with the initial states, loop to follow transitions and record every state discovered;
delete all states not recorded at the stage above.
void AlternativeAccessible(vGenerator& rGen) { // create a todo stack for state indices std::stack<Idx> todo; // create an empty StateSet for the set of accessible state StateSet accessible_states; // iterator for a StateSet StateSet::Iterator sit; // initialize the algorithm by pushing all initial states on the todo stack for (sit = rGen.InitStatesBegin(); sit != rGen.InitStatesEnd(); ++sit) { todo.push(*sit); } // process the todo stack until it's empty while (not todo.empty()) { // get and delete the next state index from the todo stack const Idx current = todo.top(); todo.pop(); // insert the current state in the set of accessible states accessible_states.Insert(current); // create transition iterator for the states of the current state TransSet::Iterator tit = rGen.TransRelBegin(current); TransSet::Iterator tit_end = rGen.TransRelEnd(current); // push successor states ton todo stack if not already discovered while (tit != tit_end) { if (not accessible_states.Exists(tit->X2)) { todo.push(tit->X2); } ++tit; } } // delete the states and transitions which are not accessible rGen.DelStates(rGen.States() - accessible_states); }
The function starts by setting up required data structures. These consist of a todo stack and a so-called StateSet, which is the FAUDES implementation of a container to hold a set of unique states. Then all initial states are pushed on the todo stack as they are reachable by definition. The main processing is done by removing the current top element of the todo stack, storing this state in the set of accessible states and following all transition paths that lead from this state to an unprocessed state. At last the non-accessible states and the transitions leading to these states are removed by calling a single operation on the set difference of the automaton's states and the accessible states.
As it can be seen, the algorithm is implemented by using abstract data-types from either the C++ standard library or from the FAUDES library. In particular, it does not depend on the internal data structure of the Generator class.
The build system runs doxygen on all *.cpp and *.h files in the make search path and thus includes the plug-in sources to the doxygen documentation. By convention, the overall include file of each plug-in is used to give an overview, perhaps including key functions and data-types as well as licensing and copyright information. Provided the all further plug-in source code complies with doxygen documentation comments, all relevant functions will nicely appear in the libFAUDES C++ API reference.
For the example plug-in overview, we add a doxygen section to pex_include.h and define it to belong to the doxygen group AllPlugins:
/** @defgroup ExamplePlugin Example Plugin @ingroup AllPlugins @section Overview This example demonstrates the libFAUDES plug-in mechanism and may serve as a template for setting up ... etc. @section License The example plug-in is distributed with libFAUDES and under the terms of the LGPL. @section Contents */
We document our add-on function in pex_altaccess.h by a technical description. We also tag the function to be a member of the doxygen group ExamplePlugin, such that it will appear in our overview section Contents.
/** * Alternative accessibility algorithm. * We use the alternative accessibility algorithm from tutorial 6 * for our example plug-in. The implementation uses a todo stack ... etc. * * <h4>Example:</h4> * <table width=100%> * <tr> * <td> @image html pex_g_notacc.png </td> * </tr> * </table> * * @param rGen * Input generator * * @ingroup ExamplePlugin */ void AlternativeAccessible(vGenerator& rGen);
Note that the above doxygen documentation refers to an image file, namely pex_g_notacc.png. You are meant to supply the file in the source directory .plugins/example/src/doxygen/faudes_images. Since the build system merges all image files to one destination directory, the plug-in prefix pex_ is essential. It is good practice to provide a tutorial that actually produces any example data, incl. images files.
Each plug-in is meant to provide a number of small C++ applications that demonstrate the intended usage of the implemented C++ API. These tutorials are located in example/tutorial/ and operate on input data in example/tutorial/data. In particular when the generated output data is substantial, it is good practice to obey the naming convention example/tutorial/tmp_ or example/tutorial/results/* in order for the build system to remove generated files on make clean.
/** * @file pex_tutorial.cpp * Tutorial, example plug-in. More text ... * @ingroup Tutorials * @include pex_tutorial.cpp */ #include "libfaudes.h" int main() { Generator gen("data/g_notacc.gen"); AlternativeAccessible(gen); gen.Write("tmp_altacc.gen"); return 0; }
Note that by specifying the source to be in group Tutorials and by sourcing the file through doxygen, the tutorial will be listed in the C++ API reference, Section see Tutorials.
Dependencies to build the tutorials are included by Makefile.plugin from Makefile.tutorial.
The libFAUDES main makefile copies headers to the libFAUDES include directory, compiles sources to objects and, finally, links objects to obtain the libFAUDES library and utility executables. While processing the main makefile, the build system includes plugins/example/Makefile.plugin. The latter extends the variables SOURCES, OBJECTS, and HEADERS in order to indicate additional targets. It also extends the make search path VPATH. The example Makefile.plugin is organized as follows:
Note that we only use variables with prefix PEX in order to avoid confusion with variables in the main makefile.
PEX_NAME = example PEX_BASE = ./plugins/$(PEX_NAME) PEX_SRCDIR = ./plugins/$(PEX_NAME)/src PEX_CPPFILES = pex_altaccess.cpp PEX_INCLUDE = pex_include.h PEX_HEADERS = $(PEX_CPPFILES:.cpp=.h) $(PEX_INCLUDE) PEX_SOURCES = $(PEX_CPPFILES:%=$(PEX_SRCDIR)/%) PEX_OBJECTS = $(PEX_CPPFILES:%.cpp=$(OBJDIR)/%.o)
Applications that use libFAUDES are meant to include all relevant headers by the #include "libfaudes.h" directive. This in turn includes the auto-generated files include/allplugins.h and include/configuration.h. They list include directives for all activated plug-ins and define run-time behaviour macros, respectively.
The example plug-in requires pex_include.h to be registered with allplugins.h and adds the macro FAUDES_PLUGIN_EXAMPLE to configuration.h.
# Append my overall include file to libfaudes.h $(INCLUDEDIR)/$(PEX_INCLUDE): $(PEX_SRCDIR)/$(PEX_INCLUDE) cp -pR $< $@ echo "#include \"$(PEX_INCLUDE)\"" >> $(INCLUDEDIR)/allplugins.h echo "/* example plugin configuration */" >> $(INCLUDEDIR)/configuration.h echo "#define FAUDES_PLUGIN_EXAMPLE" >> $(INCLUDEDIR)/configuration.h echo " " >> $(INCLUDEDIR)/configuration.h
This includes all additional sources, headers and objects. The variable VPATH directs the make tool to find sources when resolving (implicit) rules.
SOURCES += $(PEX_SOURCES) OBJECTS += $(PEX_OBJECTS) HEADERS += $(PEX_HEADERS) VPATH += $(PEX_SRCDIR)
libFAUDES provides a registry for data-types and functions as a basis for applications that transparently pass on libFAUDES extensions to the user; see C++ API, Section RTI, for technical details. The build system tries to minimise the effort that is required for a plug-in to participate in this optional feature.
The example plug-in provides only one function, which is registered via the token file src/registry/pex_definitions.rti:
<FunctionDefinition> "Example::AltAccessible" +faudes::AlternativeAccessible+ <TextDoc> "Alternative implementation to remove inaccessible states and related transitions." </TextDoc> <HtmlDoc> "example_index.html" </HtmlDoc> <Keywords> "Example" "reachablility" "accessible" </Keywords> <VariantSignatures> <Signature> "Default" "Gen" "Generator" +InOut+ </Signature> </VariantSignatures> </FunctionDefinition>
The above token stream declares a function from the plug-in Example with name AltAccessible to be implemented by the C++ function faudes::AlternativeAccessible. It gives a short description and a reference to a more detailed HTML documentation. It is up to you whether you design one document per function or whether you collect groups of related functions.
By convention the list of keywords establishes a two level hierarchical structure, where the first keyword specifies a section and the second keyword specifies a subsection. It is mandatory to provide section index pages following the naming convention sectionname_index.gtml, i.e. example_index.gtml for the example plug-in. In most cases, the first keyword aka the section name will coincide with the plug-in name. Advanced applications may use additional keywords when implementing a search by keyword.
Regarding signature(s), the libFAUDES run-time interface imposes the following restrictions:
A function may support multiple signatures to match an overloaded C++ function declaration.
Each signature has a name and a list of parameters. Each parameter is specified by a record consisting of the parameter name, type and an attribute. The type must be known to the registry. The attribute may have the value +In+ to specify an argument, +Out+ to specify a result, or +InOut+.
One of the result parameters with attribute +Out can be additionaly flagged +CReturn+ to indicate that the corresponding C++ functions implements this result as a return value. However, this feature is only supported for the elementary types Integer, Boolean and String only.
The specified C++ function must accept the parameters by reference exactly as specified by the signature. An exception are the elementary types Integer, Boolean and String, which are converted to/from their C++-counterpart automatically. Any +In+ parameters must be declared const.
Of course, any referenced HTML documentation must be provided. For convenience, the build-system pre-processes any documentation by the Perl script GTML (provided in the tools directory). The example plug-in is fine with one page of documentation only, as given in the below src/registry/example_index.gtml.
<h1>Example Plugin</h1> <p>The example plug-in addresses ... etc. etc.</p> <<FUNCTIONBEGIN(AltAccessible)>> <<DETAILS>> <p>More text ... etc.</p> <<CONDITIONS>> <p>Arguments must be such that ... etc.</p> <<FUNCTIONEND>>
The macros FUNCTIONBEGIN, DETAILS, CONDITIONS, FUNCTIONEND are used be the build-system to fill in appropriate headings and signature information. Further macros are defined in ./libfaudes/src/registry/registry_macros.gtml. The example plug-in introduces additional macros in ./example/src/registry/example_macros.gtml that contribute to the literature references. For more details and recommendations regarding the organisation of HTML documentation, see the build-system documentation.
The libFAUDES build system expects run-time interface files to be advertised via Makefile.plugin. For our example plug-in, we announce one *.rti file and one *.gtml file by appending the following lines to Makefile.plugin:
PEX_RTIDEFS = pex_definitions.rti PEX_RTIGTML = example_index.gtml PEX_RTIDEFS := $(PEX_RTIDEFS:%=$(PEX_SRCDIR)/registry/%) PEX_RTIHTML := $(PEX_RTIGTML:%.gtml=%.html) PEX_RTIGTML := $(PEX_RTIGTML:%=$(PEX_SRCDIR)/registry/%) RTIPLUGINS += $(PEX_NAME) RTIDEFS += $(PEX_RTIDEFS) RTIGTML += $(PEX_RTIGTML) RTIHTML += $(PEX_RTIHTML)
To trigger the re-build of the RTI registry, run the main makefile with targets make rti-clean and make rti-prepare.
The build system tries to minimise the effort that is required to access additional functions via luafaudes. It does so using the tool SWIG to automatically convert so called interface definitions to C wrapper code; see http://www.swig.org.
The libFAUDES build system expects interface files to be advertised via Makefile.plugin. For our example plug-in, we announce one SWIG module and one interface file by appending the following lines to Makefile.plugin:
LBP_INTERFACES += $(PEX_SRCDIR)/registry/pex_interface.i LBP_LUAMODULES += example
Note the prefix LBP that refers to the luabindings plug-in.
The actual interface pex_interface.i file resides in the example plug-in:
// Set SWIG module name // Note: must match libFAUDES plug-in name %module example // Indicate plugin to rti function definitions #ifndef SwigModule #define SwigModule "SwigExample" #endif // Load std faudes interface %include "faudesmodule.i" // Extra Lua functions: copy to faudes name space %luacode { -- Copy example to faudes name space for k,v in pairs(example) do faudes[k]=v end } // Add topic to mini help system SwigHelpTopic("Example","Example plug-in function"); // Include RTI generated functioninterface #if SwigModule=="SwigExample" %include "../../../include/rtiautoload.i" #endif
Since our plug-in provides only functions that are registered with the run-time interface, the interface file in this case is just a skeleton. The build system will fill in our function by generating the file include/rtiautoload.i.
It is good practice to supply comprehensive Lua scripts in the tutorial directory that illustrate the intended usage of the plug-ins data-types and functions.
To trigger the re-build of luabindings, run the main makefile with targets make luabindings-clean and make luabindings-prepare. The libFAUDES archive provides a copy of SWIG for convenience, however, installation is required.
libFAUDES 2.16b --- 2010-9-8 --- plugins "example synthesis observer diagnosis hiosys multitasking timed simulator iodevice luabindings"