Previous Up Next

Chapter 6  SIDL Basics


6.1  Introduction

This chapter describes the basics of the Scientific Interface Definition Language (SIDL). The goal is to provide sufficient information to enable most library and component developers to begin using SIDL to wrap their software. It begins with an overview of SIDL files followed by an introduction to the fundamental data types. More complex topics such as the object arrays, exceptions, objects, and the XML repository are then addressed.

6.2  SIDL Files

SIDL files are human-readable, language- and platform- independent interface specifications for objects and their methods. SIDL allows you to specify classes, interfaces, and the methods therein. All methods defined in SIDL are public, since the developer is writing them as part of an interface description. Any data you wish a SIDL object to hold is not declared in the SIDL file, and is private. Data should be placed in the implementation skeleton files, and cannot be publicly exported.

Babel reads the SIDL files to generate the appropriate programming language bindings. These bindings, in the form of stub, intermediate object representation (IOR), and implementation skeleton sources, provide the basis for language interoperable software using Babel. In addition, SIDL files are used to populate the XML symbol repository that can serve as an alternate source of interface specifications during the generation of programming language bindings.

Basic Structure

The basic structure of a SIDL file is illustrated below.

SIDL
package <identifier> [version <version>] { interface <identifier> [ <inheritance> ] { [<type>] <identifier> ( [<parameters>] ) [throws <exception>]; [require <contract-clause>] [ensure <contract-clause>] . . . [<type>] <identifier> ( [<parameters>] ) [throws <exception>]; [require <contract-clause>] [ensure <contract-clause>] } class <identifier> [ <inheritance> ] { [<type>] <identifier> (<parameters>) [throws <exception>]; [require <contract-clause>] [ensure <contract-clause>] . . . [<type>] <identifier> ( [<parameters>] ) [throws <exception>]; [require <contract-clause>] [ensure <contract-clause>] } package <identifier> [version <version>] { . . . } }

The main elements are packages, interfaces, classes, methods, types, and contract clauses. For a more detailed description, refer to Appendix B.

Packages
provide a mechanism for specifying name space hierarchies. That is, it enables grouping sets of interface and/or class descriptions as well as nested packages. Identified by the package keyword, packages have a scoped name that consists of one or more identifiers, or name strings, separated by a period (“.”). A package can contain multiple interfaces, classes and nested packages. By default, packages are now re-entrant. In order to make them non-re-entrant, they must be declared as final.
Interfaces
define a set of methods that a caller can invoke on an object of a class that implements the methods. Multiple inheritance of interfaces is supported, which means an interface or a class can be derived from one or more interfaces.
Classes
also define a set of methods that a caller can invoke on an object. A class can extend only one other class but it can implement multiple interfaces. So we have single inheritance of classes and multiple inheritance of interfaces.
Methods
define services that are available for invocation by a caller. The signature of the method consists of the return type, identifier, arguments, and exceptions. Each parameter has a type and a mode. The mode indicates whether the value of the specified type is passed from caller to callee (in), from callee to caller (out), or both (inout). All methods are implicitly capable of throwing a sidl.RuntimeException exception. A sidl.RuntimeException is used to indicate an error in the Babel generated code or potentially a network exception. Each additional exception that a method can throw when it detects an error must be listed. These exceptions can be either interfaces or classes so long as they inherit from sidl.BaseException. For a default implementation of the exception interfaces, the exception classes should extend sidl.SIDLException. Methods and parameter passing modes are discussed in greater detail in Section 6.7.
Types
are used to constrain the the values of parameters, exceptions, and return values associated with methods. SIDL supports basic types such as int, bool, and long as well as strings, complex numbers, classes, and arrays.
Contract clauses
are used to define properties that must hold before and/or after method invocation.

Comments and Doc-Comments

SIDL has the same commenting style as C++/Java and even has a special documentation comment (so called doc-comment) similar to those used in Javadoc. One can embed comments anywhere in their SIDL file. Documentation comments should immediately precede the class, interface, or method with which they are associated. Babel replicates documentation comments in the files it generates. It does not replicate plain comments.

SIDL
/* * 1. This is a multi-line comment. * */ // 2. This comment fits entirely on a single line. /* 3. This comment can fill less than a line. */ /** 4. This is a documentation comment. */ /** * 5. Documentation comments can span * multiple lines without the beginning * space-asterisk-space combinations * getting in the way. */

Consider the above SIDL file fragment.

  1. This comment is a regular multi-line comment that is delimited by a slash-star , star-slash (“/*”, “*/”) pair.
  2. This is a single-line comment that starts with a double slash “//” and continues to the end of the line.
  3. This comment is the same as # 1 except that it is completely contained on a single line. It can be embedded in the middle of a line anywhere a space naturally occurs.
  4. This is a documentation comment. In keeping with Javadoc, Doc++, and other tools, it is delimited by slash-star-star and star-slash (“/**”, “*/”) combinations. Documentation comments are important because their contents are preserved by Babel in the corresponding generated files. Doc-comments must directly precede the interface, class, or method that they document.
  5. This is a multi-line variant of a doc-comment. Note that initial asterisks on a line are assumed to be for human readers only and are discarded by Babel when it reads in the text. The multi-line doc-comment is the preferred way of documenting SIDL.

Packages and Versions

SIDL has both a packaging and versioning mechanism built in. Packages are essentially named scopes, serving a similar function as Java packages or C++ namespaces. Versions are decimal separated integer values where it is assumed larger numbers imply more recent versions. All classes and interfaces in that package get that same version number. If subpackages are specified, they can have their own version number assigned. If a package is declared without a version, it can only contain other packages. If a package declares interfaces or classes, a version number for that package is required.

SIDL
package mypkg { }

This SIDL file represents the minimum needed for each and every SIDL file. The package statement defines a scope where all classes within the package must reside. Since no version clause is included, the version number defaults to 0.

Packages can be nested. This is shown in the example below. The version numbers assigned to all the types is determined by the package, or subpackage, in which it resides. In the design of the SIDL file, remember that some languages get very long function names from excessively nested packages or excessively long package names.

SIDL
package mypkg version 1.0 { package thisIsAReallyLongPackageName { } package this version 0.6 { package is { package a { package really { package deeply version 0.4 { package nested { package packageName version 0.1 { } } } } } } } }

In SIDL you can use as much or as little of a type name as necessary to uniquely identify it. If absolute specificity is required, a leading dot can be used to identify the global (top) package.

