Go to the previous, next section.
With ILU version 2.0 or later, modules implemented in different programming languages can be mixed in the same address space, with ILU doing automatic translation between data representations. (14) There are a number of things to consider when doing this; this section discusses some of them.
Some languages simply cannot be mixed in the same address space because their runtimes will conflict. ILU offers no solutions to this problem. Typical examples are two languages like Franz Allegro Common Lisp and Java with "green threads". They each implement a user-level threads package, and their implementations of threads probably cannot co-exist in the same address space.
A possible solution to this problem, called the POSIX Portable Common Runtime (PPCR), is available from Xerox PARC, as ftp://ftp.parc.xerox.com/pub/ppcr/. It contains a basic runtime which can be used as the platform for a particular language implementation's runtime. Languages which use PPCR will have a lower chance of having conflicting runtimes.
In general, the non-threaded languages C, C++, and Python are the best languages to construct libraries with; that is, code which is intended to be loaded into another language's address space. Modules constructed with one of these languages can be loaded into any of the other ILU-supported languages' address spaces.
Module initialization really consists of two operations: interface initialization and object instantiation. The first operation initializes all the interfaces used or exported by the module; the second creates one or more true instances of objects to be used by other modules. The act of binding is finding a true object in the surrogate space, so that client code can access the true module.
Generally, each ILU interface must be initialized. The process of doing
this initialization varies from programming language to programming language.
In ANSI C, ILU requires explicit calls to interface__Initialize()
for interfaces being used, or interface__InitializeServer()
for interfaces
being exported.
In languages like C++ and Java, interface initialization is performed automatically,
but at some indeterminate time before
the first symbol from that interface is referenced from outside the interface. When C++ code
is used in a shared library, sometimes this initialization must be forced manually.
In Python or Common Lisp, interface initialization is performed
automatically by the language at the time the module describing the interface
is "loaded" into the address space.
In addition to initializing all interfaces being used or exported, a module must create one or more true object instances, to allow other modules to access it. Again, the specific way of doing this varies from programming language to programming language. Once the true instance has been created, it can be exported from the module by either publishing it, via the ILU simple binding system, or taking its string binding handle, and passing that outside the module for other modules to use.
When multiple languages are used in the same address space, each must be initialized
according to the standards used for that programming language. This can be tricky when using
both statically compiled and dynamically compiled languages together. Consider the case
where Python and ANSI C are linked together.
This use of Python may be as an extension language to a program written in C.
In this case, the C code must do all initialization of modules written in
C before calling into any Python module which might reference
them. Similarly, Python initialization (import
) of modules must
occur before the C code can use them. See `ILUSRC/src/examples/multlang/'
for an example of a situation of this sort.
In the other case,
C true modules which are to be used from a Python program
in the same address space must somehow be first loaded into that address space, then initialized.
The loading is done by turning the C module into a Python extension module,
and either linking it into the python
image, or creating a dynamically loadable module
from it.
The initialization is done by then calling import
on that module from within
the Python interpreter. The extension module's
initialization routine initializes all of its interfaces,
creates one or more true objects, and exports them. After the import
statement
returns, the objects are available for finding (see next section)
from within Python.
Object instances may be located by calls on the variations of LookupObject
and ObjectOfSBH
that exist in the various language runtimes. LookupObject
is implemented so that it first
looks to see if the true object for the specified object ID is available in the local address
space. If so, it returns a version of that object. Only if the object is not locally available does it perform external
lookups in an attempt to locate the object. Note that for an object to be found via LookupObject
,
the true instance must first have been published via the implementation language ILU runtime's
variant of PublishObject
. If you do not want your objects published outside your address space,
you should use ObjectOfSBH
to find them.
One of the most effective ways of building a module to be loadable into another language's address space is to create a shared library containing the module. The library can provide binding hooks in various ways, but a suggested strategy is to provide a, from the shared library, a C-callable function which returns the string binding handle of an object, and to make all the functionality of the module available through that object, possibly by getting other objects from that object. There are then a handful of stylized ways of invoking that C procedure; we'll discuss them for each programming language.
This is typically quite simple. You must implement the true module in either C or C++, leaving
a C-callable hook into the implementation available. Here's what a typical initialization
procedure for a module called testImpl
, providing the test
interface, which
implements the Strlen
object type,
might look like in C++ (using ILU's CORBA C++ mapping):
extern "C" { ilu_string testImpl__initialize () { iluServer *s; test_Strlen_impl *i; // in case static initializers weren't run, // this will cause them to be run ILU_INIT_test_SERVER_ONLY(); iluCppRuntime::iluInitialize(); // create a new ILU kernel server s = new iluServer (); // now create an instance of test.Strlen // and return its SBH i = new test_Strlen_impl((iluCString) NULL, *s); return i->iluObjectToString(); } }A C version would look quite similar.
ILU provide an Imake rule called SharedLibrary
, which can be used to build a shared library
from C code. It also works in most cases for C++ code, though in this case
you will need to explicitly specify your C++ library. A better idea, with C++,
is to use the C++ compile command to build the library. Specifics of how to do this will
differ from compiler to compiler.
It may seem odd building a shared library around a Python implementation of a module, but that will allow it to be loaded into other languages that don't support Python bytecodes. To do this, one first implements the module in Python, providing an initialization function similar to that shown in the C++ case:
def init(): global inst # we create the server carefully here. We don't create # a normal port, and it doesn't become the default server # (though it could be) server = ilu.CreateServer() # now make an instance of Strlen with the server inst = Strlen(None, server) # and return its SBH return inst.IluSBH()
For the C or C++ programming languages, accessing a shared library is trivial. The client code simply loads in the shared library, then calls
Go to the previous, next section.