This tutorial describes how to access dynamic C libraries from CorbaScript
and how to package them as CorbaScript modules.


1. Motivation.
--------------

CorbaScript is a simple and powerful object-oriented scripting language
dedicated to the CORBA world. It allows one to easily implement CORBA 
applications. However in many use cases, applications need to access
local features not provided by CORBA such as operating system calls to run
processes, time/date functions to manipulate them, sockets to deal with
HTTP, SMTP and so on. Moreover, these features are not hard-coded
into the CorbaScript engine.

A first solution to have access to these features could be to invoke CORBA
wrapper objects which provide an OMG IDL interface encapsulating their
associated Java or C++ API. Then, scripts would only have to find back and
invoke operations on these CORBA objects in order to have access to these
features. Unfortunately, this solution has a prohibitive cost because it
implies to develop specific CORBA objects in Java or C++ only used from
CorbaScript, to host them into CORBA servers, to start these processes and
to use CORBA/IIOP to benefit from these local features.

Another solution is to package these features into dynamic C libraries,
load them and invoke their provided functions from CorbaScript. Moreover,
many features, such as process, time, socket ones, are already packaged
into standard system libraries and then could be directly used with some
restrictions as discuted in the following sections.


2. Accessing dynamic C libraries.
---------------------------------

The CorbaScript engine provides the `C.Library' type to deal with any
dynamic C libraries. Instantiating this type allows one to load a dynamic
C library. This is done as following:

>>> lib = C.Library("library_file_name.extension")
>>> lib
< C.Library "library_file_name.extension" >

Then, `C.Library' instances represent loaded dynamic C libraries. 

If you try to load a dynamic C library which does not exist or is
not accessible to the operating system's dynamic library loader, then,
the CorbaScript engine raises a FileNotFound exception as following:

>>> lib = C.Library("foo")
Exception: < FileNotFound: `foo' by dynamic C library loader >
  File "stdin", line 1 in ???

If your dynamic libraries are not accessible from CorbaScript, check if you
have updated the shell variable of your operating system which contains the
library paths, i.e.:
* LD_LIBRARY_PATH for Linux and Solaris systems,
* PATH for Windows systems,
* LIBPATH for AIX systems,
* LD_LIBRARYN32_PATH for IRIX systems,
* SHLIB_PATH for HP-UX systems.

To add a path to this variable on Linux systems using a csh-like shell,
you could do:

  setenv LD_LIBRARY_PATH /path_to_my_dynamic_libraries:${LD_LIBRARY_PATH}

or using a sh-like shell, you could do:

  LD_LIBRARY_PATH=/path_to_my_dynamic_libraries:${LD_LIBRARY_PATH}
  export LD_LIBRARY_PATH

On Windows systems, you could do:

  set PATH=\drive:\path_to_my_dynamic_libraries;%PATH%


3. Accessing standard system libraries.
---------------------------------------

Each operating system already provides a set of dynamic C libraries for
standard system C functions like process, memory, time and socket ones.
For instance, loading the standard dynamic C library on Solaris systems
is done as following:

>>> TheLibC = C.Library("libc.so")

On Windows, this is done as following:

>>> TheLibC = C.Library("MSVCRT")

As shown previously, the name of the standard dynamic C library is
dependent of the operating system. To obtain the operating system name,
the CorbaScript engine provides the sys.OS variable. Then, loading
the standard C library, in an operating system independent way,
could be written as following:

  # On Windows systems.
  if (sys.OS == "Windows") {
    TheLibC = C.Library("MSVCRT")
  # On HP-UX systems.
  } else if (sys.OS == "HP-UX") {
    TheLibC = C.Library("/usr/lib/libc.sl")
  # On other operating systems.
  } else {
    TheLibC = C.Library("libc.so")
  }

To avoid writting the previous code each time it is needed, this CorbaScript
distribution provides the LIBC module (the modules/LIBC.cs file). Thus,
loading the standard dynamic C library is simply done as following:

>>> import LIBC
>>> LIBC.TheLibC
< C.Library "libc.so" >

Then, the LIBC.TheLibC variable refers to the standard dynamic C library.
Moreover, this LIBC module also provides some standard features for:
* memory management, i.e. calloc, malloc, free, realloc,
  null_pointer C functions, and the _null_pointer variable.
* error management, i.e. strerror, get_errno C functions,
  the errno class, and the raise_errno procedure.

As an other example, accessing the system library for socket management
could be done as following:

  if (sys.OS == "Windows") {
    TheLibSocket = C.Library("wsock32.dll")
  } else if (sys.OS == "SunOS") {
    TheLibSocket = C.Library("libsocket.so")
  } else {
    import LIBC
    TheLibSocket = LIBC.TheLibC
  }

As shown previously, the socket library is different on each operating
system, e.g. wsock32.dll for Windows, libsocket.so for SunOS and the
standard C library on other operating systems. Then, to deal with this
difference, CorbaScript already provides the socket module, which loads
the socket library according to the operating system, and provides some
classes for socket management.


4. Resolving C functions.
-------------------------

When libraries are loaded, it is required to declare their provided functions
in order to call them later. Unfortunately, there is no portable or simple way
allowing the CorbaScript engine to discover dynamically which functions
are contained into a dynamic library, and what their synopsis/signatures are.
Obtaining function signatures is important to avoid runtime crashes and to
allow the CorbaScript engine to check parameter types when a script calls
such a C function. Then, function synopsis must be declared in scripts, i.e.:
* the C type returned by the function,
* the function name,
* the C type and the name for each parameter.
  (Note: The parameter name is not important,
   it is just here for displaying purposes.)

Currently, CorbaScript only supports void, char, long, double, string,
untyped C pointers parameter types, and only the in passing parameter
mode (not out nor inout ones). These C types are represented by:
* C.void       when a function returns a void.
* C.char       when a function returns a char.
* C.char_i     when a parameter is a char.
* C.long       when a function returns a long.
* C.long_i     when a parameter is a long.
* C.double     when a function returns a double.
* C.double_i   when a parameter is a double.
* C.string     when a function returns a string.
* C.string_i   when a parameter is a string.
* C.pointer    when a function returns a pointer.
* C.pointer_i  when a parameter is a pointer.

To obtain a function from a dynamic C library, it is necessary to call the
resolve operation on the associated C.Library object, with the function
synopsis as parameters. For instance, the following

>>> system = LIBC.TheLibC.resolve(C.long, "system", C.string_i, "command")
>>> system
< C Symbol long system(in char* command) >

allows one to obtain the `system' function of the standard C library.
The given synopsis defines that this function takes only one string
as parameter and returns a long. CorbaScript uses this information
to check calls to the function as shown in the following:

>>> system(1)
Exception: < BadTypeCoerce: 1 narrowing type string >
  File "stdin", line 1 in ???

>>> system("ls", "")
Exception: < BadArgumentNumber: < C Symbol long system(in char* command) > needed=1 given=2 >
  File "stdin", line 1 in ???

>>> system("ls")
...
0

For information, the `system' function is already wrapped by the posix module.

>>> import posix
>>> posix.system("ls")
...
0


5. Resolving C functions with a variable argument number.
---------------------------------------------------------

Sometimes a C function could have a synopsis with a variable argument number,
e.g. printf(). In these cases, scripts must call the resolve operation for
each synopsis one may need to use allowing the CorbaScript engine to check
each synopsis call, e.g.:

>>> printf = LIBC.TheLibC.resolve(C.void, "printf", C.string_i, "format")
>>> printf
< C Symbol void printf(in char* format) >

>>> printfLong = LIBC.TheLibC.resolve(C.void, "printf", C.string_i, "format", 
                                                        C.long_i, "value")
>>> printfLong
< C Symbol void printf(in char* format, in long value) >

>>> printfDouble = LIBC.TheLibC.resolve(C.void, "printf", C.string_i, "format",
                                                          C.double_i, "value")
>>> printfDouble
< C Symbol void printf(in char* format, in double value) >

In the previous example, the `printf' variable refers to the `printf' function
with one string as parameter while the `printfLong' and `printfDouble' variables
refer to the same C function respectively with a string + a long and a string +
a double as parameters.


6. Using C pointer types.
-------------------------

Sometimes C functions return and/or take as parameters a C pointer, e.g. void*,
char* or a user defined pointer. Any C pointer type is represented in function
synopsis by the untyped C.pointer expression. For instance, resolving the `malloc'
and `free' standard C functions could be written as following (Note that these
functions are already wrapped by the LIBC module):

>>> malloc = LIBC.TheLibC.resolve(C.pointer, "malloc", C.long_i, "size")
>>> malloc
< C Symbol void* malloc(in long size) >

>>> free = LIBC.TheLibC.resolve(C.void, "free", C.pointer_i, "ptr")
>>> free
< C Symbol void free(in void* ptr) >

Here, `malloc' takes a `long' as parameters and returns an untyped C pointer
while `free' only takes an untyped C pointer as parameter. Thus, the
CorbaScript engine could check calls to these C functions:

>>> tmp = malloc(10000)
>>> free(tmp)

Here, the `free' function could only be called with an untyped C pointer
as parameter. Currently, as all C pointer types are represented by
CorbaScript untyped pointers, the CorbaScript engine can not correctly
control if pointers are valid and properly used. Then, beware of using
C pointers, a runtime crash may arise quickly!

