Go to the previous, next section.
This chapter describes the use of ILU with C++ in a manner compliant with the CORBA 2.0 C++ language mapping specification. (see http://www.omg.org/corba/corbiiop.htm) The use of ILU's original C++ support is deprecated.
Any function or type which is not part of the CORBA 2.0 specification has the prefix 'ilu'. It should be understood that use of 'ilu' prefixed functionality is not portable to other (non-ILU) CORBA implementations.
Some arguments or return values of functions (e.g. char*) have storage management requirements. Basically this revolves around whether the caller retains or gets ownership of the parameter and is therefore responsible for eventually releasing it, or if ILU takes or retains ownership, where it will be released at ILU's discretion. Any function parameter that becomes owned by ILU is marked with the comment /* ILUowned */ Any return value (or 'out' parameter) that remains under the ownership of ILU is similarly marked. Anything not so marked is not ILU's responsibility.
Note that ILU support for C++ does rely on having argument prototypes, all C++ library functions, and the capabilities of the C++ pre-processor.
The CORBA 2.0 C++ chapters 15 though 18 describes the mapping of OMG IDL
to C++. For those elements of ISL for which there is a direct counterpart
in IDL, the ISL component is mapped just as the IDL component is. Those
ISL concepts with no IDL counterpart (marked with a '-' in the table below)
have a mapping separately described in a following section.
ISL IDL
-------------------------------------
INTEGER long
SHORT INTEGER short
LONG INTEGER -
CARDINAL unsigned long
SHORT CARDINAL unsigned short
LONG CARDINAL -
BYTE octet
BOOLEAN boolean
REAL double
SHORT REAL float
LONG REAL -
CHARACTER -
SHORT CHARACTER char
PICKLE Any
ARRAY array
SEQUENCE sequence
RECORD struct
UNION union
OPTIONAL -
ENUMERATION enum
OBJECT object
CString string
SEQUENCE OF CHARACTER -
EXCEPTION -, exception
INTERFACE module
The following table describes the mappings for ISL types that have no IDL counterparts. The C++ column gives the mapping modulo indirection and/or 'const' qualification dictated by parameter directionality (i.e., IN vs OUT vs INOUT vs return values).
ISL C++
--------------------------------------------------------------
LONG INTEGER iluLongInteger
LONG CARDINAL iluLongCardinal
LONG REAL iluLongReal
CHARACTER iluCharacter
OPTIONAL X X* for operation parameters;
'managed X*' for embedded types.
SEQUENCE OF CHARACTER iluCharacter*
EXCEPTION Any ISL exception that is
not a RECORD, maps to a subclass of
CORBA::UserException, that has a _value()
member function which returns a value of
the type associated with the exception.
To provide a consistent naming scheme, in many cases, a type defined in the ILU kernel has been typedeffed to appear in C++ as the corresponding type name without the intervening underscore, and with the following letter capitalized, e.g. typedef ilu_cardinal iluCardinal;
The mapping for an ISL Object 'A' produces 3 C++ classes:
This mapping allows servers to be developed that do not contain surrogate stub code (if they don't need it), and also prevents the situation where a server method override is forgotten, resulting in surrogate stub code being called as it it were 'true' code.
For each ISL Object 'A' for which a true side implementation is to be developed, the true side implementer should define a class A_impl that inherits virtually from the C++ class A, and implements the actual true methods as member functions (in whatever manner is appropriate). The implementer is free to use delegation, implementation inheritance, whatever - the only restriction is that if a class B_impl inherits implementation from a class X, and class X inherits from a class in the abstract object hierarchy, (e.g. when X is A_impl), then X's inheritance from the abstract object hierarchy must be 'public virtual'.
Each produced C++ class e.g. A, will have a constructor C++: A ( char* pc_instance_handle, ilu_Server& r_an_ilu_server = iluServer::GetDefaultServer(), ILUCPP_BOOL b_within_object_table = ILUCPP_FALSE ) : iluObject ( A::m_ILUClassRecord, pc_instance_handle, r_an_ilu_server, b_within_object_table).
In IDL, Union arms all have names, in ISL, the names may not be specified. If a name isn't specified for an arm, the name the stubber produces is the arm's typename, prefixed with '_', and suffixed with '_arm'.
For example,
TYPE someuniontype = short cardinal UNION
bar = 0, 1 END,
integer = DEFAULT
END;
would produce the C++ names _bar_arm, and _integer_arm to reference the bar and integer arms.
OPTIONAL type maps either to the same C++ type as its base
type, if that base type is represented with an C++ pointer type, or to a pointer to that base type,
if it is not represented with a C++ pointer type.
Additionally, all OPTIONAL types T have an associated
C++ T_var.
For non-parameters (i.e., RECORD members, ARRAY and SEQUENCE elements) an
ISL OPTIONAL type maps to a 'managed pointer', analagous to the mapping
for non-parameter ISL OBJECT (CORBA interface) and
ISL SEQUENCE OF SHORT CHARACTER (CORBA string). (This
'managed pointer' behaves similarly to T_var; however, CORBA does
not allow compliant applications to use 'managed pointer' types directly, as the actual
type is implementation-specific)
The following ISL and C++ code fragments illustrate:
ISL
TYPE SomeType = ...
TYPE MyOptional = OPTIONAL SomeType;
TYPE MyRec = RECORD
member: MyOptional
END;
TYPE MyArray = ARRAY OF 10 MyOptional;
member: MyOptional
END;
C++
...
MyRec myRec;
MyArray myArray;
SomeType st;
SomeType* stPtr;
MyOptional myOptional; // MyOptional is equivalent to SomeType*
MyOptional_var stVar;
...
myRec.member = myArray[0]; // free old myRec.member, deep copy
myArray[1] = stVar; // free old myArray[1], deep copy
myRec.member = stPtr; // free old myRec.member, assume ownership
myRec.member = myOptional; // free old myRec.member, assume ownership
myRec.member = &st; // ILLEGAL - can't free &st
myRec.member = new SomeType(st); // OK; free old myRec.member, assume ownership
stPtr = myArray[2]; // Simple pointer assignment, no copy
stVar = myRec.member; // free old stVar, deep copy
The lifetime of recA.member and each myArray[n] are tied to recA
and myArray, respectively; when an optional-containing variable goes out of scope
or is destroyed, its optional members/elements are freed. Thus, the assignment
myRec.member = &st in the example above is illegal and can lead to calamitous
results when an attempt is made to free &st.
Any ISL exception that is not a RECORD, maps to a subclass of CORBA::UserException, that has a _value() member function which returns a value of the type associated with the exception.
In IDL, methods may be ASYCHRONOUS. Asynchronous methods cannot have
return values or raise exceptions. Hence, they result in a C++
member function declared to return void.
In IDL, methods may be FUNCTIONAL. In the C++ mapping,
FUNCTIONAL is ignored. The ability to create custom C++ surrogates allows
the implementation to decide what and how caching may be implemented on any
method, as well as perform any other sorts of message 'filtering'.
An ISL object being declared COLLECTIBLE has not effect on the
mapping per-se. It will however cause ILU to adjust an object's reference
count based on interest or dis-interest from clients.
If an ISL Object A has no supertypes, the A class 'public virtual' inherits from the iluObject class. An object described in IDL will implicity inherit from CORBA::Object (which in turn inherits from iluObject). (The idl2isl translator automatically adds ilu.CORBA-Object as a SUPERTYPE.) So, if you define an object in ISL, and do not explicitly declare ilu.CORBA-Object as a SUPERTYPE, you will not have the member functions of CORBA::Object available since you do not inherit from it.
The CORBA 2.0 C++ mapping allows for variations in the mapping depending on the C++ compilers support for Name Spaces, Exception Handling, and Run-Time Type information.
The ILU CORBA 2.0 C++ mapping implementation assumes that the C++ compiler supports exceptions. We also assume that the compiler supports RTTI should someone want to do narrowing within the exception hierarchy. [Given that ILU does not provide a Dynamic Invocation Interface, there's no real need to narrow exceptions anyway.]
During the configuration phase of ILU installation (or for Windows, per the definitions in `ILUSRC/runtime/kernel/iluwin.h') a determination is made as to whether or not to use namespaces, nested classes or underscores for IDL modules, based on the C++ compiler in use. [This can be overridden with the configuration option --with-cplusplus-mapping= switch to config.] This results in a C++ runtime that is constructed with one of these approaches in mind. Should a user decide to use a different mapping, this can be done by setting appropriate switches to the stubber, but you must bear in mind that the C++ runtime needs to be in correspondence, since the selection radically changes the names seen by a linker. Based on our knowledge (as of the date of this writing), of the degree of support/bugs for namespaces and nested classes, the following describes the IDL module mapping based on compiler:
C++ Compiler Module Mapping
Compiler Mapping
---------------------------------------
Microsoft Visual C++ underscores
SunPro nested classes
Gnu nested classes
To portably reference things in a interface when the stubber is directed to produce portable code, the stubber generates two macros, one for referring to things from outside the namespace (e.g. when defining a member function), and another for inside the namespace (e.g. inside a class declaration). The names of these macros are the interface name, and the interface name with a '_' (underscore) suffix. These macros expand to some other macros that are defined in the cppportability.hpp file.
To refer to things in the CORBA interface, one uses the CORBA macro. For example, CORBA(Boolean) b_my_result = ILUCPP_TRUE;
Because of possible variations in compiler support for Booleans, CORBA(Boolean) is defined as ILUCPP_BOOL, where ILUCPP_BOOL is defined as either an int (with ILUCPP_TRUE and ILUCPP_FALSE #defined as 1 and 0), or as a bool (with ILUCPP_TRUE and ILUCPP_FALSE #defined as true and false).
In ILU there is a concept of an 'server object'.
In the kernel this is the ilu_Server, which in the C++
runtime is encapsulated asn an iluServer object. This 'server'
effectively forms a 'scope' in which true objects reside. This is why
for example, and object lookup requires both the 'server' ID,
and the object's instance handle - both are needed to uniquely
denote an object.
Now a server has some number of 'ports'. A port is basically a
means of communicating with the objects inside a server, using a
particular combination of protocol and transport. For example,
when create an iluServer, the constructor for iluServer automatically
adds a port for the communication protocol and transport specified
as constructor arguments. We can call iluServer::iluAddPort to have additional
ports added. For example we may want to be able to communicate
with the objects using sunrpc over tcp/ip, as well as http over
tcp/ip. The iluServer has a notion of a default port. This is initially
one as specified during construction, but this can be changed
if when calling iluServer::iluAddPort we specify that this should become
the default port. The default port is the one used when we ask for
contact information for an object - that is, if we get the string
binding handle for an object, the contact information in that
string will reflect the default server port.
True objects may either be created ahead of time, or on an 'as needed' basis, i.e. when
a call comes in involving them. The 'as needed' situation is made possible by 'object tables'.
An iluServer may have associated with it (at construction time) an iluObjectTable object.
When a call comes involving an object in that iluServer that the ILU doesn't already
know about, the iluObjectTable object's iluObjectOfInstanceHandle gets
called. It is the job of this function to create and return a new object
with that instance handle. How it does this is specific to your application -
it may read object state off a disk for example. In any event, one thing this
function must do is ensure that when it calls the true objects constructor, that
it sets the constructor's b_within_object_table argument to true. (Otherwise,
internal locking constraints will be violated). While in the iluObjectOfInstanceHandle
function, the associated iluServer's lock is held, and if the resulting object is expected to be
of a COLLECTIBLE type, the global kernel mutex "gcmu" is also held. The fact that these locks
are held somewhat restricts what an application can do inside this mapping procedure.
The ILU C++ support may be initialized to run in either
threaded or non-threaded mode. In non threaded mode, a call to iluServer::iluRun
member function results in a call to ILU's 'mainloop'. The mainloop
basically sits waiting for an incoming request. When one comes in, the request is
invoked. If the implementation of the invoked method makes a call to some other
remote object, the mainloop is recursively entered while awaiting a reply. This
allows additional requeste to come in and get serviced, preventing deadlock.
When intialized to run in threaded mode, ILU will run one thread for each incoming connection. Note that there may be multiple connections for a particular port (either from different clients, or from the same client who needed another connection because all the ones it had so far were busy at the time). In the case of a non-concurrent protocol (sunrpc, http, courier), the connection thread receives an incoming request, processes it itself, and then waits for the next request. In the case of a concurrent protocol (csunrpc, iiop), the connnection thread receives an incoming request, spawns a worker thread to carry out the request, and immediately goes back to waiting for more incoming requests.
The ILU C++ provides no special concurrency control for methods in your objects (to do so would be presumptive on our part). The method implementor must put appropriate locking in place if it is possible that multiple threads (or recursive mainloop invocations) might be running 'in' an object simultaneously.
A surrogate is an object that is used to represent a remote object. When a method is invoked on a surrogate, the methods implementation in the surrogate transfers the call to the true object, and returns the result of this call, thus providing location transparency. There are times however when it is useful to have the surrogate's method implementation do more than just forward the call to the true object. An application may want a surrogate method implementation that caches the results of calls (potentially reducing network overhead), perform transformations on arguments, output diagnostic information, or whatever.
To facilitate this, the ILU C++ support allows an implementation
to supply a function that is called when a surrogate for a particular object
type is needed. The function iluCppRuntime::iluSetSurrogateCreator
tells the C++ runtime what function to call when a surrogate for an
object of the specified class is needed. This allows an
implementation to subclass off a surrogate class, and write a new
surrogate creation function that creates an instance of this new
subclass. Call iluCppRuntime::iluSetSurrogateCreator after you've performed
initialization, but before you do any operations which might create a
surrogate of the specified class. It basically overwrites the default
surrogate creation function set up by the surrogate stubs. It returns
the old surrogate creator function, or NULL if was previously no
surrogate creator for that class.
A surrogate creator function should at the minimum create an instance of a surrogate, call
the instances member function iluAssociateKernelObject passing the
iluKernelObject, and then return a pointer to the new instance.
A String Binding Handle is a textual representation of an object reference.
It contains the object's server id, instance id, information about how to
contact the object, as well as other information. ILU C++ provides
the functions iluCppRuntime::iluFormSBH, iluCppRuntime::iluFormSBHUsingContactInfo,
and iluCppRuntime::iluParseSBH for constructing and parsing string binding handles.
An object may be obtained from a string binding handle using iluObject::iluStringToObject
and the string bindign handle of an object may be obtained by calling the iluObjectToString
member function.
When creating a service, there needs to be some way for clients to find out
about the service. ILU C++ provides a simple mechanism
to achieve this. Objects may be published, looked up, and their publications
withdrawn using the appropriate member functions (iluPublish, iluLookup,
iluWithdraw).
An true object is initially 'Active', which means that its ISL (or IDL as the case may be) defined methods may be invoked on it from outside its process (or from another language within that same process). An object may be made unavailable to outside calls, i.e. marked 'inactive' by calling its iluDeactivate member function. It may may be reactivated by calling its iluActivate member function.
An object is initially available from the outside until it is deactivated Objects that are involved in a call (i.e. sent or received as arguments, or the object the method is being invoked on) need to be protected from deletion for the duration of that involvement (for example, you don't want some thread deleting a true object when it's currently the target of a method call). The C++ runtime keeps track of what objects are involved in a call, and will attempt to prevent them from being deleted until the call is completed.
The application programmer needs to assist in this by calling, in the
most specific destructor, iluDeactivate (inherited virtually from
iluObject). iluDeactivate blocks any further incoming calls involving
the object, and wait for any ongoing calls using the object to
complete. Next the destructor should perform any object specific
cleanup. Finally, the destructor in iluObject will break the
association between the kernel object and this object, allowing the
kernel object to be potentially freed.
A client may set the Passport to be used on outgoing calls by creating
and setting up an iluPassport, and then passing the passport in a call
to iluPassport::iluSetPassport. This sets the passport to be used in
the thread that made the call - i.e. iluPassport are on a per thread
basis. Note that before your thread exits, you should either call
iluSetPassport(NULL), or delete the iluPassport in use (assuming it's
only in use for a single thread). The iluPassport (if any) currently
setup for a thread can be retrieved by calling
iluPassport::iluGetPassport.
A Server may obtain the iluPassport of the caller (if any) of a
method by using the iluPassport::iluGetCallerPassport() function.
A iluServer may be constructed to use a particular identity by specifying a
iluPassport as a constructor argument. This identity is used to identify
the principal offering the service.
The C++ Runtime normally relies on the static initializers in the files that the stubber generates to place initialization functions onto internal lists so that they will be invoked when the application calls iluCppRuntime::Initialize. However, it is not guaranteed by the ANSI C++ that static initializers are called upon the loading of a compilation unit. We have only had a report of one compiler that did not run the static initializers at load time (in fact, it was reported that it did not run them ever! - bug!?). We have observed static initialization at load time in Visual C++, SunPro and GNU compilers. In the event that you end up using a compiler that does not call the static initializers at load time, you can use the stubber defined initialization macros that are generated in the common header file for each interface.
(It should be pointed out that the CORBA 2.0 C++ Runtime does not suffer from the static initializer issues that plagued ILU's original C++ support. No ILU calls are actually made until iluCppRuntime::iluInitialize is called, allowing one to set up different mainloops, etc.)
To generate CORBA 2.0 C++ stubs from an ISL file, use the program cpp2-stubber. The stubber has the following usage:
Usage: cpp2-stubber [arguments] Islfile [ISLFILE ...]
[arguments] can be any of the following:
[-nu|-underscores]
[-np|-portable]
[-nn|-nested]
[-ns|-namespaces]
The switches can be used to direct the stubber to produce code that using a specific module mapping, or portable module mapping. The default is whatever is found appropriate during the configuration phase of ILU installation (see "Portability and Mapping Variations"). Note that generating a specific (non-portable) mapping that does not match the platform default, is likely to cause errors at link time due to naming differences.
For an interface Foo the stubber generates:
`Foo-cpp.hpp' which contains the classes for the abstract object hierarchy, as well as any other declarations needed by both client and server.
`Foo-cpp.cpp', which contains any definitions needed by both client and server, which contains A has no supertypes, the A class 'public virtual' inherits from the iluObject class. An object described in IDL will implicity inherit from CORBA::Object (which in turn inherits from iluObject). (The idl2isl translator automatically adds ilu.CORBA-Object as a SUPERTYPE.) So, if you define an object in ISL, and do not explicitly declare ilu.CORBA-Object as a SUPERTYPE, you will not have the member functions of CORBA::Object available since you do not inherit from it.
The CORBA 2.0 C++ mapping allows for variations in the mapping depending on the C++ compilers support for Name Spaces, Exception Handling, and Run-Time Type information.
The ILU CORBA 2.0 C++ mapping implementation assumes that the C++ compiler supports exceptions. We also assume that the compiler supports RTTI should someone want to do narrowing within the exception hierarchy. [Given that ILU does not provide a Dynamic Invocation Interface, there's no real need to narrow exceptions anyway.]
During the configuration phase of ILU installation (or for Windows, per the definitions in `ILUSRC/runtime/kernel/iluwin.h') a determination is made as to whether or not to use namespaces, nested classes or underscores for IDL modules, based on the C++ compiler in use. [This can be overridden with the configuration option --with-cplusplus-mapping= switch to config.] This results in a C++ runtime that is constructed with one of these approaches in mind. Should a user decide to use a different mapping, this can be done by setting appropriate switches to the stubber, but you must bear in mind that the C++ runtime needs to be in correspondence, since the selection radically changes the names seen by a linker. Based on our knowledge (as of the date of this writing), of the degree of support/bugs for namespaces and nested classes, the following describes the IDL module mapping based on compiler:
C++ Compiler Module Mapping
Compiler Mapping
---------------------------------------
Microsoft Visual C++ underscores
SunPro nested classes
Gnu nested classes
To portably reference things in a interface when the stubber is directed to produce portable code, the stubber generates two macros, one for referring to things from outside the namespace (e.g. when defining a member function), and another for inside the namespace (e.g. inside a class declaration). The names of these macros are the interface name, and the interface name with a '_' (underscore) suffix. These macros expand to some other macros that are defined in the cppportability.hpp file.
To refer to things in the CORBA interface, one uses the CORBA macro. For example, CORBA(Boolean) b_my_result = ILUCPP_TRUE;
Because of possible variations in compiler support for Booleans, CORBA(Boolean) is defined as ILUCPP_BOOL, where ILUCPP_BOOL is defined as either an int (with ILUCPP_TRUE and ILUCPP_FALSE #defined as 1 and 0), or as a bool (with ILUCPP_TRUE and ILUCPP_FALSE #defined as true and false).
In ILU there is a concept of an 'server object'.
In the kernel this is the ilu_Server, which in the C++
runtime is encapsulated asn an iluServer object. This 'server'
effectively forms a 'scope' in which true objects reside. This is why
for example, and object lookup requires both the 'server' ID,
and the object's instance handle - both are needed to uniquely
denote an object.
Now a server has some number of 'ports'. A port is basically a
means of communicating with the objects inside a server, using a
particular combination of protocol and transport. For example,
when create an iluServer, the constructor for iluServer automatically
adds a port for the communication protocol and transport specified
as constructor arguments. We can call iluServer::iluAddPort to have additional
ports added. For example we may want to be able to communicate
with the objects using sunrpc over tcp/ip, as well as http over
tcp/ip. The iluServer has a notion of a default port. This is initially
one as specified during construction, but this can be changed
if when calling iluServer::iluAddPort we specify that this should become
the default port. The default port is the one used when we ask for
contact information for an object - that is, if we get the string
binding handle for an object, the contact information in that
string will reflect the default server port.
True objects may either be created ahead of time, or on an 'as needed' basis, i.e. when
a call comes in involving them. The 'as needed' situation is made possible by 'object tables'.
An iluServer may have associated with it (at construction time) an iluObjectTable object.
When a call comes involving an object in that iluServer that the ILU doesn't already
know about, the iluObjectTable object's iluObjectOfInstanceHandle gets
called. It is the job of this function to create and return a new object
with that instance handle. How it does this is specific to your application -
it may read object state off a disk for example. In any event, one thing this
function must do is ensure that when it calls the true objects constructor, that
it sets the constructor's b_within_object_table argument to true. (Otherwise,
internal locking constraints will be violated). While in the iluObjectOfInstanceHandle
function, the associated iluServer's lock is held, and if the resulting object is expected to be
of a COLLECTIBLE type, the global kernel mutex "gcmu" is also held. The fact that these locks
are held somewhat restricts what an application can do inside this mapping procedure.
The ILU C++ support may be initialized to run in either
threaded or non-threaded mode. In non threaded mode, a call to iluServer::iluRun
member function results in a call to ILU's 'mainloop'. The mainloop
basically sits waiting for an incoming request. When one comes in, the request is
invoked. If the implementation of the invoked method makes a call to some other
remote object, the mainloop is recursively entered while awaiting a reply. This
allows additional requeste to come in and get serviced, preventing deadlock.
When intialized to run in threaded mode, ILU will run one thread for each incoming connection. Note that there may be multiple connections for a particular port (either from different clients, or from the same client who needed another connection because all the ones it had so far were busy at the time). In the case of a non-concurrent protocol (sunrpc, http, courier), the connection thread receives an incoming request, processes it itself, and then waits for the next request. In the case of a concurrent protocol (csunrpc, iiop), the connnection thread receives an incoming request, spawns a worker thread to carry out the request, and immediately goes back to waiting for more incoming requests.
The ILU C++ provides no special concurrency control for methods in your objects (to do so would be presumptive on our part). The method implementor must put appropriate locking in place if it is possible that multiple threads (or recursive mainloop invocations) might be running 'in' an object simultaneously.
A surrogate is an object that is used to represent a remote object. When a method is invoked on a surrogate, the methods implementation in the surrogate transfers the call to the true object, and returns the result of this call, thus providing location transparency. There are times however when it is useful to have the surrogate's method implementation do more than just forward the call to the true object. An application may want a surrogate method implementation that caches the results of calls (potentially reducing network overhead), perform transformations on arguments, output diagnostic information, or whatever.
To facilitate this, the ILU C++ support allows an implementation
to supply a function that is called when a surrogate for a particular object
type is needed. The function iluCppRuntime::iluSetSurrogateCreator
tells the C++ runtime what function to call when a surrogate for an
object of the specified class is needed. This allows an
implementation to subclass off a surrogate class, and write a new
surrogate creation function that creates an instance of this new
subclass. Call iluCppRuntime::iluSetSurrogateCreator after you've performed
initialization, but before you do any operations which might create a
surrogate of the specified class. It basically overwrites the default
surrogate creation function set up by the surrogate stubs. It returns
the old surrogate creator function, or NULL if was previously no
surrogate creator for that class.
A surrogate creator function should at the minimum create an instance of a surrogate, call
the instances member function iluAssociateKernelObject passing the
iluKernelObject, and then return a pointer to the new instance.
A String Binding Handle is a textual representation of an object reference.
It contains the object's server id, instance id, information about how to
contact the object, as well as other information. ILU C++ provides
the functions iluCppRuntime::iluFormSBH, iluCppRuntime::iluFormSBHUsingContactInfo,
and iluCppRuntime::iluParseSBH for constructing and parsing string binding handles.
An object may be obtained from a string binding handle using iluObject::iluStringToObject
and the string bindign handle of an object may be obtained by calling the iluObjectToString
member function.
When creating a service, there needs to be some way for clients to find out
about the service. ILU C++ provides a simple mechanism
to achieve this. Objects may be published, looked up, and their publications
withdrawn using the appropriate member functions (iluPublish, iluLookup,
iluWithdraw).
An true object is initially 'Active', which means that its ISL (or IDL as the case may be) defined methods may be invoked on it from outside its process (or from another language within that same process). An object may be made unavailable to outside calls, i.e. marked 'inactive' by calling its iluDeactivate member function. It may may be reactivated by calling its iluActivate member function.
An object is initially available from the outside until it is deactivated Objects that are involved in a call (i.e. sent or received as arguments, or the object the method is being invoked on) need to be protected from deletion for the duration of that involvement (for example, you don't want some thread deleting a true object when it's currently the target of a method call). The C++ runtime keeps track of what objects are involved in a call, and will attempt to prevent them from being deleted until the call is completed.
The application programmer needs to assist in this by calling, in the
most specific destructor, iluDeactivate (inherited virtually from
iluObject). iluDeactivate blocks any further incoming calls involving
the object, and wait for any ongoing calls using the object to
complete. Next the destructor should perform any object specific
cleanup. Finally, the destructor in iluObject will break the
association between the kernel object and this object, allowing the
kernel object to be potentially freed.
A client may set the Passport to be used on outgoing calls by creating
and setting up an iluPassport, and then passing the passport in a call
to iluPassport::iluSetPassport. This sets the passport to be used in
the thread that made the call - i.e. iluPassport are on a per thread
basis. Note that before your thread exits, you should either call
iluSetPassport(NULL), or delete the iluPassport in use (assuming it's
only in use for a single thread). The iluPassport (if any) currently
setup for a thread can be retrieved by calling
iluPassport::iluGetPassport.
A Server may obtain the iluPassport of the caller (if any) of a
method by using the iluPassport::iluGetCallerPassport() function.
A iluServer may be constructed to use a particular identity by specifying a
iluPassport as a constructor argument. This identity is used to identify
the principal offering the service.
The C++ Runtime normally relies on the static initializers in the files that the stubber generates to place initialization functions onto internal lists so that they will be invoked when the application calls iluCppRuntime::Initialize. However, it is not guaranteed by the ANSI C++ that static initializers are called upon the loading of a compilation unit. We have only had a report of one compiler that did not run the static initializers at load time (in fact, it was reported that it did not run them ever! - bug!?). We have observed static initialization at load time in Visual C++, SunPro and GNU compilers. In the event that you end up using a compiler that does not call the static initializers at load time, you can use the stubber defined initialization macros that are generated in the common header file for each interface.
(It should be pointed out that the CORBA 2.0 C++ Runtime does not suffer from the static initializer issues that plagued ILU's original C++ support. No ILU calls are actually made until iluCppRuntime::iluInitialize is called, allowing one to set up different mainloops, etc.)
To generate CORBA 2.0 C++ stubs from an ISL file, use the program cpp2-stubber. The stubber has the following usage:
Usage: cpp2-stubber [arguments] Islfile [ISLFILE ...]
[arguments] can be any of the following:
[-nu|-underscores]
[-np|-portable]
[-nn|-nested]
[-ns|-namespaces]
The switches can be used to direct the stubber to produce code that using a specific module mapping, or portable module mapping. The default is whatever is found appropriate during the configuration phase of ILU installation (see "Portability and Mapping Variations"). Note that generating a specific (non-portable) mapping that does not match the platform default, is likely to cause errors at link time due to naming differences.
For an interface Foo the stubber generates:
`Foo-cpp.hpp' which contains the classes for the abstract object hierarchy, as well as any other declarations needed by both client and server.
`Foo-cpp.cpp', which contains any definitions needed by both client and server, which contains A has no supertypes, the A class 'public virtual' inherits from the iluObject class. An object described in IDL will implicity inherit from CORBA::Object (which in turn inherits from iluObject). (The idl2isl translator automatically adds ilu.CORBA-Object as a SUPERTYPE.) So, if you define an object in ISL, and do not explicitly declare ilu.CORBA-Object as a SUPERTYPE, you will not have the member functions of CORBA::Object available since you do not inherit from it.
The CORBA 2.0 C++ mapping allows for variations in the mapping depending on the C++ compilers support for Name Spaces, Exception Handling, and Run-Time Type information.
The ILU CORBA 2.0 C++ mapping implementation assumes that the C++ compiler supports exceptions. We also assume that the compiler supports RTTI should someone want to do narrowing within the exception hierarchy. [Given that ILU does not provide a Dynamic Invocation Interface, there's no real need to narrow exceptions anyway.]
During the configuration phase of ILU installation (or for Windows, per the definitions in `ILUSRC/runtime/kernel/iluwin.h') a determination is made as to whether or not to use namespaces, nested classes or underscores for IDL modules, based on the C++ compiler in use. [This can be overridden with the configuration option --with-cplusplus-mapping= switch to config.] This results in a C++ runtime that is constructed with one of these approaches in mind. Should a user decide to use a different mapping, this can be done by setting appropriate switches to the stubber, but you must bear in mind that the C++ runtime needs to be in correspondence, since the selection radically changes the names seen by a linker. Based on our knowledge (as of the date of this writing), of the degree of support/bugs for namespaces and nested classes, the following describes the IDL module mapping based on compiler:
C++ Compiler Module Mapping
Compiler Mapping
---------------------------------------
Microsoft Visual C++ underscores
SunPro nested classes
Gnu nested classes
To portably reference things in a interface when the stubber is directed to produce portable code, the stubber generates two macros, one for referring to things from outside the namespace (e.g. when defining a member function), and another for inside the namespace (e.g. inside a class declaration). The names of these macros are the interface name, and the interface name with a '_' (underscore) suffix. These macros expand to some other macros that are defined in the cppportability.hpp file.
To refer to things in the CORBA interface, one uses the CORBA macro. For example, CORBA(Boolean) b_my_result = ILUCPP_TRUE;
Because of possible variations in compiler support for Booleans, CORBA(Boolean) is defined as ILUCPP_BOOL, where ILUCPP_BOOL is defined as either an int (with ILUCPP_TRUE and ILUCPP_FALSE #defined as 1 and 0), or as a bool (with ILUCPP_TRUE and ILUCPP_FALSE #defined as true and false).
In ILU there is a concept of an 'server object'.
In the kernel this is the ilu_Server, which in the C++
runtime is encapsulated asn an iluServer object. This 'server'
effectively forms a 'scope' in which true objects reside. This is why
for example, and object lookup requires both the 'server' ID,
and the object's instance handle - both are needed to uniquely
denote an object.
Now a server has some number of 'ports'. A port is basically a
means of communicating with the objects inside a server, using a
particular combination of protocol and transport. For example,
when create an iluServer, the constructor for iluServer automatically
adds a port for the communication protocol and transport specified
as constructor arguments. We can call iluServer::iluAddPort to have additional
ports added. For example we may want to be able to communicate
with the objects using sunrpc over tcp/ip, as well as http over
tcp/ip. The iluServer has a notion of a default port. This is initially
one as specified during construction, but this can be changed
if when calling iluServer::iluAddPort we specify that this should become
the default port. The default port is the one used when we ask for
contact information for an object - that is, if we get the string
binding handle for an object, the contact information in that
string will reflect the default server port.
True objects may either be created ahead of time, or on an 'as needed' basis, i.e. when
a call comes in involving them. The 'as needed' situation is made possible by 'object tables'.
An iluServer may have associated with it (at construction time) an iluObjectTable object.
When a call comes involving an object in that iluServer that the ILU doesn't already
know about, the iluObjectTable object's iluObjectOfInstanceHandle gets
called. It is the job of this function to create and return a new object
with that instance handle. How it does this is specific to your application -
it may read object state off a disk for example. In any event, one thing this
function must do is ensure that when it calls the true objects constructor, that
it sets the constructor's b_within_object_table argument to true. (Otherwise,
internal locking constraints will be violated). While in the iluObjectOfInstanceHandle
function, the associated iluServer's lock is held, and if the resulting object is expected to be
of a COLLECTIBLE type, the global kernel mutex "gcmu" is also held. The fact that these locks
are held somewhat restricts what an application can do inside this mapping procedure.
The ILU C++ support may be initialized to run in either
threaded or non-threaded mode. In non threaded mode, a call to iluServer::iluRun
member function results in a call to ILU's 'mainloop'. The mainloop
basically sits waiting for an incoming request. When one comes in, the request is
invoked. If the implementation of the invoked method makes a call to some other
remote object, the mainloop is recursively entered while awaiting a reply. This
allows additional requeste to come in and get serviced, preventing deadlock.
When intialized to run in threaded mode, ILU will run one thread for each incoming connection. Note that there may be multiple connections for a particular port (either from different clients, or from the same client who needed another connection because all the ones it had so far were busy at the time). In the case of a non-concurrent protocol (sunrpc, http, courier), the connection thread receives an incoming request, processes it itself, and then waits for the next request. In the case of a concurrent protocol (csunrpc, iiop), the connnection thread receives an incoming request, spawns a worker thread to carry out the request, and immediately goes back to waiting for more incoming requests.
The ILU C++ provides no special concurrency control for methods in your objects (to do so would be presumptive on our part). The method implementor must put appropriate locking in place if it is possible that multiple threads (or recursive mainloop invocations) might be running 'in' an object simultaneously.
A surrogate is an object that is used to represent a remote object. When a method is invoked on a surrogate, the methods implementation in the surrogate transfers the call to the true object, and returns the result of this call, thus providing location transparency. There are times however when it is useful to have the surrogate's method implementation do more than just forward the call to the true object. An application may want a surrogate method implementation that caches the results of calls (potentially reducing network overhead), perform transformations on arguments, output diagnostic information, or whatever.
To facilitate this, the ILU C++ support allows an implementation
to supply a function that is called when a surrogate for a particular object
type is needed. The function iluCppRuntime::iluSetSurrogateCreator
tells the C++ runtime what function to call when a surrogate for an
object of the specified class is needed. This allows an
implementation to subclass off a surrogate class, and write a new
surrogate creation function that creates an instance of this new
subclass. Call iluCppRuntime::iluSetSurrogateCreator after you've performed
initialization, but before you do any operations which might create a
surrogate of the specified class. It basically overwrites the default
surrogate creation function set up by the surrogate stubs. It returns
the old surrogate creator function, or NULL if was previously no
surrogate creator for that class.
A surrogate creator function should at the minimum create an instance of a surrogate, call
the instances member function iluAssociateKernelObject passing the
iluKernelObject, and then return a pointer to the new instance.
A String Binding Handle is a textual representation of an object reference.
It contains the object's server id, instance id, information about how to
contact the object, as well as other information. ILU C++ provides
the functions iluCppRuntime::iluFormSBH, iluCppRuntime::iluFormSBHUsingContactInfo,
and iluCppRuntime::iluParseSBH for constructing and parsing string binding handles.
An object may be obtained from a string binding handle using iluObject::iluStringToObject
and the string bindign handle of an object may be obtained by calling the iluObjectToString
member function.
When creating a service, there needs to be some way for clients to find out
about the service. ILU C++ provides a simple mechanism
to achieve this. Objects may be published, looked up, and their publications
withdrawn using the appropriate member functions (iluPublish, iluLookup,
iluWithdraw).
An true object is initially 'Active', which means that its ISL (or IDL as the case may be) defined methods may be invoked on it from outside its process (or from another language within that same process). An object may be made unavailable to outside calls, i.e. marked 'inactive' by calling its iluDeactivate member function. It may may be reactivated by calling its iluActivate member function.
An object is initially available from the outside until it is deactivated Objects that are involved in a call (i.e. sent or received as arguments, or the object the method is being invoked on) need to be protected from deletion for the duration of that involvement (for example, you don't want some thread deleting a true object when it's currently the target of a method call). The C++ runtime keeps track of what objects are involved in a call, and will attempt to prevent them from being deleted until the call is completed.
The application programmer needs to assist in this by calling, in the
most specific destructor, iluDeactivate (inherited virtually from
iluObject). iluDeactivate blocks any further incoming calls involving
the object, and wait for any ongoing calls using the object to
complete. Next the destructor should perform any object specific
cleanup. Finally, the destructor in iluObject will break the
association between the kernel object and this object, allowing the
kernel object to be potentially freed.
A client may set the Passport to be used on outgoing calls by creating
and setting up an iluPassport, and then passing the passport in a call
to iluPassport::iluSetPassport. This sets the passport to be used in
the thread that made the call - i.e. iluPassport are on a per thread
basis. Note that before your thread exits, you should either call
iluSetPassport(NULL), or delete the iluPassport in use (assuming it's
only in use for a single thread). The iluPassport (if any) currently
setup for a thread can be retrieved by calling
iluPassport::iluGetPassport.
A Server may obtain the iluPassport of the caller (if any) of a
method by using the iluPassport::iluGetCallerPassport() function.
A iluServer may be constructed to use a particular identity by specifying a
iluPassport as a constructor argument. This identity is used to identify
the principal offering the service.
The C++ Runtime normally relies on the static initializers in the files that the stubber generates to place initialization functions onto internal lists so that they will be invoked when the application calls iluCppRuntime::Initialize. However, it is not guaranteed by the ANSI C++ that static initializers are called upon the loading of a compilation unit. We have only had a report of one compiler that did not run the static initializers at load time (in fact, it was reported that it did not run them ever! - bug!?). We have observed static initialization at load time in Visual C++, SunPro and GNU compilers. In the event that you end up using a compiler that does not call the static initializers at load time, you can use the stubber defined initialization macros that are generated in the common header file for each interface.
(It should be pointed out that the CORBA 2.0 C++ Runtime does not suffer from the static initializer issues that plagued ILU's original C++ support. No ILU calls are actually made until iluCppRuntime::iluInitialize is called, allowing one to set up different mainloops, etc.)
To generate CORBA 2.0 C++ stubs from an ISL file, use the program cpp2-stubber. The stubber has the following usage:
Usage: cpp2-stubber [arguments] Islfile [ISLFILE ...]
[arguments] can be any of the following:
[-nu|-underscores]
[-np|-portable]
[-nn|-nested]
[-ns|-namespaces]
The switches can be used to direct the stubber to produce code that using a specific module mapping, or portable module mapping. The default is whatever is found appropriate during the configuration phase of ILU installation (see "Portability and Mapping Variations"). Note that generating a specific (non-portable) mapping that does not match the platform default, is likely to cause errors at link time due to naming differences.
For an interface Foo the stubber generates:
`Foo-cpp.hpp' which contains the classes for the abstract object hierarchy, as well as any other declarations needed by both client and server.
`Foo-cpp.cpp', which contains any definitions needed by both client and server, which contains A has no supertypes, the A class 'public virtual' inherits from the iluObject class. An object described in IDL will implicity inherit from CORBA::Object (which in turn inherits from iluObject). (The idl2isl translator automatically adds ilu.CORBA-Object as a SUPERTYPE.) So, if you define an object in ISL, and do not explicitly declare ilu.CORBA-Object as a SUPERTYPE, you will not have the member functions of CORBA::Object available since you do not inherit from it.
The CORBA 2.0 C++ mapping allows for variations in the mapping depending on the C++ compilers support for Name Spaces, Exception Handling, and Run-Time Type information.
The ILU CORBA 2.0 C++ mapping implementation assumes that the C++ compiler supports exceptions. We also assume that the compiler supports RTTI should someone want to do narrowing within the exception hierarchy. [Given that ILU does not provide a Dynamic Invocation Interface, there's no real need to narrow exceptions anyway.]
During the configuration phase of ILU installation (or for Windows, per the definitions in `ILUSRC/runtime/kernel/iluwin.h') a determination is made as to whether or not to use namespaces, nested classes or underscores for IDL modules, based on the C++ compiler in use. [This can be overridden with the configuration option --with-cplusplus-mapping= switch to config.] This results in a C++ runtime that is constructed with one of these approaches in mind. Should a user decide to use a different mapping, this can be done by setting appropriate switches to the stubber, but you must bear in mind that the C++ runtime needs to be in correspondence, since the selection radically changes the names seen by a linker. Based on our knowledge (as of the date of this writing), of the degree of support/bugs for namespaces and nested classes, the following describes the IDL module mapping based on compiler:
C++ Compiler Module Mapping
Compiler Mapping
---------------------------------------
Microsoft Visual C++ underscores
SunPro nested classes
Gnu nested classes
To portably reference things in a interface when the stubber is directed to produce portable code, the stubber generates two macros, one for referring to things from outside the namespace (e.g. when defining a member function), and another for inside the namespace (e.g. inside a class declaration). The names of these macros are the interface name, and the interface name with a '_' (underscore) suffix. These macros expand to some other macros that are defined in the cppportability.hpp file.
To refer to things in the CORBA interface, one uses the CORBA macro. For example, CORBA(Boolean) b_my_result = ILUCPP_TRUE;
Because of possible variations in compiler support for Booleans, CORBA(Boolean) is defined as ILUCPP_BOOL, where ILUCPP_BOOL is defined as either an int (with ILUCPP_TRUE and ILUCPP_FALSE #defined as 1 and 0), or as a bool (with ILUCPP_TRUE and ILUCPP_FALSE #defined as true and false).
In ILU there is a concept of an 'server object'.
In the kernel this is the ilu_Server, which in the C++
runtime is encapsulated asn an iluServer object. This 'server'
effectively forms a 'scope' in which true objects reside. This is why
for example, and object lookup requires both the 'server' ID,
and the object's instance handle - both are needed to uniquely
denote an object.
Now a server has some number of 'ports'. A port is basically a
means of communicating with the objects inside a server, using a
particular combination of protocol and transport. For example,
when create an iluServer, the constructor for iluServer automatically
adds a port for the communication protocol and transport specified
as constructor arguments. We can call iluServer::iluAddPort to have additional
ports added. For example we may want to be able to communicate
with the objects using sunrpc over tcp/ip, as well as http over
tcp/ip. The iluServer has a notion of a default port. This is initially
one as specified during construction, but this can be changed
if when calling iluServer::iluAddPort we specify that this should become
the default port. The default port is the one used when we ask for
contact information for an object - that is, if we get the string
binding handle for an object, the contact information in that
string will reflect the default server port.
True objects may either be created ahead of time, or on an 'as needed' basis, i.e. when
a call comes in involving them. The 'as needed' situation is made possible by 'object tables'.
An iluServer may have associated with it (at construction time) an iluObjectTable object.
When a call comes involving an object in that iluServer that the ILU doesn't already
know about, the iluObjectTable object's iluObjectOfInstanceHandle gets
called. It is the job of this function to create and return a new object
with that instance handle. How it does this is specific to your application -
it may read object state off a disk for example. In any event, one thing this
function must do is ensure that when it calls the true objects constructor, that
it sets the constructor's b_within_object_table argument to true. (Otherwise,
internal locking constraints will be violated). While in the iluObjectOfInstanceHandle
function, the associated iluServer's lock is held, and if the resulting object is expected to be
of a COLLECTIBLE type, the global kernel mutex "gcmu" is also held. The fact that these locks
are held somewhat restricts what an application can do inside this mapping procedure.
The ILU C++ support may be initialized to run in either
threaded or non-threaded mode. In non threaded mode, a call to iluServer::iluRun
member function results in a call to ILU's 'mainloop'. The mainloop
basically sits waiting for an incoming request. When one comes in, the request is
invoked. If the implementation of the invoked method makes a call to some other
remote object, the mainloop is recursively entered while awaiting a reply. This
allows additional requeste to come in and get serviced, preventing deadlock.
When intialized to run in threaded mode, ILU will run one thread for each incoming connection. Note that there may be multiple connections for a particular port (either from different clients, or from the same client who needed another connection because all the ones it had so far were busy at the time). In the case of a non-concurrent protocol (sunrpc, http, courier), the connection thread receives an incoming request, processes it itself, and then waits for the next request. In the case of a concurrent protocol (csunrpc, iiop), the connnection thread receives an incoming request, spawns a worker thread to carry out the request, and immediately goes back to waiting for more incoming requests.
The ILU C++ provides no special concurrency control for methods in your objects (to do so would be presumptive on our part). The method implementor must put appropriate locking in place if it is possible that multiple threads (or recursive mainloop invocations) might be running 'in' an object simultaneously.
A surrogate is an object that is used to represent a remote object. When a method is invoked on a surrogate, the methods implementation in the surrogate transfers the call to the true object, and returns the result of this call, thus providing location transparency. There are times however when it is useful to have the surrogate's method implementation do more than just forward the call to the true object. An application may want a surrogate method implementation that caches the results of calls (potentially reducing network overhead), perform transformations on arguments, output diagnostic information, or whatever.
To facilitate this, the ILU C++ support allows an implementation
to supply a function that is called when a surrogate for a particular object
type is needed. The function iluCppRuntime::iluSetSurrogateCreator
tells the C++ runtime what function to call when a surrogate for an
object of the specified class is needed. This allows an
implementation to subclass off a surrogate class, and write a new
surrogate creation function that creates an instance of this new
subclass. Call iluCppRuntime::iluSetSurrogateCreator after you've performed
initialization, but before you do any operations which might create a
surrogate of the specified class. It basically overwrites the default
surrogate creation function set up by the surrogate stubs. It returns
the old surrogate creator function, or NULL if was previously no
surrogate creator for that class.
A surrogate creator function should at the minimum create an instance of a surrogate, call
the instances member function iluAssociateKernelObject passing the
iluKernelObject, and then return a pointer to the new instance.
A String Binding Handle is a textual representation of an object reference.
It contains the object's server id, instance id, information about how to
contact the object, as well as other information. ILU C++ provides
the functions iluCppRuntime::iluFormSBH, iluCppRuntime::iluFormSBHUsingContactInfo,
and iluCppRuntime::iluParseSBH for constructing and parsing string binding handles.
An object may be obtained from a string binding handle using iluObject::iluStringToObject
and the string bindign handle of an object may be obtained by calling the iluObjectToString
member function.
When creating a service, there needs to be some way for clients to find out
about the service. ILU C++ provides a simple mechanism
to achieve this. Objects may be published, looked up, and their publications
withdrawn using the appropriate member functions (iluPublish, iluLookup,
iluWithdraw).
An true object is initially 'Active', which means that its ISL (or IDL as the case may be) defined methods may be invoked on it from outside its process (or from another language within that same process). An object may be made unavailable to outside calls, i.e. marked 'inactive' by calling its iluDeactivate member function. It may may be reactivated by calling its iluActivate member function.
An object is initially available from the outside until it