From bce9d5a76072ec697ef69021818aa68709036da5 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 16 Mar 2009 08:16:43 +0200 Subject: Add support for type customization in C++/Hybrid examples/cxx/hybrid/custom/wildcard/: new example --- documentation/cxx/hybrid/guide/index.xhtml | 224 ++++++++++++++++++++++++++--- 1 file changed, 203 insertions(+), 21 deletions(-) (limited to 'documentation/cxx') diff --git a/documentation/cxx/hybrid/guide/index.xhtml b/documentation/cxx/hybrid/guide/index.xhtml index 2aa9a19..3586264 100644 --- a/documentation/cxx/hybrid/guide/index.xhtml +++ b/documentation/cxx/hybrid/guide/index.xhtml @@ -3102,21 +3102,30 @@ main ()

4.8 Customizing the Object Model

-

Sometimes it is desirable to be able to store extra, - application-specific data in some object model classes or - nested compositor classes. Cases where this functionality - may be required include handling of typeless content - matched by XML Schema wildcards as well as a need for - an application to pass extra data as part of the object - model. The C++/Hybrid mapping provides a light-weight - mechanism for storing custom data by allowing you to add - a sequence of opaque objects, stored as void*, - to select generated classes. It is also possible to - customize the parsing and serialization code for such - classes in order to populate the custom data sequence - during parsing and later serialize it to XML. See +

Sometimes it is desirable to add extra, application-specific + data or functionality to some object model classes or + nested compositor classes. Cases where this may be required + include handling of typeless content matched by XML Schema + wildcards as well as a need for an application to pass extra + data or provide custom functions as part of the object model. + The C++/Hybrid mapping provides two mechanisms for accomplishing + this: custom data and custom types. Custom data is a light-weight + mechanism for storing application-specific data by allowing you + to add a sequence of opaque objects, stored as void*, + to select generated classes. Type customization is a more + powerful mechanism that allows you to provide custom implementations + for select object model classes. You have the option of either extending + the generated version of the class (for example, by adding extra data + members and/or functions) or providing your own implementation from + scratch. The latter approach essentially allows you to change the + mapping of XML Schema to C++ on a case by case basis.

+ +

It is also possible to customize the parsing and serialization code, + for example, to populate the custom data sequence or custom data + members during parsing and later serialize them to XML. See Section 6.1, "Customizing Parsers and - Serializers" for details.

+ Serializers" for details. The remainder of this section discusses + the custom data and custom types mechanisms in more detail.

To instruct the XSD/e compiler to include custom data in a specific object model class, we need to use the @@ -3126,12 +3135,12 @@ main () name starting with the XML Schema type, for example type::sequence1. If we would like to add the ability to store custom data in the generated - person class for our person records + person class from our person records vocabulary, we can compile people.xsd like this:

-$ xsde cxx-hybrid --custom-type person people.xsd
+$ xsde cxx-hybrid --custom-data person people.xsd
   

The resulting person class will have the @@ -3336,6 +3345,178 @@ for (person::custom_data_iterator i = cd.begin (); i != cd.end (); ++i) } +

To instruct the XSD/e compiler to use a custom implementation + for a specific object model class, we need to use the + --custom-type option. The argument format for this + option is name[=[flags][/[type][/[base][/include]]]]. + The name component is the XML Schema type name being + customized. Optional flags allow you to specify whether + the custom class is fixed or variable-length since customization can + alter this property, normally from fixed-length to + variable-length. The f flag indicates the type is + fixed-length and the v flag indicates the type is + variable-length. If omitted, the default rules are used to determine + the type length (see Section 4.2, "Memory Management"). + + Optional type is a C++ type name, potentially qualified, + that should be used as a custom implementation. If specified, the + object model type is defined as a typedef alias for + this C++ type. Optional base is a C++ name that should + be given to the generated version. It is normally used as a base for + the custom implementation. Optional include is the header + file that defines the custom implementation. It is #include'ed + into the generated code immediately after (if base is + specified) or instead of the generated version. The following + examples show how we can use this option:

+ +
+--custom-type foo
+--custom-type foo=///foo.hxx
+--custom-type foo=v///foo.hxx
+--custom-type foo=f/int
+--custom-type foo=//foo_base/my/foo.hxx
+--custom-type foo=v/wrapper<foo_base>/foo_base
+  
+ +

