Index of xIDL
1. General
Basic question is why do we need an other language besides IDL? IDL was invented basically for Corba and Microsoft COM based architecture. Unfortunately the language is not really standardized. Below you see two examples one from Java and one from Microsoft COM:
Java IDL (RMI)
module HelloApp
{
interface Hello
{
string sayHello();
oneway void shutdown();
};
};
Microsoft IDL (COM)
[
uuid(ba209999-0c6c-11d2-97cf-00c04f8eea45),
version(1.0)
]
interface MyInterface
{
const unsigned short INT_ARRAY_LEN = 100;
void MyRemoteProc( [in] int param1,
[out] int outArray[INT_ARRAY_LEN] );
}
We decided to adapt our needs to any of the old languages. Instead we started defining an XML based interface definition language, called XIDL. In contrast to the old languages we added new language features to it, in terms to be open for the future. For Example the language dependant extension notation. XIDL is used by the Zeus-Framework, but can be easily adapted to your needs. This document should give you a brief overview of what XIDL is and how it works.
XIDL is part of the Zeus-Framework and provides a platform and language independant language to define interfaces. This ensures the correct declaration of interfaces in a heterogenous framework such as Zeus-Framework.
Following advantages are achieved:
- The interfaces are byte compatible in all languages (direct calls or marshalling)
- Changes are done once. We change the XIDL file and get new interfaces for C++, C#, ect.
- Extension points to support new languages and features
- Checks the language syntax
- Checks the coding conventions. Code conventions can be customized.
- Adds prefix notation to parameters according to the coding conventions
1.1 xIDL Compiler
The Zeus-Framework provides a xIDL compiler. The compiler uses the Zeus-Framework and is deployed in the directory build/[IDE]
. The compiler simply loads a XIDL file and converts it with a given XML Stylesheet (XSLT).
The XIDL convertion can be done manually too. All you need is an XML-Editor supporting XSD and XSLT.
1.1.1 Visual Studio 2010 Support
For Visual Studio 2010 a project called xidl_files
contains all xIDL-files of the Zeus-Framework. The files have a custom built step, which you can choose between C# and C++ interface files. Hit F6
to build the interface code files.
The C++ configuration directly deploys the interface headers to Zeus-Framework/src/
.
The C# configuration directly deploys the interface file to Zeus-Bindings/csharp/ZeusBaseDotNet/
.
1.1.2 xIDL compile options
You can call the xIDL tool also directly in a console. It provides a simple interface:
--language=<type> |
Selects the requested language. Valid types are C++ or C# |
--output=<path> |
Output path of the generated interface code files |
--silent |
Silent mode. Returns only Ok if succeeded or Abort if it failes. |
<xidl-file> |
xIDL file to process. |
1.2 Structure of the xIDL
The xIDL structure is defined in a XML Schema (XSD) located in Zeus-Framework/xIDL/definitions
. Following graphic explains the structure and how a xIDL file can be built.
The xidl-file must start with an xidl
node.
Example:
<xidl xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../definitions/xidl.xsd">
...
<xidl>
1.2.1 Header
The header contains general information about the interface. The header is printed as a header section in the interface code file. It contains following fields:
copyright |
Copy right information (only a short description) |
system |
To which system the interface belongs |
project |
Declaration of the associated project |
package |
Package which contains the interface |
mainmodule |
Name of the xidl file. Normally it is the name of the interface. |
author |
Name of the author (or company). |
date |
Date of the last changes. |
repository |
Name of the code repository, such as sub version or source safe. |
licence |
Name of the licence. If LGPL is used, the license text of LGPL is printed. Extension: For new license text add a file to ./configuration folder. The file must begin with the name of the license and must end with _license.xml . |
Example:
<header>
<system>Zeus-Framework</system>
<project>Zeus Base Library</project>
<package>System</package>
<mainmodule>IZVariant</mainmodule>
<author>Benjamin Hadorn</author>
<date>$Date$</date>
<repository>$File$</repository>
<licence>LGPL</licence>
</header>
1.2.2 Imports
The imports are used to reference other XIDL interfaces, enumerazions and data types such as IString and IByteArray. Imports are mandatory for inheritance and method parameter types.
name |
Name of the data type to import. |
xidl |
Path and file name of the XIDL file, which contains this type. |
dependency |
Declares how strong the dependency to this module is. weak and strong are the possibilities. |
Example:
<import name="ISerializable"
xidl="zeusbase/System/Interfaces/ISerializable.xidl"
dependency="strong">
</import>
<import name="IByteArray">
</import>
It's translated in C++ as follows:
#include <zeusbase/System/Interfaces/ISerializable.hpp>
class IByteArray;
Note that in C++ the weak dependency is translated into a forward declaration, where as the strong dependency results as a include of header files.
It's translated in C# as follows:
using Zeus.ZeusBase.System.Interfaces;
In C# all XIDL declarations result in a using declaration.
Extensions: Each language can add extension points to the import node.
cpp_include |
Defines a special include file for C++ for the specified data type. |
cpp_namespace |
Declares that the type is in a special namespace (mostly used if the data type is a sub-class of an other type). |
cs_usage |
Defines a special using for C# source files. Note that in C# following using are done automatically:
|
Example:
<import name="IStringList">
<extension name="cpp_include" value="zeusbase/System/Interfaces/IStringList.hpp"/>
<extension name="cs_usage" value="Zeus.ZeusBase.System"/>
</import>
It's translated in C++ as follows:
#include <zeusbase/System/Interfaces/IStringList.hpp>
It's translated in C# as follows:
using Zeus.ZeusBase.System;
1.2.3 Module-Declaration
The module section includes all declarations, interfaces and enumerations. In the module is translated into a namespace in C++ and C# notation.
The module has following attributes:
name |
Name of the module. For nested namespaces use a "." to seperate the hierachical modules: Zeus_2.System.Interfaces |
declarations |
Node containing the declarations |
enumeration |
Node containing the module enumerations. |
interface |
Node containing the interface declaration. |
Extensions: Each language can add extension points to the module node.
cpp_namespace_start |
Special namespace start in C++ e.g. using defines instead of direct namespace names. |
cpp_namespace_end |
Special namespace ending in C++ e.g. using defines instead of direct namespace names. |
Example:
<module name="Zeus.ZeusBase.System.Interfaces">
<extension name="cpp_namespace_start" value="BEGIN_NAMESPACE_Zeus"/>
<extension name="cpp_namespace_end" value="END_NAMESPACE_Zeus"/>
...
</module>
It's translated in C++ as follows:
BEGIN_NAMESPACE_Zeus
...
END_NAMESPACE_Zeus
It's translated in C# as follows:
namespace Zeus.ZeusBase.System.Interfaces
{
...
}
1.2.4 Declaration
The declaration section contains all types of declarations, such as type definitions in C++. Note that forward declarations are done at the import of data types.
cpp_typedef |
Type definition in C++.
The attribute |
Example:
<declarations>
<cpp_typedef name="ISerializableList" value="IList<ISerializable*>"/>
</declarations>
Following xIDL structure is translated in the C++ code below:
typedef IList<IString> IStringList;
1.2.5 Enumeration
The enumeration node specifies an enum-type.
The enumeration has following attributes:
name |
Name of the enumeration. |
description |
Describes the enumeration. |
enum_item |
An enumeration value |
Enumeration value
The enum_item
item specifies one value of the enumeration.
name |
Name of the value. |
value |
Nummeric value. |
description |
Describes the enumeration value. |
Example:
<enumeration name="EZVariantType">
<description>Datatypes of a variant</description>
<enum_item name="etEmpty" value="0">
<description>Unknown or Empty data</description>
</enum_item>
<enum_item name="etInt8" value="1">
<description>a int8 value</description>
</enum_item>
<enumeration>
1.2.6 Interface
The interface node declares an interface with methods. The interface node has following attributes:
name |
Name of the interface (class name). |
id |
GUID of the interface (globally unique identifier). |
remote |
Flag if the interface is a remote interface of the Remote Method Invocation |
inherits |
Optionally attribute if the interface inherits from a super interface. |
The interface declaration has several sub nodes:
description |
Describes the interface (comment). |
enumeration |
Describes an enumeration in the namespace of the interface. |
method |
defines a method of the interface. |
Example:
<interface name="INotifyObserver"
id="F8B4CFCA-4170-4f98-B0E4-696A89C9B939"
inherits="IObserver">
<description>Defines a simple notification observer.</description>
..
</interface>
It's translated in C++ as follows:
// {F8B4CFCA-4170-4f98-B0E4-696A89C9B939}
#define INTERFACE_INotifyObserver \
TIID(0xF8B4CFCA, 0x4170, 0x4f98, 0xB0, 0xE4, 0x69, 0x6A, 0x89, 0xC9, 0xB9, 0x39)
/*****************************************************************************/
/*! Defines a simple notification observer.
*/
/*****************************************************************************/
class INotifyObserver : public IObserver
{
}
It's translated in C# as follows:
///
/// Defines a simple notification observer.
///
[Guid("F8B4CFCA-4170-4f98-B0E4-696A89C9B939"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisibleAttribute(true)]
public interface INotifyObserver : IObserver
{
}
For C# programmers: Since the Zeus-Framework allows interoperability with other langauages we use the COM declaration here. The interface must be COM-visible. Note that the C# COM based interface is always derived from IUnknown. If the IObserver interface would inherit an other COM based interface all methods are automatically repeated at the beginning of the IObserver declaration.
1.2.7 Methods
An interface has at least one method. The method node specifies a method of the interface. A method allways has a return value (even if its void). The node has following attributes:
name
Name of the method.
modifier |
Method modifier can be "readonly" or "changable". In C++ the method becomes "const" if it is set to "readonly". |
description
Describes the method.
param |
Specifies a parameter(argument) of the method |
return |
Specifies the return value |
Parameter
The parameter is specified with following attributes:
name
Name of the parameter.
Note that the name can be given without prefix. The prefix is added by the ./configuration/prefixes.xml
file. Adapt the file to meet your needs.
use |
Usage of the parameter. |
optional |
Flag if the parameter is optional (can be null). |
default |
Optional parameter to define a default value (used for primitive values or pointers). |
The parameter has following sub elements
description
Describes the parameter inside the method description in Doxygen manner.
type |
Specifies the type of parameter. |
Return value
The return value is a must for all methods. The return specification has the possibility to document each return value seperatly.
use
Usage of the return value.
nullable |
Flag if the return parameter can also return null. |
description
Describes the return value as a whole using \return directive (see Doxygen).
retval_description |
Describes the each return value seperatly \retval directive (see Doxygen). |
type |
Specifies the type of parameter. |
Example:
<method name="getName" modifier="readonly">
<description>This methods returns the name of the object</description>
<param name="Name" use="in_ref">
<description>return parameter</description>
<type>
<value_type>IString</value_type>
</type>
</param>
<return use="out">
<type>
<primitive_type>void</primitive_type>
</type>
</return>
</method>
The method is translated into following C# code:
/******************************************/
/*! This methods returns the name of the object
\param rName: return parameter
*/
/******************************************/
[PreserveSig()]
void getName(/*IString*&*/ [In, MarshalAs(UnmanagedType.SysInt)] IntPtr rstrName);
1.2.8 Types
The xIDL supports following type groups:
primitive_type
: primitive data typesenum_type
: enumeration typereference_type
: reference types living on the heap (COM based interfaces)value_type
: value type (all other interface and value types, such asIString
,IByteArray
)function_type
: function type (Delegation in C#)
The primitive data types include all numeric values
void
: void (no return value or unspecified pointer)bool
: boolean value (true or false)Float
: platform dependant float valueFloat32
: 32bit float valueFloat64
: 64bit float valueInt
: platform dependant integer valueInt8
: 8bit integer valueInt16
: 16bit integer valueInt32
: 32bit integer valueInt64
: 64bit integer valueUint
: platform dependant unsigned integer valueUint8
: 8bit unsigned integer valueUint16
: 16bit unsigned integer valueUint32
: 32bit unsigned integer valueUint64
: 64bit unsigned integer valueRetval
: Return value type.Timeval
: Time value.Handle
: Handle type.
For optional parameter in C# an pointer structure for each primitive data type is used. For instance for Float64
a corresponding pointer structure Float64Ptr
exists. The content will be Zero for NULL.
The enumeration type should be used for all enumerations. Dont use value types, because they are passed by reference not by value.
Interface types include all interfaces which inherit IZUnknown (or IUnknown). The interface types must be allocated on the heap.
The value types include all other data types, such as stack objects (IString
, IByteArray
) or structures (InterfaceID
, TypGUID
). Typically these data types are translated into IntPtr
in C#. The IntPtr
represents any unknown pointer. For IByteArray
and IString
exist special pointer structures ByteArrayPtr
and StringPtr
.
1.3 Calling convention C++ and C#
1.3.1 Method arguments
Following tables show the converting from xIDL parameters into C++ and C# method arguments.
Table 1: converting of primitive and enumeration types:
UsageOptionalC++C#
in_ref | MUST | const Int& riValue | ref int riValue |
in_ref | OPT | const Int* piValue | Int32Ptr piValue |
in | MUST | Int iValue | int iValue |
in | OPT | Error | Error |
in_readonly | MUST | const Int iValue | int iValue |
in_readonly | OPT | Error | Error |
inout | MUST | Int& riValue | ref int riValue |
inout | OPT | Int* piValue | Int32Ptr piValue |
out | MUST | Int& riValue | out int riValue |
out | OPT | Int* piValue | Int32Ptr piValue |
Table 2: converting of value types:
UsageOptionalC++C#
in_ref | MUST | const IString& rstrValue | StringPtr rstrValue |
in_ref | OPT | const IString* pstrValue | StringPtr pstrValue |
in | MUST | Error | Error |
in | OPT | Error | Error |
in_readonly | MUST | Error | Error |
in_readonly | OPT | Error | Error |
inout | MUST | IString& rstrValue | StringPtr rstrValue |
inout | OPT | IString* pstrValue | StringPtr pstrValue |
out | MUST | IString& rstrValue | IntPtr rstrValue |
out | OPT | IString* pstrValue | IntPtr pstrValue |
Table 3: converting of reference types:
UsageOptionalC++C#
in_ref | MUST | const ISubject& rSubject | ISubject rSuject |
in_ref | OPT | const ISubject* pSubject | ISubject pSuject |
in | MUST | Error | Error |
in | OPT | Error | Error |
in_readonly | MUST | Error | Error |
in_readonly | OPT | Error | Error |
inout | MUST | ISubject& rSubject | ISubject rSubject |
inout | OPT | ISubject* pSubject | ISubject pSubject |
inout_ref | MUST | ISubject*& rpSubject | ref ISubject rpSuject |
inout_ref | OPT | ISubject** ppSubject | ??? |
out | MUST | ISubject*& rpSubject | out ISubject rpSubject |
out | OPT | ISubject** ppSubject | ??? |
1.3.2 Return values
Following tables show the converting from xIDL return values into C++ and C# return values.
Table 1: converting of primitive and enumeration types:
UsageNullableC++C#
out | Valid | Int foo(); | int foo() {} |
out | Nullable | Error | Error |
out_readonly | Valid | const Int foo(); | int foo() {} |
out_readonly | Nullable | Error | Error |
out_ref | Valid | Int& foo(); | Int32Ptr foo() {} |
out_ref | Nullable | Int* foo(); | Int32Ptr foo() {} |
out_ref_readonly | Valid | const Int& foo(); | Int32Ptr foo() {} |
out_ref_readonly | Nullable | const Int* foo(); | Int32Ptr foo() {} |
Table 2: converting of value types:
UsageNullableC++C#
out | Valid | Error | Error |
out | Nullable | Error | Error |
out_readonly | Valid | Error | Error |
out_readonly | Nullable | Error | Error |
out_ref | Valid | IString& foo(); | IntPtr foo() {} |
out_ref | Nullable | IString* foo(); | StringPtr foo() {} |
out_ref_readonly | Valid | const IString& foo(); | StringPtr foo() {} |
out_ref_readonly | Nullable | const IString* foo(); | StringPtr foo() {} |
Table 2: converting of reference types:
UsageNullableC++C#
out | Valid | Error | Error |
out | Nullable | Error | Error |
out_readonly | Valid | Error | Error |
out_readonly | Nullable | Error | Error |
out_ref | Valid | ISubject& foo(); | ISubject foo() {} |
out_ref | Nullable | ISubject* foo(); | ISubject foo() {} |
out_ref_readonly | Valid | const ISubject& foo(); | ISubject foo() {} |
out_ref_readonly | Nullable | const ISubject* foo(); | ISubject foo() {} |