## 6 OMG IDL Mapping

### 6.1 OMG IDL Mapping - Overview

The purpose of OMG IDL mapping is to act as translator between platforms and languages.

CORBA is independent of the programming language used to construct clients or implementations. In order to use the ORB, it is necessary for programmers to know how to access ORB functionality from their programming languages. It translates different IDL constructs to a specific programming language. This chapter describes the mapping of OMG IDL constructs to the Erlang programming language.

### 6.2 OMG IDL Mapping Elements

A complete language mapping will allow the programmer to have access to all ORB functionality in a way that is convenient for a specified programming language.

All mapping must define the following elements:

- All OMG IDL basic and constructed types
- References to constants defined in OMG IDL
- References to objects defined in OMG IDL
- Invocations of operations, including passing of parameters and receiving of results
- Exceptions, including what happens when an operation raises an exception and how the exception parameters are accessed
- Access to attributes
- Signatures for operations defined by the ORB, such as dynamic invocation interface, the object adapters etc.
- Scopes; OMG IDL has several levels of scopes, which are mapped to Erlang's two scopes. The scopes, and the files they produce, are described.

#### 6.2.1 ReservedCcompiler Names

The use of some names is strongly discouraged due to ambiguities. However, the use of some names is prohibited when using the Erlang mapping , as they are strictly reserved for IC.

IC reserves all identifiers starting with `OE_` and `oe_` for internal use.

Note also, that an identifier in IDL can contain alphabetic, digits and underscore characters, but the first character must be alphabetic.

Using underscores in IDL names can lead to ambiguities due to the name mapping described above. It is advisable to avoid the use of underscores in identifiers.

Refer to the IC documentation for further details.

### 6.3 Basic OMG IDL Types

The OMG IDL mapping is strongly typed and (even if you have a good knowledge of CORBA types), it is essential to read carefully the following mapping to Erlang types.

The mapping of basic types is straightforward. Note that the OMG IDL double type is mapped to an Erlang float which does not support the full double value range.

OMG IDL basic types
OMG IDL type | Erlang type | Note
float | Erlang float |
double | Erlang float | value range not supported
short | Erlang integer |
unsigned short | Erlang integer |
long | Erlang integer |
long long | Erlang integer |
unsigned long | Erlang integer |
unsigned long long | Erlang integer |
char | Erlang integer |
wchar | Erlang integer |
boolean | Erlang atoms true or false |
octet | Erlang integer |
any | Erlang record #any{typecode, value} |
long double | Not supported |
Object | Orber object reference |
void | Erlang atom ok |

The `any` value is written as a record with the field typecode which contains the Type Code representation, see also the Type Code table, and the value field itself.

Functions with return type `void` will return the atom `ok`.

### 6.4 Constructed OMG IDL Types

Constructed types all have native mappings as shown in the table below.

OMG IDL constructed types
string | Erlang string
wstring | Erlang list of Integers
struct | Erlang record
union | Erlang record
enum | Erlang atom
sequence | Erlang list
array | Erlang tuple

Below are examples of values of constructed types.