The first version instructs the XSD/e compiler not to generate + the object model class for the foo XML Schema + type. The generated code simply forward-declares foo + as a class and leaves it to you to provide the implementation. + + The second version is similar to the first, except now we specify + the header file which defines the custom implementation. + This file is automatically included into the generated header + file instead of the standard implementation. + + The third version is similar to the second, except now we specify + that the foo type is variable-length. In the previous + two cases the type length was determined automatically based on the + type definition in the schema. + + In the fourth version we specify that schema type foo + is fixed-length and should be mapped to int. + + The fifth version instructs the XSD/e compiler to generate + the object model class for type foo but call it + foo_base. It also tells the compiler to generate + the #include directive with the my/foo.hxx + file (which presumably defines foo) right after the + foo_base class. + + Finally, the last version specifies that schema type foo + is variable-length and should be mapped to wrapper<foo_base>. + The compiler is also instructed to generate the standard object + model class for type foo but call it foo_base. + + If you omit the last component (include), as in the + final version, then you can provide the custom type definitions using + one of the prologue or epilogue XSD/e compiler options. See the + XSD/e + Compiler Command Line Manual for details.

+ +

Note also that if the type length you specified with the + --custom-type option differs from the default type + length that would have been determined by the XSD/e compiler, + then you need to specify this --custom-type option + when compiling every schema file that includes or imports the + schema that defines the type being customized.

+ +

As an example, let us add a flag to the person class + from our person records vocabulary. This flag can be used by the + application to keep track of whether a particular person record + has been verified. To customize the person type we + can compile people.xsd like this:

+ +
+$ xsde cxx-hybrid --custom-type person=//person_base/person.hxx \
+people.xsd
+  
+ +

The relevant code fragment from the generated header file + looks like this:

+ +
+// person_base (fixed-length)
+//
+class person_base
+{
+  ...
+};
+
+#include "person.hxx"
+
+// people (variable-length)
+//
+class people
+{
+  ...
+
+  // person
+  //
+  typedef xsde::fix_sequence<person> person_sequence;
+  typedef person_sequence::iterator person_iterator;
+  typedef person_sequence::const_iterator person_const_iterator;
+
+  const person_sequence&
+  person () const;
+
+  person_sequence&
+  person ();
+
+private:
+  ...
+};
+  
+ +

We base our custom implementation of the person + class on generated person_base and save it to + person.hxx:

+ +
+class person: public person_base
+{
+public:
+  person ()
+    : verified_ (false)
+  {
+  }
+
+  bool
+  verified () const
+  {
+    return verified_;
+  }
+
+  void
+  verified (bool v)
+  {
+    verified_ = v;
+  }
+
+private:
+  bool verified_;
+};
+  
+ +

The client code can use our custom implementation as if the + flag was part of the vocabulary:

+ +
+people::person_sequence& ps = ...;
+
+for (people::person_iterator i = ps.begin (); i != ps.end (); ++i)
+{
+  if (!i->verified ())
+  {
+    // Verify the record.
+
+    ...
+
+    i->verified (true);
+  }
+}
+  
+ @@ -4868,13 +5049,13 @@ hello_s.post (); implementation, you will need to specify the --custom-parser or --custom-serializer option, respectively. The argument format for these two options - is type[=base[/include]]. The type + is name[=[base][/include]]]. The name component is the XML Schema type name being customized. Optional - base is a C++ name that should be given to the + base is a C++ name that should be given to the generated version. It is normally used as a base for the custom - implementation. Optional include is the header file + implementation. Optional include is the header file that defines the custom implementation. It is #include'ed - into the generated code immediately after (if base + into the generated code immediately after (if base is specified) or instead of the generated version. The following examples show how we can use these options:

@@ -4896,7 +5077,8 @@ hello_s.post (); foo_base_pimpl class. The last version instructs the XSD/e compiler to include foo/foo-custom.hxx instead of generating the parser implementation for - foo. If you omit the last component, then + foo. If you omit the last component + (include), then you can include the custom parser/serializer definitions using one of the prologue or epilogue XSD/e compiler options. See the XSD/e -- cgit v1.1