Developing Component Software with CORBA

NextPrevTopContentsIndex

A Common Lisp IDL Binding

Version 1.0 (The RFP for the IDL Common Lisp mapping was agreed by the ORBOS and PTC committees at the OMG meeting in Washington on 99/01/14.)

This chapter briefly reviews some concepts of IDL and defines the notion of a language mapping. A summary of the IDL/Common Lisp mapping is presented.

Introduction to IDL

IDL, or Interface Definition Language, is a language defined by the Object Management Group. The key data type in IDL is the interface, which describes the behavior of objects that implement that interface. The IDL definition for an interface describes all of the operations to which an object that implements that interface can respond. For each such operation, it describes the allowed types of the parameters to the operation and the allowed type of the value returned by the operation.

IDL allows types other than interfaces to be expressed. For example, primitive types such as boolean, several signed and unsigned integer types, and some floating point types may be defined.

Constructed types analogous to the C struct or Pascal record type may be defined, and some simple type aliasing is possible in a way analogous to the C typedef construct. Arrays and sequences may also be defined.

How IDL is used

IDL is typically used in the following manner. A server process wishes to make some of its functionality available for invocation by clients. These clients may not be in the same process, on the same machine, or even written in the same language.

The server publishes the IDL definitions that define the interfaces of the objects that it implements. A client can use those definitions to invoke operations on objects that reside within the server process.

The syntax used by the client to invoke a method on an object defined in IDL, and the relationship between the data types specified in IDL and the native datatypes of the language in which the client is implemented is defined by the mapping of IDL into that language.

This document describes a mapping from IDL into Common Lisp.

Mapping summary

The main points of the mapping from IDL to Common Lisp are as follows:

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.

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.

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 .

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 Type

Name of Lisp Type

Lisp Type Specifier

  1. boolean
  1. corba:boolean
  1. boolean
  1. char
  1. corba:char
  1. character
  1. octet
  1. corba:octet
  1. (unsigned-byte 8)
  1. string
  1. corba:string
  1. string
  1. short
  1. corba:short
  1. (signed-byte 16)
  1. unsigned short
  1. corba:ushort
  1. (unsigned-byte 16)
  1. long
  1. corba:long
  1. (signed-byte 32)
  1. unsigned long
  1. corba:ulong
  1. (unsigned-byte 32)
  1. float
  1. corba:float
  1. see text
  1. double
  1. corba:double
  1. see text

For example:

(typep -3 'corba:short)
> T
(typep "A string" 'corba:string)
> T
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.

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
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
string

The IDL string , both bounded and unbounded variants, are mapped to 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
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 .

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.

Introduction to named types

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

IDL naming terminology

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.

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 .

Lisp Naming Terminology

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 .

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 .

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.

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.

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.

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 .

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 .

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 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)())
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 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.

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.

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.

oneway

Operations declared oneway are mapped according to the above rules.

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

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.

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")
Mapping for attribute

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

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.

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"
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...)
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
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
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 .

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

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"))
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.

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

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)
The deduced typecode

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

Mapping overview

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

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.

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 .

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

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.

Accessing the Members

Each named slot defines two 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.

Mapping pseudo-objects to Lisp

Pseudo-objects are constructs whose definition is usually specified in IDL, but whose mapping is language specified. A pseudo-object is not (usually) a regular CORBA object.

A pseudo-object differs from a regular CORBA object in the following ways:

We have chosen the option allowed in the IDL specification to define status as void and have eliminated it for the convenience of Lisp programmers.

Each of the standard IDL pseudo-objects is mapped according to the translation rules just defined.

Narrowing

In line with the other language binding, we define an operation for narrowing an object reference. The code:

(op:narrow class-symbol object-refence)

attempts to narrow the given object reference into an object of the named client-side class.

For example, to narrow an client-side value stored in account-ref , into an object of type Bankingdemo:Checkingaccount :

