The purpose of OMG IDL, Interface Definition Language, mapping is to act as translator between platforms and languages. An IDL specification is supposed to describe data types, object types etc.
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.
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:
To begin with, we should decide which type of objects (i.e. servers) we
need and if two, or more, should export the same functionality. Let us
assume that we want to create a system for DB (database) access for different
kind of users. For example, anyone with a valid password may extract
data, but only a few may update the DB. Usually, an application
is defined within a module
, and all global datatypes are defined
on the top-level. To begin with we create a module and the interfaces we
need:
// DB IDL #ifndef _DB_IDL_ #define _DB_IDL_ // A module is simply a container module DB { // An interface maps to a CORBA::Object. interface CommonUser { }; // Inherit the Consumer interface interface Administrator : CommonUser { }; interface Access { }; }; #endif
Since the Administrator
should be able to do the same things as the
CommonUser
, the previous inherits from the latter. The Access
interface will grant access to the DB.
Now we are ready to define the functionality and data types we need. But, this
requires that we know a little bit more about the OMG IDL.
![]() |
The OMG defines a set of reserved case insensitive key-words, which may NOT be used as identifiers (e.g. module name). For more information, see Reserved Compiler Names and Keywords |
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 type | Erlang type | Note |
float | Erlang float | |
double | Erlang float | value range not supported |
short | Erlang integer | -2^15 .. 2^15-1 |
unsigned short | Erlang integer | 0 .. 2^16-1 |
long | Erlang integer | -2^31 .. 2^31-1 |
unsigned long | Erlang integer | 0 .. 2^32-1 |
long long | Erlang integer | -2^63 .. 2^63-1 |
unsigned long long | Erlang integer | 0 .. 2^64-1 |
char | Erlang integer | ISO-8859-1 |
wchar | Erlang integer | UTF-16 (ISO-10646-1:1993) |
boolean | Erlang atom | true/false |
octet | Erlang integer | |
any | Erlang record | #any{typecode, value} |
long double | Not supported | |
Object | Orber object reference | Internal Representation |
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
.
Constructed types all have native mappings as shown in the table below.
Type | IDL code | Maps to | Erlang code |
string |
typedef string S; void op(in S a); |
Erlang string | ok = op(Obj, "Hello World"), |
wstring |
typedef wstring S; void op(in S a); |
Erlang list of Integers | ok = op(Obj, "Hello World"), |
sequence |
typedef sequence <long, 3> S; void op(in S a); |
Erlang list | ok = op(Obj, [1, 2, 3]), |
array |
typedef string S[2]; void op(in S a); |
Erlang tuple | ok = op(Obj, {"one", "two"}), |
fixed |
typedef fixed<3,2> myFixed; void op(in myFixed a); |
Erlang tuple |
MF = fixed:create(3, 2, 314), ok = op(Obj, MF), |
A string
consists of all possible 8-bit quantities except null.
Most ORB:s uses, including Orber, the character set Latin-1 (ISO-8859-1).
The wstring
type is represented as a list of integers, where
each integer represents a wide character. In this case Orber uses, as
most other ORB:s, the UTF-16 (ISO-10646-1:1993) character set.
When defining a a string or wstring they can be of limited length or null terminated:
// Null terminated typedef string myString; typedef wstring myWString; // Maximum length 10 typedef string<10> myString10; typedef wstring<10> myWString10;
If we want to define a char/string or wchar/wstring constant, we can use octal (\OOO - one, two or three octal digits), hexadecimal (\xHH - one or two hexadecimal digits) and unicode (\uHHHH - one, two, three or four hexadecimal digits.) representation as well. For example:
const string SwedensBestSoccerTeam = "\101" "\x49" "\u004B"; const wstring SwedensBestHockeyTeam = L"\101\x49\u004B"; const char aChar = '\u004B'; const wchar aWchar = L'\u004C';
Naturally, we can use "Erlang"
, L"Rocks"
, 'A'
and L'A'
as well.
A sequence can be defined to be of a maximum length or unbounded, and may contain Basic and Template types and scoped names:
typedef sequence <short, 1> aShortSequence; typedef sequence <long> aLongSequence; typedef sequence <aLongSequence> anEvenLongerSequence;
Arrays are multidimensional, fixed-size arrays. The indices is language mapping specific, which is why one should not pass them as arguments to another ORB.
typedef long myMatrix[2][3];
A Fixed Point literal consists of an integer part (decimal digits),
decimal point and a fraction part (decimal digits),
followed by a D
or d
. Either the integer part or the
fraction part may be missing; the decimal point may be missing,
but not d/D. The integer part must be a positive integer less than 32.
The Fraction part must be a positive integer less than or equal to
the Integer part.
const fixed myFixed1 = 3.14D; const fixed myFixed2 = .14D; const fixed myFixed3 = 0.14D; const fixed myFixed4 = 3.D; const fixed myFixed5 = 3D;
It is also possible to use unary (+-) and binary (+-*/) operators:
const fixed myFixed6 = 3D + 0.14D; const fixed myFixed7 = -3.14D;
The Fixed Point examples above are, so called, anonymous definitions. In later CORBA specifications these have been deprecated as function parameters or return values. Hence, we strongly recommend that you do not use them. Instead, you should use:
typedef fixed<5,3> myFixed53; const myFixed53 myFixed53constant = 03.140d; typedef fixed<3,2> myFixed32; const myFixed32 myFixed32constant = 3.14d; myFixed53 foo(in myFixed32 MF); // OK void bar(in fixed<5,3> MF); // Illegal
For more information, see Fixed in Orber's Reference Manual.
Now we continue to work on our IDL specification. To begin with, we want
to limit the size of the logon parameters (Id and password). Since the
UserID
and Password
paremeters, only will be used when
invoking operations on the Access
interface, we may choose to define
them within the scope that interface. To keep it simple our DB will contain
employee information. Hence, as the DB key we choose an integer
(EmployeeNo
).
// DB IDL #ifndef _DB_IDL_ #define _DB_IDL_ module DB { typedef unsigned long EmployeeNo; interface CommonUser { any lookup(in EmployeeNo ENo); }; interface Administrator : CommonUser { void delete(in EmployeeNo ENo); }; interface Access { typedef string<10> UserID; typedef string<10> Password; CommonUser logon(in UserID ID, in Password PW); }; }; #endif
But what should, for example, the lookup
operation return? One option
is to use the any
data type. But, depending on what kind of data it
encapsulates, this datatype can be rather expensive to use. We might find a
solution to our problems among the Constructed
IDL types.
Constructed types all have native mappings as shown in the table below.
Type | IDL code | Maps to | Erlang code |
struct |
struct myStruct { long a; short b; }; void op(in myStruct a); |
Erlang record | ok = op(Obj, #'myStruct'{a=300, b=127}), |
union |
union myUnion switch(long) { case 1: long a; }; void op(in myUnion a); |
Erlang record | ok = op(Obj, #'myUnion'{label=1, value=66}), |
enum |
enum myEnum {one, two}; void op(in myEnum a); |
Erlang atom | ok = op(Obj, one), |
A struct
may have Basic, Template, Scoped Names and Constructed
types as members.
The maximum number of identifiers which may defined in an enumeration is 2³². The order in which the identifiers are named in the specification of an enumeration defines the relative order of the identifiers.
A union
may consist of:
default
case; may appear at
most once.
A case label must match the defined type of the discriminator, and may only contain a default case if the values given in the non-default labels do not cover the entire range of the union's discriminant type. For example:
// Illegal default; all cases covered by // non-default cases. union BooleanUnion switch(boolean) { case TRUE: long TrueValue; case FALSE: long FalseValue; default: long DefaultValue; }; // OK union BooleanUnion2 switch(boolean) { case TRUE: long TrueValue; default: long DefaultValue; };
It is not necessary to list all possible values of the union discriminator in the body. Hence, the value of a union is the value of the discriminator and, in given order, one of the following:
undefined
in the value field when receiving a union from an external
ORB.
The above can be summed up to:
// If the discriminator equals 1 or 2 the value // is a long. Otherwise, the atom undefined. union LongUnion switch(long) { case 1: case 2: long TrueValue; }; // If the discriminator equals 1 or 2 the value // is a long. Otherwise, a boolean. union LongUnion2 switch(long) { case 1: case 2: long TrueValue; default: boolean DefaultValue; };
![]() |
Every field in, for example, a struct must be initiated. Otherwise
it will be set to the atom |
Now we can continue to work on our IDL specification. To begin with, we should
determine the return value of the lookup
operation. Since the any
type can be rather expensive we can use a struct
or a union
instead.
If we intend to return the same information about a employee every time we can
use a struct. Let us assume that the DB contains the name, address, employee
number and department.
// DB IDL #ifndef _DB_IDL_ #define _DB_IDL_ module DB { typedef unsigned long EmployeeNo; enum Department {Department1, Department2}; struct employee { EmployeeNo No; string Name; string Address; Department Dpt; }; typedef employee EmployeeData; interface CommonUser { EmployeeData lookup(in EmployeeNo ENo); }; interface Administrator : CommonUser { void delete(in EmployeeNo ENo); }; interface Access { typedef string<10> UserID; typedef string<10> Password; // Since Administrator inherits from CommonUser // the returned Object can be of either type. CommonUser logon(in UserID ID, in Password PW); }; }; #endif
We can also define exceptions (i.e. not system exception) thrown by each interface. Since exceptions are thoroughly described in the chapter System and User Defined Exceptions, we choose not to. Hence, we are now ready to compile our IDL-file by invoking:
$ erlc DB.idl
or:
$ erl Erlang (BEAM) emulator version 5.1.1 [threads:0] Eshell V5.1.1 (abort with ^G) 1> ic:gen('DB'). ok 2> halt().
The next step is to implement our servers. But, to be able to do that, we need to know how we can access data type definitions. For example, since a struct is mapped to an Erlang record we must include an hrl-file in our callback module.
Within a scope all identifiers must be unique. The following kinds of definitions form scopes in the OMG IDL:
For example, since enumerants do not form a scope, the following IDL code is not valid:
module MyModule { // 'two' is not unique enum MyEnum {one, two}; enum MyOtherEnum {two, three}; };
But, since Erlang only has two levels of scope, module and function, the OMG IDL scope is mapped as follows:
An Erlang module, corresponding to an IDL global name, is derived by
converting occurencies of "::" to underscore, and eliminating
the leading "::". Hence, accessing MyEnum
from another module, one
use MyModule::MyEnum
For example, an operation foo
defined in interface I
, which
is defined in module M
, would be written in IDL as M::I::foo
and as 'M_I':foo
in Erlang - foo
is the function
name and 'M_I'
is the name of the Erlang module. Applying this
knowledge to a stripped version of the DB.idl gives:
// DB IDL #ifndef _DB_IDL_ #define _DB_IDL_ // ++ topmost scope ++ // IC generates oe_XX.erl and oe_XX.hrl. // XX is equal to the name of the IDL-file. // Tips: create one IDL-file for each top module // and give the file the same name (DB.idl). // The oe_XX.erl module is used to register data // in the IFR. module DB { // ++ Module scope ++ // To access 'EmployeeNo' from another scope, use: // DB::EmployeeNo, DB::Access etc. typedef unsigned long EmployeeNo; enum Department {Department1, Department2}; // Definitions of this struct is contained in: // DB.hrl // Access functions exported by: // DB_employee.erl struct employee { ... CUT ... }; typedef employee EmployeeData; ... CUT ... // If this interface should inherit an interface // in another module (e.g. OtherModule) use: // interface Access : OtherModule::OtherInterface interface Access { // ++ interface scope ++ // Types within this scope is accessible via: // DB::Access::UserID // The Stub/Skeleton for this interface is // placed in the module: // DB_Access.erl typedef string<10> UserID; typedef string<10> Password; // Since Administrator inherits from CommonUser // the returned Object can be of either type. // This operation is exported from: // DB_Access.erl CommonUser logon(in UserID ID, in Password PW); }; }; #endif
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. For example, the following
definition would generate two structures namned x_y_z
.
module x { struct y_z { ... }; interface y { struct z { ... }; }; };
Several files can be generated for each scope.
.erl
) is generated
for top level scope as well as the 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.
.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.
.erl
),
this code will contain all operation stub code and implementation
functions.
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.
.hrl
) and one Erlang source file (.erl
).
These files are named as the IDL file, prefixed with oe_
.
After compiling DB.idl, the following files have been generated:
oe_DB.hrl
and oe_DB.erl
for the top scope level.
DB.hrl
for the module DB
.
DB_Access.hrl
and DB_Access.erl
for the interface
DB_Access
.
DB_CommonUser.hrl
and DB_CommonUser.erl
for the interface
DB_CommonUser
.
DB_Administrator.hrl
and DB_Administrator.erl
for the interface
DB_Administrator
.
DB_employee.erl
for the structure employee
in module
DB
.
Since the employee
struct is defined in the top level scope,
the Erlang record definition is found in DB.hrl
. IC also generates
stubs/skeletons (e.g. DB_CommonUser.erl
) and access functions for
some datatypes (e.g. DB_employee.erl
). How the stubs/skeletons are
used is thoroughly described in
Stubs/Skeletons and
Module_Interface.
As mentioned in a previous section, struct
, union
and
exception
types yield 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. For example, the module DB_employee.erl
,
representing the employee
struct, exports the following functions:
"IDL:DB/employee:1.0"
, but
if the struct was defined in the scope of CommonUser
,
the result would be "IDL:DB/CommonUser/employee:1.0"
.
However, the user usually do not need to know the Id, just
which Erlang module contains the correct Id.
employee
struct name is "DB_employee"
.
Type Codes are, for example, used in Any values.
Hence, we can encapsulate the employee
struct in an any
type by:
%% Erlang code .... AnEmployee = #'DB_employee'{'No' = 1, 'Name' = "Adam Ivan Kendall", 'Address' = "Rasunda, Solna", 'Dpt' = 'Department1'}, EmployeeTC = 'DB_employee':tc(), EmployeeAny = any:create(EmployeeTC, AnEmployee), ....
For more information, see the Type Code listing.
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:
// m.idl module m { const float pi = 3.14; interface i { const float pi = 3.1415; }; };
Since the two constants are defined in different scopes, the IDL code
above is valid, but not necessarily a good approach. After compiling
m.idl
, the constant definitions can be extracted by invoking:
$ erlc m.idl $ erlc m.erl $ erl Erlang (BEAM) emulator version 5.1.1 [threads:0] Eshell V5.1.1 (abort with ^G) 1> m:pi(). 3.14000 2> m_i:pi(). 3.14150 3> halt().
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.
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.
Attributes are accessed through their access functions. An
attribute implicitly defines the _get
and _set
operations. These operations are handled in the same way as
normal operations. The _get
operation is defined as a readonly
attribute.
readonly attribute long RAttribute; attribute long RWAttribute;
The RAttribute
requires that you implement, in your call-back module,
_get_RAttribute
. For the RWAttribute
it is necessary to implement
_get_RWAttribute
and _set_RWAttribute
.
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). |
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 module m { interface i { readonly attribute long RAttribute; attribute long RWAttribute; long foo(in short a); long bar(in char c, inout string s, out long count); void baz(out long Id); }; };
Is used in Erlang as :
%% Erlang code .... Obj = ... %% get object reference RAttr = m_i:'_get_RAttribute'(Obj), RWAttr = m_i:'_get_RWAttribute'(Obj), ok = m_i:'_set_RWAttribute'(Obj, Long), R1 = m_i:foo(Obj, 55), {R2, S, Count} = m_i:bar(Obj, $a, "hello"), ....
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 ok
, Orber's
representation of the IDL-type void
, must be returned by
baz
and '_set_RWAttribute'
.
These operations can be implemented in the call-back module as:
'_set_RWAttribute'(State, Long) -> {reply, ok, State}. '_get_RWAttribute'(State) -> {reply, Long, State}. '_get_RAttribute'(State) -> {reply, Long, State}. foo(State, AShort) -> {reply, ALong, State}. bar(State, AShort, AString) -> {reply, {ALong, "MyString", ALong}, State}. baz(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.
![]() |
A function can also be defined to be |
Now we are ready to implement the call-back modules. There are three modules we must create:
An easy way to accomplish that, is to use the IC backend erl_template
,
which will generate a complete call-back module. One should also add
the same compile options, for example this
or from
,
used when generating the stub/skeleton modules:
$> erlc +"{be,erl_template}" DB.idl
We begin with implementing the DB_Access_impl.erl
module, which,
if we used erl_template
, will look like the following. All we need
to do is to add the logic to the logon
operation.
%%---------------------------------------------------------------------- %% <LICENSE> %% %% $Id$ %% %%---------------------------------------------------------------------- %% Module : DB_Access_impl.erl %% %% Source : /home/user/example/DB.idl %% %% Description : %% %% Creation date: 2005-05-20 %% %%---------------------------------------------------------------------- -module('DB_Access_impl'). -export([logon/3]). %%---------------------------------------------------------------------- %% Internal Exports %%---------------------------------------------------------------------- -export([init/1, terminate/2, code_change/3, handle_info/2]). %%---------------------------------------------------------------------- %% Include Files %%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Macros %%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Records %%---------------------------------------------------------------------- -record(state, {}). %%====================================================================== %% API Functions %%====================================================================== %%---------------------------------------------------------------------- %% Function : logon/3 %% Arguments : State - term() %% ID = String() %% PW = String() %% Returns : ReturnValue = OE_Reply %% OE_Reply = Object_Ref() %% Raises : %% Description: %%---------------------------------------------------------------------- logon(State, ID, PW) -> %% Check if the ID/PW is valid and what %% type of user it is (Common or Administrator). OE_Reply = case check_user(ID, PW) of {ok, administrator} -> 'DB_Administrator':oe_create(); {ok, common} -> 'DB_CommonUser':oe_create(); error -> %% Here we should throw an exception corba:raise(....) end, {reply, OE_Reply, State}. %%====================================================================== %% Internal Functions %%====================================================================== %%---------------------------------------------------------------------- %% Function : init/1 %% Arguments : Env = term() %% Returns : {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %% Raises : - %% Description: Initiates the server %%---------------------------------------------------------------------- init(_Env) -> {ok, #state{}}. %%---------------------------------------------------------------------- %% Function : terminate/2 %% Arguments : Reason = normal | shutdown | term() %% State = term() %% Returns : ok %% Raises : - %% Description: Invoked when the object is terminating. %%---------------------------------------------------------------------- terminate(_Reason, _State) -> ok. %%---------------------------------------------------------------------- %% Function : code_change/3 %% Arguments : OldVsn = undefined | term() %% State = NewState = term() %% Extra = term() %% Returns : {ok, NewState} %% Raises : - %% Description: Invoked when the object should update its internal state %% due to code replacement. %%---------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. %%---------------------------------------------------------------------- %% Function : handle_info/2 %% Arguments : Info = normal | shutdown | term() %% State = NewState = term() %% Returns : {noreply, NewState} | %% {noreply, NewState, Timeout} | %% {stop, Reason, NewState} %% Raises : - %% Description: Invoked when, for example, the server traps exits. %%---------------------------------------------------------------------- handle_info(_Info, State) -> {noreply, State}.
Since DB_Administrator
inherits from DB_CommonUser
,
we must implement delete
in the DB_Administrator_impl.erl
module, and lookup
in DB_Administrator_impl.erl
and
DB_CommonUser_impl.erl
. But wait, is that really necessary? Actually,
it is not. We simple use the IC compile option impl:
$ erlc +'{{impl, "DB::CommonUser"}, "DBUser_impl"}' +'{{impl, "DB::Administrator"}, "DBUser_impl"}' DB.idl $ erlc *.erl
Instead of creating, and not the least, maintaining two call-back modules,
we only have to deal with DBUser_impl.erl
. If we generated the
templates, we simply rename DB_Administrator_impl.erl
to
DBUser_impl.erl
. See also the
Exceptions chapter.
In the following example, only the implementation of the API functios
are shown:
%%====================================================================== %% API Functions %%====================================================================== %%---------------------------------------------------------------------- %% Function : delete/2 %% Arguments : State - term() %% ENo = unsigned_Long() %% Returns : ReturnValue = ok %% Raises : %% Description: %%---------------------------------------------------------------------- delete(State, ENo) -> %% How we access the DB, for example mnesia, is not shown here. case delete_employee(No) of ok -> {reply, ok, State}; error -> %% Here we should throw an exception if %% there is no match. corba:raise(....) end. %%---------------------------------------------------------------------- %% Function : lookup/2 %% Arguments : State - term() %% ENo = unsigned_Long() %% Returns : ReturnValue = OE_Reply %% OE_Reply = #'DB_employee'{No,Name,Address,Dpt} %% No = unsigned_Long() %% Name = String() %% Address = String() %% Dpt = Department %% Department = 'Department1' | 'Department2' %% Raises : %% Description: %%---------------------------------------------------------------------- lookup(State, ENo) -> %% How we access the DB, for example mnesia, is not shown here. case lookup_employee(ENo) of %% We assume that we receive a 'DB_employee' struct {ok, Employee} -> OE_Reply = Employee, {reply, OE_Reply, State}; error -> %% Here we should throw an exception if %% there is no match. corba:raise(....) end.
After you have compiled both call-back modules, and implemented the missing functionality (e.g. lookup_employee/1), we can test our application:
%% Erlang code .... %% Create an Access object Acc = 'DB_Access':oe_create(), %% Login is Common user and Administrator Adm = 'DB_Access':logon(A, "admin", "pw"), Com = 'DB_Access':logon(A, "comm", "pw"), %% Lookup existing employee Employee = 'DB_Administrator':lookup(Adm, 1), Employee = 'DB_CommonUser':lookup(Adm, 1), %% If we try the same using the DB_CommonUser interface %% it result in an exit since that operation is not exported. {'EXIT', _} = (catch 'DB_CommonUser':delete(Adm, 1)), %% Try to delete the employee via the CommonUser Object {'EXCEPTION', _} = (catch 'DB_Administrator':delete(Com, 1)), %% Invoke delete operation on the Administrator object ok = 'DB_Administrator':delete(Adm, 1), ....
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.
The OMG defines a set of reserved words, shown below, for use as keywords. These may not be used as, for example, identifiers. The keywords which are not in bold face was introduced in the OMG CORBA-3.0 specification.
abstract | exception | inout | provides | truncatable |
any | emits | interface | public | typedef |
attribute | enum | local | publishes | typeid |
boolean | eventtype | long | raises | typeprefix |
case | factory | module | readonly | unsigned |
char | FALSE | multiple | setraises | union |
component | finder | native | sequence | uses |
const | fixed | Object | short | ValueBase |
consumes | float | octet | string | valuetype |
context | getraises | oneway | struct | void |
custom | home | out | supports | wchar |
default | import | primarykey | switch | wstring |
double | in | private | TRUE |
The keywords listed above must be written exactly as shown. Any usage
of identifiers that collide with a keyword is illegal. For example,
long is a valid keyword; Long and LONG are
illegal as keywords and identifiers. But, since the OMG must be able
to expand the IDL grammar, it is possible to use Escaped
Identifiers. For example, it is not unlikely that native
have been used in IDL-specifications as identifiers. One option is to
change all occurances to myNative
. Usually, it is necessary
to change programming language code that depends upon that IDL as well.
Since Escaped Identifiers just disable type checking (i.e. if it is a reserved
word or not) and leaves everything else unchanged, it is only necessary to
update the IDL-specification. To escape an identifier, simply prefix it
with _. The following IDL-code is illegal:
typedef string native; interface i { void foo(in native Arg); }; };
With Escaped Identifiers the code will look like:
typedef string _native; interface i { void foo(in _native Arg); }; };
Type Codes are used in any
values. To avoid mistakes, you should
use access functions exported by the Data Types modules
(e.g. struct, union etc) or the orber_tc
module.
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_fixed, Digits, Scale} | {tk_fixed, 3, 2} |
{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}}]} |