Introduction – Download – Tutorial – Documentation – Changelog – Contact
xPP
C++ Metaprocessor
Note: This documentation is incomplete and already outdated. We're working on a php-based help system which will make inputing the doc and updating it much less time consuming, stay tuned.
Documentation
Installation
Just unzip the file in the directory of your choice, preferably somewhere in your PATH. Make sure the “templates” subdirectory is also unzipped there, as well as its subdirectories.
Command Line
-i:inputfile
Specifies the input file to process. File can either be an xhd or an xpp file (see Instrumenting a Class for how to create these).
-o:outputsubdir
Specifies the output subdirectory to output files in. If a file is normally in “../”, if will be written in “outputsubdir/../”
-t:templatedir
Adds a custom template directory to the search list
-d:symbol=value
Defines a global symbol. Templates may retreive these as variables.
-fl:outlist.txt
Creates a file with the list of files generated by xpp
-file:file.txt[,class]
Preprocess the content of file.txt, optionally using the given class as a context, otherwise using the first class in the xpp, and print the result on the standard output.
-stdin[:class]
Preprocesses the standard input, optionally using the given class as a context, otherwise using the first class in the xpp, and print the result on the standard output.
-q
Quiet, only output errors
-vq
Very quiet, only output the result of -file / -stdin
-np
No parsing, assume C++ files have not changed
-npp
Np preprocessing, do not generate the template target files
-y
Do not ask confirmation before overwriting a generated file.
Limitations, Conventions and Glossary
Although xPP will work no matter how many classes you have in one file, it is limited to the instrumentation of only one of these classes per file pair (one cpp and one h file with the same base filename). This convention pervades the use of the program, and only those classes that are fully declared within one file pair (with no #include within the declaration) will be handled correctly.
Because the word class may mean a lot of slightly different things, we need to make our vocabulary more precise. Traditionally, the way to remove the ambiguity in its meaning has been to introduce the term interface, thus an interface is a sort of “logical class”, as in an XPCOM interface, a COM interface, etc., which may be implemented using several actual C++ classes, and the word class refers to these, as in :
class ClassName : public Ancestor { ... };
There remain however an ambiguity in the sense that by class we may mean solely the above, or this :
class Ancestor { ... };
class ClassName : public
Ancestor { ... };
That is, we sometimes use the word class to refer to the entire construction resulting from the instantiation of our class, that is, we sometimes mean the class, along with all its inherited behavior, and sometimes we mean the single C++ class, disregarding its ancestry and descendancy.
To clear this confusion up whenever it may arise, we will use the word class when its signification is not ambiguous or when the term requires being general, and we will call a single C++ classes, excluding its ancestry or descendancy, a class layers (as an analogy with layers in photo compositing softwares). Thus, our two class layers amount to one class named “ClassName”, and if we ever add an interface to it (say, an XPCOM interface), it will be implemented using class layers, which will then be part of that same “ClassName” class.
With this in mind, we can define the rest of the glossary :
Meta Template
An xPP template, called “meta” template so as to make it distinct from C++ templates. Meta templates implement automatic interfaces via the description of their class layers in an xml language that has access to information about the hierarchy of that class, its methods, parameters, and data members.
Meta Macro
An inline meta template embedded in a C++ comment.
Template Meta Template (TMT)
We're in trouble, what is a meta template about a template ? A meta-meta-template ? Anyway, a “template meta template” is a meta template to handle C++ templates as parameter types for a given interface meta template. Lost yet ? Well, for instance the DataServer meta template needs to know about the parameters types it manipulates, but because there could be any number of user-defined C++ template types that it does not know about, it handles these thru the use of “template meta templates” so that you can add more if you need. Let's call them TMTs for short.
XPP File
An xPP project file, it contains the list of all the instrumented classes in your project and a link to their XHD file.
XHD File
A per-class file read and then rewritten by xPP upon each preprocessing run. This file contains customization options for the meta templates. See Building for Multiple Targets for more information
Classdecl
An xPP xml function used in meta macros to declare a class. This is going to be used quite often in this document and so it might as well be listed here. This function creates the class declaration code, along with the specified inheritance, and sends its extra parameters into the XHD. See Xml Dictionary and Building for Multiple Targets for more information.
Instrumented Class
Instrumented classes are those classes that xPP knows about. Each of this class has an associated xhd file, but not all these classes use a classdecl metamacro (class that only need to be known by xPP but not be changed by it do not require inline xml in the code).
Instrumenting a Class
Instrumenting a class is the first step to use xPP, you begin with one class, and gradually instrument more as needed: xPP does not need to know all about your code. For automatic templates, xPP only needs to know about the program's “end points”, where its interfaces are exposed to the world. For generic inline metamacros, xPP does not need to know anything else than what your metacode refers to.
Since our class is going to be partially declared by xPP (via its classdecl), and since xPP needs a valid class definition to be able to generate source code (in order to understand the class declaration), there is a bit of a chicken and egg problem, but fortunately, it is not very difficult to get an instrumented class started, this involves 3 or 4 steps (replace “classname” with your own class name) :
1. If you do not yet have an xpp project file, create one with the following code (otherwise, insert a new class entry) :
<xpp precompfile=”stdafx.h”>
<class
name=”ClassName” file=”ClassName.xhd”/>
</xpp>
note that precompfile=”stdafx.h” is for default VC settings, you may ommit it or change it to whatever your project requires or simple ommit it.
2. Optional, if you only need xPP to analyze this class so that it can know about it when dealing with other classes, you do not need to perform step 2. If you wish xPP to modify your class so that it changes its ancestry antomatically to accommodate the needs of the templates, you need to perform this step.
2a. * If you are creating a new class :
Create a clean, empty implementation of your class, enclose it in a classdecl, and add an automatic header (<autoheader/>) so that xPP can keep the compiler happy :
cclassname.h :
#ifndef _CCLASSNAME_H
#define
_CCLASSNAME_H
/*<?<autoheader/>*/
/*?>*/
/*<?<classdecl
name=”ClassName” implname=”CClassName”
inherit=”Ancestor1;private Ancestor2”/>*/
class
CClassName : public Ancestor1, private Ancestor2 {
/*?>*/
};
#endif
cclassname.cpp :
#include “stdafx.h”
#include
“cclassname.h”
2b. * If you are instrumenting an existing class :
Enclose your class declaration in a classdecl, for instance, if your class declaration is :
class CClassName : public Ancestor1, private Ancestor2 {
Then you should change it to :
/*<?<classdecl name=”ClassName”
implname=”CClassName” inherit=”Ancestor1;private
Ancestor2/>*/
class CClassName : public Ancestor1, public
Ancestor2 {
/*?>*/
3. Create a minimal XHD file for your class. This is what will let xpp know about your class. If you have not performed step 2, xpp will not create any code for this class, it will simply parse it in order for its structure to be accessible to the templates.
classname.xhd :
<xhd>
<class name=”ClassName”
implementation=”CClassName”
class_header=”cclassname.h”
class_module=”cclassname.cpp”>
</class>
</xhd>
4. Setup a custom build step within your environment, so that your xpp file gets compiled with your project. For instance, in Visual C++, this would be something like :
Commands :
xpreproc /i:$(InputDir)\$(InputName).xpp /y
echo 1 >
"$(OutDir)\$(InputName).out"
Outputs :
$(OutDir)\$(InputName).out
note that the .out timestamp file trick is only needed because there is no template associated with our class yet, but after we add one, we can just include one or all of the generated files in the Outputs field instead of creating a timestamp file.
But if you are using automake on unix, you can set up a
custom Automake.am such that including the xpp files to your list of
source files will
automatically compile all the resulting .cpp.
Here is an example of this for a project named “xpptest”
with a custom template directory :
AUTOMAKE_OPTIONS=foreign
bin_PROGRAMS = all-xpp
xpptest
xpptest_SOURCES = xpptest.xpp xpptest.cpp
cclassname.cpp
# set the include path found by
configure
INCLUDES= $(all_includes)
# the library search
path.
xpptest_LDFLAGS = $(all_libraries)
xpptest_DEPENDENCIES
=
LDADD = `cat xpp.output | grep \.cpp$$ | sed
s/cpp$$/o/`
XPPDIR = /usr/local/bin
XPPEXE =
$(XPPDIR)/xpp
PROGTEMPLATES = -t:../xpptemplates
XPPTEMPLATES =
$(XPPDIR)/templates
all-xpp:
for i in *.xpp; do $(XPPEXE)
-i:"$$i" -y $(PROGTEMPLATES) -filelist:xpp.output ;
done
for i in $(shell cat xpp.output | grep .cpp$$); do
$(CXXCOMPILE) "$$i" -c; done
You should be able to compile your project and either see the xPP progress bar (in win32) or the log of the preprocessing on the screen (in linux) as it works on your classes. On VisualC, depending on your setup, it may be needed to right click the xpp file in the project tree and compile it manually
Adding and Removing Interfaces
To add an interface, simple add it in a classdecl parameter named “factory”. For instance :
/*<?<classdecl name=”ClassName”
implname=”CClassName” inherit=”Ancestor1;private
Ancestor2” factory=”XPCOM”/>*/
class
CClassName : public Ancestor1, private Ancestor2 {
/*?>*/
After recompilation of the xpp file, your classdecl will look like this :
/*<?<classdecl name=”ClassName”
implname=”CClassName” inherit=”Ancestor1;private
Ancestor2” factory=”XPCOM”/>*/
class
CClassName : public Ancestor1, private Ancestor2, nsIClassNameX
{
/*?>*/
The files nsIClassNameX.cpp, nsIClassNameX.h and nsIClassName.idl will have been generated, you will need xpidl from the mozilla source code to compile the idl file (sorry, getting the XPCOM environment going remains a pain), and the cpp file should be included in your project. You will notice that your autoheader function automatically added a #include “nsIClassNameX.h” so that the class can actually compile.
Currently available meta templates are :
OleControlModule
Creates a plugin infrastructure for ActiveX, this should be used on one class while using OleControl on another
OleControl
Provides an OLE Control interface, for ActiveX & co.
npMozilla
Creates a plugin infrastructure for mozilla, this should be used on one class while using XPCom on another.
XPCOM
Provides
an XPCOM interface thru the creation of an idl and of a class layer
that binds to the result of its compilation
Interface
A mixed Pure Virtual / Wasabi Dispatchable meta template, this one will do a pure virtual unless you add dispatchable=”1” to your classdecl.
ScriptObject
A Wasabi script object meta template, to automatize the exposure of widgets to the scripting system.
DataServer
Automatically exposes data thru accessors. Also create events to be trapped by inheritors.
Currently available TMTs :
Wasabi PtrList
XML Syntax
xPP's template language is pure xml, this means that it obeys the syntactic requirements of xml, which are not always very nice to work with, however the format is particularly well adapted to the inline manipulation and generation of text.
The language is case insensitive, and there is no punctuation. The source is organized hierarchically, control structures such as if, else, foreach, etc. use the structure of the xml to perform their task and get their arguments.
Functions are xml tags and come in three flavors, Singleton, Compound or Mixed. Here is a <singleton/>. And here is a <compound/>function</compound>. Mixed mode functions support both uses. For instance, the foreach enumerator is compound, as it is a control structure which contains more code to execute. The function <autoheader/> however is a singleton, it merely spits text that depends on the context and does not hold anything. The <set/> function however can be used both ways, such as <set id=”varid” value=”hello, world”/> or <set id=”varid”>hello, world</set>.
FOREACH
Any piece of xPP code runs within a context. By default, this context is that of the class which is being processed by xPP. For instance, at the beginning of a metatemplate, the context is set to the class for which this template is being run. There are several kind of contexts and many contexts can overlap, for instance, if a template enumerates the parameters of a function of a class, the enumeration code is in all three contexts simultaneously: that of the particular class the template is run on, and the particular method and parameter pointed to by the enumeration process.
Context is changed by using the <foreach> control structure, using the objects=”context” parameter. For instance <foreach objects=”classes”></foreach> will run the content of the tag pair for each class that xPP knows about. <foreach object=”params”></foreach> will iterate all the parameters of the method in the current context, and will generate an error if there is no method context (ie, no enumeration is proceeding, or a simple class enumeration is : a parameter context requires a method context).
Type : Compound.
Parameters :
objects
: The context of the enumeration
Possible
values :
classes :
enumerates all classes known by
xPP
methods :
enumerates all methods from the class in the current
context
params :
enumerates all parameters from the method in the current context
(requires a method context!)
templateparam :
enumerates all template parameters from the parameter in the current
context (requires a parameter context!)
Example :
<foreach
objects=”methods”>
Method
<forindex/> is named <method data=”name”/><br/>
</foreach>
Result :
Method 0 is
named ClassName
Method 1 is named ~ClassName
Method 1 is named
DoSomething
Method 2 is named DoSomethingElse
...
IF
Tests a condition.
Type : Compound.
Parameters :
object
: the type of object you wish to perform a test on, possible objects
depend on the current context. Use this parameter in conjunction with
the isoftype or inherits
parameters)
Possible
values :
class :
tests a property of the class in the current
context
method :
tests a property of the method in the current
context
param :
tests a property of the parameter in the current
context
templateparam :
tests a property of the template parameter in the current context
isoftype
: tests a flag on the object, can hold multiple flags
separated by |, in which cas an OR operator is used by default (see
the op parameter)
Possible
values for class object :
-
no flag yet
Possible
values for method objects :
ctor :
tests if the method in the current context is a
constructor
dtor :
tests if the method in the current context is a
desstructor
defaultctor :
tests if the method in the current context is the default
(parameterless) constructor
virtual :
tests if the method in the current context is a virtual
function
static :
tests if the method in the current context is a static
function
inline :
tests if the method in the current context is an inline
function
pure :
tests if the method in the current context is a pure virtual
function
raw :
tests if the method in the current context is neither virtual nor
static nor inline
void :
tests if the method in the current context returns
void
voidparam :
tests if the method in the current context is
parameterless
public :
tests if the method in the current context is
public
private :
tests if the method in the current context is
private
protected :
tests if the method in the current context is
protected
script :
tests if the method in the current context was marked with the SCRIPT
passive macro
event :
tests if the method in the current context was marked with the EVENT
passive macro
multiple :
tests if the method in the current context has sibblings named the
same with different parameters
firstofmultiple :
tests if the method in the current context is the first of several
with the same name but different parameters
Possible
values for param and templateparam objects
:
template :
tests if the param in the current context is of a template type (ie,
std::list<something>)
pointer :
tests if the param in the current context is a pointer (ie,
something*)
reference :
tests if the param in the current context is a reference (ie,
something&)
in :
tests if the param in the current context has been marked with the IN
passive macro
out :
tests if the param in the current context has been marked with the
OUT passive macro
inout :
tests if the param in the current context has been marked with the
INOUT passive macro
op :
operator to use when specifying multiple types in the isoftype
parameter.
Possible
values :
and :
all type tests must be
succeed
or :
at least one type test must succeed
not : 1 to reverse the results of the test
inherit : tests if the class in the current context directly inherits the class provided in the parameter.
text : tests the properties of the text in the parameter (use in conjunction with the match parameter)
match : performs a regexp match test on the text provided in the text parameter.
Example :
<if object=”method” isoftype=”void” not=”1”>return</if><method data=”name”/>();
Result if the method is void :
MethodName();
Result if the method returns a value :
return MethodName();
ELSE
Perform code if an IF's result was false.
Type : Compound.
Parameters : none
Example :
<if object=”method”
isoftype=”void”>
// method
<method data=”name”/> is
void<br/>
<else>
//
method <method data=”name”/> returns type <method
data=”returntype”/><br/>
</else>
</if>
Result if the method is void :
// method MethodName is void
Result if the method returns an int :
// method MethodName returns type int
XML Dictionary
-todo-
XML Common Modifiers
-todo-
Practical Examples
Here are some of the things you can do.
Testing Inheritance to Pick Appropriate Code
If you need to make a part of your source code be commented out when your class is not inheriting a specific ancestor, you would normally use a #define DERIVE_MYANCESTOR which you would or would not set and then #ifdef on that. This is not always safe, since one may forget to re-enable the #define when restoring the ancestor, or to disable it when no longer inheriting, and then go on never noticing it. With xPP you can instead do:
/*<?
<br/>
<if
object="class" inherits="nsI$(class.name)X">
//
XPCOM interface - get a pointer to the mozilla
nsIMyClass
nsITestClass *test =
this;
// ... and do stuff with
it
<else>
//
** XPCom interface is disabled **
</else>
</if>
<br/><br/>
*/
//
** XPCom interface is disabled **
/*?>*/
Or alternatively :
/*<?<if object=”class”
inherits=”nsI$(class.name)X” not=”1”/>#if
0</if>*/
#if 0
/*?>*/
// XPCOM interface - get
a pointer to the mozilla nsIMyClass
nsITestClass *test = this;
//
... and do stuff with it
/*<?<if object=”class”
inherits=”nsI$(class.name)X”
not=”1”/>#endif</if>*/
#endif
/*?>*/
Building for Multiple Targets
-todo-
Automatically Exposing Data via Accessors
The DataServer template can be used to easily expose data members via read and/or write accessors, as well as implement events to notify you when these member change. Exposing template types can be customized by creating a data template (see directory templates/data). Try adding “DataServer” in your list of factories, and look for YourClassDS.cpp/h after processing. To expose a member, simply precede it with READ, WRITE, or READWRITE. Other templates in your stack will interpret these flags for themselves too, for instance the XPCOM interface will create the appropriate properties for your data in the idl file.