(when (op:Is_a account-ref 
        (op:id Bankingdemo:_Tc_Checkingaccount))
   (setf account-ref 
   (op:narrow 'Bankingdemo:Checkingaccount account-ref)))
Typecodes for parsed IDL

Parameters holding the typecode value are generated for all parsed IDL types. If the IDL parser generates a Lisp type of name A:B , then the typecode of the given type is available in the parameter A:_TC_B .

For example, the IDL code

module example { interface array_interface{}};

leads to the definition of a parameter

example:_tc_array_interface

with

(op:kind example:_tc_array_interface)
> :tk_objref

The mapping of IDL into Common Lisp servants

This section discusses how implementations create and register objects with the ORB runtime.

Mapping of native types

Specifically, the native type PortableServer::Servant is mapped to the Lisp class named PortableServer:ServantBase . The native type PortableServer::ServantLocator::Cookie can take any lisp value.

Implementation objects

An interface corresponding to a class named by a Lisp symbol s with package p and name n may be implemented by extending the class named by the symbol whose package is p and whose name is the concatenation of n to the string " -SERVANT ".

If the interface has no base interfaces, then the associated skeleton class has as direct superclasses the class corresponding to the class named portableserver:ServantBase .

Otherwise, if the interface has base interfaces named A , B , C , and so forth, then its associated servant class has as direct superclasses the class corresponding to the servant classes corresponding to A , B , C , and so forth.

Attributes in an interface generate slots of the corresponding name in the OP package, together with server-side accessor methods.

Defining methods

The only portable way to implement an operation on a servant class is to use the corba:define-method macro. The syntax of corba:define-method is intended to follow the syntax of the Lisp defmethod macro as closely as possible.

The syntax of corba:define-method is as follows:

corba:define-method function-name {method-qualifier}* lambda-list
   form*
function-name::= {operation-name | (setf operation-name)}
operation-name:: symbol
method-qualifier::={:before | :after | :around}
corba-specialized-lambda-list ::= setf-lambda-list 
                                  | normal-lambda-list
setf-lambda-list ::= (argument-specifier receiver-specifier)
normal-lambda-list ::= (receiver-specifier {parameter-specifer}* context-identifier)
context-identifier ::= symbol
receiver-specifer ::= (receiver-name receiver-class)
receiver-name ::= symbol
receiver-class ::= symbol
parameter-specifier ::= symbol

This corba:define-method macro is used to implement an operation on an interface. operation-name is a symbol whose name is the name either of an operation or of an attribute declared in an IDL interface implemented by the class named by the symbol receiver-class .

The number of parameter-specifiers listed in the normal-lambda-list must equal the combined number of in and inout parameters declared in the signature of the operation denoted by the function-name , or 0 if the operation is an attribute. If the function-name is a list whose car is setf , the corresponding operation-name should name an attribute that is not readonly.

If function-name denotes an operation, then the effect of
corba:define-method is to inform the ORB that requests for the operation on instances of the class receiver-class should return the value or values returned by the body forms of the define-method macro, executed in a new lexical environment in which each parameter-specifier is bound to the actual parameters and in which the context-identifer is bound to the value of the corresponding context.

The operation of corba:define-method in the case in which function-name names an attribute is analogous. The behavior of auxiliary specifiers and of dispatch is the same as their corresponding action under defmethod. Attribute accessors will be generated automatically and inherited by subclasses of the servant classes; the methods can be overridden by user definitions.

Note that the syntax of corba:define-method is a strict subset of that of defmethod : every legal corba:define-method invocation is also a legal defmethod invocation. The main difference between them is that corba:define-method only allows specialization on the first argument. An implementation is free to extend the syntax of corba:define-method , for example, to allow type-checking, interlocking, or multiple dispatch.

Examples

A Named Grid Example

The first example shows how one might encapsulate a named grid , which is a grid of strings.

This is the IDL of the interface to a named grid of strings:

module example{
  interface named_grid{
    readonly attribute string name;
    string get_value ( in unsigned short row,
        in unsigned short column);
    void set_value ( in unsigned short row,
        in unsigned short column,
        in string value);
  }
}

The IDL compiler might generate a class corresponding to the example::named_grid interface using code something like this:

(defpackage :example)
(defclass example:named_grid(corba:object)())

Servant Class Examples

In order to implement the IDL interface, the user would extend the class example:named_grid-servant .

;;Sample implementation of named_grid
(defclass grid-implementation (example:named_grid-servant)
  ((grid :initarg :grid
        :initform (make-array '(2 3) :initial-element "Init")))

The attribute in the IDL will cause the class to have a slot op:name with the appropriate accessors specializing on the class.

Implementation of the IDL operations

The corba:define-method macro is used to define the methods that implement each of the operations defined in the IDL interface. These implementations do not perform any of the argument or range checking that a production system would, of course, perform.

The implementation is free to define other methods on the class, including print-object methods and auxiliary methods for initialize-instance .

(corba:define-method get_value ((the-grid grid-implementation)
                                  row column)
  (aref (slot-value the-grid 'grid) row column))
(corba:define-method set_value ((the-grid grid-implementation)
                                  row column value))
  (setf (aref the-grid row column) value))

 


Developing Component Software with CORBA - 22 Dec 2009

NextPrevTopContentsIndex