SIDL
1 package foo { 2 class A {} 3 package foo { 4 class A { 5 foo.A bar(); //which foo.A? 6 .foo.A wuux(); //first foo.A. 7 .foo.foo.A wuxx(); //second foo.A. 8 } 9 } 10 }

External types can be expressed in one of two ways. The fully scoped external type can be used anywhere in the class description. Alternatively, an import statement can be used to put the type in the local package-space. import statements can request a specific version of the package, if that version is not found, Babel will print an error. If no version is specified, Babel will take whatever version it is being run on. Babel can not be run on two versions of a given package at the same time, even if you only import or require one of them.

Another way to restrict the package version you use is the restrict statement. restrict does not import the package, but if you do later import the package or refer to something in that package by it’s fully scoped name, Babel will guarantee that the correct version of the package will be used. Also note that all restrict statements must come before the first import statement.

Below is a sample SIDL file, that should help bring all of these concepts together.

SIDL
require pkgC version 2.0; // restrict pkgC to version 2.0, not imported import pkgA version 1.0; // restrict pkgA version 1.0. Includes class pkgA.A import pkgB; // import pkgB regaurdless of version. Includes class pkgB.B package mypkg version 2.0 { class foo { setA( A ); // imported from pkgA, must be pkgA.A-v1.0 setB( B ); // imported from pkgB, must be pkgB.B, no version restriction setC( pkgC.C ); // must be pkgC.C-v2.0 setD( pkgD.D ); // no version restriction } }

Re-entrant Packages

By default, SIDL packages are re-entrant. This means that Babel allows sub-packages to be broken into separate files, but you’d still have to run Babel on all the files at the same time. Here’s how it works.

First define the outermost package in a file.

SIDL
package mypkg version 2.0 { }

Then define a sub-package in a second file.

SIDL
package mypkg.subpkg version 2.0 { }

Note that both files begin with the identical version statement. Now as long as you run Babel on both SIDL files at the same time (with the outermost one first on the commandline), all is fine.

This works because the package statement takes a scoped identifier as an argument. As long as Babel knows that a package mypkg exists, it can handle a new package called subpkg. (This would also work if subpkg were a class. Version statements require an identifier for the outermost package. Since packages cannot have dots “.” in their names, the only dots in version statements should appear at the numbers, not the package names.

Running the second file without the first will (and should) generate an error since the enclosing package was not declared. Re-entrance should be used judiciously. This feature may be disabled by labeling a given package as final.

The From Clause

The from clause is a special SIDL statement that allows an implementor of multiple interfaces to add or rename the extensions of conflicting methods from interfaces. However, only method extensions can be changed, and the methods must have different signatures. For example, one can change the name of conflicting methods from two interfaces:

SIDL
interface A { void set(in int i); } interface B{ void set(in float i); } class C implements A, B { void set[Int](in int i) from A.set; void set[Float](in float i) from B.set; }

Or change the name of an interface method that conflicts with your inherited class methods:

SIDL
interface A { void set(in int i); } class B { void set(in float i); } class C extends B implements A { void set[Int](in int i) from A.set; void set(in float i); //Cannot use the from clause on class methods }

But it doesn’t work for methods that have the same signature:

SIDL
/* X THIS WILL NOT COMPILE X */ interface A { void set(in int i); } interface B{ void set(in int i); } class C implements A, B { void set[A](in int i) from A.set; //ERROR void set[B](in int i) from B.set; //signature conflict } /* X THIS WILL NOT COMPILE X */

6.3  Fundamental Types


Table 6.1: SIDL Types
SIDL TYPESIZE (BITS)
bool1
char8
int32
long64
float32
double64
fcomplex64
dcomplex128
opaque64
stringvaries
enum32
interfacevaries
classvaries
array<Type,Dim>varies
array< >varies
rarray<Type,Dim>(index variables)varies

Table 6.1 briefly shows the different data types that are supported in Babel. Refer to each chapter for the language specific bindings for each SIDL type. The “S” in SIDL stands for “Scientific.” This emphasis is reflected in the fundamental support for complex numbers (fcomplex and dcomplex) and dynamic multidimensional arrays (array<Type,Dim>).

C++ developers looking at the SIDL syntax for arrays, might think that SIDL is a templated IDL, but this is not so. Although the syntax for SIDL arrays looks like a template, it is specific only to the array type. Developers cannot create templated classes or methods in SIDL.

Rationale: Although C++ templates are a very powerful programming mechanism, they apply only to C++. For Babel to implement similar hashing routines, method names in languages other than C++ would become prohibitively (thousands of characters) long. Moreover, this C++ template hashing mechanism is compiler specific so while C++ is very good at hiding the expanded template names (unless there is an error to report) we would have to add babel C++ bindings on a compiler by compiler basis.

Discussion of the various types is broken up into sections. Numeric types such as bool, char, int, long, float, double, fcomplex, dcomplex, strings, as well as information about enumerated types and the opaque type are all covered in this Subsection 6.3.

Information about extended types such as Interfaces and Classes along with the methods they contain are described in Section 6.7, and Section 6.4 covers Array.

Numeric Types

The SIDL types bool, char, int, long, float, double, fcomplex, and dcomplex are the smallest and easiest data types to transfer between languages transparently. They all have a fixed size and can just as reasonably be copied as passed by reference.

Most languages natively support all of these data types (though perhaps less so with complex types). There are a few notable exceptions that may be of interest.

ANSI C does not define the size of int and long, only that the latter be at least as big as the former. As of the C99 standard, there are types int32_t and int64_t that are signed integers that explicitly support a fixed number of bits. Most compilers already have these symbols defined appropriately in sys/types.h (pre C99 standard) or inttypes.h.

Python defines its int and long to be equivalent to C, and therefore suffers the same platform dependent integer size problem with less flexibility for a workaround. It is not uncommon for regression tests involving longs and Python to fail on certain platforms. Python 2.2 has a patch to make SIDL long support better.

Strings

Strings are an interesting datatype because they are fundamental to many pieces of software, but represented differently by practically every single programming language. Strings can have a high overhead to support language interoperability because there is invariably so much copying involved.

fortran 77 and Fortran 90/95 support for strings is limited to a predetermined buffer size. Since the results of a string assignment into that buffer in Fortran does not propagate the length of the string, trailing whitespace is always trimmed for any string begin passed out from a Fortran implementation.

Opaque

The opaque type is dangerous and rarely useful. However, there are particular times when an opaque type is the only way to solve a problem; for example, it is one of the few portable ways to implement an object with state in fortran 77. When a SIDL file uses an opaque type, Babel guarantees only bits will be relayed exactly between caller and callee. If there is a need to pass more information than an opaque provides, than the developer can simply pass a pointer to that information.

Use of a opaque carries a heavy penalty. Method calls with opaque types in the argument list (or return type) are restricted to in-process calls only.

Rationale: Since opaque is typically used for a pointer to memory, this sequence of bits has no meaning outside of its own process space.

Enumerations

An enumeration is typically used in programming languages to specify a limited range of states to enable dealing with them by names instead of hard-coded values. For language interoperability purposes — especially to support this concept on languages with no native support — we’ve had to create specific rules for the integer values associated with enumerated types.

SIDL
package enumSample version 1.0 { // undefined integer values enum color { red, orange, yellow, green, blue, violet }; // completely defined integer values enum car { /** * A sports car. */ porsche = 911, /** * A family car. */ ford = 150, /** * A luxury car. */ mercedes = 550 }; // partially defined integer value enum number { notZero, // This non-doc comment will not be retained. notOne, zero=0, one=1, negOne=-1, notNeg }; }

Above is a sample of enumerations taken directly from our regression tests. It defines a package enumSample that contains three enumerations. C/C++ developers will find the syntax very familiar. When defining an enumeration, the actual integer values assigned can be undefined, completely defined, or partially defined.

SIDL defines the following rules for adding integer values to enumerated states that don’t have a value explicitly defined.

  1. Error if two states are explicitly assigned the same value
  2. Assign all explicit values to their named state.
  3. Assign smallest unused non-negative value to first unassigned state in enumeration.
  4. Repeat 3 until all states have assigned (unique) values.

To verify the application of these rules, the enumSample.number enumeration will have the following values assigned to its states: NotZero=2, NotOne=3, zero=0; one=1, negOne=-1, notNeg=4.

6.4  Arrays

Support for multi-dimensional arrays is one of the features that separates SIDL/Babel language interoperability from Microsoft’s COM/DCOM and the OMG’s CORBA. SIDL supports two kinds of arrays: normal and raw. Normal SIDL arrays provide all the features of a normal SIDL type, while raw SIDL arrays, called r-arrays, exist to provide more efficient, lower level access to numeric arrays. For example, a one-dimensional r-array in C appears as a double pointer and a length parameter. To highlight the contrast, normal SIDL arrays appear as a struct in C, a template class in C++, an 64-bit integer in fortran 77, and a derived type in Fortran 90/95.

The SIDL array API and data structure can be used in client code to prepare arguments for passing to a SIDL method. It is used inside the implementation code to get data and meta-data from incoming array arguments. The remainder of this section will focus on the C API for arrays because it is the basis for the other language APIs.

SIDL Arrays

Normal SIDL arrays provide all the features of a normal SIDL type. They are meant to generalize the array types built into many languages. They are not parallel array classes or particularly sophisticated, but are very, very general. It is expected that developers requiring parallel array libraries build them on top of the SIDL array type.

Characteristics of normal SIDL arrays are:

Generic Arrays

The design of the (normal) array data structure enables the concept of a generic array, an array whose data type and dimension are unspecified. In SIDL, a generic array is indicated with the type array< >. There is no type or dimension information between the < and >.

Generic arrays are useful for making interfaces that are very flexible without requiring numerous methods to be defined. For example, if you were writing an interface to serialize an array, you could write one method void serialize(in array< > array); to handle an array of any type or dimension. Without generic arrays, you would have to define 77 different serialize methods to handle each possible array type and dimension.

In C, you can use the macro API to determine the dimension, bounds on each dimension and stride for a generic array. All other languages except Python provide a function API to determine the same information for a generic array.

Starting with Babel 1.1.0, Babel’s Python binding now uses either the Python NumPy or Numeric Python array API. This switch in Babel follows a switch in the Python community where the Python community has deprecated Numeric Python and appointed NumPy as its successor. If you need examples about how to write code that can work with either Python array API look at Babel’s array regression tests, arrays and ordering.

The function API for generic arrays includes the following methods: addRef, smartCopy, deleteRef, dimen, lower, upper, length, stride, isColumnOrder, isRowOrder, and type. With the exception of type, these methods have all been presented above. The name of the method has the type left empty. Where the name for addRef in C on a double array is sidl_double_array_addRef, its name is sidl__array_addRef for a generic array. Note, there are two underscores between sidl and array in the generic array case.

The type method is defined as follows in the case of C.

ANSI C
/** * Return an integer indicating the type of elements held by the * array. Zero is returned if array is NULL. */ int32_t sidl__array_type(const struct sidl__array* array);

It returns a value that indicates what the underlying type of the array actually is. The return value is either zero or one of the values in sidl_array_type.

ANSI C
enum sidl_array_type { /* these values must match values used in F77 & F90 too */ sidl_bool_array = 1, sidl_char_array = 2, sidl_dcomplex_array = 3, sidl_double_array = 4, sidl_fcomplex_array = 5, sidl_float_array = 6, sidl_int_array = 7, sidl_long_array = 8, sidl_opaque_array = 9, sidl_string_array = 10, sidl_interface_array = 11 /* an array of sidl.BaseInterface's */ };

Once you’ve discovered the underlying type of the generic array, you can safely cast its pointer to the actual pointer type (in languages like C). Each language binding provides a way to cast generic array pointers to specific types and vice versa.

In the case of a sidl_interface_array, you can case the array to an array of sidl.BaseInterface interface references. Your code should treat it as such. You can downcast individual elements of the array as you need. Your code should consider the possibility that downcasting may fail. Babel can only guarantee that the elements of the array are sidl.BaseInterface’s.

R-arrays

Since SIDL was designed to serve the high performance computing community, both SIDL object developers and clients may require direct access to the underlying array data structure for optimization purposes, such as instruction pipelining or cache performance. Hence, support for raw SIDL arrays was introduced for low level access to numeric arrays. At present, they are available in C, C++, fortran 77, Fortran 90/95 and Fortran 2003/2008. In all other languages R-arrays are implemented as regular SIDL arrays with no particular performance advantage.

Unlike normal SIDL arrays, the use of r-arrays are more restricted. More specifically, they have the following constraints:

  1. Only the in and inout parameter modes are available for r-arrays. R-arrays cannot be used as return values or as out parameters.
  2. R-arrays must be contiguous in memory, and multi-dimensional arrays must be in column-major order (i. e., Fortran order).
  3. NULL is not an allowable value for an r-array parameter.
  4. The semantics for inout r-array parameters are different. The implementation is not allowed to deallocate the array and return a new r-array. inout means that the array data is transferred from caller to callee at the start of a method invocation and from callee to caller at the end of the a method invocation.
  5. The implementation of a method taking an r-array parameter cannot change the shape of the array.
  6. The lower index is always 0, and the upper index is n−1 where n is the length in a particular dimension. This is contrary to the normal convention for Fortran arrays.
  7. It can only be used for arrays of SIDL int, long, float, double, fcomplex, and dcomplex types.
Rationale: The way r-arrays are passed to the server-side code, particularly fortran 77, makes it impossible for them to be allocated or deallocated. This makes out and return values impossible. Because the data has to be accessible directly from fortran 77 without any additional meta-data, the array data must be in column-major order.

Arrays of char are not currently supported for r-arrays because in some languages characters are treated as 16-bit Unicode characters.

The advantages of r-arrays include:

When you declare an r-array, you also declare the index variables that will hold the size of the array in each dimension. For example, here is a method to solve one of the fundamental problems of linear algebra, A x = b:

SIDL
void solve(in rarray<double,2> A(m,n), inout rarray<double> x(n), in rarray<double> b(m), in int m, in int n);

In this example, A is a 2-D array of doubles with m rows and n columns. x is a 1-D array of doubles of length n, and b is a 1-D array of doubles of length m. Note that by explicitly declaring the index variables, SIDL takes avoid using extra array size parameters by taking advantage of the fact that the sizes of A, x and b are all inter-related. The explicit declaration also allows the developer to control where the index parameters appear in the argument list. In many cases, the argument types and order can match existing APIs.

The mapping for the solve method will be shown for C, C++, fortran 77 and Fortran 90/95 in the following chapters. In languages that do not support low level access such as Python and Java, r-arrays are treated just like normal SIDL arrays, and the redundant index arguments are dropped from the argument list. The indexing information is available from the SIDL array data structure.

SIDL Language Features

As of release 0.6.5, interface definitions can specify that an array argument or return value must have a particular ordering for a method. The type array<int, 2, row-major> indicates a dense, 1 two-dimensional array of 32 bit integers in row-major order; and likewise, the type array<int, 2, column-major> indicates an dense array in column-major order. Some numerical routines can only provide high performance with a particular type of array. The ordering is part of the interface definition to give clients the information they need to use the underlying code efficiently. The ordering specification is optional.

For one-dimensional arrays, specifying row-major or column-major allows you to specify that the array must be dense, that is stride 1. Otherwise, for one-dimensional arrays row-major and column-major are identical.

If you pass an array into a method and the array does not have the specified ordering, the skeleton code will make a copy of the array with the required ordering and pass the copy to the method. This copying is necessary for correctness, but it will cause a decrease in performance. The implementor of the method can count on an incoming array to have the required ordering.

For out parameters and return values, an ordering specification means that the method promises to return an array with the specified ordering. The implementation should create the out arrays with the proper ordering; because if it does not, the skeleton code will have to copy the outgoing array into a new array with the required ordering.

For inout parameters, an ordering specification means the ordering specification will be enforced by the skeleton code for the incoming and outgoing array value.

At the time of writing this, the ordering constraints are enforced for Python implementation because Python uses NumPy or Numeric Python arrays, so Babel cannot control the array ordering as fully. The Python skeletons do force outgoing arrays (i. e., arrays passed back from Python) to have the required ordering.

Independent and borrowed arrays

From a memory perspective, there are two main kinds of arrays: independent and borrowed. The independent arrays owns and manages its data. It allocates space for the array elements when the array is created, and it deallocates that space when the array is finally destroyed.

The borrowed array does not own or manage its data. It borrows its array element data from another source that it cannot manage, and it only allocates space for the index bounds and stride information. The rationale for borrowed arrays is to allow data from another source to temporarily appear as a SIDL array without requiring data be copied.

If you slice an independent array, the resulting array is also considered independent even though it borrows data from the original independent array. The resulting array can still manage its data by retaining a reference to the original array; hence, its element data cannot disappear until the resulting array is destroyed. If you slice a borrowed array, the resulting array is also borrowed because like its original array, it doesn’t manage the underlying data.

In the Babel generated code, r-arrays are converted to borrowed arrays. These borrowed arrays are allocated on the stack rather than on the heap to improve performance of r-arrays.

The Life of an Array

The existence of borrowed arrays causes the arrays to deviate from the normal reference counting pattern. You may recall that all arrays are reference counted, and an array’s resources are reclaimed when the reference count goes to zero. However, a borrowed array’s array element data will disappear whenever the source of the borrowed data determines that it should regardless of the reference count in corresponding the SIDL array. This behavior means that developers should consider any SIDL array that they did not create themselves, for example incoming arguments to methods, as potential borrowed arrays. When a method wants to keep a copy of an array that might be a borrowed array, it should use the smartCopy method documented below.

Here are some rules of thumb about the use of borrowed arrays:

The NULL Array

NULL is a special array reference value that refers to no array. It’s used to indicate that an array reference currently points to nothing. The way you refer to NULL varies from language to language, but the concept is the same. In C++ and fortran 77, 0 (numeral zero) is the value of the NULL array. In C, the preprocessor symbol NULL is the value for the NULL array. In Python, it’s the special constant None, and in Java it’s null. In Fortran 90/95, there is a function set_null, to initialize a pointer to the NULL value, and there is a logical function is_null to test whether an array is equal to NULL.

The Language Bindings

The C++ binding for array provides access to the C API in case you need to take the gloves off and revel in the data directly. But the C++ binding also provides a templated wrapper class to provide a more natural look and feel for C++ programmers.

In some cases, the Python binding for arrays must copy SIDL arrays to/from NumPy or Numeric Python arrays; it should not happen for normally strided arrays except when an ordering constraint requires it. Arrays in Python don’t have the SIDL methods available. They just have the NumPy or Numeric Python API available.

The fortran 77 API mimics the C API; all the C functions have been Fortran’ified and have _f appended to their names. The Fortran 90/95 API uses function overloading to allow programmers to use the short array method names.

The Array API

In the following presentation, we use the SIDL double type; however, everything in this section applies to all types except where noted. The basic types are in the SIDL namespace. Table 6.2 shows the prefix for SIDL base types and the actual value type held by the array...


Table 6.2: SIDL types to array function prefixes
SIDL TYPEARRAY FUNCTION PREFIXVALUE TYPE
bool sidl_bool sidl_bool
char sidl_char char
dcomplexsidl_dcomplexstruct sidl_dcomplex
doublesidl_double double
fcomplexsidl_fcomplex struct sidl_fcomplex
float sidl_float float
int sidl_int int32_t
long sidl_long int64_t
opaque sidl_opaque void *
stringsidl_string char *

For arrays of interfaces or classes, the name of the array function prefix is derived from the fully qualified type name. For example, for the type sidl.BaseClass, the array functions all begin with sidl_BaseClass. For sidl.BaseInterface, they all begin with sidl_BaseInterface.

When you add an object or interface to an array, the reference count of the element being overwritten is decremented, and the reference count of the element being added is incremented. When you get an object or interface from an array, the caller owns the returned reference.

For arrays of strings when you add a string to any array, the array will store a copy of the string. When you retrieve a string from an array, you will receive a copy of the string. You should sidl_String_free the returned string when you are done with it.

When you create an array of interfaces, classes, or strings, all elements of the array are initialized to NULL. Other arrays are not initialized. When an array of interfaces, classes, or strings is destroyed, it releases any held references in the case of objects or interfaces. In the case of strings, it frees any non-NULL pointers.

The name of the data structure that holds the array if double is struct sidl_double__array. For some types, the data structure is an opaque type, and for others, it is defined in a public C header file.


Table 6.3: SIDL Array Functions
SHORT NAMEDESCRIPTION
createCol Creates a column-major order SIDL array
createRow Creates a row-major order SIDL array
create1d Creates a dense one-dimensional SIDL array
create2dCol Creates a dense, column-major, two-dimensional SIDL array
create2dRow Creates a dense, column-major, two-dimensional SIDL array
slice Creates a sub-array of another array. Takes parameters to define array properties.
borrow Makes a SIDL array from third party data without copying it
smartCopy Copies a borrowed array or addRefs a non-borrowed array
addRef Increments the reference count.
deleteRef Decrements the reference count.
get1 Returns the indexed element from a one-dimensional array
get2 Returns the indexed element from a two-dimensional array
get3 Returns the indexed element from a three-dimensional array
get4 Returns the indexed element from a four-dimensional array
get5 Returns the indexed element from a five-dimensional array
get6 Returns the indexed element from a six-dimensional array
get7 Returns the indexed element from a seven-dimensional array
get Returns the indexed element from an array of any dimension
set1 Sets the indexed element in a one-dimensional array
set2 Sets the indexed element in a two-dimensional array
set3 Sets the indexed element in a three-dimensional array
set4 Sets the indexed element in a four-dimensional array
set5 Sets the indexed element in a five-dimensional array
set6 Sets the indexed element in a six-dimensional array
set7 Sets the indexed element in a seven-dimensional array
set Sets the indexed element in an array of any dimension
dimen Returns the dimension of the array
lower Returns the lower bound of the specified dimension
upper Returns the upper bound of the specified dimension
stride Returns the stride of the specified dimension
length Returns the length of the Array in the specified dimension
isColumnOrder Returns true if the array is a dense column-major order array, false otherwise
isRowOrder Returns true if the array is a dense row-major order array, false otherwise
copy Copies the contents of source array to dest array
ensure Returns an array with guaranteed ordering and dimension from any array.
first Provides direct access to the element data of the array.

The functions are listed succinctly in Table 6.3 as well as in detail over the next few pages.

Function: createCol

SIDL
/* C */ struct sidl_double__array* sidl_double__array_createCol(int32_t dimen, const int32_t lower[], const int32_t upper[]); // // C++ static sidl::array<double> sidl::array<double>::createCol(int32_t dimen, const int32_t lower[], const int32_t upper[]); C C FORTRAN 77 subroutine sidl_double__array_createCol_f(dimen, lower, upper, result) integer*4 dimen integer*4 lower(dimen), upper(dimen) integer*8 result ! ! FORTRAN 90 subroutine createCol(lower, upper, result) integer (selected_int_kind(9)), dimension(:), intent(in) :: lower, upper type (sidl_double_3d), intent(out) :: result ! type depends on dimension ! dimension of result is inferred from the size of lower // Java // (isRow should be false to get a column order array) public Array(int dim, int[] lower, int[] upper, boolean isRow);

This method creates a column-major, multi-dimensional array in a contiguous block of memory. dimen should be strictly greater than zero, and lower and upper should have dimen elements. lower[i] must be less than or equal to upper[i]-1 for i ≥ 0 and i < dimen. If this function fails for some reason, it returns NULL. lower[i] specifies the smallest valid index for dimension i, and upper[i] specifies the largest. Note this definition is somewhat un-C like where the upper bound is often one past the end. In SIDL, the size of dimension i is 1 + upper[i] - lower[i].

The function makes copies of the information provided by dimen, lower, and upper, so the caller is not obliged to maintain those values after the function call.

For Fortran, the new array is returned in the last parameter, result. A zero value in result indicates that the operation failed. For Fortran 90/95, you can use the function not_null to verify that result is a valid array.

Function: createRow

ANSI C
/* C */ struct sidl_double__array* sidl_double__array_createRow(int32_t dimen, const int32_t lower[], const int32_t upper[]); // // C++ static sidl::array<double> sidl::array<double>::createRow(int32_t dimen, const int32_t lower[], const int32_t upper[]); C C FORTRAN 77 subroutine sidl_double__array_createRow_f(dimen, lower, upper, result) integer*4 dimen integer*4 lower(dimen), upper(dimen) integer*8 result ! ! FORTRAN 90 subroutine createRow(lower, upper, result) integer (selected_int_kind(9)), dimension(:), intent(in) :: lower, upper type(sidl_double_3d), intent(out) :: result ! type depends on dimension ! dimension of result is inferred from the size of lower // Java // (isRow should be true to get a row order array) public Array(int dim, int[] lower, int[] upper, boolean isRow);

This method creates a row-major, multi-dimensional array in a contiguous block of memory. Other than the difference in the ordering of the array elements, this method is identical to createCol.

Function: create1d

ANSI C
/* C */ struct sidl_double__array* sidl_double__array_create1d(int32_t len); // C++ static sidl::array<double> sidl::array<double>::create1d(int32_t len); C FORTRAN 77 subroutine sidl_double__array_create1d_f(len, result) integer*4 len integer*8 result ! FORTRAN 90 subroutine create1d(len, result) integer (selected_int_kind(9)), intent(in) :: len type(sidl_double_1d), intent(out) :: result // Java public Array1(int s0, boolean isRow);

This method creates a dense, one-dimensional vector of ints with a lower index of 0 and an upper index of len − 1. This is defined primarily as a convenience for C and C++ programmers; Fortran programmers should note that this subroutine creates arrays whose lower index is 0 not like standard Fortran arrays whose lower index is 1. If len ≤ 0, this routine returns NULL.

Function: create2dCol

ANSI C
/* C */ struct sidl_double__array* sidl_double__array_create2dCol(int32_t m, int32_t n); // C++ static sidl::array<double> sidl::array<double>::create2dCol(int32_t m, int32_t n); C FORTRAN 77 subroutine sidl_double__array_create2dCol_f(m, n, result) integer*4 m, n integer*8 result ! FORTRAN 90 subroutine create2dCol(m, n, result) integer (selected_int_kind(9)), intent(in) :: m, n type(sidl_double_2d), intent(out) :: result // Java // isRow should be false to get a column order array public Array2(int s0, int s1, boolean isRow);

This method creates a dense, column-major, two-dimensional array of ints with a lower index of (0, 0) and an upper index of (m − 1, n − 1). If m ≤ 0 or n ≤ 0, this method returns NULL. This is defined primarily as a convenience for C and C++ programmers; Fortran programmers should note that this subroutine creates arrays whose lower index is 0 not like standard Fortran arrays whose lower index is 1.

Function: create2dRow

ANSI C
/* C */ struct sidl_double__array* sidl_double__array_create2dRow(int32_t m, int32_t n); // C++ static sidl::array<double> sidl::array<double>::create2dRow(int32_t m, int32_t n); C FORTRAN 77 subroutine sidl_double__array_create2dRow_f(m, n, result) integer*4 m, n integer*8 result ! FORTRAN 90 subroutine create2dRow(m, n, result) integer (selected_int_kind(9)), intent(in) :: m, n type(sidl_double_2d), intent(out) :: result // Java // isRow should be false to get a column order array public Array2(int s0, int s1, boolean isRow);

This method creates a dense, row-major, two-dimensional array of ints with a lower index of (0, 0) and an upper index of (m − 1, n − 1). If m ≤ 0 or n ≤ 0, this method returns NULL. This is defined primarily as a convenience for C and C++ programmers; Fortran programmers should note that this subroutine creates arrays whose lower index is 0 not like standard Fortran arrays whose lower index is 1.

Function: slice

ANSI C
/* C */ struct sidl_double__array * sidl_double__array_slice(struct sidl_double__array *src, int32_t dimen, const int32_t numElem[], const int32_t *srcStart, const int32_t *srcStride, const int32_t *newStart); // // C++ array<double> sidl::array<double>::slice(int dimen, const int32_t numElem[], const int32_t *srcStart = 0, const int32_t *srcStride = 0, const int32_t *newStart = 0); C C FORTRAN 77 subroutine sidl_double__array_slice_f(src, dimen, numElem, srcStart, $ srcStride, newStart, result) integer*8 src, result integer*4 dimen integer*4 numElem(srcDimen), srcStart(srcDimen) integer*4 srcStride(srcDimen), newStart(dimen) ! ! FORTRAN 90 subroutine slice(src, dimen, numElem, srcStart, srcStride, newStart, result) type(sidl_double_3d), intent(in) :: src ! type depends on dimension type(sidl_double_2d), intent(out) :: result ! type depends on dimension integer (selected_int_kind(9)), intent(in) :: dimen integer (selected_int_kind(9)), intent(in), dimension(:) :: & numElem, srcStart, srcStride, newStart // Java public native Array _slice(int dimen, int[] numElem, int[] srcStart, int[] srcStride, int[] newStart);

This method will create a sub-array of another array. The resulting array shares data with the original array. The new array can be of the same dimension or potentially less than the original array. If you are removing a dimension, indicate the dimensions to remove by setting numElem[i] to zero for any dimension i that should go away in the new array. The meaning of each argument is covered below.

src
the array to be created will be a subset of this array. If this argument is NULL, NULL will be returned. The returned array borrows data from src, so modifying one array modifies both. In C++, the this pointer takes the place of src.
dimen
this argument must be greater than zero and less than or equal to the dimension of src. An illegal value will cause a NULL return value.
numElem
this specifies how many elements from src should be in the new array in each dimension. A zero entry indicates that the dimension should not appear in the new array. This argument should be an array with an entry for each dimension of src. NULL will be returned for src if either
srcStart[i] + numElem[i] * srcStride[i]>upper[i], or
srcStart[i] + numElem[i] * srcStride[i]<lower[i]
srcStart
this parameter specifies which element of src will be the first element of the new array. If this argument is NULL, the first element of src will be the first element of the new array. If non-NULL, this argument provides the coordinates of an element of src, so it must have an entry for each dimension of src. NULL will be returned for src if either
srcStart[i] < lower[i], or srcStart[i] > upper[i].
srcStride
this argument lets you specify the stride between elements of src for each dimension. For example with a stride of 2, you could create a sub-array with only the odd or even elements of src. If this argument is NULL, the stride is taken to be one in each dimension. If non-NULL, this argument should be an array with an entry for each dimension of src. The stride values are relative to the original source array, src, so the default stride of one in each dimension is appropriate for cases where you want a dense subsection of the original array.
newLower
this argument is like the lower argument in a create method. It sets the coordinates for the first element in the new array. If this argument is NULL, the values indicated by srcStart will be used. If non-NULL, this should be an array with dimen elements.

Assuming the method is successful and the return value is named newArray, src[srcStart] refers to the same underlying element as newArray[newStart].

If src is not a borrowed array (i. e., it manages its own data), the returned array can manage its by keeping a reference to src. It is not considered a borrowed array for purposes of smartCopy.

Function: borrow

ANSI C
/* C */ struct sidl_double__array* sidl_double__array_borrow(double* firstElement, int32_t dimen, const int32_t lower[], const int32_t upper[], const int32_t stride[]); // // C++ void sidl::array<double>::borrow(double* firstElement, int32_t dimen, const int32_t lower[], const int32_t upper[], const int32_t stride[]); C C FORTRAN 77 subroutine sidl_double__array_borrow_f(firstElement, dimen, lower, $ upper, stride, result) real*8 firstElement() integer*4 dimen, lower(dimen), upper(dimen), stride(dimen) integer*8 result ! ! FORTRAN 90 subroutine borrow(firstElement, dimen, lower, upper, stride, & result) real (selected_real_kind(17,308)), intent(in) :: firstElement integer (selected_int_kind(9)), intent(in) :: dimen integer (selected_int_kind(9)), dimension(:), intent(in) :: lower, upper,& stride type(sidl_double_1d), intent(out) :: result ! type depends on array dimension

This method creates a proxy SIDL multi-dimensional array using data provided by a third party. In some cases, this routine can be used to avoid making a copy of the array data. dimen, lower, and upper have the same meaning and constraints as in SIDL_double__array_createCol. The firstElement argument should be a pointer to the first element of the array; in this context, the first element is the one whose index is lower.

stride[i] specifies the signed offset from one element in dimension i to the next element in dimension i. For a one dimensional array, the first element has the address firstElement, the second element has the address firstElement + stride[0], the third element has the address firstElement + 2 * stride[0], etc. The algorithm for determining the address of the element in a multi-dimensional array whose index is in array ind[] is as follows:

ANSI C
int32_t* addr = firstElement; for(int i = 0; i < dimen; ++i) { addr += (ind[i] - lower[i])*stride[i]; } /* now addr is the address of element ind */

Note elements of stride need not be positive.

The function makes copies of the information provided by dimen, lower, upper, and stride. The type of firstElement is changed depending on the array value type (see Table 6.2).

Function: smartCopy

ANSI C
/* C */ struct sidl_double__array* sidl_double__array_smartCopy(struct sidl_double__array *array); // C++ void sidl::array<double>::smartCopy(); C FORTRAN 77 subroutine sidl_double__array_smartCopy_f(array, result) integer*8 array, result ! FORTRAN 90 subroutine smartCopy(array, result) type(sidl_double_1d), intent(in) :: array ! type depends on dimension type(sidl_double_1d), intent(out) :: result ! type depends on dimension // Java public native Array _smartCopy();

This method will copy a borrowed array or increment the reference count of an array that is able to manage its own data. This method is useful when you want to keep a copy of an incoming array. The C++ method operates on this.

Function: addRef

ANSI C
/* C */ void sidl_double__array_addRef(struct sidl_double__array* array); // C++ void sidl::array<double>::addRef() throw ( NullIORException ); C FORTRAN 77 subroutine sidl_double__array_addRef_f(array) integer*8 array ! FORTRAN 90 subroutine addRef(array) type(sidl_double_1d), intent(in) :: array ! type depends on array dimension

This increments the reference count by one. In C++, this method should be avoided because the C++ wrapper class manages the reference count for you.

Function: deleteRef

ANSI C
/* C */ void sidl_double__array_deleteRef(struct sidl_double__array* array); // C++ void sidl::array<double>::deleteRef() throw ( NullIORException ); C FORTRAN 77 subroutine sidl_double__array_deleteRef_f(array) integer*8 array ! FORTRAN 90 subroutine deleteRef(array) type(sidl_double_1d), intent(out) :: array ! type depends on dimension

This decreases the reference count by one. If this reduces the reference count to zero, the resources associated with the array are reclaimed. In C++, this method should be avoided because the C++ wrapper class manages the reference count for you.

Function: get1

ANSI C
/* C */ double sidl_double__array_get1(const struct sidl_double__array* array, int32_t i1); // C++ double sidl::array<double>::get(int32_t i1); C FORTRAN 77 subroutine sidl_double__array_get1_f(array, i1, result) integer*8 array integer*4 i1 real*8 result ! FORTRAN 90 subroutine get(array, i1, result) type(sidl_int_1d), intent(in) :: array integer (selected_int_kind(9)), intent(in) :: i1 real (selected_real_kind(17,308)), intent(out) :: result // Java public double get(int i);

This method returns the element with index i1 for a one dimensional array. The return type of this method is the value type for the SIDL type being held (see Table 6.2). This method must only be called for one dimensional arrays. For objects and interfaces, the client owns the returned reference (i. e., the client is obliged to call deleteRef() when they are done with the reference unless it is NULL). For arrays of strings, the client owns the returned string (i. e., the client is obliged to call free on the returned pointer unless it is NULL). There is no reliable way to determine from the return value cases when i1 is out of bounds.

Function: get2

ANSI C
/* C */ double sidl_double__array_get2(const struct sidl_double__array* array, int32_t i1, int32_t i2); // C++ double sidl::array<double>::get(int32_t i1, int32_t i2); C FORTRAN 77 subroutine sidl_int__array_get2_f(array, i1, i2, result) integer*8 array integer*4 i1, i2 real*8 result ! FORTRAN 90 subroutine get(array, i1, i2, result) type(sidl_int_2d), intent(in) :: array integer (selected_int_kind(9)), intent(in) :: i1, i2 real (selected_real_kind(17,308)), intent(out) :: result // Java public double get(int i, int j);

This method returns the element with indices (i1, i2) for a two dimensional array. The return type of this method is the value type for the SIDL type being held (see Table 6.2. This method must only be called for two dimensional arrays. For objects and interfaces, the client owns the returned reference (i. e., the client is obliged to call deleteRef when they are done with the reference unless it is NULL). For arrays of strings, the client owns the returned string (i. e., the client is obliged to call free on the returned pointer unless it is NULL). There is no reliable way to determine from the return value cases when i1, i2 are out of bounds.

Function: get3

ANSI C
/* C */ double sidl_double__array_get3(const struct sidl_double__array* array, int32_t i1, int32_t i2, int32_t i3); // C++ double sidl::array<double>::get(int32_t i1, int32_t i2, int32_t i3); C FORTRAN 77 subroutine sidl_double__array_get3_f(array, i1, i2, i3, result) integer*8 array integer*4 i1, i2, i3 real*8 result ! FORTRAN 90 subroutine get(array, i1, i2, i3, result) type(sidl_double_3d), intent(in) :: array integer (selected_int_kind(9)), intent(in) :: i1, i2, i3 real (selected_real_kind(17,308)), intent(out) :: result // Java public double get(int i, int j, int k);

This method returns the element with indices (i1, i2, i3) for a three dimensional array. The return type of this method is the value type for the SIDL type being held (see Table 6.2). This method must only be called for three dimensional arrays. For objects and interfaces, the client owns the returned reference (i. e., the client is obliged to call deleteRef() when they are done with the reference unless it is NULL). For arrays of strings, the client owns the returned string (i. e., the client is obliged to call free() on the returned pointer unless it is NULL). There is no reliable way to determine from the return value cases when i1, i2, i3 are out of bounds.

Function: get4

ANSI C
/* C */ double sidl_double__array_get4(const struct sidl_double__array* array, int32_t i1, int32_t i2, int32_t i3, int32_t i4); // C++ double sidl::array<double>::get(int32_t i1, int32_t i2, int32_t i3, int32_t i4); C FORTRAN 77 subroutine sidl_double__array_get4_f(array, i1, i2, i3, i4, result) integer*8 array integer*4 i1, i2, i3, i4 real*8 result ! FORTRAN 90 subroutine get(array, i1, i2, i3, i4, result) type(sidl_double_4d), intent(in) :: array integer (selected_int_kind(9)), intent(in) :: i1, i2, i3, i4 real (selected_real_kind(17,308)), intent(out) :: result // Java public double get(int i, int j, int k, int l);

This method returns the element with indices(i1, i2, i3, i4) for a four dimensional array. The return type of this method is the value type for the SIDL type being held (see Table 6.2). This method must only be called for four dimensional arrays. For objects and interfaces, the client owns the returned reference (i. e., the client is obliged to call deleteRef() when they are done with the reference unless it is NULL). For arrays of strings, the client owns the returned string (i. e., the client is obliged to call free() on the returned pointer unless it is NULL). There is no reliable way to determine from the return value cases when i1, i2, i3, or i4 are out of bounds.

Function: get5-7

Methods get5get7 are defined in an analogous way.

Function: get

ANSI C
/* C */ double sidl_double__array_get(const struct sidl_double__array* array, const int32_t indices[]); // C++ double sidl::array<double>::get(const int32_t indices[]); C FORTRAN 77 subroutine sidl_double__array_get_f(array, indices, result) integer*8 array integer*4 indices() real*8 result ! FORTRAN 90 subroutine get(array, indices, result) type(sidl_real_1d), intent(in) :: array ! type depends on dimension integer (selected_int_kind(9)), dimension(:), intent(in) ::indices real (selected_real_kind(17,308)), intent(out) :: result // Java public native double _get(int i, int j, int k, int l, int m, int n, int o);

This method returns the element whose index is indices for an array of any dimension. The return type of this method is the value type for the SIDL type being held (see Table 6.2). This method can be called for any positively dimensioned array. For objects and interfaces, the client owns the returned reference (i. e., the client is obliged to call deleteRef() when they are done with the reference unless it is NULL). For arrays of strings, the client owns the returned string (i. e., the client is obliged to call free() on the returned pointer unless it is NULL). There is no reliable way to determine from the return value cases when indices has an element out of bounds.

Function: set1

ANSI C
/* C */ void sidl_double__array_set1(struct sidl_double__array* array, int32_t i1, double value)); // C++ void sidl::array<double>::set(int32_t i1, double value); C FORTRAN 77 subroutine sidl_double__array_set1_f(array, i1, value) integer*8 array integer*4 i1 real*8 value ! FORTRAN 90 subroutine set(array, i1, value) type(sidl_double_1d), intent(in) :: array integer (selected_int_kind(9)), intent(in) :: i1, real (selected_real_kind(17,308)), intent(in) :: value // Java public void set(int i, double value) {

This method sets the value in index i1 of a one dimensional array to value. The type of the argument value is the value type for the SIDL type being held (see Table 6.2). This method must only be called for one dimensional arrays. For arrays of objects and interfaces, the array will make its own reference by calling addRef() on value, so the client retains its reference to value. For arrays of strings, the array will make a copy of the string, so the client retains ownership of the value pointer.

Function: set2

ANSI C
/* C */ void sidl_double__array_set2(struct sidl_double__array* array, int32_t i1, int32_t i2, double value)); // C++ void sidl::array<double>::set(int32_t i1, int32_t i2, double value); C FORTRAN 77 subroutine sidl_double__array_set2_f(array, i1, i2, value) integer*8 array integer*4 i1, i2 real*8 value ! FORTRAN 90 subroutine set(array, i1, i2, value) type(sidl_int_2d), intent(in) :: array integer (selected_int_kind(9)), intent(in) :: i1, i2 real (selected_real_kind(17,308)), intent(in) :: value // Java public void set(int i, int j, double value) {

This method sets the value in index (i1, i2) of a two dimensional array to value. The type of the argument value is the value type for the SIDL type being held (see table  6.2). This method must only be called for two dimensional arrays. For arrays of objects and interfaces, the array will make its own reference by calling addRef() on value, so the client retains its reference to value. For arrays of strings, the array will make a copy of the string, so the client retains ownership of the value pointer.

Function: set3

ANSI C
/* C */ void sidl_double__array_set3(struct sidl_double__array* array, int32_t i1, int32_t i2, int32_t i3, double value)); // C++ void sidl::array<double>::set(int32_t i1, int32_t i2, int32_t i3, double value); C FORTRAN 77 subroutine sidl_double__array_set3_f(array, i1, i2, i3, value) integer*8 array integer*4 i1, i2, i3 real*8 value ! FORTRAN 90 subroutine set(array, i1, i2, i3, value) type(sidl_double_3d), intent(in) :: array integer (selected_int_kind(9)), intent(in) :: i1, i2, i3 real (selected_real_kind(17,308)), intent(in) :: value // Java public void set(int i, int j, int k, double value) {

This method sets the value in index (i1, i2, i3) of a three dimensional array to value. The type of the argument value is the value type for the SIDL type being held (see table  6.2). This method must only be called for three dimensional arrays. For arrays of objects and interfaces, the array will make its own reference by calling addRef() on value, so the client retains its reference to value. For arrays of strings, the array will make a copy of the string, so the client retains ownership of the value pointer.

Function: set4

ANSI C
/* C */ void sidl_double__array_set4(struct sidl_double__array* array, int32_t i1, int32_t i2, int32_t i3, int32_t i4, double value)); // // C++ void sidl::array<double>::set(int32_t i1, int32_t i2, int32_t i3, int32_t i4, double value); C C FORTRAN 77 subroutine sidl_double__array_set4_f(array, i1, i2, i3, i4, value) integer*8 array integer*4 i1, i2, i3, i4 real*8 value ! ! FORTRAN 90 subroutine set(array, i1, i2, i3, i4, value) type(sidl_double_4d), intent(in) :: array integer (selected_int_kind(9)), intent(in) :: i1, i2, i3, i4 real (selected_real_kind(17,308)), intent(in) :: value // Java public void set(int i, int j, int k, int l, double value) {

This method sets the value in index (i1, i2, i3, i4) of a four dimensional array to value. The type of the argument value is the value type for the SIDL type being held (see table  6.2). This method must only be called for four dimensional arrays. For arrays of objects and interfaces, the array will make its own reference by calling addRef() on value, so the client retains its reference to value. For arrays of strings, the array will make a copy of the string, so the client retains ownership of the value pointer.

Function: set5-7

Methods set5set7 are defined in an analogous way.

Function: set

ANSI C
/* C */ void sidl_double__array_set(struct sidl_double__array* array, const int32_t indices[], double value); // C++ void sidl::array<double>::set(const int32_t indices[], double value); C FORTRAN 77 subroutine sidl_double__array_set_f(array, indices, value) integer*8 array integer*4 indices() real*8 value ! FORTRAN 90 subroutine set(array, indices, value) type(sidl_double_1d), intent(in) :: array ! type depends on dimension integer (selected_int_kind(9)), intent(in), dimension(:) :: indices real (selected_real_kind(17,308)), intent(in) :: value // Java public native void _set(int i, int j, int k, int l, int m, int n, int o, double value);

This method sets the value in index indices for an array of any dimension to value. The type of the argument value is the value type for the SIDL type being held (see table  6.2). For arrays of objects and interfaces, the array will make its own reference by calling addRef() on value, so the client retains its reference to value. For arrays of strings, the array will make a copy of the string, so the client retains ownership of the value pointer.

Function: dimen

ANSI C
/* C */ int32_t sidl_double__array_dimen(const struct sidl_double__array *array); // C++ int32_t sidl::array<double>::dimen() const; C FORTRAN 77 subroutine sidl_double__array_dimen_f(array, result) integer*8 array integer*4 result ! FORTRAN 90 integer (selected_int_kind(9)) dimen(array) type(sidl_double_1d) :: array ! type depends on dimension // Java public native int _dim();

This method returns the dimension of the array.

Function: lower

ANSI C
/* C */ int32_t sidl_double__array_lower(const struct sidl_double__array *array, int32_t ind); // C++ int32_t sidl::array<double>::lower(int32_t ind) const; C FORTRAN 77 subroutine sidl_double__array_lower_f(array, ind, result) integer*8 array integer*4 ind, result ! FORTRAN 90 integer (selected_int_kind(9)) function lower(array, ind) type(sidl_double_1d), intent(in) :: array ! type depends on dimension integer (selected_int_kind(9)) :: ind // Java public native int _lower(int dim);

This method returns the lower bound on the index for dimension ind of array.

Function: upper

ANSI C
/* C */ int32_t sidl_double__array_upper(const struct sidl_double__array *array, int32_t ind); // C++ int32_t sidl::array<double>::upper(int32_t ind) const; C FORTRAN 77 subroutine sidl_double__array_upper_f(array, ind, result) integer*8 array integer*4 ind, result ! FORTRAN 90 integer (selected_int_kind(9)) function upper(array, ind) type(sidl_double_1d), intent(in) :: array ! type depends on dimension integer (selected_int_kind(9)), intent(in) :: ind // Java public native int _upper(int dim);

This method returns the upper bound on the index for dimension ind of array. If the upper bound is greater than or equal to the lower bound, the upper bound is a valid index (i. e., it is not one past the end).

Function: stride

ANSI C
/* C */ int32_t sidl_double__array_stride(const struct sidl_double__array *array, int32_t ind); // C++ int32_t sidl::array<double>::stride(int32_t ind) const; C FORTRAN 77 subroutine sidl_double__array_stride_f(array, ind, result) integer*8 array integer*4 ind, result ! FORTRAN 90 integer (selected_int_kind(9)) function stride(array, ind) type(sidl_double_1d), intent(in) :: array ! type depends on dimension integer (selected_int_kind(9)) :: ind // Java public native int _stride(int dim);

This method returns the stride for a particular dimension. This stride indicates how much to add to a pointer to get for the current element this the particular dimension to the next.

Function: length

ANSI C
/* C */ int32_t sidl_double__array_length(const struct sidl_double__array *array, int32_t ind); // C++ Default dimension is 1. int32_t sidl::array<int32_t>::length(int32_t ind = 0) const; C FORTRAN 77 subroutine sidl_double__array_length_f(array, ind, result) integer*8 array integer*4 ind, result ! FORTRAN 90 integer (selected_int_kind(9)) function length(array, ind) type(sidl_double_1d), intent(in) :: array ! type depends on dimension integer (selected_int_kind(9)) :: ind // Java public native int _length(int dim); // For one dimensional Java arrays. Array1: public int lenth();

This method returns the length for a particular dimension. It is equivalent to the statement upper(dim) - lower(dim) + 1.

There is also a shortcut for one-dimensional arrays available in C++ and Java. In C++, if length is called with no arguments, it defaults to the first dimension. In Java Array1 one-dimensional Java arrays have a length function that takes no arguments.

Function: isColumnOrder

ANSI C
/* C */ sidl_bool sidl_double__array_isColumnOrder(const struct sidl_double__array *array); // C++ bool sidl::array<double>::isColumnOrder() const; C FORTRAN 77 subroutine sidl_double__array_isColumnOrder_f(array, result) integer*8 array logical result ! FORTRAN 90 logical function isColumnOrder(array) type(sidl_double_2d), intent(in) :: array ! type depends on dimension // Java public native boolean _isColumnOrder();

This method returns a true value if and only if array is dense, column-major ordered array. It does not modify the array at all.

Function: isRowOrder

ANSI C
/* C */ sidl_bool sidl_double__array_isRowOrder(const struct sidl_double__array *array); // C++ bool sidl::array<double>::isRowOrder() const; C FORTRAN 77 subroutine sidl_double__array_isRowOrder_f(array, result) integer*8 array logical result ! FORTRAN 90 logical function isRowOrder(array) type(sidl_double_1d), intent(int) :: array ! type depends on dimension // Java public native boolean _isRowOrder();

This method returns a true value if and only if array is dense, row-major ordered array. It does not modify the array at all.

Function: copy

ANSI C
/* C */ void sidl_double__array_copy(const struct sidl_double__array *src struct sidl_double__array *dest); // C++ void sidl::array<double>::copy(const sidl::array<double> &src); C FORTRAN 77 subroutine sidl_double__array_copy_f(array, dest) integer*8 array, dest ! FORTRAN 90 subroutine copy(array, dest) type(sidl_double_1d), intent(in) :: array ! type depends on array dimension type(sidl_double_1d), intent(in) :: dest ! type depends on array dimension // Java public void _copy(sidl.Double.Array dest);

This method copies the contents of src to dest. For the copy to take place, both arrays must exist and be of the same dimension. This method will not modify dest’s size, index bounds, or stride; only the array element values of dest may be changed by this function. No part of src is changed by this method.

If dest has different index bounds than src, this method only copies the elements where the two arrays overlap. If dest and src have no indices in common, nothing is copied. For example, if src is a 1-d array with elements 0-5 and dest is a 1-d array with element 2-3, this function will copy element 2 and 3 from src to dest. If dest had elements 4-10, this method could copy elements 4 and 5.

Function: ensure

ANSI C
/* C */ struct sidl_double__array * sidl_double__array_ensure(const struct sidl_double__array *src, int32_t dimen, int ordering); // C++ void sidl::array<double>::ensure(int32_t dimen, int ordering); C FORTRAN 77 subroutine sidl_double__array_ensure_f(src, dimen, ordering, result) integer*8 src, result integer*4 dimen, ordering ! FORTRAN 90 subroutine ensure(src, dimen, ordering, result) type(sidl_double_1d), intent(in) :: src ! type depends on array dimension type(sidl_double_1d), intent(out) :: result! type depends on array dimension integer (selected_int_kind(9)) :: dimen, ordering

This method is used to obtain a matrix with a guaranteed ordering and dimension from an array with uncertain properties. If the incoming array has the required ordering and dimension, its reference count is incremented, and it is returned. If it doesn’t, a copy with the correct ordering is created and returned. In either case, the caller knows that the returned matrix (if not NULL) has the desired properties.

This method is used internally to enforce the array ordering constraints in SIDL. Clients can use it in similar ways. However, because the method was intended as an internal Babel feature, is not available in Java or Python.

The ordering parameter should be one of the constants defined in enum sidl_array_ordering (e. g.
sidl_general_order, sidl_column_major_order, or sidl_row_major_order). If you pass in
sidl_general_order, this routine will only check the dimension of the matrix.

Function: first

ANSI C
/* C */ double * sidl_double__array_first(const struct sidl_double__array *src); // C++ double* first() throw(); C FORTRAN 77 subroutine sidl_double__array_access_f(array, ref, lower, upper, $ stride, index) integer*8 array, index integer*4 lower(), upper(), stride() integer*4 ref()

This method provides direct access to the element data. Using this pointer and the stride information, you can perform your own array accesses without function calls. This method isn’t available for arrays of strings, interface and objects because of memory/reference management issues. There is no equivalent of this function in Java or Python. To see how to get direct array access in Fortran 90/95, see Chapter 11.

The Fortran versions of the method return the lower, upper and stride information in three arrays, each with enough elements to hold an entry for each dimension of array. Because fortran 77 does not have pointers, you must pass in a reference array, array. Upon exit, ref(index) is the first element of the array. The type of ref depends on the type of the array.


WARNING:
While calling the Fortran direct access routines, there is a possibility of an alignment error between your reference pointer, ref, and the pointer to the first element of the array data. The problem is more likely with arrays of double or dcomplex; although, it could occur with any type on some future platform. If index is zero on return, an alignment error occurred. If an alignment error occurs, you may be able to solve it by recompiling your Fortran files with flags to force doubles to be aligned on 8 byte boundaries. For example, the -malign-double flag for g77 forces doubles to be aligned on 64-bit boundaries. An alignment error occurs when (char *)ref minus (char *)sidl_double__array_first(array) is not integer divisible by sizeof(datatype) where ref refers to the address of the reference array.


Here is an example fortran 77 subroutine to output each element of a 1-dimensional array of doubles using the direct access routine. Fortran 90/95 has a pointer in the array derived type when direct access is possible.

fortran 77
C This subroutine will print each element of an array of doubles subroutine print_array(dblarray) implicit none integer*8 dblarray, index real*8 refarray(1) integer*4 lower(1), upper(1), stride(1), dimen, i if (dblarray .ne. 0) then call sidl_double__array_dimen_f(dblarray, dimen) if (dimen .eq. 1) then call sidl_double__array_access_f(dblarray, refarray, $ lower, upper, stride, index) if (index .ne. 0) then do i = lower(1), upper(1) write(*,*) refarray(index + (i-lower(1))*stride(1)) enddo else write(*,*) 'Alignment error occured' endif endif endif end

For a 2-dimensional array, the loop and array access is

fortran 77
do i = lower(1), upper(1) do j = lower(2), upper(2) write(*,*) refarray(index+(i-lower(1))*stride(1)+ $ (j - lower(2))*stride(2)) enddo enddo

Suppose you are wrapping a legacy Fortran application and you need to pass a SIDL array to a Fortran subroutine. Further suppose there is a fortran 77 and Fortran 90/95 version of the subroutine. For example, the fortran 77 subroutine has a signature such as:

fortran 77
subroutine TriedAndTrue(x, n) integer n real*8 x(n) C insert wonderful, efficient, debugged code here end

The Fortran 90/95 subroutine has basically the same signature as follows:

Fortran 90/95
subroutine TriedAndTrue(x, n) integer (selected_int_kind(9)) :: n real (selected_real_kind(17, 308)) :: x(n) ! insert wonderful, efficient, debugged code here end subroutine TriedAndTrue

Here is one way to wrap this method using SIDL. First of all, the SIDL method definition specifies that the array must be a 1-dimensional, column-major ordered array. This forces the incoming array to be a dense column.

SIDL
static void TriedAndTrue(inout array<double,1,column-major> arg);

Given that method definition in a class named Class and a package named Pkg, the implementation of the wrapper should look something like the following for fortran 77:

fortran 77
subroutine Pkg_Class_TriedAndTrue_fi(arg) implicit none integer*8 arg C DO-NOT-DELETE splicer.begin(Pkg.Class.TriedAndTrue) real*8 refarray(1) integer*4 lower(1), upper(1), stride(1) integer*8 index integer n call sidl_double__array_access_f(arg, refarray, $ lower, upper, stride, index) if (index .ne. 0) then c we can assume stride(1) = 1 because of column-major specification n = 1 + upper(1) - lower(1) call TriedAndTrue(refarray(index), n) else write(*,*) 'ERROR: array alignment' endif C DO-NOT-DELETE splicer.end(Pkg.Class.TriedAndTrue) end

Similarly, it should look something like the following for Fortran 90/95, where the include statements are required at the top of the “Impl” file to ensure proper handling of subroutine names that have automatically been mangled by the Babel compiler:

Fortran 90/95
#include "Pkg_Class_fAbbrev.h" #include "sidl_BaseClass_fAbbrev.h" #include "sidl_BaseInterface_fAbbrev.h" ! DO-NOT-DELETE splicer.begin(_miscellaneous_code_start) #include "sidl_double_fAbbrev.h" ! DO-NOT-DELETE splicer.end(_miscellaneous_code_start) . . . subroutine Pkg_Class_TriedAndTrue_mi(arg) ! DO-NOT-DELETE splicer.begin(Pkg.Class.TriedAndTrue.use) use SIDL_double_array ! DO-NOT-DELETE splicer.end(Pkg.Class.TriedAndTrue.use) implicit none type(sidl_double_a) :: arg ! DO-NOT-DELETE splicer.begin(Pkg.Class.TriedAndTrue) real (selected_real_kind(17,308)), dimension(1) :: refarray integer (selected_int_kind(8)), dimension(1) :: low, up, str integer (selected_int_kind(8)) :: index, n call access(arg, refarray, low, up, str, index) if (index .ne. 0) then ! We can assume stride(1) = 1 because of column-major specification n = 1 + upper(1) - lower(1) call TriedAndTrue(refarray(index), n) else write(*,*) 'ERROR: array alignment' endif ! DO-NOT-DELETE splicer.end(Pkg.Class.TriedAndTrue) end subroutine Pkg_Class_TriedAndTrue_mi

The C Macro API

Many of the SIDL array access functions have a corresponding C macro API for those who fear the function overhead of the C function API. When efficiency is not a concern, we recommend using the function API, but the C macro API is preferable to the direct access to the data structure. Parts of the macro API are not available for arrays of strings, interfaces or objects because the issues associated with memory and object reference management.

The macro API is very similar to the function API; however, a single set of macros applies to all the supported array types. The macro names are independent of the type of array you’re accessing.

ANSI C
sidlArrayDim(array)

Return the dimension of array.

ANSI C
sidlLower(array,ind)

Return the lower bound on dimension ind.

ANSI C
sidlUpper(array,ind)

Return the upper bound on dimension ind.

ANSI C
sidlLength(array,ind)

Return the extent on dimension ind. The extent is equal to sidlUpper(array,ind) - sidlLower(array,ind) + 1.

ANSI C
sidlStride(array,ind)

Return the stride for dimension ind. The stride is the offset between elements in a particular dimension. It can be positive or negative. It is in terms of number of value types (i. e., it’s 1 means contiguous regardless of what data type).

The macros to access array elements of array elements are unavailable for arrays of strings, classes and interfaces.

ANSI C
sidlArrayElem1(array, ind1) sidlArrayElem2(array, ind1, ind2) sidlArrayElem3(array, ind1, ind2, ind3) sidlArrayElem4(array, ind1, ind2, ind3, ind4) sidlArrayElem5(array, ind1, ind2, ind3, ind4, ind5) sidlArrayElem6(array, ind1, ind2, ind3, ind4, ind5, ind6) sidlArrayElem7(array, ind1, ind2, ind3, ind4, ind5, ind6, ind7)

Provide access to array elements to arrays of dimension 1–7. This macro can appear on the left hand side of an assignment or on the right hand side in an expression. These macros blindly assume that the dimension and indices are correct.

The macros to access the address of array elements are unavailable for arrays of strings, classes, and interfaces.

ANSI C
sidlArrayAddr1(array, ind1) sidlArrayAddr2(array, ind1, ind2) sidlArrayAddr3(array, ind1, ind2, ind3) sidlArrayAddr4(array, ind1, ind2, ind3, ind4) sidlArrayAddr5(array, ind1, ind2, ind3, ind4, ind5) sidlArrayAddr6(array, ind1, ind2, ind3, ind4, ind5, ind6) sidlArrayAddr7(array, ind1, ind2, ind3, ind4, ind5, ind6, ind7)

Return the address of elements in arrays of dimension 1–7. This macro can appear on the left hand side of an assignment or on the right hand side in an expression. These macros blindly assume that the dimension and indices are correct.

The C Data Structure

If even the macro interface is not fast enough for you, you can access the internal data structure for all the basic types except string. You cannot access the internal data structure for arrays of strings, interfaces and objects.

The basic form of the C data structure for type XXXX is:

ANSI C
struct sidl__array_vtable { /* Release resources associted with the array (refcount at zero) */ void (*d_destroy)(struct sidl__array *); /* Clone or addRef depending on whether data is borrowed */ struct sidl__array *(*d_smartcopy)(struct sidl__array *); /* Return the type of the array. */ int32_t (*d_arraytype)(void); }; struct sidl__array { int32_t *d_lower; int32_t *d_upper; int32_t *d_stride; const struct sidl__array_vtable *d_vtable; int32_t d_dimen; int32_t d_refcount; }; struct sidl_XXXX__array { struct sidl__array d_metadata; <value type for XXXX> *d_firstElement; };

The string “<value type for XXXX>” should be replaced by something like sidl_boolfor an array of bool, int32_t for any array of int, double for an array of double, int64_t for an array of long, etc. (See Table 6.2)

d_dimen
tells the dimension of the multi-dimensional array. d_lower, d_upper, and d_stride each point to arrays of d_dimen int32_t’s. d_lower[i] provides the lower bound for the index in dimension i, and d_upper[i] provides the upper bound for the index in dimension i. Both the lower and upper bounds are valid index values; the upper bound is not one past the end.
d_borrowed
is true if the array does not managed the data that d_firstElement points too, and it is false otherwise. This mainly influences the behavior of the destructor.

Clients should not modify d_lower, d_upper, d_stride, d_dimen, d_borrowed or (in the case of pointers) the values to which they point.

d_stride[i]
determines how elements are packed in dimension i. A value of 1 means that to get from element j to j+1 in dimension i, you add one to the data pointer. Negative values for d_stride can be used to express a transposed matrix. The definition also allows either column or row major ordering for the data, and it also allows treating a subsection of an array as an array.

The data structure was inspired by the data structure used by Numeric Python; although, in Numeric Python, the stride is in terms of bytes. In SIDL, the stride is in terms of number of objects. One can convert to the Numeric Python view of things by multiplying the stride by the sizeof the value type.

6.5  Interface Contracts

Interface contracts define behaviors expected of callers (or clients) and callees (or servers) of methods. These behaviors are specified within clauses of SIDL interfaces and classes and may be checked at runtime through options used to establish an enforcement policy. Executable interface contracts thereby provide a mechanism for helping ensure software is implemented and used correctly. This section focuses on specification basics and traditional interface contract enforcement options.

Contract Clauses

Contract clauses define constraints on properties of methods (including argument and return values)2 and objects. Babel supports three types of clauses using SIDL syntax borrowed from Eiffel []. Those clauses are: preconditions, postconditions, and class invariants.

Each clause corresponds to a different set of enforcement points. Precondition and postcondition clauses are specified on a method basis. A precondition declares constraints on invocation of a method while a postcondition constrains its effects. In some cases, there may be properties needing to hold throughout the life of an instance of a class. Rather than require the assertions be specified in the precondition and postcondition clauses of every method, the class invariant clause, which can also be specified on interfaces, should be used.

The general structure of a SIDL method specification is provided below. The specification defines the method signature; that is, it provides the name, parameter list, return type, and any exceptions thrown (or raised) by the method. The SIDL specification can also include the definition of preconditions in the require clause and postconditions in the ensure clause.

[<type>] <identifier> ( [<parameters>] ) [throws <exception>]; [require <contract-clause-expressions>] [ensure <contract-clause-expressions>]

When contract clauses are added to the specification, each method’s throws clause must explicitly list the appropriate contract clause violation exception. The exceptions, as defined in sidl.sidl, are shown below. The need for explicitly declaring the exceptions is based on Babel’s current SIDL-to-C++ exceptions mapping in the generated middleware.

SIDL
/** * <code>PreViolation</code> indicates an assertion within a precondition * clause of the interface contract has been violated. */ class PreViolation extends SIDLException implements RuntimeException {} /** * <code>PostViolation</code> indicates an assertion within a postcondition * clause of the interface contract has been violated. */ class PostViolation extends SIDLException implements RuntimeException {} /** * <code>InvViolation</code> indicates an assertion within a invariant * clause of the interface contract has been violated. */ class InvViolation extends SIDLException implements RuntimeException { }

The basic structure of a contract clause, including the clause type (i. e., require, ensure, or invariants), is provided below. Each clause, when present, contains a list of assertions. Each assertion may be preceded by a label. The label serves two purposes. First, if thoughtfully written, it provides a succinct “description” of the purpose of the assertion in the specification. Second, through its automatic inclusion in the exception message of a violated contract, the label helps identify the offended assertion.

<clause-type> [label-1:] <assertion-expression-1>; [[label-2:] <assertion-expression-2>; ... [label-n:] <assertion-expression-n>;]

For example, the SIDL specification of a vector dot product method with a contract is:

SIDL
/** * Return the dot (, inner, or scalar) product of the specified vectors. */ double vuDot(in array<double> u, in array<double> v, in double tol) throws sidl.PreViolation, sidl.PostViolation; require not_null_u : u != null; u_is_1d : dimen(u) == 1; not_null_v : v != null; v_is_1d : dimen(v) == 1; same_size : size(u) == size(v); non_neg_tolerance : tol >= 0.0; ensure no_side_effects : is pure; vuAreEqual(u, v, tol) implies (result >= 0.0); (vuIsZero(u, tol) and vuIsZero(v, tol)) implies nearEqual(result, 0.0, tol);

This specification includes both precondition and postcondition clauses. The precondition clause, identified by require, contains six executable assertions. The first five assertions require the two normal SIDL arrays, u and v, be non-null, one-dimensional arrays of the same size. The fifth assertion requires the tolerance argument, tol, be non-negative. The postconditions clause, identified by ensure, contains three assertions. The is pure assertion indicates implementations should be side-effect free3. This allows the method to be included in the contract of another method. The remaining assertions indicate all implementations of the method must ensure the following, assuming the preconditions are satisfied:

  1. if u and v are equal then the result of calling vuDot should be non-negative; and
  2. if u and v are both zero vectors then the result should be within the provided tolerance.

The tolerance argument, tol, was only added here to support assertions in the postconditions clause. That is, the argument is not expected to be needed or used by any implementation of the method.

It is important to keep in mind that the assertions within interface contracts only need to hold at the method call boundary. The implementations of some methods may have to temporarily violate the contract during processing. However, as long as the corresponding assertions hold at the call boundary, the contract is not actually violated.

Optional interface contracts consist of clauses defining obligations on callers and callees. Clauses, when specified, must contain assertions required to hold at the appropriate point(s) during execution. Assertions within precondition (and invariant) clauses must hold immediately before the method is executed; whereas, postcondition (and invariant) clause assertions must hold immediately after control returns from the method. More information on SIDL specifications, including supported operators and built-in functions, can be found in Chapter 21.

Contract Enforcement

The options used to establish an interface contract enforcement policy should be based on the goals of a particular application run. During testing and debugging, contract enforcement tends to focus on determining whether the caller and callee conform to the specification. Historically, contract enforcement is disabled during deployment. Consequently, traditional interface contract enforcement tends to be all-or-nothing for one or more type of contract clause.

The simplest approach is to always enforce all contract clauses. SIDL provides the setEnforceAll helper method, whose specification is provided below, for setting the associated enforcement options. While this strategy makes testing straightforward, it is important to keep in mind that it carries the risk of not fully testing the compliance of the codes. For example, if the test suite is not sufficiently thorough, there is a risk of not exposing non-compliance of contract clauses in downstream methods when there can be dependencies involving sequences of method calls.

SIDL
/** * <code>EnfPolicy</code> maintains the current interface * contract enforcement policy. */ class EnfPolicy { /** * Sets the enforcement policy to always check the specified * type(s) of contracts. This is equivalent to calling * setPolicy() with ALWAYS as the enforcement frequency * and the specified (or default) contract class. * * @param contractClass Contract classification * [Default = ALLCLASSES] * @param clearStats TRUE if enforcement statistics are to be * cleared; FALSE otherwise. */ static void setEnforceAll(in ContractClass contractClass, in bool clearStats); /* ... */ }

A traditional alternative for test suites singling out callers from callees involves separately enforcing precondition and postcondition clauses. For example, when testing whether one or more implementations of an interface comply with their specification, enforcement is generally limited to postconditions (with test codes satisfying the preconditions). Once confidence is gained in the implementation, the callers may be tested with only precondition enforcement enabled (with test codes violating the preconditions). The full range of clause type-based ContractClass options are provided below. Separating clause enforcement in this manner therefore allows test suites to distinguish between caller and callee contract compliance.

SIDL
/** * Contract classification. The classification is used to filter * contract clauses by the corresponding characteristic(s). */ enum ContractClass { /** * All classifications of interface contract clauses. */ ALLCLASSES, /* ... */ /** * Only invariant clauses. */ INVARIANTS, /** * Invariant plus postcondition clauses. */ INVPOST, /** * Invariant plus precondition clauses. */ INVPRE, /* ... */ /** * Only postcondition clauses. */ POSTCONDS, /** * Only precondition clauses. */ PRECONDS, /** * Precondition plus postcondition clauses. */ PREPOST, /* ... */ };

Assertion enforcement has historically been considered to be too time consuming to allow during deployment. So the traditional approach has been to disable their enforcement. SIDL provides the setEnforceNone helper method, whose specification is shown below, for disabling contract enforcement at runtime.

SIDL
/** * <code>EnfPolicy</code> maintains the current interface * contract enforcement policy. */ class EnfPolicy { /* ... */ /** * Sets the policy options to disable all contract enforcement. * This is equivalent to calling setPolicy() with NEVER as the * enforcement frequency. * * @param clearStats TRUE if enforcement statistics are to be * cleared; FALSE otherwise. */ static void setEnforceNone(in bool clearStats); /* ... */ }

Enforcement policies in affect should be based on the goals of a given execution. Testing and debugging with contracts tend to focus on caller and callee compliance; therefore, enforcement can involve checking all or type-specific subsets of contract clauses. Contract enforcement is traditionally disabled during deployment, however. All of these strategies are supported through two SIDL helper methods. Language-specific examples of the use of these methods are provided in Part II. Additional information, including advanced and experimental enforcement capabilities, can be found in Chapter 21.

6.6  SIDL Runtime

The runtime library supports a collection of interfaces and classes, some of which form the basis of the SIDL object model while, as discussed in other sections of this chapter, others provide enhanced capabilities.

Inheritance

The object model core consists of base interfaces, classes, and exceptions. All interfaces implicitly inherit from sidl.BaseInterface. Classes implicitly inherit from sidl.BaseClass, which implements sidl.BaseInterface. Hence, all objects can be cast to sidl.BaseInterface and sidl.BaseClass. Exceptions must explicitly implement the interfaces in sidl.BaseException. The easiest way to do this is to extend sidl.SIDLException, which the basic Exception functionality, including getNote and setNote. One or more of these functions can also be overriden. If a method in SIDL claims to throw an object that does not inherit from sidl.BaseException, Babel will report it as an error.

Interfaces

The SIDL runtime library supports the six interface categories described below.

Base
The base class, interface, and exception upon which all Babel-enabled software builds.
Contract Enforcement
Contract enforcement policy class and contract clause exceptions used to establish enforcement options and identify, at runtime, contract clause violations, respectively.
Library Handler
The DLL and Loader classes facilitate dynamic loading of objects at runtime.
Introspection
The ClassInfo interface and ClassInfoI class enable checking meta-data associated with a class.
I/O
The input-output package used for serializing and deserializing SIDL types.
RMI
The rmi package used for managing remote method invocations.

The associated capabilities, as defined in sidl.sidl, are:

SIDL
// // File: sidl.sidl // Revision: @(#) $Revision$ // Date: $Date$ // Description: sidl interface description for the basic sidl run-time library // // Copyright (c) 2001-2007, The Regents of the University of Calfornia. // Produced at the Lawrence Livermore National Laboratory. // Written by the Components Team <components@llnl.gov> // UCRL-CODE-2002-054 // All rights reserved. // // This file is part of Babel. For more information, see // http://www.llnl.gov/CASC/components/. Please read the COPYRIGHT file // for Our Notice and the LICENSE file for the GNU Lesser General Public // License. // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License (as published by // the Free Software Foundation) version 2.1 dated February 1999. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and // conditions of the GNU Lesser General Public License for more details. // // You should have recieved a copy of the GNU Lesser General Public License // along with this program; if not, write to the Free Software Foundation, // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA /** * The <code>sidl</code> package contains the fundamental type and interface * definitions for the <code>sidl</code> interface definition language. It * defines common run-time libraries and common base classes and interfaces. * Every interface implicitly inherits from <code>sidl.BaseInterface</code> * and every class implicitly inherits from <code>sidl.BaseClass</code>. * */ final package sidl version 0.9.17 { /** * Every interface in <code>sidl</code> implicitly inherits * from <code>BaseInterface</code>, and it is implemented * by <code>BaseClass</code> below. */ interface BaseInterface { /** * <p> * Add one to the intrinsic reference count in the underlying object. * Object in <code>sidl</code> have an intrinsic reference count. * Objects continue to exist as long as the reference count is * positive. Clients should call this method whenever they * create another ongoing reference to an object or interface. * </p> * <p> * This does not have a return value because there is no language * independent type that can refer to an interface or a * class. * </p> */ void addRef(); /** * Decrease by one the intrinsic reference count in the underlying * object, and delete the object if the reference is non-positive. * Objects in <code>sidl</code> have an intrinsic reference count. * Clients should call this method whenever they remove a * reference to an object or interface. */ void deleteRef(); /** * Return true if and only if <code>obj</code> refers to the same * object as this object. */ bool isSame(in BaseInterface iobj); /** * Return whether this object is an instance of the specified type. * The string name must be the <code>sidl</code> type name. This * routine will return <code>true</code> if and only if a cast to * the string type name would succeed. */ bool isType(in string name); /** * Return the meta-data about the class implementing this interface. */ ClassInfo getClassInfo(); } /** * Every class implicitly inherits from <code>BaseClass</code>. This * class implements the methods in <code>BaseInterface</code>. */ class BaseClass implements BaseInterface { /** * <p> * Add one to the intrinsic reference count in the underlying object. * Object in <code>sidl</code> have an intrinsic reference count. * Objects continue to exist as long as the reference count is * positive. Clients should call this method whenever they * create another ongoing reference to an object or interface. * </p> * <p> * This does not have a return value because there is no language * independent type that can refer to an interface or a * class. * </p> */ final void addRef(); /** * Decrease by one the intrinsic reference count in the underlying * object, and delete the object if the reference is non-positive. * Objects in <code>sidl</code> have an intrinsic reference count. * Clients should call this method whenever they remove a * reference to an object or interface. */ final void deleteRef(); /** * Return true if and only if <code>obj</code> refers to the same * object as this object. */ final bool isSame(in BaseInterface iobj); /** * Return whether this object is an instance of the specified type. * The string name must be the <code>sidl</code> type name. This * routine will return <code>true</code> if and only if a cast to * the string type name would succeed. */ bool isType(in string name); /** * Return the meta-data about the class implementing this interface. */ final ClassInfo getClassInfo(); } /** * This package has some I/O capability that's not core to the * SIDL object model, but still needed by parts of the generated code */ package io { /** * Objects that implement Serializable will be serializable (copyable) * over RMI, or storable to streams. Classes that can pack or unpack * themselves should implement this interface */ interface Serializable { void packObj( in Serializer ser ); void unpackObj( in Deserializer des ); } } /** * Every exception implements <code>BaseException</code>. This interface * declares the basic functionality to get and set error messages and stack * traces. */ interface BaseException extends sidl.io.Serializable{ /** * Return the message associated with the exception. */ string getNote(); /** * Set the message associated with the exception. */ void setNote(in string message); /** * Returns formatted string containing the concatenation of all * tracelines. */ string getTrace(); /** * Adds a stringified entry/line to the stack trace. */ void add[Line](in string traceline); /** * Formats and adds an entry to the stack trace based on the * file name, line number, and method name. */ void add(in string filename, in int lineno, in string methodname); } /** * This exception type is the default exception for every method. * */ interface RuntimeException extends BaseException {} /** * <code>SIDLException</code> provides the basic functionality of the * <code>BaseException</code> interface for getting and setting error * messages and stack traces. */ class SIDLException implements-all BaseException { } /** * <code>PreViolation</code> indicates an assertion within a precondition * clause of the interface contract has been violated. */ class PreViolation extends SIDLException implements RuntimeException { } /** * <code>PostViolation</code> indicates an assertion within a postcondition * clause of the interface contract has been violated. */ class PostViolation extends SIDLException implements RuntimeException { } /** * <code>InvViolation</code> indicates an assertion within a invariant * clause of the interface contract has been violated. */ class InvViolation extends SIDLException implements RuntimeException { } /** * Contract clause types. */ enum ClauseType { INVARIANT, PRECONDITION, POSTCONDITION, }; /** * Contract classification. The classification is used to filter * contract clauses by the corresponding characteristic(s). */ enum ContractClass { /** * All classifications of interface contract clauses. */ ALLCLASSES, /** * Only constant-time complexity, or O(1), clauses. */ CONSTANT, /** * Only cubic-time complexity, or O(n^3), clauses. */ CUBIC, /** * Only invariant clauses. */ INVARIANTS, /** * Invariant plus postcondition clauses. */ INVPOST, /** * Invariant plus precondition clauses. */ INVPRE, /** * Only linear-time complexity, or O(n), clauses. */ LINEAR, /** * Method calls. Only clauses containing at least one method call. */ METHODCALLS, /** * Only postcondition clauses. */ POSTCONDS, /** * Only precondition clauses. */ PRECONDS, /** * Precondition plus postcondition clauses. */ PREPOST, /** * Only quadratic-time complexity, or O(n^2), clauses. */ QUADRATIC, /** * Only quartic-time complexity, or O(n^4), clauses. */ QUARTIC, /** * Only quintic-time complexity, or O(n^5), clauses. */ QUINTIC, /** * Results. Only clauses containing at least one assertion on an * out, inout, or result argument. */ RESULTS, /** * Only septic-time complexity, or O(n^7), clauses. */ SEPTIC, /** * Only sextic-time complexity, or O(n^6), clauses. */ SEXTIC, /** * Simple expressions. Only clauses consisting solely of * simple expressions (i.e., no method calls). */ SIMPLEEXPRS, }; /** * Contract clause enforcement frequency. */ enum EnforceFreq { /** * Never. Disable enforcement by completely by-passing checks * regardless of the selected contract classification. */ NEVER, /** * Always. Every clause of the selected contract classification * is enforced. */ ALWAYS, /** * Adaptive fit. Check clauses of the selected contract classification * only if they will not result in exceeding the overhead limit based * on accumulations of estimated execution times. */ ADAPTFIT, /** * Adaptive timing. Check clauses of the selected contract classification * only if their estimated execution time is within the overhead limit * applied to the estimated time of the corresponding method. */ ADAPTTIMING, /** * Periodic. Check clauses of the selected contract classification * at the specified interval. */ PERIODIC, /** * Random. Check clauses of the selected contract classifcation on a * random basis using the specified maximum. */ RANDOM, /** * Simulated Annealing. Essentially Adaptive fit but checks are * allowed to randomly exceed the overhead limit with decreasing * probability over time. */ SIMANNEAL, }; /** * Contract enforcement tracing levels. Enforcement traces rely on * runtime timing automatically inserted within the middleware. */ enum EnfTraceLevel { /** * None. No tracing is to be performed. */ NONE, /** * Core. Time trace start and end only. This can be useful for * simple program timing. */ CORE, /** * Basic enforcement tracing. CORE plus interface contract clause timing. */ BASIC, /** * Overhead of enforcement decisions. BASIC plus timing of * enforcement decisions. (Experimental feature.) */ OVERHEAD, }; /** * <code>EnfPolicy</code> maintains the current interface * contract enforcement policy. */ class EnfPolicy { /** * Sets the enforcement policy to always check the specified * type(s) of contracts. This is equivalent to calling * setPolicy() with ALWAYS as the enforcement frequency * and the specified (or default) contract class. * * @param contractClass Contract classification * [Default = ALLCLASSES] * @param clearStats TRUE if enforcement statistics are to be * cleared; FALSE otherwise. */ static void setEnforceAll(in ContractClass contractClass, in bool clearStats); /** * Sets the policy options to disable all contract enforcement. * This is equivalent to calling setPolicy() with NEVER as the * enforcement frequency. * * @param clearStats TRUE if enforcement statistics are to be * cleared; FALSE otherwise. */ static void setEnforceNone(in bool clearStats); /** * Sets enforcement policy and options. This method should be * invoked directly to avoid the default enforcement behavior. * * @param contractClass Contract classification * [Default = ALLCLASSES] * @param enforceFreq Enforcement frequency * [Default = ALWAYS] * @param interval Sampling interval representing the * period (for PERIODIC) or maximum * random number/window (for RANDOM) * [Default = 0 if negative specified] * @param overheadLimit Limit on performance overhead [0.0 .. 1.0) * [Default = 0.0 (or 0%) if negative] * @param appAvgPerCall Average extra, application-specific * execution time, normalized by calls * to annotated methods * [Default = 0.0 if negative] * @param annealLimit Limit on simulated annealing function * to ensure its termination * (0.0 .. 2.72] * [Default = 2.72 if negative specified] * @param clearStats TRUE if enforcement statistics are to be * cleared; FALSE otherwise. */ static void setPolicy(in ContractClass contractClass, in EnforceFreq enforceFreq, in int interval, in double overheadLimit, in double appAvgPerCall, in double annealLimit, in bool clearStats); /** * Returns TRUE if contract enforcement is enabled; FALSE otherwise. */ static bool areEnforcing(); /** * Returns the contract classification policy option. */ static ContractClass getContractClass(); /** * Returns the enforcement frequency policy option. */ static EnforceFreq getEnforceFreq(); /** * Returns the interval for PERIODIC (i.e., the interval) or * RANDOM (i.e., the maximum random number). Returns 0 by default. */ static int getSamplingInterval(); /** * Returns the desired enforcement overhead limit for * performance-driven frequency options (i.e., ADAPTFIT, * ADAPTTIMING, and SIMANNEAL). Returns 0.0 by default. */ static double getOverheadLimit(); /** * Returns the average assumed execution time associated * with the program or application. Returns 0.0 by default. */ static double getAppAvgPerCall(); /** * Returns the annealing limit for SIMANNEAL enforcement * frequency option. Returns 0.0 by default. */ static double getAnnealLimit(); /** * Returns the name, or description, of the enforcement policy. * The caller is responsible for calling sidl_String_free() * on the name when done with it. * * @param useAbbrev TRUE if the abbreviated name is to be * returned. */ static string getPolicyName(in bool useAbbrev); /** * Prints statistics data to the file with the specified name. * The file is opened (for append) and closed on each call. * * @param filename Name of the file to which the statistics * data should be written. * @param header TRUE if the header line is to be printed * prior to the statistics line (for compressed * output only). * @param prefix String description for identifying information, * if any, intended to preceed the statistics * data. Useful for distinguishing between * different objects, for example. * @param compressed TRUE if the enforcer state is to be dumped * on a single line with semi-colon separators * between fields. */ static void dumpStats(in string filename, in bool header, in string prefix, in bool compressed); /** * Starts enforcement trace file generation. * * @param filename Name of the destination trace file. * @param traceLevel Level of trace timing and reporting required. * [Default = NONE] */ static void startTrace(in string filename, in EnfTraceLevel traceLevel); /** * Returns TRUE if contract enforcement tracing is enabled; * FALSE otherwise. */ static bool areTracing(); /** * Returns the name of the trace file. If one was not provided, * the default name is returned. */ static string getTraceFilename(); /** * Returns the level of enforcement tracing. */ static EnfTraceLevel getTraceLevel(); /** * Terminates enforcement trace file generation. Takes a final * timestamp and logs the remaining trace information. */ static void endTrace(); } /** * When loading a dynamically linked library, there are three * settings: LOCAL, GLOBAL and SCLSCOPE. */ enum Scope { /** Attempt to load the symbols into a local namespace. */ LOCAL, /** Attempt to load the symbols into the global namespace. */ GLOBAL, /** Use the scope setting from the SCL file. */ SCLSCOPE } /** * When loading a dynmaically linked library, there are three * settings: LAZY, NOW, SCLRESOLVE */ enum Resolve { /** Resolve symbols on an as needed basis. */ LAZY, /** Resolve all symbols at load time. */ NOW, /** Use the resolve setting from the SCL file. */ SCLRESOLVE } /** * The <code>DLL</code> class encapsulates access to a single * dynamically linked library. DLLs are loaded at run-time using * the <code>loadLibrary</code> method and later unloaded using * <code>unloadLibrary</code>. Symbols in a loaded library are * resolved to an opaque pointer by method <code>lookupSymbol</code>. * Class instances are created by <code>createClass</code>. */ class DLL { /** * Load a dynamic link library using the specified URI. The * URI may be of the form "main:", "lib:", "file:", "ftp:", or * "http:". A URI that starts with any other protocol string * is assumed to be a file name. The "main:" URI creates a * library that allows access to global symbols in the running * program's main address space. The "lib:X" URI converts the * library "X" into a platform-specific name (e.g., libX.so) and * loads that library. The "file:" URI opens the DLL from the * specified file path. The "ftp:" and "http:" URIs copy the * specified library from the remote site into a local temporary * file and open that file. This method returns true if the * DLL was loaded successfully and false otherwise. Note that * the "ftp:" and "http:" protocols are valid only if the W3C * WWW library is available. * * @param uri the URI to load. This can be a .la file * (a metadata file produced by libtool) or * a shared library binary (i.e., .so, * .dll or whatever is appropriate for your * OS) * @param loadGlobally <code>true</code> means that the shared * library symbols will be loaded into the * global namespace; <code>false</code> * means they will be loaded into a * private namespace. Some operating systems * may not be able to honor the value presented * here. * @param loadLazy <code>true</code> instructs the loader to * that symbols can be resolved as needed (lazy) * instead of requiring everything to be resolved * now (at load time). */ bool loadLibrary(in string uri, in bool loadGlobally, in bool loadLazy); /** * Get the library name. This is the name used to load the * library in <code>loadLibrary</code> except that all file names * contain the "file:" protocol. */ string getName(); /** * Return true if the library was loaded into the global namespace. */ bool isGlobal(); /** * Return true if the library was loaded using lazy symbol resolution. */ bool isLazy(); /** * Unload the dynamic link library. The library may no longer * be used to access symbol names. When the library is actually * unloaded from the memory image depends on details of the operating * system. */ void unloadLibrary(); /** * Lookup a symbol from the DLL and return the associated pointer. * A null value is returned if the name does not exist. */ opaque lookupSymbol(in string linker_name); /** * Create an instance of the sidl class. If the class constructor * is not defined in this DLL, then return null. */ BaseClass createClass(in string sidl_name); } /** * Interface <code>Finder</code> is an interface for classes that resolve * dynamic libraries. * Class <code>Loader</code> takes one of these interfaces through the * method <code>setFinder</code>. If NULL is passed to setFinder, the * class <code>DefaultFinder</code> is used. */ interface Finder { /** * Find a DLL containing the specified information for a sidl * class. This method searches through the files in set set path * looking for a shared library that contains the client-side or IOR * for a particular sidl class. * * @param sidl_name the fully qualified (long) name of the * class/interface to be found. Package names * are separated by period characters from each * other and the class/interface name. * @param target to find a client-side binding, this is * normally the name of the language. * To find the implementation of a class * in order to make one, you should pass * the string "ior/impl" here. * @param lScope this specifies whether the symbols should * be loaded into the global scope, a local * scope, or use the setting in the file. * @param lResolve this specifies whether symbols should be * resolved as needed (LAZY), completely * resolved at load time (NOW), or use the * setting from the file. * @return a non-NULL object means the search was successful. * The DLL has already been added. */ DLL findLibrary(in string sidl_name, in string target, in Scope lScope, in Resolve lResolve); /** * Set the search path, which is a semi-colon separated sequence of * URIs as described in class <code>DLL</code>. This method will * invalidate any existing search path. */ void setSearchPath(in string path_name); /** * Return the current search path. If the search path has not been * set, then the search path will be taken from environment variable * SIDL_DLL_PATH. */ string getSearchPath(); /** * Append the specified path fragment to the beginning of the * current search path. If the search path has not yet been set * by a call to <code>setSearchPath</code>, then this fragment will * be appended to the path in environment variable SIDL_DLL_PATH. */ void addSearchPath(in string path_fragment); } /** * This class is the Default Finder. If no Finder is set in class Loader, * this finder is used. It uses SCL files from the filesystem to * resolve dynamic libraries. * * The initial search path is taken from the SIDL_DLL_PATH * environment variable. */ class DFinder implements-all Finder { } /** * Class <code>Loader</code> manages dyanamic loading and symbol name * resolution for the sidl runtime system. The <code>Loader</code> class * manages a library search path and keeps a record of all libraries * loaded through this interface, including the initial "global" symbols * in the main program. * * Unless explicitly set, the <code>Loader</code> uses the default * <code>sidl.Finder</code> implemented in <code>sidl.DFinder</code>. * This class searches the filesystem for <code>.scl</code> files when * trying to find a class. The initial path is taken from the * environment variable SIDL_DLL_PATH, which is a semi-colon * separated sequence of URIs as described in class <code>DLL</code>. */ class Loader { /** * Load the specified library if it has not already been loaded. * The URI format is defined in class <code>DLL</code>. The search * path is not searched to resolve the library name. * * @param uri the URI to load. This can be a .la file * (a metadata file produced by libtool) or * a shared library binary (i.e., .so, * .dll or whatever is appropriate for your * OS) * @param loadGlobally <code>true</code> means that the shared * library symbols will be loaded into the * global namespace; <code>false</code> * means they will be loaded into a * private namespace. Some operating systems * may not be able to honor the value presented * here. * @param loadLazy <code>true</code> instructs the loader to * that symbols can be resolved as needed (lazy) * instead of requiring everything to be resolved * now. * @return if the load was successful, a non-NULL DLL object is returned. */ static DLL loadLibrary(in string uri, in bool loadGlobally, in bool loadLazy); /** * Append the specified DLL to the beginning of the list of already * loaded DLLs. */ static void addDLL(in DLL dll); /** * Unload all dynamic link libraries. The library may no longer * be used to access symbol names. When the library is actually * unloaded from the memory image depends on details of the operating * system. */ static void unloadLibraries(); /** * Find a DLL containing the specified information for a sidl * class. This method searches SCL files in the search path looking * for a shared library that contains the client-side or IOR * for a particular sidl class. * * This call is implemented by calling the current * <code>Finder</code>. The default finder searches the local * file system for <code>.scl</code> files to locate the * target class/interface. * * @param sidl_name the fully qualified (long) name of the * class/interface to be found. Package names * are separated by period characters from each * other and the class/interface name. * @param target to find a client-side binding, this is * normally the name of the language. * To find the implementation of a class * in order to make one, you should pass * the string "ior/impl" here. * @param lScope this specifies whether the symbols should * be loaded into the global scope, a local * scope, or use the setting in the SCL file. * @param lResolve this specifies whether symbols should be * resolved as needed (LAZY), completely * resolved at load time (NOW), or use the * setting from the SCL file. * @return a non-NULL object means the search was successful. * The DLL has already been added. */ static DLL findLibrary(in string sidl_name, in string target, in Scope lScope, in Resolve lResolve); /** * Set the search path, which is a semi-colon separated sequence of * URIs as described in class <code>DLL</code>. This method will * invalidate any existing search path. * * This updates the search path in the current <code>Finder</code>. */ static void setSearchPath(in string path_name); /** * Return the current search path. The default * <code>Finder</code> initializes the search path * from environment variable SIDL_DLL_PATH. * */ static string getSearchPath(); /** * Append the specified path fragment to the beginning of the * current search path. This method operates on the Loader's * current <code>Finder</code>. This will add a path to the * current search path. Normally, the search path is initialized * from the SIDL_DLL_PATH environment variable. */ static void addSearchPath(in string path_fragment); /** * This method sets the <code>Finder</code> that * <code>Loader</code> will use to find DLLs. If no * <code>Finder</code> is set or if NULL is passed in, the Default * Finder <code>DFinder</code> will be used. * * Future calls to <code>findLibrary</code>, * <code>addSearchPath</code>, <code>getSearchPath</code>, and * <code>setSearchPath</code> are deligated to the * <code>Finder</code> set here. */ static void setFinder(in Finder f); /** * This method gets the <code>Finder</code> that <code>Loader</code> * uses to find DLLs. */ static Finder getFinder(); } /** * This provides an interface to the meta-data available on the * class. */ interface ClassInfo { /** * Return the name of the class. */ string getName(); /** * Return the version number of the class. This should be a string * with a sequence of numbers separated by periods. */ string getVersion(); /** * Get the version of the intermediate object representation. * This will be in the form of major_version.minor_version. */ string getIORVersion(); } /** * An implementation of the <code>ClassInfo</code> interface. This * provides methods to set all the attributes that are read-only in * the <code>ClassInfo</code> interface. */ class ClassInfoI implements-all ClassInfo { /** * Set the name of the class. */ final void setName(in string name); /** * Set the version number of the class. */ final void setVersion(in string ver); /** * Set the IOR major and minor version numbers. */ final void setIORVersion(in int major, in int minor); } /** * Exception thrown from Babel internals when memory allocation * fails. This exception is special in that it avoids any memory * allocation. For this reason, the trace or note may be truncated * to fit in the preallocated buffers. */ class MemAllocException extends sidl.SIDLException implements RuntimeException { /** * Returns the preallocated copy of this exception. Any * failure of memory allocation should throw the exception returned * by this method to avoid further allocation failures. */ static MemAllocException getSingletonException(); /** * Return the message associated with the exception. */ string getNote(); /** * Set the message associated with the exception. */ void setNote(in string message); /** * Returns formatted string containing the concatenation of all * tracelines. */ string getTrace(); /** * Adds a stringified entry/line to the stack trace. */ void add[Line](in string traceline); /** * Formats and adds an entry to the stack trace based on the * file name, line number, and method name. */ void add(in string filename, in int lineno, in string methodname); } /** * Exception is thrown when a cast fails and the failure needs to * be communicated up the call stack. (Note: babel _cast does NOT * throw this exception) */ class CastException extends sidl.SIDLException implements RuntimeException { } /** * This Exception is thrown by the Babel runtime when a non SIDL * exception is thrown from an exception throwing language such as * C++ or Java. */ class LangSpecificException extends sidl.SIDLException implements RuntimeException { } /** * This Exception is thrown when a method is called that an * implmentation has not been written for yet. The throw code is * placed into the _Impl files automatically when they are generated. */ class NotImplementedException extends sidl.SIDLException implements RuntimeException { } /** * This package has some I/O capability that's not core to the SIDL * object model, but still needed by parts of the generated code */ package io { /** generic exception for I/O issues */ class IOException extends sidl.SIDLException implements RuntimeException { } /** * Standard interface for packing Babel types */ interface Serializer { void packBool( in string key, in bool value ) ; void packChar( in string key, in char value ) ; void packInt( in string key, in int value ) ; void packLong( in string key, in long value ) ; void packOpaque( in string key, in opaque value ) ; void packFloat( in string key, in float value ) ; void packDouble( in string key, in double value ) ; void packFcomplex( in string key, in fcomplex value ) ; void packDcomplex( in string key, in dcomplex value ) ; void packString( in string key, in string value ) ; void packSerializable( in string key, in Serializable value ) ; /** * pack arrays of values. It is possible to ensure an array is * in a certain order by passing in ordering and dimension * requirements. ordering should represent a value in the * sidl_array_ordering enumeration in sidlArray.h If either * argument is 0, it means there is no restriction on that * aspect. The boolean reuse_array flag is set to true if the * remote unserializer should try to reuse the array that is * passed into it or not. */ void packBoolArray( in string key, in array<bool> value, in int ordering, in int dimen, in bool reuse_array ); void packCharArray( in string key, in array<char> value, in int ordering, in int dimen, in bool reuse_array ); void packIntArray( in string key, in array<int> value, in int ordering, in int dimen, in bool reuse_array ); void packLongArray( in string key, in array<long> value, in int ordering, in int dimen, in bool reuse_array ); void packOpaqueArray( in string key, in array<opaque> value, in int ordering, in int dimen, in bool reuse_array ); void packFloatArray( in string key, in array<float> value, in int ordering, in int dimen, in bool reuse_array ); void packDoubleArray( in string key, in array<double> value, in int ordering, in int dimen, in bool reuse_array ); void packFcomplexArray( in string key, in array<fcomplex> value, in int ordering, in int dimen, in bool reuse_array ); void packDcomplexArray( in string key, in array<dcomplex> value, in int ordering, in int dimen, in bool reuse_array ); void packStringArray( in string key, in array<string> value, in int ordering, in int dimen, in bool reuse_array ); void packGenericArray( in string key, in array<> value, in bool reuse_array ); void packSerializableArray( in string key, in array<Serializable> value, in int ordering, in int dimen, in bool reuse_array ); } /** * Standard interface for unpacking Babel types */ interface Deserializer { /* unpack values */ void unpackBool( in string key, inout bool value ); void unpackChar( in string key, inout char value ); void unpackInt( in string key, inout int value ); void unpackLong( in string key, inout long value ); void unpackOpaque( in string key, inout opaque value ); void unpackFloat( in string key, inout float value ); void unpackDouble( in string key, inout double value ); void unpackFcomplex( in string key, inout fcomplex value ); void unpackDcomplex( in string key, inout dcomplex value ); void unpackString( in string key, inout string value ); void unpackSerializable( in string key, inout Serializable value ); /** unpack arrays of values * It is possible to ensure an array is * in a certain order by passing in ordering and dimension * requirements. ordering should represent a value in the * sidl_array_ordering enumeration in sidlArray.h If either * argument is 0, it means there is no restriction on that * aspect. The rarray flag should be set if the array being * passed in is actually an rarray. The semantics are slightly * different for rarrays. The passed in array MUST be reused, * even if the array has changed bounds. */ void unpackBoolArray( in string key, inout array<bool> value, in int ordering, in int dimen, in bool isRarray ); void unpackCharArray( in string key, inout array<char> value, in int ordering, in int dimen, in bool isRarray ); void unpackIntArray( in string key, inout array<int> value, in int ordering, in int dimen, in bool isRarray ); void unpackLongArray( in string key, inout array<long> value, in int ordering, in int dimen, in bool isRarray ); void unpackOpaqueArray( in string key, inout array<opaque> value, in int ordering, in int dimen, in bool isRarray ); void unpackFloatArray( in string key, inout array<float> value, in int ordering, in int dimen, in bool isRarray ); void unpackDoubleArray( in string key, inout array<double> value, in int ordering, in int dimen, in bool isRarray ); void unpackFcomplexArray( in string key, inout array<fcomplex> value, in int ordering, in int dimen, in bool isRarray ); void unpackDcomplexArray( in string key, inout array<dcomplex> value, in int ordering, in int dimen, in bool isRarray ); void unpackStringArray( in string key, inout array<string> value, in int ordering, in int dimen, in bool isRarray ); void unpackGenericArray( in string key, inout array<> value); void unpackSerializableArray( in string key, inout array<Serializable> value, in int ordering, in int dimen, in bool isRarray ); } } //end package io /** * This package contains necessary interfaces for RMI protocols to * hook into Babel, plus a Protocol Factory class. The intention is * that authors of new protocols will create classes that implement * InstanceHandle, Invocation and Response (they could even have one * object that implements all three interfaces). */ package rmi { /** * Generic Network Exception */ class NetworkException extends sidl.io.IOException { int getHopCount(); void packObj( in sidl.io.Serializer ser ); void unpackObj( in sidl.io.Deserializer des ); void setErrno(in int err); int getErrno(); } /** * This exception is thrown by the RMI library when a * host can not be found by a DNS lookup. */ class UnknownHostException extends NetworkException {} /** * This exception is normally thrown by the RMI library when the * server is started up and the port it is assigned to use is * already in use. */ class BindException extends NetworkException {} /** * This exception is thrown by the RMI library when an * attempt to connect to a remote host fails. */ class ConnectException extends NetworkException {} /** * This exception is thrown by the RMI library when a host * can be found by DNS, but is not reachable. It usually means * a router is down. */ class NoRouteToHostException extends NetworkException {} /** * This exception is thrown by the RMI library when a request * times out. */ class TimeOutException extends NetworkException {} /** * This exception is thrown by the RMI library when the network * unexpected loses it's connection. Can be caused by reset, * software connection abort, connection reset by peer, etc. */ class UnexpectedCloseException extends NetworkException {} /** * This exception is thrown by a server when a passed in object * id does not match any known object. */ class ObjectDoesNotExistException extends NetworkException {} /** * This exception is thrown by the RMI library when a passed in URL * is malformed. */ class MalformedURLException extends NetworkException {} /** * This is a base class for all protocol specific exceptions. */ class ProtocolException extends NetworkException {} /** * This exception thrown when one attempts to pass a local object remotely but * there is no local server running to serve the object */ class NoServerException extends NetworkException {} /** * This singleton class keeps a table of string prefixes * (e.g. "babel" or "proteus") to protocol implementations. The * intent is to parse a URL (e.g. "babel://server:port/class") and * create classes that implement * <code>sidl.rmi.InstanceHandle</code>. */ class ProtocolFactory { /** * Associate a particular prefix in the URL to a typeName * <code>sidl.Loader</code> can find. The actual type is * expected to implement <code>sidl.rmi.InstanceHandle</code> * Return true iff the addition is successful. (no collisions * allowed) */ static bool addProtocol( in string prefix, in string typeName ); /** * Return the typeName associated with a particular prefix. * Return empty string if the prefix */ static string getProtocol( in string prefix ); /** * Remove a protocol from the active list. */ static bool deleteProtocol( in string prefix ); /** * Create a new remote object and return an instance handle for that * object. * The server and port number are in the url. Return nil * if protocol unknown or InstanceHandle.init() failed. */ static InstanceHandle createInstance( in string url, in string typeName ); /** * Create an new connection linked to an already existing * object on a remote server. The server and port number are in * the url, the objectID is the unique ID of the remote object * in the remote instance registry. Return null if protocol * unknown or InstanceHandle.init() failed. The boolean addRef * should be true if connect should remotely addRef */ static InstanceHandle connectInstance( in string url, in string typeName, in bool ar); /** * Request that a remote object be serialized to you. The server * and port number are in the url, the objectID is the unique ID * of the remote object in the remote instance registry. Return * null if protocol unknown or InstanceHandle.init() failed. */ static sidl.io.Serializable unserializeInstance( in string url); } /** * This interface holds the state information for handles to * remote objects. Client-side messaging libraries are expected * to implement <code>sidl.rmi.InstanceHandle</code>, * <code>sidl.rmi.Invocation</code> and * <code>sidl.rmi.Response</code>. * * Every stub with a connection to a remote object holds a pointer * to an InstanceHandle that manages the connection. Multiple * stubs may point to the same InstanceHandle, however. Babel * takes care of the reference counting, but the developer should * keep concurrency issues in mind. * * When a new remote object is created: * sidl_rmi_InstanceHandle c = * sidl_rmi_ProtocolFactory_createInstance( url, typeName, * _ex ); * * When a new stub is created to connect to an existing remote * instance: * sidl_rmi_InstanceHandle c = * sidl_rmi_ProtocolFactory_connectInstance( url, _ex ); * * When a method is invoked: * sidl_rmi_Invocation i = * sidl_rmi_InstanceHandle_createInvocation( methodname ); * sidl_rmi_Invocation_packDouble( i, "input_val" , 2.0 ); * sidl_rmi_Invocation_packString( i, "input_str", "Hello" ); * ... * sidl_rmi_Response r = sidl_rmi_Invocation_invokeMethod( i ); * sidl_rmi_Response_unpackBool( i, "_retval", &succeeded ); * sidl_rmi_Response_unpackFloat( i, "output_val", &f ); * */ interface InstanceHandle { /** initialize a connection (intended for use by the * ProtocolFactory, (see above). This should parse the url and * do everything necessary to create the remote object. */ bool initCreate( in string url, in string typeName ); /** * initialize a connection (intended for use by the ProtocolFactory) * This should parse the url and do everything necessary to connect * to a remote object. */ bool initConnect( in string url, in string typeName, in bool ar); /** Get a connection specifically for the purpose for requesting a * serialization of a remote object (intended for use by the * ProtocolFactory, (see above). This should parse the url and * request the object. It should return a deserializer.. */ sidl.io.Serializable initUnserialize( in string url); /** return the short name of the protocol */ string getProtocol(); /** return the object ID for the remote object*/ string getObjectID(); /** * return the full URL for this object, takes the form: * protocol://serviceID/objectID (where serviceID would = server:port * on TCP/IP) * So usually, like this: protocol://server:port/objectID */ string getObjectURL(); /** create a serializer handle to invoke the named method */ Invocation createInvocation( in string methodName ); /** * closes the connection (called by the destructor, if not done * explicitly) returns true if successful, false otherwise * (including subsequent calls) */ bool close(); } /** * This type is used to pack arguments and make the Client->Server * method invocation. */ interface Invocation extends sidl.io.Serializer { /** * this method is one of a triad. Only one of which * may be called, and it must the the last method called * in the object's lifetime. */ Response invokeMethod(); /** * This method is second of the triad. It returns * a Ticket, from which a Response is later extracted. */ Ticket invokeNonblocking(); /** * This method is third of the triad. It returns * and exception iff the invocation cannot be delivered * reliably. It does not wait for the invocation to * be acted upon and returns no values from the invocation. */ void invokeOneWay(); } /** * This type is created when an invokeMethod is called on an * Invocation. It encapsulates all the results that users will * want to pull out of a remote method invocation. */ interface Response extends sidl.io.Deserializer { /** * May return a communication exception or an execption thrown * from the remote server. If it returns null, then it's safe * to unpack arguments */ sidl.BaseException getExceptionThrown(); } /** * This interface is implemented by the Server side deserializer. * Deserializes method arguments in preperation for the method * call. */ interface Call extends sidl.io.Deserializer { } /** * This interface is implemented by the Server side serializer. * Serializes method arguments after the return from the method * call. */ interface Return extends sidl.io.Serializer { /** * This method serialized exceptions thrown on the server side * that should be returned to the client. Assumed to invalidate * in previously serialized arguments. (Also assumed that no * more arguments will be serialized.) */ void throwException(in sidl.BaseException ex_to_throw); } /** * Used in lieu of a Response in nonblocking calls */ interface Ticket { /** blocks until the Response is recieved */ void block(); /** * returns immediately: true iff the Response is already * received */ bool test(); /** creates an empty container specialized for Tickets */ TicketBook createEmptyTicketBook(); /** returns immediately: returns Response or null * (NOTE: needed for implementors of communication * libraries, not expected for general use). */ Response getResponse(); } /** * This is a collection of Tickets that itself can be viewed * as a ticket. */ interface TicketBook extends Ticket { /** insert a ticket with a user-specified ID */ void insertWithID( in Ticket t, in int id ); /** insert a ticket and issue a unique ID */ int insert( in Ticket t ); /** remove a ready ticket from the TicketBook * returns 0 (and null) on an empty TicketBook */ int removeReady( out Ticket t ); /** * immediate, returns the number of Tickets in the book. */ bool isEmpty(); } /** * This singleton class is implemented by Babel's runtime for RMI * libraries to invoke methods on server objects. It maps * objectID strings to sidl_BaseClass objects and vice-versa. * * The InstanceRegistry creates and returns a unique string when a * new object is added to the registry. When an object's refcount * reaches 0 and it is collected, it is removed from the Instance * Registry. * * Objects are added to the registry in 3 ways: * 1) Added to the server's registry when an object is * create[Remote]'d. * 2) Implicity added to the local registry when an object is * passed as an argument in a remote call. * 3) A user may manually add a reference to the local registry * for publishing purposes. The user hsould keep a reference * to the object. Currently, the user cannot provide their own * objectID, this capability should probably be added. */ class InstanceRegistry { /** * Register an instance of a class. * * the registry will return an objectID string guaranteed to be * unique for the lifetime of the process */ static string registerInstance( in sidl.BaseClass instance ); /** * Register an instance of a class with the given instanceID * * If a different object already exists in registry under * the supplied name, a false is returned, if the object was * successfully registered, true is returned. */ static string registerInstance[ByString]( in sidl.BaseClass instance, in string instanceID); /** * returns a handle to the class based on the unique objectID * string, (null if the handle isn't in the table) */ static sidl.BaseClass getInstance[ByString]( in string instanceID ); /** * takes a class and returns the objectID string associated * with it. (null if the handle isn't in the table) */ static string getInstance[ByClass]( in sidl.BaseClass instance ); /** * removes an instance from the table based on its objectID * string.. returns a pointer to the object, which must be * destroyed. */ static sidl.BaseClass removeInstance[ByString]( in string instanceID ); /** * removes an instance from the table based on its BaseClass * pointer. returns the objectID string, which much be freed. */ static string removeInstance[ByClass]( in sidl.BaseClass instance ); } /** * This singleton class is implemented by Babel's runtime for to * allow RMI downcasting of objects. When we downcast an RMI * object, we may be required to create a new derived class object * with a connect function. We store all the connect functions in * this table for easy access. * * This Class is for Babel internal use only. */ class ConnectRegistry { /** * The key is the SIDL classname the registered connect belongs * to. Multiple registrations under the same key are possible, * this must be protected against in the user code. Babel does * this internally with a static boolean. */ static void registerConnect( in string key, in opaque func); /** * Returns the connect method for the class named in the key */ static opaque getConnect( in string key ); /** * Returns the connect method for the class named in the key, * and removes it from the table. */ static opaque removeConnect( in string key ); } /** * ServerInfo is an interface (possibly implemented by the ORB * itself) that provides functions to deal with the problems * associated with passing local object remotely. It should be * registered with the ServerRegistry for general use. */ interface ServerInfo { string getServerURL(in string objID); /** * For internal Babel use ONLY. Needed by Babel to determine if * a url points to a local or remote object. Returns the * objectID if is local, Null otherwise. */ string isLocalObject(in string url); /** * This gets an array of logged exceptions. If an exception * can not be thrown back to the caller, we log it with the * Server. This gets the array of all those exceptions. THIS * IS SOMETHING OF A TEST! THIS MAY CHANGE! */ array<sidl.io.Serializable,1> getExceptions(); } /** * This singleton class is simply a place to register a * ServerInfo interface for general access. This ServerInfo * should give info about the ORB being used to export RMI objects * for the current Babel process. * * This Registry provides two important functions, a way to get * the URL for local object we wish to expose over RMI, and a way * to tell if an object passed to this process via RMI is actually * a local object. This abilities are protocol specific, the * ServerInfo interface must by implemented by the protocol * writer. * * THIS CLASS IS NOT DESIGNED FOR CONCURRENT WRITE ACCESS. (Only * one server is assumed per Babel process) */ class ServerRegistry { /** * Register the server with the ServerRegistry. */ static void registerServer(in sidl.rmi.ServerInfo si); /** * Get the registered server from the Server Registery. */ static sidl.rmi.ServerInfo getServer(); /** * Perhaps this should take BaseClass and look the objectID up in * the Instance Registry */ static string getServerURL(in string objID); /** * For internal Babel use ONLY. Needed by Babel to determine if a * url points to a local or remote object. Returns the objectID * if is local, Null otherwise. */ static string isLocalObject(in string url); /** * This gets an array of logged exceptions. If an exception * can not be thrown back to the caller, we log it with the * Server. This gets the array of all those exceptions. THIS * IS SOMETHING OF A TEST! THIS MAY CHANGE! */ static array<sidl.io.Serializable,1> getExceptions(); } } //end package rmi }

6.7  Objects

One of the strategies that SIDL uses to enforce language interoperability is to define an object model that it supports across all language bindings. This enables real object-oriented programming in non-OO languages such as C and fortran 77. This also means that the inheritance mechanisms inside real OO languages may be circumvented.

Contrary to newer scripting languages such as Python and Ruby, not everything in SIDL is an object. Only classes (abstract or not) and interfaces are objects. Everything else (e. g. arrays, enums, strings, ints) is something other than an object and therefore outside the scope of this section.

Babel’s Object Model

SIDL defines three types of objects: interfaces, classes, and abstract classes. A SIDL interface is akin to a Java interface or a C++ pure abstract base class. It is an object that defines methods (aka member functions), but carries no implementation of those methods. A class by comparison is always concrete; meaning that there is an implementation for each of its methods and it can be instantiated. An abstract class falls somewhere between an interface and a class. It has at least one method unimplemented, so it cannot be instantiated, but it also may have several methods that are implemented and these implementations can be inherited.

SIDL supports multiple inheritance of interfaces and single inheritance of implementation. This is a strategy found in other OO languages such as Java and ObjectiveC. The words to distinguish these two forms of inheritance are extends and implements. Interfaces can extend multiple interfaces, but they cannot implement anything. Classes can extend at most one other class (abstract or not), but can implement multiple interfaces.

Furthermore, any inherited abstract methods (inherited from either and abstract parent class or and implemented interface) will default to abstract unless they are re-declared in the current class. If a concrete class implements many large interfaces, this can result in a fairly large list of redeclared functions in the class definition. As a shortcut, we included the implements-all directive, a short hand that states explicitly that we intend to implement every method in the named interface concretely. That’s why, in the following example, class B must be declared abstract, but class D is concrete. Class B does not redeclare the printMe function, but class D implements-all. There is no similar directive for inheritance from abstract classes.

We display a small SIDL file below and finish this subsection with a discussion of its details.

SIDL
package object version 1.0 { interface A { void display(); void printMe(); } abstract class B implements A { void display(); } class C extends B { void printMe(); } class D implements-all A { } }

object.A is an interface that has two methods display() and print(). Both of these methods take no arguments and return no value. (We will discuss arguments and return values in the next section.) Since object.A is an interface, there is no implementation associated with it, and Babel will not generate any implementation code associated with it.

object.B is an abstract class that inherits from object.A. Since it redeclares the display() method, Babel will generate the appropriate code for an implementation of this method only. It will not generate code for the other inherited method print() (since it wasn’t declared in the SIDL file) and it will not generate constructors/destructors since the class is abstract.

object.C is a concrete class that extends the abstract class object.B it then lists only the unimplemented method print(), implying that it will use the implementation of display() it inherited from its parent.

object.D is also a concrete class that uses the implements-all

directive. This is identical to using implements and then listing all the methods declared in the interface. The implements-all directive was added to SIDL as a convenience construct and to save excessive typing in the SIDL file. By virtue of the implements-all directive, object.D will provide its own implementation of all of object.A’s methods, namely display() and print().

Methods on Objects

Methods in SIDL are virtual by default. This means that the actual binding of a method invocation to an actual implementation is determined at runtime, based on the concrete type of the object.

SIDL currently defines three modifiers to methods that change their default behavior.

Starting with Babel 0.11.0, all SIDL methods implicitly throw sidl.RuntimeException. A sidl.RuntimeException can be generated by the Babel generated glue code. For example, if the code is making a call across the network using remote method invocation and the network goes down, Babel’s glue code would generate a RuntimeException. In cases where the implementation throws an unexpected exception (i. e., not one that is declared in the method’s SIDL declaration), the glue code can generate a RuntimeException.

Parameter Passing

Each parameter in a method call obeys the following syntax

[ (modifier) ] (mode) (type) (name)

Where (mode) is one of in, out, or inout; (type) is any SIDL recognized type; and (name) is any non-reserved word4. The (modifier) is optional, and currently unimplemented. SIDL currently reserves the word copy for future use as an parameter modifier, and may add others in the future5.

For new users, the parameter’s mode (e. g. in, out, or inout) is perhaps the most troublesome. On the surface, it’s easy to explain that in parameters are passed into the code, out parameters come out, and inout parameters do both. More specifically the rules are:

  1. in does not mean const.
  2. in arguments are passed by value, therefore what happens inside the function has no effect on the value passed in (from the perspective of the caller).
  3. inout arguments are passed by reference. The callee is allowed to do whatever it wants with the data passed in, and changes made by the callee are sent back to the caller. For interfaces, classes, and normal arrays, the callee can even destroy the reference, create a new object or array, and return a reference to it.
  4. Objects, interfaces and arrays should be allocated using the create methods provided. Types created on the stack should never be passed as an inout argument, since the implementation may want to destroy it.
  5. out arguments are also passed by reference, but the incoming value is ignore and typically overwritten. Do Not attempt to pass in a value to a function through an out argument. There is no guarantee that the data will make it to the Implementation, and if the data is lost, there is no guarantee the reference will be correctly destroyed.

When an exception is thrown, the value of out parameters is undefined. Thus, the client code should not attempt to free out string values or decrement reference counts for objects or arrays when an exception has been thrown. Some bindings may initialize point types to a NULL value, but the client should not depend on this behavior.

For strings and reference counted objects (i. e., objects, interfaces, and arrays) called a resource here, these rules can be confusing. It is useful to think about who retains ownership of the resource and the phases of method call.

in
The client owns the resource before the call, and the callee borrows the reference. In the case of a string, the client owns the string data, and the implementation would have to copy the string if it wanted to retain a reference. Similarly, the implementation would have to increment the reference count of an object or smart copy an array. In the case of an exception being thrown, the in parameters are unmodified.
inout
The client initially owns the resource before the call, and it transfers its ownership to the callee. Thus, the callee is allowed to decrement the reference count of the object or free a string. When the callee is finished, it transfers ownership back to the caller (or potentially returns a NULL object/string). In the event of an exception, the caller should ignore the outgoing parameter values, and the implementation must insure that the object’s reference counts are decremented and incoming string values are free’ed.
out
The incoming values of out parameters are ignored, and the callee creates any strings or objects that are to be returned. When the callee returns, it transfers ownership of the parameters to the caller. In the event of an exception, the caller must ignore the value of out parameters because they may be uninitialized.

Method Overloading

Method overloading is the object-oriented practice of defining more than one method with the same name in a class. Doing so allows the convenient reuse of a method name when, for example, the underlying implementations differ based on the types of the arguments. Actually, support for overloaded methods typically relies on the signature of each method to ensure uniqueness. In this case, the signature consists of the method name along with the number, types, and ordering of its arguments.

Since Babel supports languages that do not support method overloading, a mechanism for generating unique names was needed. These are typically generated by compilers based on hashing the argument types into the method name. However, developers often manually address this with far fewer characters than would be used by a compiler. Consequently, it was determined it would be more efficient to leave the task of identifying the unique name to the developer. Therefore, Babel allows the specification of the base, or short, method name along with an optional method name extension as illustrated in the SIDL file below for the getValue method.

SIDL
package Overload version 1.0 { class Sample { int getValue ( ); int getValue[Int]( in int v ); double getValue[Double]( in double v ); } }

Thus, the full method name is the concatenation of the short name followed by the name extension. When generating code for supported languages, Babel makes use of either the short or full method name as appropriate for the language(s) involved. For those that support method overloading, such as C++ and Java, Babel relies only on the short method name, thus ignoring the extension. For the rest, like C, Fortran, and Python, Babel must make use of the full name to ensure methods are uniquely identified.

In the example above, the first method specification takes no arguments so has no name extension. This is acceptable because there are no potentially conflicting methods at this point for any programming language supported by Babel. The second method, with the user-defined name extension of Int, takes a single int argument, resulting in the unique method name getValueInt. The last method, with a user-defined name extension of Double, takes a single double argument, resulting in the unique method name of getValueDouble. Examples of calling overloaded methods from Babel-supported languages can be found in the respective language binding chapters.

6.8  XML Repositories

Even though SIDL is currently the primary input format for Babel, it is not the only format Babel understands. For type repositories (similar in function to include directories for C/C++ headers) the preferred language to articulate types is XML.

Babel has the capabilities to convert SIDL files into XML files adhering to the sidl.dtd. This capability is explained further in Chapter 16. The XML files in these repositories can be included in subsequent runs quickly since all the external references were resolved by Babel during their creation. A SIDL file may refer to unresolved types.


1
meaning non-strided
2
Contract clauses should never replace defensive programming data checks since clause enforcement may be disabled during deployment. The data checks of defensive programming, on the other hand, should be executed on every run since they are needed to protect against serious, undesirable side-effects that include abrupt, unexplained termination.
3
The is pure assertion is a non-executable annotation since Babel is not equiped to statically analyze source code for any of the supported programming languages.
4
Refer to Section A.2 for the list of reserved words
5
Babel is still pre-1.0 after all!

Previous Up Next