The `equality', `difference', `test', and `negation' operators could be used
on C pointer values: 

>>> tmp == tmp
true
>>> tmp != tmp
false
>>> if (tmp) { println("Not null!") };
Not null!
>>> if (!tmp) { println("Null!") } else { println("Not Null!") };
Not null!

The LIBC module provides a `null_pointer' function to obtain a null C pointer:

>>> LIBC.null_pointer()
< C.PointerValue 0x0 >


7. Creating wrapper C libraries/modules.
----------------------------------------

Previous sections have discussed how to use dynamic C libraries and
how to resolve their provided C functions. Unfortunately, C.Library
objects do not allow one to access C functions with a complex synopsis
(e.g. out or inout parameters), global C variables, and C structure fields.
However, these features can be encapsulated into dynamic C libraries written
by users, also called wrapped C libraries. To simplify the compilation of
these wrapper libraries, this CorbaScript distribution provides a simple 
framework described in this section.

The wrappers/ directory contains all our wrapper library sources and makefiles.
Each wrapper library source is named LibXXX.c where XXX is the name of the 
wrapper library. To create a new wrapper library, you need to create a new
LibXXX.c file in the wrappers/ directory and add a LibXXX entry into
wrappers.mk and wrappers.mak makefiles. For instance, the example discussed
in the following sections is contained in the wrappers/LibExample.c file.

A wrapper library source must begin as following:

  /* File: wrappers/LibExample.c */

  #include <wrappers.h>

This header file defines the CS_EXPORT macro which must be put before
the synopsis of each C functions exported by the wrapper library.
Be careful that exported function synopsis could only use void,
long, double, char, string, and pointer types.
For instance, LibExample provides the do_something function:

  CS_EXPORT void do_something(char* message)
  {
      printf("do_something(%s)\n", message);
  }

To compile a wrapper library, it is required to:
* add an entry for the wrapper library in the wrappers.mk and
  wrappers.mak makefiles,
* go to the CorbaScript build directory, and
* build the wrapper libraries, i.e.
  - On Unix systems, type `make'.
  - On Windows systems, type `nmake /f Makefile.mak'.

Then, updated and/or new wrapper libraries are compiled and linked.

To access this wrapper library, in an independent operating system way,
the LIBC module provides the loadWrapperLibrary procedure:

>>> import LIBC
>>> WrapperLibrary = LIBC.loadWrapperLibrary("Example")
>>> do_something = WrapperLibrary.resolve(C.void, "do_something",
                                          C.string_i, "message")
>>> do_something
< C Symbol void do_something(in char* message) >
>>> do_something("Hello world!")
Hello world!

To avoid writing this code each time it is needed, an interested solution is
to package this wrapper library into a script module. The following example
is contained into the wrappers/LibExample.cs file:

  import LIBC
  _WrapperLibrary = LIBC.loadWrapperLibrary("Example")
  do_something = _WrapperLibrary.resolve(C.void, "do_something",
                                         C.string_i, "message")
  
Then, using this wrapper library/module is simply done as following:

>>> import LibExample
>>> LibExample.do_something("Hello world!")
"Hello world!"

  
8. Accessing global C variables.
--------------------------------

Accessing global C variables can be done by exporting from a wrapper library
a C function to read each global variable and one to write each one if needed.
These functions are resolved into the script module associated with the wrapper
library.

For instance, the wrappers/LibExample.c file contains:

  /* A global int variable. */
  static int a_global_var;

  CS_EXPORT int get_global_var()
  {
      return a_global_var;
  }

  CS_EXPORT void set_global_var(int value)
  {
      a_global_var = value;
  }

While the wrappers/LibExample.cs file contains:

  get_global_var = _WrapperLibrary.resolve(C.long, "get_global_var")

  set_global_var = _WrapperLibrary.resolve(C.void, "set_global_var",
                                           C.long_i, "value")

Then accessing the global variable is done as following:

>>> import LibExample
>>> LibExample.set_global_var(10)
>>> LibExample.get_global_var()
10

The pattern presented in this section is for instance to access the
global errno variable of the standard C library: see the wrappers/LibC.c
file and the modules/LIBC.cs script.


9. Initializing wrapper C libraries/modules.
--------------------------------------------

When it is needed to initialize wrapper libraries at loading time, e.g.
setting global variables, the simpler solution is to export, from the library,
an initialization function and to call it from the associated script module.

