Tree/Customization guide
From Code Synthesis Wiki
Revision as of 10:52, 26 September 2006 Boris (Talk | contribs) (Add a section on customizing the generated type, simple case) ← Previous diff |
Revision as of 10:59, 26 September 2006 Boris (Talk | contribs) (→Customizing the generated type - the simple case) Next diff → |
||
Line 139: | Line 139: | ||
The first option tell the compiler that we are providing our own implemntation for the <code>contact</code> type as well instructs it to generate the standard mapping with the name <code>contact_base</code>. We will use the generated <code>contact_base</code> class as a base for our implementation of <code>contact</code>. The second option instructs the compiler to add <code>#include "contacts-custom.hxx"</code> at the end of the generated header file. The <code>contacts-custom.hxx</code> file is where we will define our own <code>contact</code> class: | The first option tell the compiler that we are providing our own implemntation for the <code>contact</code> type as well instructs it to generate the standard mapping with the name <code>contact_base</code>. We will use the generated <code>contact_base</code> class as a base for our implementation of <code>contact</code>. The second option instructs the compiler to add <code>#include "contacts-custom.hxx"</code> at the end of the generated header file. The <code>contacts-custom.hxx</code> file is where we will define our own <code>contact</code> class: | ||
+ | // contacts-custom.hxx | ||
+ | // | ||
#include <iosfwd> // std::ostream | #include <iosfwd> // std::ostream | ||
Revision as of 10:59, 26 September 2006
Introduction
XSD provides you with mechanisms to customize the generated type system for the C++/Tree mapping. Common customization examples include:
- using a different type for one of the built-in types (e.g.,
boost::gregorian::date
from the Boost libraries forxsd:date
) - adding a member function or data member to one or more generated types (e.g., add a
print()
function) - adding virtual functions to the base type of a type hierarchy and implementing them in the derived types (e.g., when using
xsi:type
dynamic typing and/or substitution groups)
XSD provided two command-line options, --custom-type
and --custom-type-regex
, that allow you to specify which types should be customized and how these types will be customized. The format for the --custom-type
option is as follows:
--custom-type name[=type[/base]]
The name component specifies the name of a type as defined in XML Schema. The name is unqualified since the target namespace of the schema being compiled is always assumed. The optional type component is a C++ type name that should be used instead of the generated type. If type is empty or not specified then the same name as the XML Schema type name is assumed. Finally, if the optional base component is provided then the standard mapping for the type is still generated but with this name. A few examples should make all this clear. Suppose we have the following schema:
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.com/hello"> <complexType name="names"> <sequence> <element name="name" type="string"/> </sequence> </complexType> </schema>
Compiling this schema without specifying any options will result in the following C++ code:
namespace hello { class names { ... }; }
Now we compile the above schema with the following option:
--custom-type names
The generated code will look like this:
namespace hello { class names; }
The compiler simply generated a forward declaration for names
expecting you to provide the implementation. Let's say we want to use the my_names
type instead. Then we can use the following option:
--custom-type names=my_names
The generated code will look like this:
namespace hello { typedef my_names names; }
What if we wanted to use class template names
which is defined in namespace templates
? Then we could use the following option:
--custom-type names=::templates::names<char>
The resulting code would look like this:
namespace hello { typedef ::templates::names<char> names; }
Now what if all we want to do is add a simple function to the names
type? It would be too much work if we had to implement all the code that gets generated ourselves. Fortunately we don't have to. We can ask the compiler to generate the standard mapping with a different name and then inherit our custom type from the generated one:
--custom-type names=/names_base
This will result in the following C++ code:
namespace hello { class names_base; class names; class names_base { ... }; }
In our implementation of the names
class we can use names_base
as the base. Here is another example:
--custom-type names=::templates::names<names_base>/names_base
This results in the following C++ code being generated:
namespace hello { class names_base; typedef ::templates::names<names_base> names; class names_base { ... }; }
While this example may seem a bit far-fetched, this technique is actually used in some special cases as will be shown later.
The --custom-type-regex
option is similar to --custom-type
except it allows you to use regular expressions
to match several names at once. For more information on this option refer to the XSD command line interface documentation (man pages).
Customizing the generated type - the simple case
Let's now look at the complete set of steps necessary to do a simple customization. All the code in this section is taken from the contacts
example which can be found in the examples/cxx/tree/custom/contacts
directory of the XSD distribution. Our schema looks like this:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cts="http://www.codesynthesis.com/contacts" targetNamespace="http://www.codesynthesis.com/contacts"> <xsd:complexType name="contact"> <xsd:sequence> <xsd:element name="name" type="xsd:string"/> <xsd:element name="email" type="xsd:string"/> <xsd:element name="phone" type="xsd:string"/> </xsd:sequence> </xsd:complexType> <xsd:complexType name="catalog"> <xsd:sequence> <xsd:element name="contact" type="cts:contact" maxOccurs="unbounded"/> </xsd:sequence> </xsd:complexType> <xsd:element name="catalog" type="cts:catalog"/> </xsd:schema>
We would like to add the print()
function to the generated contact
type. The first step is to compile our schema. We will use the following XSD options (explained below):
--custom-type contact=/contact_base --hxx-epilogue '#include "contacts-custom.hxx"'
The first option tell the compiler that we are providing our own implemntation for the contact
type as well instructs it to generate the standard mapping with the name contact_base
. We will use the generated contact_base
class as a base for our implementation of contact
. The second option instructs the compiler to add #include "contacts-custom.hxx"
at the end of the generated header file. The contacts-custom.hxx
file is where we will define our own contact
class:
// contacts-custom.hxx // #include <iosfwd> // std::ostream namespace contacts { class contact: public contact_base { // The following constructor signatures are copied from // contact_base except for the copy constructor and the // _clone function where we had to change the type from // contact_base to contact. // public: contact (const name::type&, const email::type&, const phone::type&); contact (const xercesc::DOMElement&, xml_schema::flags = 0, xml_schema::type* = 0); contact (const contact&, xml_schema::flags = 0, xml_schema::type* = 0); virtual contact* _clone (xml_schema::flags = 0, xml_schema::type* = 0) const; // Our customizations. // public: void print (std::ostream&) const; }; }