The user specifies in an XML file the class objects that should be created and the class functions that should be called.
OOPSMP interprets the XML file using DLLs or shared libraries and factory classes which produce instances of class objects and call class functions based on their names. The XML files included with the
OOPSMP distribution are found in
OOPSMP/input/xml
Follow the links below for more information:
Classes and class functions are registered for plug-and-play using macros provided by
OOPSMP. These macros indicate whether or not the class is abstract, if it extends any base class, and which class functions should be registered for plug-and-play. The following lines of code illustrate what must be added to the header and source files of a class
ClassName in order to successfully register the class
ClassName and several of the
ClassName functions:
If the class function
fnName does not receive any arguments, then it is registered using
RegisterFnFactory(fnName). Otherwise, the argument types are specified as a comma-separated list, where the macro for the
i-th argument is given by the following table:
if type = {bool, int, float, double, char} then use FACTORY_VAL_ARG(type, i)
if type = {bool*, int*, float*, double*, char*, Class*, Class**} then use FACTORY_PTR_ARG(type, i)
Illustration of C++ code for registering an abstract class and some of its functions:
class Example
{
public:
Example(void);
virtual ~Example(void);
void fn1(void);
void fn2(const double x, const int y);
virtual void fn3(char *str, double *array) = 0;
void fn4(void);
};
DeclareNoInstanceFactory(Example, Factory);
BeginImplementNoInstanceFactory(Example, Factory);
RegisterFnFactory(fn1);
RegisterFnFactory(fn2, FACTORY_VAL_ARG(const double, 0), FACTORY_VAL_ARG(const int, 1));
RegisterFnFactory(fn3, FACTORY_PTR_ARG(char*, 0), FACTORY_PTR_ARG(double*, 1));
EndImplementNoInstanceFactory(Example);
Illustration of C++ code for registering a non-abstract class:
The user specifies in an XML file the class objects that should be created and the class functions that should be called. The XML syntax given in Bacus-Naur form (
BNF) is as follows:
XML_factory ::= <factory instance="XML_className"> XML_callFns </factory>
XML_className ::= XML_string
XML_callFns ::= empty | XML_callFn XML_callFns
XML_callFn ::= <call fn="XML_fnName"> XML_fnArgs </call>
XML_fnName ::= XML_string
XML_fnArgs ::= empty | XML_fnArg XML_fnArgs
XML_fnArg ::= <arg type="bool"> XML_bool </arg> |
<arg type="int"> XML_int </arg> |
<arg type="float"> XML_float </arg> |
<arg type="double"> XML_double </arg> |
<arg type="char"> XML_char </arg> |
<arg type="string" XML_keep > XML_string </arg> |
<arg type="pointer" > XML_pointer </arg> |
<arg type="bool_array" XML_keep > XML_bool_array </arg> |
<arg type="int_array" XML_keep > XML_int_array </arg> |
<arg type="float_array" XML_keep > XML_float_array </arg> |
<arg type="double_array" XML_keep > XML_double_array </arg> |
<arg type="char_array" XML_keep > XML_char_array </arg> |
<arg type="string_array" XML_keep > XML_string_array </arg> |
<arg type="pointer_array" XML_keep > XML_pointer_array </arg>
XML_bool ::= false | true
XML_int ::= standard C/C++ definition, i.e., whatever scanf uses with %d
XML_float ::= standard C/C++ definition, i.e., whatever scanf uses with %f
XML_double ::= standard C/C++ definition, i.e., whatever scanf uses with %lf
XML_char ::= standard C/C++ definition, i.e., whatever scanf uses with %c
XML_string ::= standard C/C++ definition, i.e., whatever scanf uses with %s
XML_pointer ::= XML_factory
XML_bool_array ::= XML_bool | XML_bool XML_space XML_bool_array
XML_int_array ::= XML_int | XML_int XML_space XML_int_array
XML_float_array ::= XML_float | XML_float XML_space XML_float_array
XML_double_array ::= XML_double | XML_double XML_space XML_double_array
XML_char_array ::= XML_char | XML_char XML_char_array
XML_string_array ::= XML_string | XML_string XML_space XML_string_array
XML_pointer_array::= XML_pointer | XML_pointer XML_pointer_array
XML_keep ::= empty | keep="true" | keep="false"
XML_space ::= standard C/C++ definition, i.e., whatever scanf defines as white space
The correspondence between a function argument and the XML specification for that argument is given by the following table:
if type is then use
const bool | bool ::= <arg type="bool"> XML_bool </arg>
const int | int ::= <arg type="int"> XML_int </arg>
const float | float ::= <arg type="float"> XML_float </arg>
const double | double ::= <arg type="double"> XML_double </arg>
const char | char ::= <arg type="char"> XML_char </arg>
const char* | char* ::= <arg type="string"> XML_string </arg>
const Class* | Class* ::= <arg type="pointer"> XML_pointer </arg>
const bool* | bool* ::= <arg type="bool_array"> XML_bool_array </arg>
const int* | int* ::= <arg type="int_array"> XML_int_array </arg>
const float* | float* ::= <arg type="float_array"> XML_float_array </arg>
const double* | double* ::= <arg type="double_array"> XML_double_array </arg>
const char* | char* ::= <arg type="char_array"> XML_char_array </arg>
const char** | char** ::= <arg type="string_array"> XML_string_array </arg>
const Class** | Class** ::= <arg type="pointer_array"> XML_pointer_array </arg>
NOTE:
Class refers to any class that has been registered for plug-and-play. After a function call specified in the XML, the memory allocated to the
i-th argument is not freed iff the argument is a class pointer or the attribute
keep="true" is present in the XML specification of the argument, e.g.,
Example of an XML user file that specifies the class objects that should be created and the class functions that should be called (the XML file has a similar effect as the C++ code indicated alongside the XML):
<factory instance="Example2"> # Example2 *ex2 = new Example2();
<call fn="fn1"> #
</call> # ex2->fn1();
<call fn="fn3"> #
<arg type="string" keep="true">abcdef</arg># char *s = strdup("abcdef");
<arg type="double_array">1.0 2.0 3.0</arg> # double *a = (double *) malloc(4, sizeof(double)); a[0]=1.0; a[1]=2.0; a[2]=3.0;
</call> # ex2->fn3(s, a); free(a);
<call fn="fn2"> #
<arg type="double">1.0</arg> #
<arg type="int">4</arg> #
</call> # ex2->fn2(1.0, 4);
<call fn="fn5"> #
<arg type="pointer"> #
<factory instance="Example2"> # Example2 *ex = new Example2();
<call fn="fn2"> #
<arg type="double">3.0</arg> #
<arg type="int">5</arg> #
</call> # ex->fn2(3.0, 5);
<call fn="fn1"> #
</call> # ex->fn1();
</factory> #
</arg> #
</call> # ex2->fn5(ex);
</factory>
The plug-and-play functionality of
OOPSMP is achieved through the use of dynamically linked libraries (DLLs or shared libraries as commonly referred to in unix/linux systems) and factory classes which produce instances of class objects and call class functions based on their names.
OOPSMP in a way acts as a simple interpreter of XML files, which, as said before, can be used to specify the class objects that should be created and the class functions that should be called. Let's look again at the C++ illustration code for registering the classes
Example and
Example2, but this time with macros expanded:
DeclareNoInstanceFactory(Example, Factory);
BeginImplementNoInstanceFactory(Example, Factory);
RegisterFnFactory(fn1);
RegisterFnFactory(fn2, FACTORY_VAL_ARG(const double, 0), FACTORY_VAL_ARG(const int, 1));
RegisterFnFactory(fn3, FACTORY_PTR_ARG(char*, 0), FACTORY_PTR_ARG(double*, 1));
EndImplementNoInstanceFactory(Example);
DeclareInstanceFactory(Example2, ExampleFactory);
BeginImplementInstanceFactory(Example2, ExampleFactory);
RegisterFnFactory(fn5, FACTORY_PTR_ARG(Example*, 0));
EndImplementInstanceFactory(Example2);
When parsing the XML specification upon seeing the line <factory instance="Example2">, the loaded DLLs are searched for a "C" function
Example2_createFactoryFn. A call to this function returns an object
factory of the Factory class. A call to the
factory->createInstance() function creates an object of the class
Example2, as illustrated in the C++ code below:
Then the XML specification is parsed to see which functions are called and what the arguments to those functions should be. To continue the illustration, upon parsing the XML fragment
<call fn="fn2">
<arg type="double">1.0</arg>
<arg type="int">4</arg>
</call>
OOPSMP calls the function
factory->callFn("fn2", args)
where
args is an array of two elements and
args[0] and
args[1] are pointers of type
double * and
int * and the contents of these pointers are
1.0 and
4, respectively. The call to
factory->callFn("fn2", args) produces a call to
((Example2 *) object)->fn2(*((double *) args[0]), *((int *) args[1]));
If
arg[i] is not a pointer to a class object, then the memory associated with
arg[i] is freed at the end of
factory->callFn call unless the attribute
keep="true" is present in the XML specification of the
i-th argument.