This chapter describes the .NET code generation in more detail. The .NET wrapper consists of a native DLL and a managed assembly. The source code for both the native DLL and the managed assembly is generated with a self-written code generator program. The code generator reads a bunch of XML files and transforms this into wrapper code. The native DLL is a flattened-out representation of the nGI class hierarchy. Functions from this DLL can be called by the managed assembly via DllImport statements.

XML interface definition

For each class, one XML interface definition file has to be written. This interface definition spells out the details of the wrapped classes: the name, the constructors, properties and methods, their parameters etc. The wrapper generator reads in the interface definition and builds an object tree out of them. This tree is then walked by the wrapper generator in order to generate source code.

Elements of the interface
definition

The <binary_operator> element is used to specify binary operators. It has name, type and optional qualifier attributes, but no child elements. The name attribute specifies the operator name and the type attribute specifies the return type. For more information about the type attribute, see the <type> element. If used, the qualifier attribute can either contain the string const or an empty string. If the qualifier attribute is missing, the empty string is used by default.

The <constructor> element is used to specify a constructor of a class. It is a child of the <class> element. It has no attributes but one or more <parameter> child elements.

The <copy_constructor> element is used to specify a copy constructor of a class. It is a child of the <class> element. It has no attributes or child elements.

The <class> element specifies a class. The <class> element is a child of the <file> element. A <class> element can have constructors, operators, properties, methods, events and some other aspects. The <class> element has a name attribute. Class constructors are specified with the <default_constructor>, <constructor> and <copy_constructor> child elements. Operators are specified with the <unary_operator> and <binary_operator> child elements. Properties are specified with the <property> child elements. Methods are specified with the <method> and <static_method> child elements. Events are specified with the <event> child element. If the specified class has template parameters, it needs a <variant> child element for each template parameter, in order to specify concrete types for the template instantiation.

The <default_constructor> element is used to specify a default constructor of a class. It is a child of the <class> element. It has no attributes or child elements.

The <enum> element specifies an enumeration. It can be a child element of either the <file> or the <class> elements. The <enum> element has a name attribute and usually several <enum_value> child elements. It is used to define enumerations at file or class scope.

The <enum_value> element specifies an enumeration value. It can be a child element of an <enum> element. The <enum_value> element has both name and value attributes. The name attribute specifies the name of an enumeration value, the value attribute specifies its value. Both attributes are mandatory.

The <event> element is used to specify events that a class can fire. The <event> element has type and name attributes, and it can have <parameter> child elements. The <event> element is a child of the <class> element. The name attribute specifies the event name, and the type attribute specifies the events return type. For more information about the type attribute, see the <type> element.

After the usual XML boilerplate stuff -<?xml version="1.0" encoding="utf-8"?> - each interface definition file starts with the <file> element. The <file> element corresponds to a header file in the nGI include directory. The <file> element does not have any attributes, but it has one or more <include>, <class>, <static_class> and/or <enum> child elements.

The <include> element can be a child of the <file> element. The <include> element has a name attribute but no other child elements. The name attribute is used to build include statements. Include elements are needed to make classes known to the wrapper generator that are used as parameters within the file.

The <method> element is used to specify methods. It has name, type and optional qualifier attributes, and optional <parameter> child elements. The name attribute specifies the operator name and the type attribute specifies the return type. For more information about the type attribute, see the <type> element. If used, the qualifier attribute can either contain the string const or an empty string. If the qualifier attribute is missing, the empty string is used by default. The <parameter>child elements specify the method parameters.

The <parameter> element is used to specify parameters of a method, property, operator or event. The <parameter> element has name and type attributes, but no child elements. The <parameter> element can be a child of the <method>, <static_method>, <unary_operator>, <binary_operator>, <property> and <event> elements. The name attribute specifies the parameter name and the type attribute specifies the parameter type. For more information about the type attribute, see the <type>element.

The <property> element is used to specify properties. It has name, type and optional kind attributes, and optional <parameter> child elements. The name attribute specifies the property name and the type attribute specifies the property type. For more information about the type attribute, see the <type> element. The kind attribute can either contain the strings rw for read/write, ro for read-only. You can also attach the string ref for reference properties. The <parameter> child elements specify the property parameters.

The <static_class> element specifies a class with static methods only. The <static_class> element is a child of the <file> element. The <static_class> element has a name attribute. The <static_class> can have <static_method> child elements only.

The <static_method> element is used to specify methods. It has name and type attributes, and optional <parameter> child elements. The name attribute specifies the operator name and the type attribute specifies the return type. For more information about the type attribute, see the <type>element. The <parameter> child elements specify the method parameters.

The <type>element is used to specify types for template instantiation. The <type>element has a name attribute that specifies the type. The <type>element is a child element of the <variant> element. Type names are the C++ types, such as int, char or void. Strings are specified as string. If template parameters need to be used, the angle brackets have to be replaced by curly braces, because angle brackets cannot be used within XML files (use buffer{int} instead of buffer<int>).

The <unary_operator> element is used to specify unary operators. It has name, type and optional qualifier attributes, but no child elements. The name attribute specifies the operator name and the type attribute specifies the return type. For more information about the type attribute, see the <type> element. If used, the qualifier attribute can either contain the string const or an empty string. If the qualifier attribute is missing, the empty string is used by default.

The <variant> element is used to specify concrete types for template instantiation. It is a child element of the <class> element. The <variant> element has name and net attributes. The name attribute specifies the variant name and the net attribute specifies the mapping to a .NET type. It has one or more <type> child elements to specify the set of concrete types. For each template parameter of a nGI class one <variant> element is used.

Native DLL

The native DLL is used to provide some means that the .NET wrapper can call into. C# can call into a DLL, provided that the functions within the DLL have a C interface. The first task of the native DLL is therefore to provide such a DLL which contains C-callable functions. In order to provide this, the hierarchical class interface had to be flattened. The other task of the native DLL is to explicitly spell out the types that the class templates should instantiate. If we have a type template <typename T> point, and this should be instantiated for types point<int> and point<double>, the native DLL provides two sets of functions to do so.

Constructors of a class are mapped onto functions that create an object using new and return a pointer to the created object. While the object lives on the heap, member functions of a class take this pointer as their first parameter, similar to the hidden this pointer of a class member in C++. The destructor takes this first parameter and calls delete.

Mapping of native functions to C++
functions

In reality, the opaque type hidden behind the void * pointer is a bit more complicated for reasons that will become clearer later. The construct functions return a shared_ptr * that itself points to a pair of pointers, where the first pointer points to the newed object and the second pointer points to a string returned by the typeid(...).name() function.

Object model

A handle is a pointer that points to a newed shared_ptr. When the native DLL deletes the shared_ptr, the shared_ptr deletes the std::pair * and the first pointer, or - depending on the deallocator passed when constructing the shared_ptr - deletes the std::pair * only.

Managed assembly

The managed assembly is written in C#. Most of this assembly is generated but some portions are written by hand. The wrapper generator uses the XML interface description to rebuild the class hierarchy and calls through to the flat native DLL as needed.