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.
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.
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.
The main points of the mapping from IDL to Common Lisp are as follows:
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.
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.
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 .
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:
(typep -3 'corba:short)
> T
(typep "A string" 'corba:string)
> T
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.
IDL
char
maps to the Lisp type
character
. The type specifier
corba:char
specifies this type.
(typep #\x corba:char)
> T
(typep "x" 'corba:char)
> nil
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)
.
(typep 255 'corba:octet)
> T
(typep -1 'corba:octet)
> nil
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.
(typep "A string" 'corba:string)
> T
(typep nil 'corba:string)
> nil
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
.
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.
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.
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.
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
.
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
.
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
.
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:
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.
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.
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
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
.
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.
module example{
interface foo {};
interface bar {};
interface fum : foo,bar {};
}
(defclass example:foo(corba:object)())
(defclass example:bar(corba:object)())
(defclass example:fum (example:foo example:bar)())
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.
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.
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 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".
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.
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.
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);
};
}
(defpackage :example)
(defclass example:face (corba:object)())
...
; 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")
An attribute is mapped using a naming convention similar to that for operation.
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.
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
.
module example{
interface attributes {
attribute string attr1;
readonly attribute long attr2;};}
;; 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"
An IDL module is mapped to a Lisp package whose name is the name of the scoped symbol for that module.
interface outer_interface {};
module example {
interface inner_interface {};
module nested_inner_example {...
interface nested_inner_interface{};
module doubly_nested_inner_example{...};
};
}
(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...)
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.
module example{
enum foo {hello, goodbye, farewell};
};
(defpackage :example)
(deftype example:foo ()
'(member :hello :goodbye :farewell))
(typep :goodbye 'example:foo)
> T
(typep :not-a-member 'example:foo)
> nil
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.
module structmodule{
struct struct_type {
long field1;
string field2;
};
};
(defpackage :structmodule)
(defstruct structmodule:struct_type ...)
(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
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
.
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.
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;
};
};
(defpackage :example)
(defstruct (example:union_type ...))
(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
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.
module example {
const long constant = 321;
};
(defpackage :example)
(defconstant example:constant 321)
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.
module example {
typedef short array1[2];
interface array_interface{
array1 op();
}
}
(defpackage :example)
(deftype example:array1 () '(array t (2)))
;; mapping for the interface...
(defclass example:array_interface...)
(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
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
.
module example {
typedef sequence< long > unbounded_data;
interface seq{
boolean param_is_valid(in unbounded_data arg);
};
}
(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
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
.
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"))
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.
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
.
module example{
typedef unsigned long foo;
typedef string bar;
};
(defpackage :example)
(deftype example:foo () 'corba:ulong)
(deftype example:bar() 'corba:string)
(typep -3 'example:foo)
> nil
(typep 6000 'example:bar)
> nil
(typep "hello" 'example:bar)
>T
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.
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 actual typecode of a Lisp value v is defined as follows:
corba:_tc_long
.
corba:_tc_typecode
.
corba:object
, then the typecode of the corba object reference.
corba:_tc_float
.
corba:_tc_double
.
nil
or
t
, then
corba:_tc_boolean
.
corba:_tc_char
.
corba:_tc_any
.
corba:_tc_string
(an unbounded string).The detailed mapping guidelines for specific types is designed to conform to a small set of uniform principles.
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.
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
.
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 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.
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:
any
.
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.
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)))
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
.
module example { interface array_interface{}};
leads to the definition of a parameter
example:_tc_array_interface
(op:kind example:_tc_array_interface)
> :tk_objref
This section discusses how implementations create and register objects with the ORB runtime.
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.
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.
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.
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)())
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.
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))