All Manuals > Developing Component Software with CORBA® > Appendix A: Common Lisp IDL Binding

A.4 Mapping in more detail

This section describes the mapping of IDL into the Lisp language. In most cases examples of the mapping are provided. It should be noted that the examples are code fragments that try to illustrate only the language construct being described.

A.4.1 Mapping concepts

By an IDL entity we mean an element defined in some IDL file. For example, consider the code fragment:

module A {
  interface B {
    void op1(in long bar);
  };
}

The IDL entities are the module named A, the interface named B, the operation named op1, the formal parameter named bar, and the primitive data types void and long.

Our mapping will associate a corresponding Lisp entity to each IDL entity declared in a an IDL specification. The Lisp entity corresponding to a given IDL entity will be said to be generated from the IDL entity.

If the IDL entity has a name, then the corresponding Lisp entity will also have a name. Whereas IDL entities are named by strings (in other words, identifiers), Lisp entities are named by symbols.

This chapter specifies, for each IDL construct, the Lisp entity, and the name of that entity, that is generated by the mapping.

A.4.2 Semantics of type mapping

The statement that an IDL type I is mapped to a Lisp type L indicates that if V is a Lisp value whose corresponding IDL type is I, then the consequences are not specified if the value of V is not a member of the type L. For example, if V is passed as a parameter to an IDL operation or if V is returned from an IDL operation, then a conforming implementation may reasonably perform any of the following actions if V is not of the type L.

A.4.3 Mapping for basic types

The following table shows the basic mapping. The first column contains the IDL name of the IDL type to be mapped. Each IDL type denotes a set of IDL abstract values.

The set of values denoted by an entry in the first column is mapped, under the mapping described in this document, to a set of Lisp values. That set of Lisp values is described in two ways:

IDL TypeName of Lisp TypeLisp Type Specifier

boolean

corba:boolean

boolean

char

corba:char

character

octet

corba:octet

(unsigned-byte 8)

string

corba:string

string

short

corba:short

(signed-byte 16)

unsigned short

corba:ushort

(unsigned-byte 16)

long

corba:long

(signed-byte 32)

unsigned long

corba:ulong

(unsigned-byte 32)

float

corba:float

see text

double

corba:double

see text

For example:

(typep -3 'corba:short)
> T
(typep "A string" 'corba:string)
> T
A.4.3.1 boolean

The IDL boolean constants TRUE and FALSE are mapped to the corresponding Lisp boolean literals t and nil. The type specifier corba:boolean specifies this type.

A.4.3.2 char

IDL char maps to the Lisp type character. The type specifier corba:char specifies this type.

For example:

(typep #\x corba:char)
> T
(typep "x" 'corba:char)
> nil
A.4.3.3 octet

The IDL type octet, an 8-bit quantity, is mapped as an unsigned quantity to the type corba:octet. The type specifier corba:octet denotes the set of integers between 0 and 255, inclusive. This set can also be denoted by the type specifier (unsigned-byte 8).

For example:

(typep 255 'corba:octet)
> T
(typep -1 'corba:octet)
> nil
A.4.3.4 string

The IDL string, both bounded and unbounded variants, are mapped to corba:string. Range checking for characters in the string as well as bounds checking of the string shall be done at marshal time. The type specifier corba:string denotes the set of Lisp strings.

For example:

(typep "A string" 'corba:string)
> T
(typep nil 'corba:string)
> nil
A.4.3.5 Integer types

The integer types each map to the Lisp integer type. Each IDL integer type has a corresponding type specifier that denotes the range of integers to which it corresponds.

The names of the type specifiers are corba:long, corba:short, corba:ulong, and corba:ushort.

A.4.3.6 Floating point types

The floating point types float and double map to Lisp types named corba:float and corba:double, respectively. These types must be subtypes of the type real. They must allow representation of all numbers specified by the corresponding CORBA types.

A.4.4 Introduction to named types

We now discuss the mapping of types that are named. We begin with a discussion of terminological issues.

Notation for naming can be confusing, so some care is needed. Our specification is not formally rigorous, but we have tried to illustrate enough points with examples so that situations likely to arise in practice can be handled.

A.4.4.1 IDL naming terminology

By "the IDL name of an IDL entity", we mean the string that is the simple name of that entity. An IDL entity can be declared at the top-level or nested inside some other IDL entity. We say that the outer IDL entity encloses the inner one. We will sometimes elide the quotation marks in describing the names of IDL (and other entities) when no confusion is likely to result.

Here is an IDL example:

module A{
  interface B{
    struct c {long foo;};};}

The name of the struct is the string c. The name of the interface is the string B. The name of the module is the string A. The name of the struct member is the string foo. The innermost enclosing IDL entity of the struct is the interface named B. The innermost enclosing module of the struct is the module named A.

A.4.4.2 Lisp naming terminology

In Common Lisp, the name of a symbol is a string used to identify the symbol. Packages are collections of symbols. A symbol has a home package, which also has a name. A package can be named by a symbol or a string. We sometimes loosely say "the package x" when we mean "the package named by x".

A package may have nicknames, and we will consider that the nicknames of a package name the package. Unless otherwise stated, we will assume that distinct package names refer to distinct packages.

The notation for symbols consists of three concatenated parts: the name of the home package of the symbol, followed by the character ":", followed by the name of the symbol. Case is not significant when this notation is used. Thus, all symbols generated by this mapping are external symbols of their home package.

A symbol can name a function, a package, a class, a type, a slot, or a variable. These namespaces are disjoint. All alphabetic characters in the names of symbols used in this document are upper-case unless otherwise stated.

Thus, the names notated here are implicitly converted to uppercase when they name a symbol. For example, when we write the symbol named
hello-goodbye or the symbol hello-goodbye, we actually mean the symbol whose name is the string HELLO-GOODBYE.

A.4.5 Distinguished packages

This document will refer to two kinds of packages:

The first kind of package consists of these three distinct packages: the root package, the corba package, and the operation package.

The names of these packages are described below. The name of the root package is the string "OMG.ORG/ROOT". The name of the corba package is "OMG.ORG/CORBA". The name of the operation package is the string "OMG.ORG/OPERATION".

The precise semantics of these three packages is described below. Informally, the root package is the package in which Common Lisp names corresponding to IDL definitions not contained in a top-level module are interned. The corba package is the package in which Common Lisp names corresponding to IDL definitions and pseudo-IDL definitions in the CORBA module are interned. The operation package is the package into which names of Common Lisp functions corresponding to IDL operations are interned.

In addition, this specification makes use of the standard Common Lisp packages named KEYWORD and COMMON-LISP.

A.4.5.1 Nicknames for distinguished packages

An implementation is expected to support the addition of nicknames for a package via the standard Common Lisp nicknames facility. An ORB should support the following default nicknames:

This document will use these nicknames without comment.

A.4.6 Scoped names and scoped symbols

Many of the Common Lisp entities we consider will be named according to the scoped naming convention described in this section. In particular, the following entities will be mapped according to this naming convention:

A scoped symbol will be associated with the IDL entity, and it is this scoped symbol that names the Lisp value generated by the given IDL entity.

A.4.6.1 Definitions

For any named IDL entity I there is a Lisp symbol S called the scoped symbol of I. The scoping separator is the string "/".

If I is a top-level module, then the name of S is the name of I.

If I is a module nested within another module J, then the name of S is the concatenation of the name of the scoped symbol of J, the scoping separator, and the name of I. The home package of the scoped symbol of a module is :keyword.

Suppose I is a named IDL entity that is not a module. The name of the scoping symbol S of I is determined as follows. If the declaration of I is enclosed inside another IDL entity J that is not a module, then the name of S is the concatenation of the name of the scoping symbol for J, the scoping separator, and the name of I. Otherwise the name of S is the name of I.

If I is enclosed in a module M, then the home package of S is named by the scoped symbol for M. Otherwise the home package for S is the root package.

A.4.6.2 Examples of scoping symbols

First we consider a simple example:

module a { interface foo {};}

The scoped symbol of the module is :a. Thus, the home package of this symbol is :keyword and the name of the symbol is the string A. The scoped symbol of the interface is the symbol a:foo. Thus, the name of the symbol is the string FOO, and the home package of the symbol is the package whose name is the string A.

module a {
  interface outer {
    struct inner {
      in long member;
    };
   };
}

Here the scoped symbol for the module is :a, the scoped symbol for the interface is a:outer, and the scoped symbol for struct is a:outer/inner.

module a{
  module b{
    interface c{
      struct d{
        long foo;
      };
    };
  };
}

The scoped symbol for the struct is a/b:c/d. The scoped symbol for the struct member is a/b:c/d/foo.

A.4.7 The package_prefix pragma

A package_prefix pragma has the form:

#pragma package_prefix string 

where string is an IDL string literal. For example, #pragma package_prefix COM.LISPWORKS.

A package_prefix pragma affects the mapping of all top-level modules whose definition textually follows that pragma in the IDL file. The name of the scoping symbol for such a top-level module is the concatenation of the given package_prefix with the name of the module.

#pragma package_prefix COM/LISPWORKS
 
module a{
  module b{
    interface c{};
  };
};

The scoped symbol for the interface is COM/LISPWORKS/A/B:C.

A.4.8 Mapping for interface

An IDL interface is mapped to a Lisp class. The name of this class is the scoped symbol for the interface. The direct superclasses of a generated Lisp class are determined as follows.

If the given IDL interface has no declared base interfaces, the generated class has the single direct superclass named corba:object. Otherwise, the generated Lisp class has direct superclasses that are the generated classes corresponding to the declared base interfaces of the given interface. The Lisp value nil can be passed wherever an object reference is expected.

An IDL interface is also mapped into server-side classes. The server classes are described in A.6 The mapping of IDL into Common Lisp servants.

For example, in IDL:

module example{
  interface foo {};
  interface bar {};
  interface fum : foo,bar {};
}

And in generated Lisp:

(defclass example:foo(corba:object)())
(defclass example:bar(corba:object)())
(defclass example:fum (example:foo example:bar)())

A.4.9 Mapping for operation

This section discusses only how the user is to invoke mapped operations, not how the user is to implement them. The implementation of operations is discussed in A.6 The mapping of IDL into Common Lisp servants.

An IDL operation is mapped to a Lisp function named by the symbol whose print-name is given by the name of the operation interned in the operation package.

We will assume that all operation names have been appropriately imported into the current package in the examples.

Thus, when an example is given in which there is a reference to the symbol naming the mapped function corresponding to an IDL operation, the package of that symbol will be assumed to be the operation package. Common Lisp provides a number of facilities for the implementation of this functionality and for handling name conflicts.

A.4.9.1 Parameter passing modes

The function defined by the IDL operation expects actual arguments corresponding to each formal argument that is declared in or inout, in the order in which they are declared in the IDL definition of the operation.

A.4.9.2 Return values

The function defined by the IDL operation returns multiple values. The first value returned is that value corresponding to the declared return value, unless the declared return value is void. Following the value corresponding to the declared return value, if any, the succeeding returned values correspond to the parameters that were declared out and inout, in the order in which those parameters were declared in the IDL declaration.

Note that this implies that generated functions corresponding to operations declared void, which have neither out nor inout formal parameters, return zero values.

A.4.9.3 oneway

Operations declared oneway are mapped according to the above rules.

A.4.9.4 Efficiency optimization: Using macros instead of functions

A conforming implementation may map an operation to a macro whose name and invocation syntax are consistent with the above mapping. For the sake of terminological simplicity, however, this document will continue to refer to mapped operations as "functions".

A.4.9.5 exception

An invocation of a function corresponding to a given IDL operation may result in the certain conditions being signalled, including the conditions generated by the exceptions declared in the raises clause of the operation, if any. Such conditions are signalled in the dynamic environment of the caller.

An invocation of a function may also result in the signalling of conditions corresponding to system exceptions.

A.4.9.6 context

If the operation is specified to take a context (using the IDL context clause), the generated operation takes an extra optional parameter corresponding to a context object generated using the normal IDL context manipulation operations.

For example, in IDL:

module example {
  interface face {
    long sample_method (in long arg);
    void voidmethod();
    void voidmethod2(out short arg);
    string method3 (out short arg1,inout string arg2,in boolean arg3);
  };
}

In generated Lisp:

(defpackage :example)
(defclass example:face (corba:object)())
...

And in use:

; Suppose x is bound to a value of class example:face.
(sample_method x 3)
> 24
 
(voidmethod x)
> ; No values returned
 
(voidmethod2 x)
> 905 ; This is the value corresponding to the out arg
 
(method3 x "Argument corresponding to arg2" T)
> "The values returned" -23 "New arg2 value"
 
; The Lisp construct multiple-value-bind can also be used 
; to recover these values.
(multiple-value-bind (result arg1 arg2)
   (method3 x "Argument corresponding to arg2" T)
(list result arg1 arg2))
> ("The values returned" -23 "New arg2 value")

A.4.10 Mapping for attribute

An attribute is mapped using a naming convention similar to that for operation.

A.4.10.1 readonly attribute

An attribute that is declared with the readonly modifier is mapped to a method whose name is the name of the given attribute and whose home package is the operation package.

This method is specialized on the class corresponding to the IDL interface in which the attribute is defined.

A.4.10.2 normal attribute

Attributes that are not declared readonly are mapped to a pair of methods that follow the convention used for default slot accessors generated by defclass. Specifically, a reader-method is defined whose name follows the convention for readonly attributes. A writer is defined whose name is (setf name) where name is the name of the defined reader-method.

For example, in IDL:

module example{   
  interface attributes {     
    attribute string attr1;    
    readonly attribute long attr2;};} 

And in use:

;; Assume x is bound to an object of class example:attributes
(attr2 x)
> 40001
 
(attr1 x)
> "Sample"
 
(setf (attr1 x) "New value")
> "New value"
 
(attr1 x)
> "New value"

A.4.11 Mapping of module

An IDL module is mapped to a Lisp package whose name is the name of the scoped symbol for that module.

For example, in IDL:

interface outer_interface {};
module example {
  interface inner_interface {};
  module nested_inner_example {...
    interface nested_inner_interface{};
    module doubly_nested_inner_example{...};
  };
}

And in generated Lisp:

(defpackage :example)
(defpackage :example/nested_inner_example)
(defpackage   :example/nested_inner_example/doubly_nested_inner_example)
(defclass omg.root:outer_interface...)
(defclass example:inner_interface ...)
(defclass example/nested_inner_example:nested_inner_interface…)

A.4.12 Mapping for enum

An IDL enum is mapped to a Lisp type whose name is the corresponding scoped symbol.

Each member of the enum is mapped to a symbol with the same name as that member whose home package is the keyword package.

For example, in IDL:

module example{
  enum foo {hello, goodbye, farewell};
};

In generated Lisp:

(defpackage :example)
(deftype example:foo ()
'(member :hello :goodbye :farewell))

And in use:

(typep :goodbye 'example:foo)
> T
 
(typep :not-a-member 'example:foo)
> nil

A.4.13 Mapping for struct

An IDL struct is mapped to a Lisp type whose name is the corresponding scoped symbol. Each member of the struct is mapped to an initialization keyword, a reader, and a writer. The initialization keyword is a symbol whose name is the name of the member and whose package is the keyword package.

The reader is named by a symbol that follows the conventions for attribute accessors. In the case of a reader, its package is the operation package, and its name is the name of the member. The writer is formed by using setf on the generalized place named by the reader.

The type corba:struct is supertype of all such generated types.

An IDL struct has a corresponding constructor whose name is the same as the name of the mapped Lisp type. This constructor takes keyword arguments whose package is the keyword package and whose name equals the name of the corresponding member.

For example, in IDL:

module structmodule{
  struct struct_type {
    long field1;
    string field2;
  };
};

In generated Lisp:

(defpackage :structmodule)
(defstruct structmodule:struct_type ...)

And a usage example:

(setq struct (structmodule:struct_type
  :field1 100000
  :field2 "The value of field2"))
 
(op:field1 struct)
> 100000
 
(setf (op:field1 struct) -500)
> -500
 
(op:field1 struct)
> -500

A.4.14 Mapping for union

An IDL union is mapped to a Lisp type named by the corresponding scoped symbol. This type is a subtype of corba:union.

The value of the discriminator can be accessed using the accessor function named union-discriminator whose home package is the operation package and using an initialization argument named :union-discriminator.

The value can be accessed using the accessor function named union-value in the operation package with initialization argument :union-value.

An IDL union has a corresponding constructor whose name is the same as the name of the type. This constructor takes two constructors whose names are :union-value and :union-discriminator.

A.4.14.1 Member accessors

Each union member has an associated constructor and accessor.

The symbol-name of the name of the constructor corresponding to a particular member is the concatenation of the name of the union constructor to the scoping separator to the name of the member. The home package of the name of the constructor corresponding to a particular member is the home package of the name of the union constructor. A constructor corresponding to a member takes a single argument, the value of the union. The discriminator is set to the value of the first case label corresponding to that member.

It is an error if a member reader is invoked on a union whose discriminator value is not legal for that member. The member writer sets the discriminator value to the first case label corresponding to that member.

The default member is treated as if it were a member named default whose case labels include all legal case labels that are not case labels of other members in the union.

For example, in IDL:

module example {
  enum enum_type {first,second,third,fourth,fifth};
  union union_type switch (enum_type) {
    case first: long win;
    case second: short place;
    case third:
    case fourth: octet show;
    default: boolean other;
  };
};

In generated Lisp:

(defpackage :example) 
(defstruct (example:union_type ...)) 

And in use:

(setq union 
  (example:union_type
     :union-discriminator :first
     :union-value -100000))
 
(op:union-value union)
> -100000
 
(op:union-discriminator union)
> :FIRST
 
(setq same-union (example:union_type/win -100000))
 
(op:union-discriminator same-union)
> :FIRST
 
(setf (op:show same-union) 3)
> 3
 
(op:union-discriminator same-union)
> :THIRD
 
(op:show same-union)
> 3
 
(setf (op:default same-union) nil)
> nil
 
(op:union-discriminator same-union)
> :FIFTH

A.4.15 Mapping for const

An IDL const is mapped to a Lisp constant whose name is the scoped symbol corresponding to that const and whose value is the mapped version of the corresponding value.

For example, in IDL:

module example {
  const long constant = 321;
};

And in generated Lisp:

(defpackage :example)
(defconstant example:constant 321)

A.4.16 Mapping for array

An IDL array is mapped to a Lisp array of the same rank. The element type of the mapped array must be a supertype of the Lisp type into which the element type of the IDL array is mapped.

Multidimensional IDL arrays are mapped to multidimensional Lisp arrays of the same dimensions.

For example, in IDL:

module example {
  typedef short array1[2];
  interface array_interface{
    array1 op();
  }
}

In generated Lisp:

(defpackage :example)
(deftype example:array1 () '(array t (2)))
 
;; mapping for the interface...
(defclass example:array_interface...)

And in use:

(setq a2 (op x)) ; Get an array
 
(aref a2 1) ; Access an element
> 3 ; Just an example, could be any value that is a short

A.4.17 Mapping for sequence

An IDL sequence is mapped to a Lisp sequence. Bounds checking shall be done on bounded sequences when they are marshaled as parameters to IDL operations. An implementation is free to specify the type of the mapped list more specifically.

Suppose foo is an IDL data type and let L be the corresponding Lisp type. This means that anywhere a parameter of type sequence<foo> is expected, either a vector (all of whose elements are of type L) or a list (all of whose elements are of type L) may be passed.

Conversely, when such a sequence is returned from an operation invocation, the LispWorks ORB will always return a value of type vector.

For example, in IDL:

module example {
  typedef sequence< long > unbounded_data;
  interface seq{
    boolean param_is_valid(in unbounded_data arg);
  };
}

And in generated Lisp:

(defpackage :example)
(defun unbounded_data_p (sequence)
  (and (typep sequence 'sequence)
       (every #'(lambda(elt)
          (typep elt 'corba:long)))))
(deftype example:unbounded_data()
  '(satisfies unbounded_data-p))
 
; Let x be an object of type example:seq
(param_is_valid x '(-2 3))
> T
 
(param_is_valid x #(-200 33))
>T

A.4.18 Mapping for exception

Each IDL exception is mapped to a Lisp condition whose name is the scoped symbol for that exception. User exceptions inherit from a condition named corba:userexception. And exception is a subclass of serious-condition.

System exceptions inherit from a condition named corba:systemexception.

Both corba:userexception and corba:systemexception inherit from the condition corba:exception.

A.4.19 User exception

The reader functions and initialization arguments for a condition generated by an IDL exception follow the convention for the mapping of IDL structs. For example:

module example {
  exception ex1 { string reason; };
};
; generated Lisp
(defpackage :example)
(define-condition example:ex1 (corba:userexception)
  ((reason :initarg :reason ...)))
; Usage example
(error (example:ex1 :reason "Example of condition"))

A.4.20 System exception

The standard IDL system exceptions are mapped to Lisp conditions that are subclasses of corba:systemexception. Such generated conditions have reader-functions and initargs consistent with the IDL definition of these exceptions.

A.4.21 Mapping for typedef

IDL typedef is mapped to a Lisp type whose name is the scoped symbol corresponding to that typedef. This name of this type denotes the set of Lisp values that correspond to the Lisp type that is generated by the mapping of the IDL type to which the typedef corresponds.

However, it is not required to perform recursive checking of the contents of constructed types like array, sequence, and struct.

For example, in IDL:

module example{
  typedef unsigned long foo;
  typedef string bar;
};

In generated Lisp:

(defpackage :example)
(deftype example:foo () 'corba:ulong)
(deftype example:bar() 'corba:string)

And in use:

(typep -3 'example:foo)
> nil
(typep 6000 'example:bar)
> nil
(typep "hello" 'example:bar)
>T

A.4.22 Mapping for "any"

The IDL type any represents an IDL entity with an associated typecode and value. It is mapped to the type corba:any, which encompasses all Lisp values with a corresponding typecode.

A.4.23 Constructors

The constructor corba:any takes two keyword arguments named any-value and any-typecode. If any-typecode is specified, then any-value must be specified. If any-value and any-typecode are each specified, then any-value must be a member of the type denoted by any-typecode.

An any may also be created with the invocation:

(corba:any :any-typecode val :any-value type)

A.4.24 The deduced typecode

The actual typecode of a Lisp value v is defined as follows:

A.4.25 Mapping overview

The detailed mapping guidelines for specific types is designed to conform to a small set of uniform principles.

A.4.25.1 Rule 1: How names of types are formed

If an IDL identifier I names a type at the top level of some module named M, then the corresponding Lisp type is named M:I, that is, the symbol in package M whose name is the string "I".

Nested types are separated by the character "/". Thus, if there is another type J defined within the scope of the type named by I, the corresponding Lisp symbol is M:I/J. This retains consistency with the way in which repository IDs are formed.

A.4.25.2 Rule 2: How names of operations are formed

The rule for operation package mapping is simpler: all symbols that correspond to Lisp functions that correspond to IDL operations are interned in a single package. This package can be denoted by "OP". Thus, op:foo denotes the operation named foo.

A.4.25.3 Rule 3: Lisp functions corresponding to IDL types

IDL defines many kinds of types: unions, structs, interfaces, and exceptions. We can think of each of these types, informally, as denoting entities with named slots. For example, the named slots of a struct, union, or exception are its members; the named slots of an interface are its attributes.

For each IDL type, there is an associated constructor function that creates a value of that type and there are accessors for each member.

The constructor function corresponding to a type is identical to the (fully scoped) name of the type. It takes keyword initialization arguments whose names are the names of the named members of that type; these initialize the given members.

Each named slot defines two accessor functions: a reader and a writer. The reader has the same name as the named slot. The writer uses the standard (setf name) convention familiar to Lisp users. Of course, the home package of the reader is, as for all such function names, the package OP.

Note: In applying Rule 3, remember that not all of the associated functions make sense for all of the types. For example, there is obviously no constructor function defined for an interface, nor are there writer functions defined for attributes declared readonly.


Developing Component Software with CORBA® - 01 Dec 2021 19:38:37