For instance, the wrappers/LibExample.c file contains:

  CS_EXPORT void initLibrary()
  {
      a_global_var = 0;
  }

While the wrappers/LibExample.cs file resolves and calls this initialization
function:

  _WrapperLibrary.resolve(C.void, "initLibrary")()

Then, the initialization C function of the wrapper library is implicitly
called when the associated script module is imported from CorbaScript.


10. Accessing C structures.
---------------------------

When it is necessary to access C structures, e.g. allocate them and read/write
their fields, the solution is to export from a wrapper library:
* a function to obtain the size of the structure,
* a function to read each required field, and
* a function to write each required field.
Then, these functions could be encapsulated into a script class.

For instance, the following C structure is defined into the wrappers/LibExample.c
file:

  struct Structure
  {
      int field1;
      int field2;
  };

The associated wrapper C functions, defined into the wrappers/LibExample.c
file, are:

  CS_EXPORT size_t get_sizeof_Structure()
  {
      return sizeof(struct Structure);
  }

  CS_EXPORT int get_Structure_field1(struct Structure* s)
  {
      return s->field1;
  }

  CS_EXPORT void set_Structure_field1(struct Structure* s, int value)
  {
      s->field1 = value;
  }

  CS_EXPORT int get_Structure_field2(struct Structure* s)
  {
      return s->field2;
  }

  CS_EXPORT void set_Structure_field2(struct Structure* s, int value)
  {
      s->field2 = value;
  }

The associated wrapper class, defined into the wrappers/LibExample.cs file, is:

  class Structure
  {
    proc __Structure__(self)
    {
        # Allocate memory for the size of the structure.
        self._ptr = LIBC.malloc(_get_sizeof_Structure())
    }

    proc finalize(self)
    {
        # Free the C structure when the Structure instance is garbaged.
        LIBC.free(self._ptr)
    }

    proc getField1(self)
    {
        return _get_Structure_field1(self._ptr)
    }

    proc setField1(self, value)
    {
        _set_Structure_field1(self._ptr, value)
    }

    proc getField2(self)
    {
        return _get_Structure_field2(self._ptr)
    }

    proc setField2(self, value)
    {
        _set_Structure_field2(self._ptr, value)
    }

    proc getFields(self)
    {
      return [ self.getField1(), self.getField2() ]
    }

    proc resetFields(self)
    {
        self.setField1(0)
        self.setField2(0)
    }

    # Resolve the get_sizeof_Structure function.
    _get_sizeof_Structure = _WrapperLibrary.resolve(C.long,
                                                    "get_sizeof_Structure")

    # Resolve the get_Structure_field1 function.
    _get_Structure_field1 = _WrapperLibrary.resolve(C.long,
                                                    "get_Structure_field1",
                                                    C.pointer, "s")

    # Resolve the set_Structure_field1 function.
    _set_Structure_field1 = _WrapperLibrary.resolve(C.void,
                                                    "set_Structure_field1",
                                                    C.pointer, "s",
                                                    C.long_i, "value")

    # Resolve the get_Structure_field2 function.
    _get_Structure_field2 = _WrapperLibrary.resolve(C.long,
                                                    "get_Structure_field2",
                                                    C.pointer, "s")

    # Resolve the set_Structure_field2 function.
    _set_Structure_field2 = _WrapperLibrary.resolve(C.void,
                                                    "set_Structure_field2",
                                                    C.pointer, "s",
                                                    C.long_i, "value")
  }

The pattern presented in this section is intensively used in the time module
to access the fields of the time management structure.


11. Standard provided CorbaScript modules.
------------------------------------------

The modules/ directory contains the following modules:

* LIBC          for standard C functions,
* math          for math functions,
* IO            for text file management,
* posix         for standard posix functions,
* time          for time/date management,
* socket        for socket management.

These system modules are implemented according to the information provided
and discussed in this tutorial.


12. Current restrictions.
-------------------------

Currently, the following restrictions apply to accessing dynamic C libraries:
* Only void, long, double, char, string, untyped pointer parameter types.
* Only in passing parameter modes not out and inout ones.
* No direct access to global C variables, see solution in Section 8.
* No direct access to C structure fields, see solution in Section 10.


13. Asking for contributions.
-----------------------------

As CorbaScript is developed and distributed in an Open Source way,
any contribution is welcome to improve it and especially:
* new wrapper libraries and their associated script modules,
* new features in already provided wrapper libraries/modules,
* new features around dynamic C library management.
  For this, have a look and update the src/os_DynamicLibrary.cpp file.

Thanks in advance to discute or send all contributions into the 
goode@univ-lille1.fr mailing list.
