2 Erlang IDL language mapping
2.1 Introduction
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. This chapter describes the mapping of OMG IDL constructs to the Erlang programming language.
All language mappings have roughly the same structure, they all must define the expressing in the language:
- All OMG IDL basic types
- All OMG IDL 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 result
- 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, and so forth.
A complete language mapping will allow the programmer to have access to all ORB functionality in a way that is convenient for the particular programming language.
2.2 Mapping peculiarities for Erlang
This section describes those areas in the mapping that are not immediately native to Erlang, i.e. areas where an Erlang programmer would have done different.
2.2.1 Names reserved by the compiler
The IDL compiler reserves all identifiers starting with
OE_
andoe_
for internal use. Identifiers starting with underscore is illegal in IDL.2.2.2 Scoped names
There are many scopes in OMG IDL, modules, interfaces and types define scopes. Erlang has only two levels of scope, module and function. The Erlang function scope is used for constants, operations and attributes. The Erlang module scope must then handle the rest of OMG IDL scopes. Erlang module corresponding to an OMG IDL global name is derived by converting occurencies of "::" to underscore, and eliminating the leading "::". So for example an operation
op1
defined in interfaceI1
which is defined in moduleM1
would be written asM1::I1::op1
in IDL and as'M1_I1':op1
in Erlang. Whereop1
is the function name and'M1_I1'
is the name of the Erlang module.
If underscores are used in IDL names it can lead to ambiguities due to the name mapping described above. therefor it is advisable to avoid the use of underscores in identifiers.
2.2.3 Files
Several files can be generated for each scope :
- Two files will be generated for those definitions at top level scope. One of the files is an Erlang header file (
.hrl
), and the other file is an Erlang source code file (.erl
).
- An Erlang header file (
.hrl
) will be generated for each scope. The header file will contain record definitions for allstruct
,union
andexception
types in that scope.
- Modules that contain at least one constant definition, will yield to 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 yield to Erlang source code files (
.erl
). which will contain all operation stub and implementation functions are generated.
- In addition to the scope related files an erlang source file will be generated for each definition of the types
struct
,union
andexception
(these are the types that will be represented in Erlang as records). This file will contain special access functions for that record.
For 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 ); }; };Will produce the files :
oe_spec.hrl
andoe_spec.erl
for the top scope level.
m.hrl
for the modulem
.
m_i.hrl
andm_i.erl
for the interfacei
.
m_s.erl
for the structures
in modulem
.
2.3 Basic OMG IDL types
The mapping of basic types is straight forward. Note that the OMG IDL double type is mapped to an Erlang float which does not support the full double value range.
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 unsigned long Erlang integer char Erlang integer boolean Erlang atoms true or false octet Erlang integer any Erlang record #any{typecode, value} Object Orber object reference void Erlang atom ok OMG IDL basic types The any value is written as a record with the fields typecode which contains the tk representation of the type, see also the tk value table, and the value field itself.
Functions with return type
void
shall return the atom ok.2.4 Constructed OMG IDL types
Constructed types all have native mappings as shown in the table below.
string Erlang string struct Erlang record union Erlang record enum Erlang atom sequence Erlang list array Erlang tuple OMG IDL constructed types Below are examples of values of constructed types.
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"}), Typical values 2.5 References to constants
Constants are generated as Erlang functions, and access is by a normal function call. The functions are put in the file corresponding to the scope where it is defined. There need be no object started for a constant, the functions work without it.
For example
// IDL module M { const long c1 = 99; };Would result in the following conceptual code:
module('M'). -export([c1/0]). c1() -> 99.2.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.
2.7 Invocations of operations
Operation invocation is through function call. The first parameter to the function shall be the object reference and then all
in
andinout
parameters follows in the same order as specified in the IDL specification. The result is returned as return value unless the function hasinout
orout
parameters specified in which case a tuple of the return value followed by the parameters shall be returned.For example:
// IDL interface i1 { long op1(in short a); long op2(in char c, inout string s, out long count); };Is used in Erlang as :
%% Erlang f() -> ... Obj = ... %% get object reference R1 = i1:op1(Obj, 55), {R2, S, Count} = i1:op2(Obj, $a, "hello"), ...Note how the
inout
parameter is passed and returned. There is no way to use a single occurence of a variable for this in Erlang.2.7.1 Operation implementation
A standard Erlang
gen_server
behaviour is used for object implementation. Thegen_server
state is then used as the object internal state. The object is then implemented by implementing its operations and attribute operations. These functions shall have the internal state as their first parameter followed by anyin
andinout
parameters. Do not confuse the object internal state with its object reference. The object internal state is an Erlang term which format is completely left to the user.The special function
init/1
is called at object start time and is expected to return the tuple{ok, InitialInternalState}
.2.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 possibilty of a non-local return. This includes invocation of ORB and IFR services.
Exception parameters are mapped as an Erlang record and accessed as such.
An object implementation that raises an exception shall use the
corba:raise/1
function, passing the exception record as parameter.2.9 Access to attributes
Attributes are accessed through their access functions. An attribute implicitly define a
_get
and a_set
operation. Only the_get
operation is defined for a read only attribute. These operations are handled in the same way as normal operations.2.10 Record access functions.
As mentioned in a previous section,
struct
,union
andexception
types yield to record definitions and access code for that record. The functions are put in the file corresponding to the scope where it is defined. Three functions are accessible for each record :
- 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.2.11 Signatures for operations defined by the ORB
2.12 TK type representation
Type Codes are used in
any
values. The table below corresponds to the table on page 12-11 in the OMG CORBA specification.
TCKind Example tk_null tk_void tk_short tk_long tk_ushort tk_ulong tk_float tk_double tk_boolean tk_char 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_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}}]} Type Code tuples 2.13 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 variableTHIS
is the object reference and the variableState
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 is then used by som 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.