Print
Category: Zeus Framework
Hits: 7183

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:

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:
  • using System;
  • using System.Collections.Generic;
  • using System.Runtime.InteropServices;
  • using System.Text;

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 name contains the type definition name and the attribute type the replacement.

Example:

<declarations>
  <cpp_typedef name="ISerializableList" value="IList&lt;ISerializable*&gt;"/>
</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:

nameName of the method.

modifier Method modifier can be "readonly" or "changable". In C++ the method becomes "const" if it is set to "readonly".

descriptionDescribes the method.

param Specifies a parameter(argument) of the method
return Specifies the return value

Parameter

The parameter is specified with following attributes:

nameName 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

descriptionDescribes 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.

useUsage of the return value.

nullable Flag if the return parameter can also return null.

descriptionDescribes 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:

The primitive data types include all numeric values

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() {}