This chapter gives an overview of the C bindings for SIDL. Common aspects of the bindings, such as the mapping of SIDL data types to their C representatives, are presented in Section 8.2. Issues of concern to callers written in C are addressed in the client-side bindings discussion in Section 8.3, while callees written in C would benefit from a review of implementation-side issues in Section 8.4. Although it would defeat the multilingual interoperability goals of Babel, programs can be written solely with a C compiler since Babel’s Intermediate Object Representation (IOR) and all objects in the sidl name space (e. g. sidl.BaseClass, etc.) are implemented in C.
As with any programming language-neutral technology, translations must be made between abstract constructs supported by the technology and the corresponding concrete constructs in the native programming language. Due to the need to identify types in a global context, Subsection 8.2.1 describes the convention used to establish name spaces. Conventions for generating language-specific method signatures are given in Subsection 8.2.2. The mapping of SIDL fundamental types is given in Subsection 8.2.3. Finally, the process of casting between different types is described in Subsection 8.2.4.
Since C does not have built-in mechanisms for protecting the global name space, generated bindings avoid name space collisions by using struct and method names that incorporate all relevant naming information. Without this approach, there would be multiple structures or routines with the same name. For a type Z in package X.Y, for example, the name of the type that C clients use for an object reference is X_Y_Z. The name is defined as follows in the X_Y_Z.h header file:
struct X_Y_Z__object; struct X_Y_Z__array; typedef struct X_Y_Z__object* X_Y_Z; |
Method names, as discussed in Subsection 8.2.2, are built in a similar manner.
The name of a C routine used to call a SIDL method is a concatenation of the package, class (or interface), and method name, with period characters replaced with underscores. If the method is specified as overloaded (i. e., has a name extension), the extension is appended. The object (or interface) pointer is automatically inserted as the first parameter in the signature of non-static methods. This parameter operates like an in parameter. With the addition of remote method invocation (RMI) support, all methods now implicitly throw exceptions. Hence, an extra out parameter for the exception is added as the last parameter of the signature.
The following SIDL method — taken from the Babel regression tests — is an example of a method that can throw multiple exception types:
int getFib(in int n, in int max_depth, in int max_value, in int depth) throws NegativeValueException, FibException; |
The corresponding C API is:
int32_t ExceptionTest_Fib_getFib( ExceptionTest_Fib self, int32_t n, int32_t max_depth, int32_t max_value, int32_t depth, sidl_BaseInterface *_ex); |
Note the addition of the object pointer (i. e., self) and exception (i. e., _ex) parameters.
Basic SIDL types are mapped into C according to Table 8.1. The remainder of this subsection illustrates 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 enums followed by the specified enumeration type and enumeration value name, where an underscore is used to separate each part. 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.h" /* ...deleted lines... */ enum enums_color__enum 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 and use in languages such as C, C++, and Fortran. This subsection discusses both within the context of C bindings. More information on the C version of the SIDL array API can be found in Subsection 6.4.
In addition to defining the object structure and associated type for user-defined interfaces and classes, Babel also defines a corresponding array structure for normal SIDL arrays. The C mappings for the SIDL base interface and class are:
/** * Symbol "sidl.BaseInterface" (version 0.9.12) * * Every interface in <code>SIDL</code> implicitly inherits * from <code>BaseInterface</code>, which is implemented * by <code>BaseClass</code> below. */ struct sidl_BaseInterface__object; struct sidl_BaseInterface__array; typedef struct sidl_BaseInterface__object* sidl_BaseInterface; /** * Symbol "sidl.BaseClass" (version 0.9.12) * * Every class implicitly inherits from <code>BaseClass</code>. This * class implements the methods in <code>BaseInterface</code>. */ struct sidl_BaseClass__object; struct sidl_BaseClass__array; typedef struct sidl_BaseClass__object* sidl_BaseClass; |
Given the package num and class Linsol with the solve method specified in Subsection 6.4, the corresponding generated C API is:
/** C client-side API for solve method */ void num_Linsol_solve(/* in */ num_Linsol self, /* in rarray[m,n] */ double* A, /* inout rarray[n] */ double* x, /* in */ int32_t m, /* in */ int32_t n, /* out */ sidl_BaseInterface *_ex); |
In this example, data for each array is passed as a double pointer with the index parameters being normal in integers. The one catch for C programmers is that A is in column-major order — not the typical row-major ordering used in C.
Access to the element in row i and column j can be facilitated using RarrayElem2(A,i,j,m). RarrayElem2, defined in sidlArray.h, is a convenience macro — for C and C++ programmers — supplied to facilitate accessing r-arrays in column-major order. Access to memory by stride one involves making the first index argument to RarrayElem2, i, the inner loop. Since valid pointers are always required for raw arrays, passing NULL for A, x, or b is not allowed.
C bindings for interfaces and classes include two implicitly defined methods for performing type casts. The methods are: _cast and _cast2. The _cast method casts a SIDL interface or object pointer to a sidl.BaseClass pointer. The _cast2 method casts a SIDL interface or object pointer to a named type pointer. In the latter case, the client is responsible for casting the return value into the proper pointer type. Using sidl.BaseClass as an example, signatures of the two methods are:
sidl_BaseClass sidl_BaseClass__cast(void* obj, /* out */ sidl_BaseInterface *_ex); void* sidl_BaseClass__cast2(void* obj, const char* type, /* out */ sidl_BaseInterface *_ex); |
Using either method results in the reference count of the underlying object being increased if the cast succeeded. Success can be determined by checking the return value for a non-NULL result. That is, if a NULL value is returned from either method, then the cast failed or obj was NULL.
NOTE: These methods did not increment the reference count in Babel releases prior to 0.11.0.
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 conventions used to name C header files are 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.
C stubs (i. e. code to support C clients for a set of SIDL-specified classes or interfaces), are created by invoking Babel as follows1:
% babel --exclude-external --client=C file.sidl
or more cryptically
% babel -E -cC file.sidl
Using the --
exclude-external flag avoids generation
of files for symbols referenced (but not specified) in file.sidl;
thereby,
reducing the total number of generated files. Of the files generated, those
ending in _IOR.h and _IOR.c contain the Intermediate Object
Representation (IOR). Files ending with _Stub.c are the C stubs
— the interface between C clients and the IOR. The remaining header
files contain the C client API.
To use the bindings, you must compile and link the stub files against the SIDL runtime library and an implementation of the API.
The hierarchical nature of SIDL packages lends itself to multiple options for including enumerations, interfaces, and classes. The naming convention for associated header files uses underscore-separated parts corresponding to the package hierarchy. That is, type X.Y.Z — where X is the name of the package, Y the subpackage, and Z the class — is included with #include "X_Y_Z.h". The header files for the whole subpackage, X.Y, are included with #include "X_Y.h". For instance, all types in the sidl name space are included in #include "sidl.h".
Babel ensures each generated client-side header file automatically includes sidl_header.h, which defines:
SIDL-specified objects are managed through explicit creation and reference counting. An additional implicit method, called _create, must be invoked to create new instances of a concrete class. The _create method returns a new reference that must be managed by the client. The following is an example of its signature:
/** * Constructor function for the class. */ sidl_BaseClass sidl_BaseClass__create(/* out */sidl_BaseInterface *_ex); |
References are then managed through methods inherited from sidl.BaseInterface. The methods are addRef and deleteRef, where addRef is used to increment the reference counter while deleteRef decrements it and, if the count reaches zero, frees any associated memory — assuming the developer properly implemented the destructor. Their C APIs for sidl.BaseInterface are:
void sidl_BaseInterface_addRef(/* in */ sidl_BaseInterface self, /* out */ sidl_BaseInterface *_ex); void sidl_BaseInterface_deleteRef(/* in */ sidl_BaseInterface self, /* out */ sidl_BaseInterface *_ex); |
These same methods can be called from the sidl.BaseClass bindings. In fact, since all SIDL-specified interfaces inherit from sidl.BaseInterface and all classes from sidl.BaseClass, every C binding for an interface or class will inherit addRef and deleteRef methods. Their C APIs for sidl.BaseClass are:
void sidl_BaseClass_addRef(/* in */ sidl_BaseClass self, /* out */ sidl_BaseInterface *_ex); void sidl_BaseClass_deleteRef(/* in */ sidl_BaseClass self, /* out */ sidl_BaseInterface *_ex); |
Static methods are class-wide so not associated with an object. As a result, self is not automatically added as the first argument to the method signature in the bindings.
Using the overload_sample.sidl file from Section 6.7 as an example, recall that three versions of the getValue method are specified. The first signature takes no arguments, the second takes an integer, and the third a boolean. The code snippet below illustrates object creation, method invocation for each of the overloaded methods, and exception handling.
int32_t b1, i1, iresult, nresult; sidl_BaseInterface ex; Overload_Sample t = Overload_Sample__create (&ex); SIDL_CHECK(ex); nresult = Overload_Sample_getValue(t, &ex); SIDL_CHECK(ex); iresult = Overload_Sample_getValueInt(t, i1, &ex); SIDL_CHECK(ex); bresult = Overload_Sample_getValueBool(t, b1, &ex); SIDL_CHECK(ex); |
SIDL_CHECK is used to check if an exception has been thrown. If so, control jumps to the code after the EXIT label, which is not illustrated here but is in the example presented in Subsection 8.3.6.
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. For maximum backward compatibility and consistency, the argument is of type sidl.BaseInterface. When the exception parameter value is not NULL, an exception has been thrown. In that case, the caller should ignore the value of the other out parameters as well as any return value.
To facilitate exception management, sidl_Exception.h provides several helper utilities. Chief among them are: SIDL_CHECK, SIDL_CATCH, and SIDL_CLEAR. Their use follows from their names. Their signatures are:
/* Macros to facilitate managing exceptions */ SIDL_CHECK(EX_VAR) SIDL_CLEAR(EX_VAR) /* Helper function to facilitate catching exceptions of a specific type */ int SIDL_CATCH(struct sidl_BaseInterface__object *ex_var, const char *sidl_Name); |
EX_VAR (or ex_var) is the exception object itself and sidl_NAME is the string name of the exception type expected to be caught.
The following example, based on the getFib method from Subsection 8.2.2, illustrates not only catching an exception but determining whether it is one of the types identified in the specification:
#include "sidl_Exception.h" /* ...numerous lines deleted... */ int x; sidl_BaseInterface _ex = NULL; x = ExceptionTest_Fib_getFib(f, 10, 1, 100, 0, &_ex); if (SIDL_CATCH(_ex, "ExceptionTest.TooDeepException")) { traceback(_ex); SIDL_CLEAR(_ex); } else if (SIDL_CATCH(_ex, "ExceptionTest.TooBigException")) { traceback(_ex); SIDL_CLEAR(_ex); } else if (_ex == NULL) { return FALSE; } SIDL_CHECK(_ex); return TRUE; EXIT:; traceback(_ex); SIDL_CLEAR(_ex); return FALSE; |
As an alternative to using SIDL_CHECK, _ex can be compared to NULL directly. Similarly, instead of using SIDL_CATCH, type casting can be used to determine which of the potential exception types was actually thrown.
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:
hooks_Basics obj; sidl_BaseInterface _ex = NULL; obj = hooks_Basics__create(&exception); SIDL_CHECK(exception); /* Enable hooks execution (enabled by default if hooks generated) */ /* ... for static methods */ hooks_Basics__set_hooks_static(TRUE, &_ex); SIDL_CHECK(exception); /* ... for non-static methods */ hooks_Basics__set_hooks(obj, TRUE, &_ex); SIDL_CHECK(exception); /* ...do something meaningful... */ /* Disable hooks execution */ /* ... for static methods */ hooks_Basics__set_hooks_static(FALSE, &_ex); SIDL_CHECK(exception); /* ... for non-static methods */ hooks_Basics__set_hooks(obj, FALSE, &_ex); SIDL_CHECK(exception); /* ...do something meaningful... */ |
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 8.4.5.
Interface contracts specify the expected behaviors of callers (or clients) and callees (or servers) of methods defined for interfaces and classes. Once specified, contracts are optionally enforced at runtime, through checks automatically integrated into the middleware generated by the Babel compiler. This section provides an example of a specification and code snippets for performing basic, traditional contract enforcement — introduced in Section 6.5 — in 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 using 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.h" #include "sidl_Exception.h" /* ... */ { sidl_BaseInterface exception = NULL; struct sidl_double__array* x = NULL; struct sidl_double__array *u = NULL; struct sidl_double__array *v = NULL; sidl_BaseInterface _ex = NULL; u = sidl_double__array_create1d(max_size); v = sidl_double__array_create1d(max_size); /* Initialize u and v IF they are non-NULL. */ /* Enable FULL contract enforcement. */ sidl_EnfPolicy_setEnforceAll(sidl_ContractClass_ALLCLASSES, TRUE, &exception); SIDL_CHECK(exception); /* Do something meaningful before execute method. */ x = vect_Utils_vuSum(u, v, (sidl_BaseInterface*)(&_ex)); if (_ex != NULL) { if (SIDL_CATCH(_ex, "sidl.PreViolation")) { /* Precondition violated. Do something meaningful. */ } else if (SIDL_CATCH(_ex, "sidl.PostViolation")) { /* Postcondition violated. Do something meaningful. */ } else { /* Unrecognized or unhandled exception. Do something meaningful. */ goto EXIT; } } /* Do something meaningful with the result, x. */ /* ... */ if (x != NULL) sidl_double__array_deleteRef(x); sidl__array_deleteRef((struct sidl__array*)u); sidl__array_deleteRef((struct sidl__array*)v); return 0; EXIT:; if (x != NULL) sidl_double__array_deleteRef(x); if (_ex !=NULL) { sidl_BaseInterface _tae = NULL; sidl_SIDLException _cue = sidl_SIDLException__cast(_ex, &_tae); if (_cue != NULL) { printExceptionNote(_cue); sidl_SIDLException_deleteRef(_cue, &_tae); } sidl_BaseInterface_deleteRef((sidl_BaseInterface)_ex, &_tae); _ex = NULL; } return -1; } |
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.h" #include "sidl_Exception.h" { /* ... */ /* * Enable only precondition contract enforcement. * (Useful when only need to ensure callers comply with contract.) */ sidl_EnfPolicy_setEnforceAll(sidl_ContractClass_PRECONDS, FALSE, &exception); SIDL_CHECK(exception); /* * Enable only postcondition contract enforcement. * (Useful when only need to ensure implementation(s) comply with contract.) */ sidl_EnfPolicy_setEnforceAll(sidl_ContractClass_POSTCONDS, FALSE, &exception); SIDL_CHECK(exception); /* * Disable contract enforcement. * (Should only be used when have confidence in caller AND implementation.) */ sidl_EnfPolicy_setEnforceNone(FALSE, &exception); SIDL_CHECK(exception); EXIT: /* Do something with exception */ } |
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. Since access to private data requires special steps in C, the process for defining and managing that data is discussed. Throwing exceptions in the implementation is then illustrated. Finally, the results of generating implementations with pre- and post-method “hooks” are shown.
To create the C implementation bindings for a set of SIDL classes, Babel should be invoked as follows:
% babel --exclude-external --server=C file.sidl
or use the short form
% babel -E -sC file.sidl
The command creates a number of files. Specifically, a Makefile fragment, called babel.make, headers, and source files are generated. The only files needing hand-editing are the C “Impl” files — which are header and source files whose names end in _Impl.h or _Impl.c, respectively. More on this in Subsection 8.4.2.
Implementation details must be added to the “Impl” files generated in Subsection 8.4.1. Changes to these files must be made between code splicer pairs to ensure their retention in subsequent invocations of Babel. The following is an example of a code splicer pair in C:
/* DO-NOT-DELETE splicer.begin(num.Linsol._includes) */ /* Insert-Code-Here {num.Linsol._includes} (includes and arbitrary code) */ /* DO-NOT-DELETE splicer.end(num.Linsol._includes) */ |
A snippet from the Babel-generated implementation file for the solve example from Subsection 6.4 is given below, wherein r-array data are presented as double pointers, and index variables are normal integers.
void impl_num_Linsol_solve(/* in */ num_Linsol self, /* in rarray[m,n] */ double* A, /* inout rarray[n] */ double* x, /* in */ int32_t m, /* in */ int32_t n, /* out */ sidl_BaseInterface *_ex) { *_ex = 0; /* DO-NOT-DELETE splicer.begin(num.Linsol.solve) */ /* Insert-Code-Here {num.Linsol.solve} (solve method) */ /* DO-NOT-DELETE splicer.end(num.Linsol.solve) */ } |
Data for the 2-D array, A, is in column-major order. The RarrayElem2 helper macro, described in Subsection 8.2.3, can be used to access A.
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.c file — or local to an instance — as in variables declared through the private data structure automatically generated in the class’s _Impl.h file. 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 data structure automatically generated in the implementation’s header file. As illustrated in the foo.bar example below, the implementer is free to define suitable contents.
/* * Private data for class foo.bar */ struct foo_bar__data { /* DO-NOT-DELETE splicer.begin(foo.bar._data) */ int d_my_int_array[MY_MAX_ARRAY_SIZE]; double d_my_double; /* DO-NOT-DELETE splicer.end(foo.bar._data) */ } |
Upon instantiation, the object’s data structure is automatically initialized to NULL before the built-in _ctor() method is invoked. Initialization of private data first requires sufficient memory be allocated, as follows:
void impl_foo_bar__ctor( /* in */ foo_bar self) { /* DO-NOT-DELETE splicer.begin(foo.bar._ctor) */ int i; struct foo_bar__data *dataPtr = malloc(sizeof(struct foo_bar__data)); TSTT_Triangle_Mesh__set_data(self, dataPtr); if (dataPtr) { for (i=0; i<MY_MAX_ARRAY_SIZE; i++) { dataPtr->d_my_int_array[i] = i; } dataPtr->d_my_double = 0.0; } /* DO-NOT-DELETE splicer.end(foo.bar._ctor) */ } |
To avoid leaking memory, allocated private data must be released during instance destruction. This is accomplished through the built-in _dtor() method. Continuing with the foo.bar example, the memory is freed as follows:
void impl_foo_bar__dtor( /* in */ foo_bar self) { /* DO-NOT-DELETE splicer.begin(foo.bar._dtor) */ struct foo_bar__data *dataPtr = foo_bar__get_data(self); if (dataPtr) { memset(dataPtr, 0, sizeof(struct foo_bar__data)); free((void*)dataPtr); } foo_bar__set_data(self, NULL); /* DO-NOT-DELETE splicer.end(foo.bar._dtor) */ } |
Notice all memory locations are initialized to zero before being freed and the internal data pointer set to NULL. These practices are recommended.
Hence, Babel supports the declaration and maintenance of private data on class and instance basis.
In addition to the helpers discussed in Subsection 8.3.6, sidl_Exception.h provides the following SIDL_THROW macro for throwing exceptions:
SIDL_THROW(EX_VAR,EX_CLS,MSG) |
The first argument to the macro is the exception output parameter; the second is the type of exception being thrown; and the third provides a textual description of the exception. The following code snippet, which is an extension of the Subsection 8.3.6 example, illustrates the process of using the macro to throw an exception:
#include "sidl_Exception.h" /* ...numerous lines deleted... */ int32_t impl_ExceptionTest_Fib_getFib( ExceptionTest_Fib self, int32_t n, int32_t max_depth, int32_t max_value, int32_t depth, sidl_BaseInterface* _ex) { /* DO-NOT-DELETE splicer.begin(ExceptionTest.Fib.getFib) */ if (n < 0) { SIDL_THROW(*_ex, ExceptionTest_NegativeValueException, "called with negative n"); } /* ...lines deleted... */ EXIT:; /* SIDL_THROW macro will jump here. */ /* Clean up code should be here. */ return theValue; /* DO-NOT-DELETE splicer.end(ExceptionTest.Fib.getFib) */ } |
EX_VAR is the exception object itself; EX_CLS is the string containing the name of the desired SIDL exception type; and MSG is the string containing the message to be included with the exception. As with the other helpers, the presence of the EXIT label is assumed in the macro. Statements following EXIT should be used to conduct clean up operations, such as deleting any references that were to be returned to the caller.
A good practice we recommend is to set all inout and out array, interface or class pointers to NULL. This makes things work out better for clients who forget to check if an exception occurred or willfully choose to ignore it.
As discussed in Subsection 8.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 8.3.7, the generated _pre method
implementation is:
void impl_hooks_Basics_aStaticMeth_pre(int32_t i, int32_t io, sidl_BaseInterface *_ex) { *_ex = 0; { /* 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 impl_hooks_Basics_aStaticMeth_post(int32_t i, int32_t o, int32_t io, int32_t _retval, sidl_BaseInterface *_ex) { *_ex = 0; { /* 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.