This chapter gives an overview of the C++ bindings for SIDL as of Babel 1.0. The original C++ bindings, available in the first public release (0.5.0 in July 2001), underwent a significant redesign thanks to Steve Parker at the University of Utah. The result became known as the Utah C++ (i. e., initially tagged UCxx) alternative to the original bindings (0.10.0 January 2005). As of 1.0.0, the Utah version is the only binding released for C++.
Common aspects of those bindings, such as the mapping of SIDL data types to their C++ counterparts, are presented in Section 9.2. Issues of concern to callers written in C++ are addressed in the client-side discussion in Section 9.3, while issues for callees appear in the implementation-side discussion in Section 9.4.
This section summarizes basic features that are common to both client and implementation bindings. Subsection 9.2.1 describes conventions used to establish name spaces, while those associated with the generation of subroutines from methods are given in Subsection 9.2.2. The mapping of fundamental and key SIDL types is given in Subsection 9.2.3. Finally, casting between different types is discussed in Subsection 9.2.4.
The C++ bindings take advantage of language features to protect the global name space. In particular, SIDL packages are mapped to C++ name spaces. Interfaces and classes are mapped to proxy classes, called “stubs”, which serve as the firewall between the application in C++ and Babel’s internal workings. Static SIDL methods are translated into static C++ member functions, while non-static methods are mapped to non-static C++ member functions.
NOTE: For backward compatibility, as of the 1.0 release, the SIDL_USE_UCXX, UCXX, and UCXX_LOCAL preprocessor macros are undefined. SIDL_USE_UCXX is used in #ifdef SIDL_USE_CXX/#endif blocks to specify the ucxx name space. Hence, UCXX was to be used where ::ucxx would normally appear and UCXX_LOCAL where ucxx:: would appear.
Since the bindings are able to map well into C++ language constructs, C++ method signatures correspond very closely to those in the specification. Adapted from the Babel regression tests, the following is an example of a package called ExceptionTest that has a class named Fib with a method, getFib, declared in SIDL as follows:
int getFib(in int n, in int max_depth, in int max_value, in int depth) throws NegativeValueException, FibException; |
The corresponding C++ method signature is:
int32_t getFib (/* in */int32_t n, /* in */int32_t max_depth, /* in */int32_t max_value, /* in */int32_t depth ) // throws: // ::ExceptionTest::FibException // ::ExceptionTest::NegativeValueException // ::sidl::RuntimeException ; |
Basic SIDL types are mapped into C++ according to Table 9.1. The remainder of this subsection illustrates the use of enumerations and arrays.
Since SIDL enumerations map to C++ enumerations, their use is fairly straight-forward. The appropriate header file must be included. The naming convention for enumerations is for enums to be the name space followed by the specified enumeration type as the name of the enumeration. A given enumeration value name starts with the enumeration type and the name of the constant, with an underscore as the separator. For example, a variable can be assigned the blue constant for the color enumeration of the sample in Subsection 6.3 as follows:
#include "enums_color.hxx" /* ...deleted lines... */ enums::color myColor = enums::color_blue; |
As discussed in Section 6.4, SIDL supports both normal and raw arrays (i. e., r-arrays). Normal SIDL arrays can be used by any supported language; whereas, r-arrays are restricted to numeric types. This subsection discusses both within the context of C++ bindings.
Although it is feasible to expose the underlying C array API to create, destroy and access normal 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_ucxx.hxx. The array template is specialized in the corresponding stub header for SIDL interfaces and classes. The extensive use of template specialization is used in an effort to hide details that the array implementation shifts between the C++ type externally, and the C-based types stored in the IOR. (See basearray in sidl_ucxx.hxx for the traits classes and grungy implementation details.) For example, the process to create a one-dimensional SIDL array of prime numbers is:
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<int32_t> a = sidl::array<int32_t>::createRow(dim, lower, upper); for( int i=0; i<len; ++i ) { prime = nextPrime( prime ); a.set(i, v); } |
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 ( ior_array_t * src ); // internal array ( const array & src ); // copy constructor // destructor ~array() ; // create row-size of 1 to 7 dimensions static array<T> createRow( int32_t dimen, const int32_t lower[], const int32_t upper[]); // create column-wise of 1 to 7 dimensions static array<T> createCol( int32_t dimen, const int32_t lower[], const int32_t upper[]); // create 1-D array of specified length static array<T> create1d( int32_t len ); // create 1-D array of specified length and init static array<T> create1d(int32_t len, ior_item_internal_t data) // create 2-D array of specified extents static array<T> create2dCol( int32_t m, int32_t n); // create 2-D array of specified extents static array<T> create2dRow( int32_t m, int32_t n); // get a slice of the array array<T> slice( int32_t dimen, const int32_t numElem[], const int32_t *srcStart = 0, const int32_t *srcStride = 0, const int32_t *newStart = 0); void borrow( item_ior_t * first_element, int32_t dimen, const int32_t lower[], const int32_t upper[], const int32_t stride[]); void ensure( int32_t dimen, array_ordering ordering ); void addRef(); void deleteRef(); // get/set cxx_item_t get(int32_t i); cxx_item_t get(int32_t i1, int32_t i2); cxx_item_t get(int32_t i1, int32_t i2, int32_t i3); cxx_item_t get(int32_t i1, int32_t i2, int32_t i3, int32_t i4); cxx_item_t get(int32_t i1, int32_t i2, int32_t i3, int32_t i4, int32_t i5); cxx_item_t get(int32_t i1, int32_t i2, int32_t i3, int32_t i4, int32_t i5, int32_t i6); cxx_item_t get(int32_t i1, int32_t i2, int32_t i3, int32_t i4, int32_t i5, int32_t i6, int32_t i7); cxx_item_t get(const int32_t *indices); void set(int32_t i, cxx_item_t elem); void set(int32_t i1, int32_t i2, cxx_item_t elem); void set(int32_t i1, int32_t i2, int32_t i3, cxx_item_t elem); void set(int32_t i1, int32_t i2, int32_t i3, int32_t i4, cxx_item_t elem); void set(int32_t i1, int32_t i2, int32_t i3, int32_t i4, int32_t i5, cxx_item_t elem); void set(int32_t i1, int32_t i2, int32_t i3, int32_t i4, int32_t i5, int32_t i6, cxx_item_t elem); void set(int32_t i1, int32_t i2, int32_t i3, int32_t i4, int32_t i5, int32_t i6, int32_t i7, cxx_item_t elem); void set(const int32_t *indices, cxx_item_t elem); // [] overloaded to be same as get(i) cxx_item_t operator[](int32_t i) const ; bool is1dPacked() const; // returns STL forward iterator iff 1DPacked, else null iterator begin(); // returns STL forward iterator iff 1DPacked, else null const_iterator begin(); // returns STL forward iterator iff 1DPacked, else null iterator end(); // returns STL forward iterator iff 1DPacked, else null const_iterator end(); const int32_t* first() const; int32_t* first(); void copy( const array< T >& src ); // other accessors int32_t dimen() const; int32_t lower( int32_t dim ) const; int32_t upper( int32_t dim ) const; int32_t stride( 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; // get a non-const pointer to the actual array ior array_ior_t* _get_ior(); void _set_ior( ior_array_t * s); array& operator =(const array &rhs); array& operator =(const basearray &rhs); |
where
cxx_array_t,
cxx_item_t,
ior_array_t,
ior_item_t,
ior_item_internal_t,
iterator,
const_iterator,
pointer, and
value_type are all public typedefs in the array class.
Table 6.3 provides a brief description of each
function in the array API.
The values of these typedefs are determined by traits classes, which are a fairly standard, albeit advanced, C++ templating idiom. Refer to any advanced C++ text for a detailed explanation. Both the array_traits<> and array<> template specializations for int32_t are reproduced below. More built-in types and the UCxx stubs for user-defined types can be found in sidl_ucxx.hxx.
// template specialization for array_traits<int32_t> template<> struct array_traits<int32_t> { typedef array<int32_t> cxx_array_t; typedef int32_t cxx_item_t; typedef struct sidl_int__array ior_array_t; typedef int32_t ior_item_t; typedef const int32_t* ior_item_internal_t; typedef cxx_item_t value_type; typedef value_type* pointer; typedef const value_type* const_pointer; }; template<> class array< int32_t > : public basearray { public: typedef basearray Base; typedef array_traits<int32_t>::cxx_array_t cxx_array_t; typedef array_traits<int32_t>::cxx_item_t cxx_item_t; typedef array_traits<int32_t>::ior_array_t ior_array_t; typedef array_traits<int32_t>::ior_item_t ior_item_t; typedef array_traits<int32_t>::ior_item_internal_t ior_item_internal_t; typedef array_iter< array_traits<int32_t> > iterator; typedef const_array_iter< array_traits<int32_t> > const_iterator; typedef array_traits< int32_t > ::pointer pointer; typedef array_traits< int32_t > ::value_type value_type; // lots of methods to follow } |
The C++ mapping for r-arrays is essentially identical to the mapping for C (see Section 8.2.3). The only difference is that the C++ client header provides an overloaded version of each method containing an r-array taking normal SIDL arrays instead of raw data. For example, the solve method from Section 6.4 produces the following code in the header file.
void solve (/*in*/ double* A, /*inout*/ double* x, /*in*/ double* b, /*in*/ int32_t m, /*in*/ int32_t n) throw (); void solve (/*in*/ ::sidl::array<double> A, /*inout*/ ::sidl::array<double>& x, /*in*/ ::sidl::array<double> b) throw(); |
Multi-dimensional arrays, such as A in the above example, are stored in column-major order. Babel provides macros to access r-array data correctly. In this case, for example, RarrayElem2(A, i, j, m) can be used to access the element in row i and column j. In addition, memory can be accessed by stride one by making the row index the inner loop and the column index the outer. Similar macros are available in sidlArray.h for arrays of dimension 1 through 7.
There are two forms of type casting: upcasting and downcasting. Upcasting involves casting from a derived, or subclass, to a more general base class. As a result, it is safely handled with simple assignment. Downcasting works in the other direction; that is, it involves casting an instance of a base class to a more specific subclass. It should be done with sidl::babel_cast<>(). Downcasts are successful if the resulting pointer is non-NULL. This can be checked by a call using either _is_nil() — for determining cast failure — or _not_nil() — for success.
NOTE: Never use dynamic_cast<>() on a SIDL object since Babel’s runtime system needs to be involved in verifying the legality of the downcast.
This section summarizes aspects of generating and using the C++ bindings associated with software wrapped with Babel’s language interoperability middleware. The bindings generation process is presented before the convention used to name C++ header files is described. Object management and invocation of static and overloaded methods are also summarized. The process of catching exceptions is then discussed. Finally, the processes for enabling and disabling implementation-specific pre- and post-method instrumentation — referred to as “hooks” — are illustrated.
To create the C++ stubs from a SIDL file, invoke Babel as follows1:
% babel --exclude-external --client=C++ file.sidl
or simply
% babel -E -cC++ file.sidl
This 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 “.cxx” or “.hxx” are the C++ binding. The files will need to be compiled and linked together to use the C++ stubs.
There is one command line option particular to this language binding. Using the option --cxx-ior-exception (or it’s short form -x) will generate C++ Babel stubs that check for a null IOR whenever a method is called on them. If a method is called on a stub holding a null IOR, it will throw a NullIORException. If this option is not passed to Babel, the program will simply crash, as C++ would do normally with a null pointer.
All C++ code generated by Babel #include’s a file called "sidl_ucxx.hh". This file includes babel_config.h, the C header file that defines configuration information. Finally, sidl_ucxx.hh defines some C++ classes in the SIDL name space, such as:
NOTE: C++ headers have a ".hh" or a “.hxx” suffix to distinguish them from C header files. In pre-Babel 0.11, all C++ bindings used the “.hh” suffix. Since Babel 0.11, the “.hh” suffix was exclusively for the original, deprecated binding, while “.hxx” was introduced for the current, UCxx binding.
SIDL-specified objects are managed through explicit creation with explicit reference counting basically unnecessary. Babel automatically generates a static method called _create that must be invoked to instantiate a concrete class. The default constructor creates the equivalent of a NULL pointer. Below is an example, using standard Babel classes, that creates an object of the base class then upcasts it to its parent interface.
#ifdef SIDL_USE_UCXX using namespace ucxx; #endif sidl::BaseClass object = sidl::BaseClass::_create(); sidl::BaseInterface interface = object; |
SIDL C++ stubs can be treated as smart-pointers. Constructors, destructors, and operators are overloaded making explicit calls to addRef() or deleteRef() rarely needed.
As one would expect, proxy (or “stub”) classes maintain minimal state so that, unlike C or fortran 77, there is no special context argument added to non-static member functions. An example call to the static addSearchPath method of the sidl.Loader class is:
#ifdef SIDL_USE_UCXX using namespace ucxx; #endif std::string s("/try/looking/here"); sidl::Loader::addSearchPath( s ); |
Note the function is invoked directly, through the use of its class name, rather than through an instance.
Since C++ is an object-oriented language, the language is much more amenable to the SIDL programming model and less demanding of the programmer than bindings to non-OO languages, such as C and fortran 77.
The basic process of invoking overloaded methods is illustrated below based on the overload_sample.sidl file shown in Section 6.7. Recall that the file describes three versions of the getValue method. The first takes no arguments, the second an integer, and the third a boolean.
#ifdef SIDL_USE_UCXX using namespace ucxx; #endif bool b1, bresult; int i1, iresult, nresult; Overload::Sample t = Overload::Sample::_create(); nresult = t.getValue(); bresult = t.getValue(b1); iresult = t.getValue(i1); |
Since all methods can now throw sidl.RuntimeException, Babel ensures there is an out argument to hold an exception. If not explicitly specified, Babel will automatically add the argument. Using the example shown in Subsection 9.2.2, a C++ code fragment (from the regression tests) that utilizes the getFib method is:
#ifdef SIDL_USE_UCXX using namespace ucxx; #endif /* ...lines deleted... */ 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 ) { // ... } /* ...lines deleted... */ |
Note that SIDL exceptions map well into C++ exceptions allowing native exception mechanisms to be employed.
If a given component supports pre- and post-method invocation instrumentation, also known as “hooks”, their execution can be enabled or disabled at runtime through the built-in _set_hooks method. For example, given the following SIDL specification:
package hooks version 1.0 { class Basics { /** * Basic illustration of hooks for static methods. */ static int aStaticMeth(in int i, out int o, inout int io); /** * Basic illustration of hooks for static methods. */ int aNonStaticMeth(in int i, out int o, inout int io); } } |
which has a single static function and a member function for the Basics class, the processes for enabling and disabling execution of the implementation-specific hooks are:
#ifdef SIDL_USE_UCXX using namespace ucxx; #endif try { hooks::Basics obj = hooks::Basics::_create(); /* Enable hooks execution (enabled by default) */ /* ... for static methods */ hooks::Basics::_set_hooks_static(TRUE); /* ... for non-static methods */ obj._set_hooks(TRUE); /* ...do something meaningful... */ /* Disable hooks execution (on by default) */ /* ... for static methods */ hooks::Basics::_set_hooks_static(FALSE); /* ... for non-static methods */ obj._set_hooks(FALSE); /* ...do something meaningful... */ } catch (::sidl::RuntimeException e) { cout << e.getNote() << endl; } |
It is important to keep in mind that the _set_hooks_static method must be used to enable/disable invocation of hooks for static methods and the _set_hooks method must be used for those of non-static methods. Also, Babel does not provide client access to the _pre and _post methods; therefore, they cannot be invoked directly. More information on the instrumentation process is provided in Subsection 9.4.5.
Interface contracts specify the expected behaviors of clients and servers of interface and class methods. Once specified, contracts can automatically be enforced at runtime. This section provides an example of a specification and associated code snippets for performing basic, traditional contract enforcement — introduced in Section 6.5 — within a C++ client.
A SIDL specification, including preconditions and postconditions, for calculating the sum of two vectors is given below. (Refer to Section 6.5 for an introduction to the contract syntax.) According to the preconditions, all callers are expected to provide two one-dimensional, SIDL arrays of the same size as arguments. The postconditions specify that all implementations are expected to return a non-null, one-dimensional array of the same size (as the first SIDL array), assuming the preconditions are satisfied.
package vect version 1.0 { class Utils { /* ... */ /** * Return the sum of the specified vectors. */ static array<double> vuSum(in array<double> u, in array<double> v) 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); ensure no_side_effects : is pure; result_not_null: result != null; result_is_1d : dimen(result) == 1; result_correct_size: size(result) == size(u); } /* ... */ } |
An example of a C++ client calling the method is given below. The code snippet illustrates declaring and creating the arrays; enabling full contract enforcement (i. e., checking all contract clauses); executing vuSum; handling contract violation exceptions; and cleaning up references is given below.
#include "sidl_EnfPolicy.hxx" /* ... */ { ::sidl::array<double> u = ::sidl::array<double>::create1d(max_size); ::sidl::array<double> v = ::sidl::array<double>::create1d(max_size); ::sidl::array<double> x; /* Initialize u and v. */ /* Enable FULL contract enforcement. */ ::sidl::EnfPolicy::setEnforceAll(::sidl::ContractClass_ALLCLASSES, true); /* Do something meaningful before execute method. */ try { x = vect::Utils::vuSum(u, v); if (x) { /* Do something useful with the result, x. */ } } catch ( ::sidl::PreViolation preExc ) { std::cerr<preExc.getNote()<<std::endl; } catch ( ::sidl::PostViolation postExc ) { std::cerr<postExc.getNote()<<std::endl; } catch (...) { std::cerr<"Caught unexpected exception."<<std::endl; /* Do something meaningful. */ } if (x) { x.deleteRef(); } u.deleteRef(); v.deleteRef(); return 0; } |
Alternative enforcement options can be set, as described in Section 6.5, through the two basic helper methods: setEnforceAll and setEnforceNone. The code snippet below shows the C++ calls associated with the traditional options of enabling only precondition enforcement, enabling postcondition enforcement, or completely disabling contract enforcement.
#include "sidl_EnfPolicy.hxx" /* ... */ /* * Enable only precondition contract enforcement. * (Useful when only need to ensure callers comply with contract.) */ ::sidl::EnfPolicy::setEnforceAll(::sidl::ContractClass_PRECONDS, false); /* * Enable only postcondition contract enforcement. * (Useful when only need to ensure implementation(s) comply with contract.) */ ::sidl::EnfPolicy::setEnforceAll(::sidl::ContractClass_POSTCONDS, false); /* * Disable contract enforcement. * (Should only be used when have confidence in caller AND implementation.) */ ::sidl::EnfPolicy::setEnforceNone(false); |
This section illustrates the basic interfaces and processes for traditional interface contract enforcement for a C++ client. Additional enforcement policy options and methods as well as more information regarding the specification and enforcement of contracts can be found in Chapter 21.
This section summarizes aspects of generating and wrapping software written in C++. The bindings generation and basic implementation processes are presented first. Accessing private data is then discussed before illustrating the process of throwing exceptions. Finally, the results of generating implementations with pre- and post-method “hooks” are illustrated.
Much of the information for generating client-side bindings is pertinent to implementing a SIDL class in C++. SIDL type mappings are listed in Table 9.1. An implementation can call other SIDL methods, in which case the rules for client calls must be followed.
To create the implementation, a valid SIDL file must be generated by invoking Babel as follows:
% babel --exclude-external --server=C++ file.sidl
or simply
% babel -E -sC++ file.sidl
As a result, a makefile fragment called babel.make, several C header and source files, and numerous C++ header and source files are created. The only files that need to be hand-edited are the C++ “Impl” files (i. e., header and source files that end in _Impl.hxx or _Impl.cxx). More on this in Subsection 9.4.2.
Implementation details must be added to the “Impl” files generated in Subsection 9.4.1. Changes to these files must be made between code splicer pairs to ensure their retention in subsequent invocations of Babel. Below is an example of a code splicer pair in C++. The actual implementation needs to replace the “// Insert code here...” line.
void MyPackage::MyClass_impl::myMethod_impl() { // 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 to allow developers to add #include’s and other miscellaneous items, respectively. In the headers, there is a splicer block that allows a user to make the “Impl” class inherit from some other class. From SIDL’s point of view this is private inheritance — meaning that it is useful for inheriting implementation details — since they cannot be automatically exposed to the SIDL method dispatch mechanism. There is a splicer block inside the class definition for developers to add any desired data members. In the source files, splicer blocks appear in each method implementation. Examples of filling in these splicer blocks are provided in the subsections to follow.
Any variables declared in the implementation source file will, by virtue of Babel’s encapsulation, be private. The data can be global to the class — as in static variables declared within the _includes splicer block at the top of the class’s _Impl.cxx file — or “local” to an instance. In the former case, special initialization procedures can be added to the built-in _load() method that is guaranteed to be called exactly once per class — before any user-defined methods can even be invoked. The latter case relies on the class-specific name space automatically generated in the implementation’s header file. As illustrated in the foo.bar example below, the implementor is free to define suitable contents.
namespace foo { /** * Symbol "foo.bar" (version 0.1) */ class bar_impl : public virtual ::foo::bar // DO-NOT-DELETE splicer.begin(foo.bar._inherits) // Put additional inheritance here... // DO-NOT-DELETE splicer.end(foo.bar._inherits) { // All data marked protected will be accessable by // descendant Impl classes protected: bool _wrapped; // DO-NOT-DELETE splicer.begin(foo.bar._implementation) char* d_timestamp; // DO-NOT-DELETE splicer.end(foo.bar._implementation) public: /* ...lines deleted... */ } } |
If the object has no state, these functions are typically empty. The built-in _ctor() method is invoked upon instantiation. Hence, private data should be initialized in the method. For example:
void foo::bar_impl::_ctor() { // DO-NOT-DELETE splicer.begin(foo.bar._ctor) time_t currTime = time(NULL); sidl_String_strdup(d_timestamp, ctime(&currTime)); // DO-NOT-DELETE splicer.end(foo.bar._ctor) } |
To avoid leaking memory, private data must be released during instance destruction. This is accomplished through the built-in _dtor() method. The memory is then freed as follows:
void foo::bar_impl::_dtor() { // DO-NOT-DELETE splicer.begin(foo.bar._dtor) sidl_String_free(d_timestamp); // DO-NOT-DELETE splicer.end(foo.bar._dtor) } |
Hence, Babel supports the declaration and maintenance of private data on class and instance basis.
The example below shows the standard way to throw an exception in C++. Use of setNote and add methods is not strictly required; however, they do provide information that may be helpful in debugging or error reporting.
int32_t ExceptionTest::Fib_impl::getFib_impl ( /*in*/ int32_t n, /*in*/ int32_t max_depth, /*in*/ int32_t max_value, /*in*/ int32_t depth ) // throws: // ::ExceptionTest::FibException // ::ExceptionTest::NegativeValueException // ::sidl::RuntimeException { // DO-NOT-DELETE splicer.begin(ExceptionTest.Fib.getFib) if (n < 0) { UCXX ::ExceptionTest::NegativeValueException ex = UCXX ::ExceptionTest::NegativeValueException::_create(); ex.setNote("n negative"); ex.add(__FILE__, __LINE__, "ExceptionTest::Fib_impl::getFib"); throw ex; } else if (depth > max_depth) { UCXX ::ExceptionTest::TooDeepException ex = UCXX ::ExceptionTest::TooDeepException::_create(); ex.setNote("too deep"); ex.add(__FILE__, __LINE__, "ExceptionTest::Fib_impl::getFib"); throw ex; } else if (n == 0) { return 1; } else if (n == 1) { return 1; } else { int32_t a = getFib(n-1, max_depth, max_value, depth+1); int32_t b = getFib(n-2, max_depth, max_value, depth+1); if (a + b > max_value) { UCXX ::ExceptionTest::TooBigException ex = UCXX ::ExceptionTest::TooBigException::_create(); ex.setNote("too big"); ex.add(__FILE__, __LINE__, "ExceptionTest::Fib_impl::getFib"); throw ex; } return a + b; } // DO-NOT-DELETE splicer.end(ExceptionTest.Fib.getFib) } |
As discussed in Subsection 9.3.7, when hooks execution
is enabled, implementation-specific instrumentation is executed. Using
the --
generate-hooks option on the Babel
command line when generating implementation-side bindings results
in the automatic generation of a _pre and _post
method for every static and non-static method associated with each class
in the specification. For the aStaticMethod specified in
Subsection 9.3.7, the generated _pre method
implementation is:
void hooks::Basics_impl::aStaticMeth_pre_impl(int32_t i, int32_t io ) { // DO-NOT-DELETE splicer.begin(hooks.Basics.aStaticMeth_pre) /* * Add instrumentation here to be executed immediately prior * to dispatch to aStaticMeth(). */ // DO-NOT-DELETE splicer.end(hooks.Basics.aStaticMeth_pre) } |
while that of the _post method is:
void hooks::Basics_impl::aStaticMeth_post_impl(int32_t i, int32_t o, int32_t io, int32_t _retval) { // DO-NOT-DELETE splicer.begin(hooks.Basics.aStaticMeth_post) /* * Add instrumentation here to be executed immediately after * return from dispatch to aStaticMeth(). */ // DO-NOT-DELETE splicer.end(hooks.Basics.aStaticMeth_post) } |
Per the normal implementation process, the desired instrumentation should be added within the splicer blocks of aStaticMethod_pre and aStaticMethod_post. As stated in the comments within those blocks, aStaticMethod_pre will be executed immediately prior to dispatch to aStaticMethod when the latter is invoked by a client. Assuming no exceptions are encountered, aStaticMethod_post is executed immediately upon return from aStaticMethod.