Typical values
Type | IDL code | Erlang code
string | typedef string S; void op(in S a); | ok = op(Obj, "Hello World"),
struct | struct S {long a; short b;}; void op(in S a); | ok = op(Obj, #'S'{a=300, b=127}),
union | union S switch(long) { case 1: long a;}; void op(in S a); | ok = op(Obj, #'S'{label=1, value=66}),
enum | enum S {one, two}; void op(in S a); | ok = op(Obj, one),
sequence | typedef sequence <long, 3> S; void op(in S a); | ok = op(Obj, [1, 2, 3]),
array | typedef string S[2]; void op(in S a); | ok = op(Obj, {"one", "two"}),

| Every field in, for example, a struct must be initiated. Otherwise it will be set to the atom `undefined`, which Orber cannot encode when communicating via IIOP. In the example above, invoking the opertion with #'S'{a=300} will fail (equal to #'S'{a=300, b=undefined})

### 6.5 References to Constants

Constants are generated as Erlang functions, and are accessed by a single function call. The functions are put in the file corresponding to the scope where they are defined. There is no need for an object to be started to access a constant.

Example:

    // IDL
    module M {
        const long c1 = 99;
    };

Would result in the following conceptual code:

    -module('M').
    -export([c1/0]).

    c1() -> 99.

### 6.6 References to Objects Defined in OMG IDL

Objects are accessed by object references. An object reference is an opaque Erlang term created and maintained by the ORB.

Objects are implemented by providing implementations for all operations and attributes of the Object, see operation implementation.

### 6.7 Invocations of Operations

A function call will invoke an operation. The first parameter of the function should be the object reference and then all `in` and `inout` parameters follow in the same order as specified in the IDL specification. The result will be a return value unless the function has `inout` or `out` parameters specified; in which case, a tuple of the return value, followed by the parameters will be returned.

Example:

    // IDL
    interface i1 {
        long op1(in short a);
        long op2(in char c, inout string s, out long count);
        void op3(out long Id);
    };

Is used in Erlang as :

    %% Erlang
    f() ->
        ...
        Obj = ...    %% get object reference
        R1 = i1:op1(Obj, 55),
        {R2, S, Count} = i1:op2(Obj, $a, "hello"),
        {ok, Id} = i1:op3(Obj),
        ...

Note how the `inout` parameter is passed and returned. There is no way to use a single occurrence of a variable for this in Erlang. Also note, that you must return `ok`, Orber's representation of the IDL-type `void`, must be returned by `op3`. These operations can be implemented in the call-back module as:

    op1(State, AShort) ->
        {reply, ALong, State}.

    op2(State, AShort, AString) ->
        {reply, {ALong, "MyString", ALong}, State}.

    op3(State) ->
        {reply, {ok, AId}, State}.

The operations may require more arguments (depends on IC options used). For more information, see Stubs/Skeletons and Module_Interface.

#### 6.7.1 Operation Implementation

A standard Erlang `gen_server` behavior is used for object implementation. The `gen_server` state is then used as the object internal state. Implementation of the object function is achieved by implementing its methods and attribute operations. These functions will usually have the internal state as their first parameter, followed by any `in` and `inout` parameters.

Do not confuse the object internal state with its object reference. The object internal state is an Erlang term which has a format defined by the user.

| It is is not always the case that the internal state will be the first parameter, as stubs can use their own object reference as the first parameter (see the IC documentation).

The special function `init/1` is called at object start time and is expected to return the tuple `{ok, InitialInternalState}`.

See also the stack example.

### 6.8 Exceptions

Exceptions are handled as Erlang catch and throws. Exceptions are translated to messages over an IIOP bridge but converted back to a throw on the receiving side. Object implementations that invoke operations on other objects must be aware of the possibility of a non-local return. This includes invocation of ORB and IFR services. See also the Exceptions section.

Exception parameters are mapped as an Erlang record and accessed as such.

An object implementation that raises an exception will use the `corba:raise/1` function, passing the exception record as parameter.

### 6.9 Access to Attributes

Attributes are accessed through their access functions. An attribute implicitly defines the `_get` and `_set` operations. The `_get` operation is defined as a read-only attribute. These operations are handled in the same way as normal operations.

### 6.10 Typecode, Identity and Name Access Functions

As mentioned in a previous section, `struct`, `union` and `exception` types yield to record definitions and access code for that record. For `struct`, `union`, `exception`, `array` and `sequence` types, a special file is generated that holds access functions for `TypeCode`, `Identity` and `Name`. These functions are put in the file corresponding to the scope where they are defined :

- tc - returns the type code for the record.
- id - returns the identity of the record.
- name - returns the name of the record.

For example:

    // IDL
    module m {

      struct s {
        long x;
        long y;
       };

    };

Would result in the following code on file `m_s.erl`:

    -module(m_s).

    -include("m.hrl").

    -export([tc/0,id/0,name/0]).

    %% returns type code
    tc() -> {tk_struct,"IDL:m/s:1.0","s",[{"x",tk_long},{"y",tk_long}]}.

    %% returns id
    id() -> "IDL:m/s:1.0".

    %% returns name
    name() -> m_s.

### 6.11 Type Code Representation

Type Codes are used in `any` values. The table below corresponds to the table on page 12-11 in the OMG CORBA specification.

Type Code tuples
Type Code | Example
tk_null |
tk_void |
tk_short |
tk_long |
tk_longlong |
tk_ushort |
tk_ulong |
tk_ulonglong |
tk_float |
tk_double |
tk_boolean |
tk_char |
tk_wchar |
tk_octet |
tk_any |
tk_TypeCode |
tk_Principal |
{tk_objref, IFRId, Name} | {tk_objref, "IDL:M1\I1:1.0", "I1"}
{tk_struct, IFRId, Name, [{ElemName, ElemTC}]} | {tk_struct, "IDL:M1\S1:1.0", "S1", [{"a", tk_long}, {"b", tk_char}]}
{tk_union, IFRId, Name, DiscrTC, DefaultNr, [{Label, ElemName, ElemTC}]} Note: DefaultNr tells which of tuples in the case list that is default, or -1 if no default | {tk_union, "IDL:U1:1.0", "U1", tk_long, 1, [{1, "a", tk_long}, {default, "b", tk_char}]}
{tk_enum, IFRId, Name, [ElemName]} | {tk_enum, "IDL:E1:1.0", "E1", ["a1", "a2"]}
{tk_string, Length} | {tk_string, 5}
{tk_wstring, Length} | {tk_wstring, 7}
{tk_sequence, ElemTC, Length} | {tk_sequence, tk_long, 4}
{tk_array, ElemTC, Length} | {tk_array, tk_char, 9}
{tk_alias, IFRId, Name, TC} | {tk_alias, "IDL:T1:1.0", "T1", tk_short}
{tk_except, IFRId, Name, [{ElemName, ElemTC}]} | {tk_except, "IDL:Exc1:1.0", "Exc1", [{"a", tk_long}, {"b", {tk_string, 0}}]}

### 6.12 Scoped Names

Various scopes exist in OMG IDL. Modules, interfaces and types define scopes. However, Erlang has only two levels of scope; module and function:

- Function Scope:
  used for constants, operations and attributes.
- Erlang Module Scope:
  The Erlang module scope handles the remaining OMG IDL scopes.

#### 6.12.1 Syntax Specific Structures for Scoped Names

An Erlang module, corresponding to an IDL global name, is derived by converting occurencies of "::" to underscore, and eliminating the leading "::".

For example, an operation `op1` defined in interface `I1` which is defined in module `M1` would be written in IDL as `M1::I1::op1` and as `'M1_I1':op1` in Erlang, where `op1` is the function name and `'M1_I1'` is the name of the Erlang module.

#### 6.12.2 Files

Several files can be generated for each scope.

- An Erlang source code file (`.erl`) is generated for top level scope as well as the Erlang header file.
- An Erlang header file (`.hrl`) will be generated for each scope. The header file will contain record definitions for all `struct`, `union` and `exception` types in that scope.
- Modules that contain at least one constant definition, will produce Erlang source code files (`.erl`). That Erlang file will contain constant functions for that scope. Modules that contain no constant definitions are considered empty and no code will be produced for them, but only for their included modules/interfaces.
- Interfaces will produce Erlang source code files (`.erl`), this code will contain all operation stub code and implementation functions.
- In addition to the scope-related files, an Erlang source file will be generated for each definition of the types `struct`, `union` and `exception` (these are the types that will be represented in Erlang as records). This file will contain special access functions for that record.
- The top level scope will produce two files, one header file (`.hrl`) and one Erlang source file (`.erl`). These files are named as the IDL file, prefixed with `oe_`.

Example:

    // IDL, in the file "spec.idl"
    module m {

      struct s {
        long x;
        long y;
      };

      interface i {

        void foo( in s a, out short b );

     };

    };

This will produce the following files:

- `oe_spec.hrl` and `oe_spec.erl` for the top scope level.
- `m.hrl` for the module `m`.
- `m_i.hrl` and `m_i.erl` for the interface `i`.
- `m_s.erl` for the structure `s` in module `m`.

#### 6.12.3 A Mapping Example

This is a small example of a simple stack. There are two operations on the stack, push and pop. The example shows all generated files as well as conceptual usage of a stack object.

    // The source IDL file

    interface stack {
        exception overflow {};
        void push(in long val);
        long pop() raises (overflow);
    };

When this file is compiled it produces four files, two for the top scope and two for the stack interface scope. The generated Erlang code for the stack object server is shown below:

    -module(stack).
    -export([push/2, pop/1]).

    init(Env) ->
        stack_impl:init(Env).

    %% This is the stub code used by clients
    push(THIS, Val) ->
        corba:call(THIS, push, [Val]).

    pop(THIS) ->
        corba:call(THIS, pop, []).

    %% gen_server handle_calls
    handle_call({THIS, push, [Val]}, From, State) ->
        case catch stack_impl:push(State, Val) of
          {'EXCEPTION', E} ->
            {reply, {'EXCEPTION', E}, State};
          {reply, Reply, NewState} ->
            {reply, Reply, NewState}
        end;

    handle_call({THIS, pop, []}, From, State) ->
        case catch stack_impl:pop(State) of
          {'EXCEPTION, E} ->
            {reply, {'EXCEPTION', E}, State};
          {reply, Reply, NewState} ->
            {reply, Reply, NewState}
        end.

The Erlang code has been simplified but is conceptually correct. The generated `stack` module is the Erlang representation of the stack interface. Note that the variable `THIS` is the object reference and the variable `State` is the internal state of the object.

So far the example only deals with interfaces and call chains. It is now time to implement the stack. The example represents the stack as a simple list. The push operation then is just to add a value on to the front of the list and the pop operation is then to return the head of the list.

In this simple representation the internal state of the object becomes just a list. The initial value for the state is the empty list as shown in the `init/1` function below.

The implementation is put into a file called `stack_impl.erl`.

    -module(stack_impl).

    -include("stack.hrl").

    -export([push/2, pop/1, init/1]).

    init(_) ->
        {ok, []}.

    push(Stack, Val) ->
        {reply, ok, [Val | Stack]}.

    pop([Val | Stack]) ->
        {reply, Val, Stack};

    pop([]) ->
        corba:raise(#stack_overflow{}).

The stack object can be accessed client code. This example shows a typical `add` function from a calculator class:

    -module(calc_impl).

    -export([add/1]).

    add({Stack, Memory}) ->
        Sum = stack:pop(Stack)+stack:pop(Stack),
        stack:push(Stack, Sum),
        {ok, {Stack, Memory}}.

Note that the `Stack` variable above is an object reference and not the internal state of the stack.

Copyright © 1991-2002 Ericsson Utvecklings AB