The intent of this file is to provide the minimum information necessary for people who are familiar with object-oriented/component oriented software development and the SIDL (Scientific Interface Definition Language) to implement classes in C++ or use classes implemented by someone else from a C++ driver. If you are unfamiliar with SIDL, additional material is available from http://www.llnl.gov/CASC/components/.The assumption for this document is that you already have a SIDL file for a software library, and you need to call it from C++ or implement it in C++.
Unlike C or FORTRAN 77, there is no runtime library created for a particular C++ compiler at installation. Instead, when you generate C++ from SIDL, you will find Stubs (aka proxy classes) generated for SIDL base classes and will have to compile and link them into your application.That said, if you switch to a different compiler after installation, there may be some values set in babel_config.h that become invalid. This can be overcome by copying the headerfile, making the necessary changes, and placing the modified headerfile earlier in the include path than the original one.
The first thing that C++ users will notice is that C++ headers have a ".hh" suffix to distinguish them from C's ".h" suffix. This convention was born out of necessity to distinguish both differing headerfiles and their include guards.
All C++ code generated by Babel #includes a file called "SIDL_cxx.hh". This file includes babel_config.h, the C header file that defines configuration information. SIDL_cxx.hh also puts things like std::string and std::complex into the global namespace. Finally, SIDL_cxx.hh defines some C++ classes in the SIDL namespace that
- SIDL::StubBase [implementation detail] Common base class for all C++ stubs (proxy classes)
- template<T,U,V> SIDL::array_mixin [implementation detail] Common base class for all C++ array classes.
- typedefs for SIDL::fcomplex, SIDL::dcomplex, and SIDL::opaque (usually std::complex
, std::complex and void*, respectively) - template<T> SIDL::array Template array type for SIDL arrays.
- template specializations [implementation detail] specialization of arrays of all SIDL types are defined in this file.
The basic types in SIDL are mapped into C++ according to the following table:
SIDL TYPE C++ TYPE NOTES int int32_t long int64_t float float double double bool bool char char string std::string fcomplex sidl::fcomplex dcomplex sidl::dcomplex enum enum opaque sidl::opaque interface class class class array sidl::array (template specialization)
Since C++ is an object-oriented language, there is a lot less programmer overhead in using SIDL from the C++ perspective than from non-OO languages such as C or FORTRAN 77. Here's a table summarizing how SIDL features are mapped to C++.
SIDL Feature C++ Implementation packages C++ namespaces (no name transformations) version numbers ignored interface C++ class, (called "stub", serves as a proxy to the implementation) class C++ class, (called "stub", serves as a proxy to the implementation) methods C++ member functions, no name mangling
NOTE: member functions beginning with a leading underscore "_" may be Babel internals, or specific to C++ binding.static methods static C++ member functions, no name mangling, even works for dynamically loaded objects exceptions thrown and caught using C++ exception handling reference counting SIDL C++ stubs can be treated as smart-pointers.
Constructors, destructors, and operators are overloaded so that explicit calls to addRef() or deleteRef() are rarely needed.casting
- Assignment operators are overloaded to handle safe casting up and down the inheritance heirarchy
- User should never call dynamic_cast<>() on a SIDL object... the stubs inheritance heirarchy does not follow the SIDL inheritance heirarchy.
- Attempted downcasts using assignment should be checked by a call to (is_nil(), or not_nil()).
instance creation Use static member function "_create". The default constructor for a C++ stub creates the equivalent of a NULL pointer. Works only with non-abstract classes. These proxy classes (we call "stubs") serve as the firewall between the application in C++ and Babel's internal workings. As one would expect, the proxy classes maintain minimal state so that (unlike C or F77) there is no special context argument added to non-static member functions.
Here are example using standard classes:
SIDL::BaseClass object = SIDL::BaseClass::_create(); SIDL::BaseInterface interface = object;Here is an example call to the addSearchPath in the SIDL.Loader class:std::string s('/try/looking/here'); SIDL::Loader::addSearchPath( s );Here is another example adapted from the BABEL regression tests. Package ExceptionTest has a class named Fib with a method declared in SIDL as follows:int getFib(in int n, in int max_depth, in int max_value, in int depth) throws NegativeValueException, FibException;Here is the outline of a C++ code fragment to use this method.ExceptionTest::Fib fib = ExceptionTest::Fib::_create(); try { int result = fib.getFib( 4, 100, 32000, 0 ); cout << "Result of fib.getFib() = " << result << endl; } catch ( ExceptionTest::NegativeValueException e ) { // ... } catch ( ExceptionTest::FibException e ) { // ... }Here is how you should invoke BABEL to create the C++ stubs from a SIDL file.babel --client=C++ file.sidlor simplybabel -cC++ file.sidlThis will create a babel.make file, some C headers and sources, and many C++ headers and sources. Files ending in ".c" or ".h" are in C, files ending in ".cc" or ".hh" are C++.You will need to compile and link the files together to use the C++ stubs.
Much of the information from the previous section is pertinent to implementing a SIDL class in C++. The types of the arguments are as indicated in the type table. Your implementation can call other SIDL methods, in which case follow the rules for client calls.To create the implementation, you must first have a valid SIDL file, then invoke Babel as follows:
babel --server=C++ file.sidlor use the shorter argumentsbabel -sC++ file.sidlThis will create a makefile fragment called babel.make, several C headers and source files, and numerous C++ header and source files. To create a working implementation, the only files that need to be hand-edited are the C++ "Impl" files (header and source files that end in _Impl.hh or _Impl.cc). All your additions to this file should be made between code splicer pairs. Code splicing is a technique Babel uses to preserve hand-edited code between multiple invoactions of Babel. This allows a developer to refine their SIDL file without ruining all their previous implementations. Code between splicer pairs will be retained by subsequent invocations of BABEL; code outside splicer pairs is not.Here is an example of a code splicer pair in C++. In this example, you would replace the line "// Insert code here... " with your implementation.
void MyPackage::MyClass::myMethod() { // DO-NOT-DELETE splicer.begin(MyPackage.MyClass.myMethod) // Insert code here... // DO-NOT-DELETE splicer.end(MyPackage.MyClass.myMethod) }It is important to understand where and why splicer blocks occur. Splicer blocks appear at the beginning and end of each Impl header and source file; for developers to add #includes and other miscellaneous items respectively. In the headers, there is a splicer block inside the class definition for developers to add any data members to the class that they wish. There is not currently a splicer block that allows a user to make the impl class inherit from some other class. This may change in the future, but current schools of thought hold that best practice is to use delegation instead of inheritance if developers want to hook SIDL generated impl classes to some existing C++ class library. In the source files, splicer blocks appear in each method implementation. There are two implicit methods (i.e. methods that did not appear in the SIDL file) that must also be implemented. The _ctor method is a constructor function that is run whenever an object is created. The _dtor method is a destructor function that is run whenever an object is destroyed. If the object has no state, these functions are typically empty.
Although it would be feasible to expose the underlying C array API to create, destroy and access array elements and meta-data, the C++ bindings provide a sidl::array<T> template mechanism that is more in keeping with C++ idioms.For SIDL built-in types, template specializations of sidl::array<T> are defined in SIDL_cxx.hh. For SIDL interface and classes, the array template is again specialized in the corresponding stub header. The reason for the extensive use of template specialization is an effort to hide the detail that the array implementation is really templated on three terms: the type of the C struct that represents the array internally, the internal representation of each item in the array, and the C++ representation of each item in the array. (See array_mixin in SIDL_cxx.hh for grungy implementation details.)
An example is given below.
int32_t len = 10; // array length=10 int32_t dim = 1; // one dimensional int32_t lower[1] = {0}; // zero offset int32_t upper[1] = {len-1}; int32_t prime = nextPrime(0); // create a SIDL array of primes. SIDL::array<int> a; a.create(dim,lower,upper); for( int i=0; i<len; ++i ) { prime = nextPrime( prime ); a.set(v,i); }Of course, the example above is only one way to create an array. The list of member functions for all C++ array classes is:// constructors array ( array_ior_t * src ); // internal array () ; // empty // destructor ~array() ; // creation bool create( int32_t dimen, const int32_t lower[], const int32_t upper[]); bool borrow( item_ior_t * first_element, int32_t dimen, const int32_t lower[], const int32_t upper[], const int32_t stride[]); void destroy(); // get/set item_cxx_wrapper_t get(int32_t i, int32_t j=0, int32_t k=0, int32_t l=0); void set(item_cxx_wrapper_t element, int32_t i, int32_t j=0, int32_t k=0, int32_t l=0); // other accessors int32_t dim() const; int32_t lower( int32_t dim ) const; int32_t upper( int32_t dim ) const; bool is_nil() const; bool not_nil() const; // get a const pointer to the actual array ior const array_ior_t* _get_ior() const { return d_array; } // get a non-const pointer to the actual array ior array_ior_t* _get_ior() { return d_array;}where
- array_ior_t is the type of the C struct that represents the array internally,
- item_ior_t is the internal representation of each item in the array,
- item_cxx_wrapper_t is the C++ representation of each item in the array