This chapter provides an overview of the Fortran 90/95 bindings for SIDL. Common aspects of the bindings, such as the mapping of SIDL data types to their native Fortran 90/95 representatives, are presented in Section 11.2. Issues of concern to Fortran 90/95 callers are addressed in the client-side bindings discussion in Section 11.3, while issues of interest to callees of Fortran 90/95 appear in the implementation-side discussion in Section 11.4.
This section summarizes basic features that are common to both client and implementation bindings. Conventions used to protect the global name space are described in Subsection 11.2.1, while those associated with the generation of subroutines from methods are given in Subsection 11.2.2. Translations between SIDL and native Fortran 90/95 constructs are described in Subsection 11.2.3. Finally, the process of casting between different types is illustrated in Subsection 11.2.4.
The name of the module that holds method definitions is derived from the fully qualified name of the class or interface. Module names are essentially formed by replacing all periods in the fully qualified name with underscores. The name of the module holding the derived type of the class or interface is the same as the one holding the methods with the exception of having _type appended. For example, the methods for sidl.SIDLException are defined in a module named sidl_SIDLException in the file sidl_SIDLException.F90. Defined in the file sidl_SIDLException_type.F90, the types for sidl.SIDLException are called sidl_SIDLException_t and, for the array, sidl_SIDLException_a.
All SIDL methods are implemented as Fortran 90/95 subroutines regardless of whether they have a return value. The name of a subroutine that clients invoke is the method’s full name from the SIDL description. Hence, in cases where the method has a name extension (so is overloaded), the full name is the concatenation of the specified short name and extension. On the implementation-side, the name is formed as the concatenation of the package, class (or interface), full method name, and “mi”, with each part separated by an underscore and name mangling used to ensure uniqueness if the resulting name exceeds the character limit.
The same process used for fortran 77, described in Subsection 10.2.2, is used to build up the parameters for generated methods. That is, object (or interface) pointers, return values, and exception pointers are added, as needed. More specifically, 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. When a method has a return value, a parameter to hold the return value is also added after all of the formally declared arguments. This extra argument behaves like an out 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 automatically added as the last parameter of the signature. An example that illustrates the SIDL specification and corresponding routines can be found in Subsections 11.3.5 and 11.4.4.
The mapping for simple SIDL types to Fortran 90/95 is given in Table 11.1. The kind parameters, given in the sidl F90 module, define integer parameters for sidl_int, sidl_long, sidl_float, sidl_double, sidl_fcomplex, sidl_dcomplex, sidl_enum and sidl_opaque to give sizes that match the corresponding SIDL types. The remainder of this subsection elaborates on mappings of strings, pointers, enumerations, and arrays.
The SIDL string type mapping is currently identical to that of the fortran 77 mapping. That is, all Fortran 90/95 strings have a limited fixed size. When implementing a subroutine with an out parameter, the size of the string is restricted to 512 characters.
NOTE: Modification of the value of SIDL_F90_STR_MINSIZE in runtime/sidl/babel_config.h prior to configuring Babel can be used to change the string size limitation.
Pointer types are: opaque, interface, class, and array. This subsection elaborates on each within the context of the Fortran 90/95 language bindings. Opaque pointers are mapped to the equivalent of SIDL double. That is, the intermediate object reference (IOR) assumes a 64-bit integer is used to enable portability between systems with 32-bit and 64-bit address spaces. On a 32-bit system, the upper 32 bits of these quantities are ignored. Systems with more than 64-bit pointers aren’t currently supported. A derived type is used to hold opaque pointers for interfaces, classes, and arrays. The derived type for arrays of numeric types also has a pointer to an array to provide native access without function calls. For each interface and class, there are two modules created. In the first module, the derived type for the object and array are defined. In the second, methods for the object (or interface) and arrays of the object (or interface) are defined. Clients of a class (or interface), typically use the module containing the methods. It, in turn, uses the module containing the types.
Generally, clients should treat opaque, interface, class, and array values as black boxes. However, the value zero is special since it is the equivalent of NULL. Hence, any non-zero value is or should be a valid object reference. The method module provides built-in functions to test whether an interface, class, or array value is_null or is not_null. There is also a subroutine to initialize the value to set_null. Clients should generally initialize new class (or interface) pointers to NULL.
SIDL enumerations map to integer values, which are defined in a module. Given the specification from Section 6.3 for an enumeration type called car, Babel will produce the following enumerated type:
! File: enums_car.F90 ! Symbol: enums.car-v1.0 ! Symbol Type: enumeration ! Babel Version: 0.8.2 ! Description: Client-side module for enums.car module enums_car ! Symbol "enums.car" (version 1.0) use sidl integer (kind=sidl_enum), parameter :: porsche = 911 integer (kind=sidl_enum), parameter :: ford = 150 integer (kind=sidl_enum), parameter :: mercedes = 550 end module enums_car |
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 starts with a discussion normal and generic arrays before proceeding with an example of the interfaces for r-arrays.
The normal SIDL array API is available in a module for creating, destroying, and accessing array elements and meta-data for normal arrays. More information on the API can be found in Subsection 6.4. For sidl.SIDLException, the array module — called sidl_SIDLException_array — is defined in sidl_SIDLException_array.F90. The derived type for a SIDL array is named after the class, interface, or basic type that it holds and the dimension of the array. For sidl.SIDLException, the array derived types are named sidl_SIDLException_1d, sidl_SIDLException_2d, sidl_SIDLException_3d, … up to sidl_SIDLException_7d. For basic types, they are treated as sidl.dcomplex, sidl.double, sidl.fcomplex, etc. Each of these derived types has a 64-bit integer to hold an opaque pointer.
NOTE: Normal Fortran 90/95 arrays or normal SIDL arrays can be used when calling a Fortran 90/95 method, but they cannot be mixed.
Derived types for SIDL types dcomplex, double, fcomplex, float, int, and long have pointers to arrays of the appropriate type and dimension that facilitate direct access to array elements. For example, the derived type for 2d and 3d arrays of doubles is:
use sidl type sidl_double_2d sequence integer (kind=sidl_arrayptr) :: d_array real (kind=sidl_double), pointer, & dimension(:,:) :: d_data end type sidl_double_2d type sidl_double_3d sequence integer (kind=sidl_arrayptr) :: d_array real (kind=sidl_double), pointer, & dimension(:,:,:) :: d_data end type sidl_double_3d |
For the other types, the array API must be used to access elements. In this case, the array can be accessed with the F90 array pointer d_data just like any other F90 array. However, the F90 built-in methods allocate or deallocate on d_data must not be used. Instead, SIDL functions, createCol, createRow, create1d, create2dRow, or create2dCol, must be used to create a new array. These SIDL routines initialize d_data to refer to the data allocated in d_array.
NOTE: create1d, create2dRow, and create2dCol create arrays whose lower index is 0 not 1. To create arrays with a lower index of 1, createCol or createRow must be used.
Software packages like LINPACK or BLAS can be called, but the stride should be checked to make sure the array is suitably packed. Using stride(i) will provide the distance between elements in dimension i. A value of 1 means elements are packed densely. Negative stride values are possible and, when an array is sliced, the resulting array might not even have one densely packed dimension.
As discussed in Section 6.4, the type of a generic array is not specified. As a result, Fortran 90/95 represents generic arrays as the derived type sidl__array as defined in the sidl_array_type module. (Note the use of a two underscore separator.) The following subroutines, defined in the sidl_array_array module, apply to generic arrays: addRef, deleteRef, dimen, type, isColumnOrder, isRowOrder, is_null, no_null, set_null, lower, upper, length, stride, and smartCopy.
Finally, SIDL r-arrays are passed to and from methods as normal Fortran 90/95 arrays. Index variables do not need to be included because the values are determined from the Fortran 90/95 array extents in each dimension. For example, the client-side interface for solve — introduced in Section 6.4 — behaves as if it is a Fortran 90/95 function with the following overloaded interface:
private :: solve_1s, solve_2s interface solve module procedure solve_1s, solve_2s end interface recursive subroutine solve_1s(self, A, x, exception) implicit none ! in num.Linsol self type(num_Linsol_t) , intent(in) :: self ! in array<double,2,column-major> A type(sidl_double_2d) , intent(in) :: A ! inout array<double,column-major> x type(sidl_double_1d) , intent(inout) :: x ! out sidl.BaseInterface exception type(sidl_BaseInterface_t) , intent(out) :: exception end subroutine solve_1s recursive subroutine solve_2s(self, A, x, exception) implicit none ! in num.Linsol self type(num_Linsol_t) , intent(in) :: self ! in rarray<double,2> A(m,n) real (kind=sidl_double) , intent(in), dimension(:, :) :: A ! inout rarray<double> x(n) real (kind=sidl_double) , intent(inout), dimension(:) :: x ! out sidl.BaseInterface exception type(sidl_BaseInterface_t) , intent(out) :: exception ! in int m integer (kind=sidl_int) :: m ! in int n integer (kind=sidl_int) :: n end subroutine solve_2s |
The server-side interface, shown below, is similar.
recursive subroutine num_Linsol_solve_mi(self, A, x, m, n, exception) use sidl use sidl_BaseInterface use sidl_RuntimeException use num_Linsol use sidl_double_array use num_Linsol_impl ! DO-NOT-DELETE splicer.begin(num.Linsol.solve.use) ! Insert-Code-Here {num.Linsol.solve.use} (use statements) ! DO-NOT-DELETE splicer.end(num.Linsol.solve.use) implicit none type(num_Linsol_t) :: self ! in integer (kind=sidl_int) :: m ! in integer (kind=sidl_int) :: n ! in type(sidl_BaseInterface_t) :: exception ! out real (kind=sidl_double), dimension(0:m-1, 0:n-1) :: A ! in real (kind=sidl_double), dimension(0:n-1) :: x ! inout ! DO-NOT-DELETE splicer.begin(num.Linsol.solve) ! Insert-Code-Here {num.Linsol.solve} (solve method) ! DO-NOT-DELETE splicer.end(num.Linsol.solve) end subroutine num_Linsol_solve_mi |
NOTE: The lower index of each dimension of every incoming array is always zero.
Babel automatically generates the cast() method for casting between different interface and class types. Actually, a set of overloaded methods support every allowable cast between a type and all its parent types (both objects and interfaces). The first argument is the object (or interface) to be cast, and the second is a variable of the desired type. The cast is successful if, after the call to cast(), the value of the second argument is not_null. The caller then owns (and is responsible for) the returned reference. Examples of type casting can be found in Subsections 11.3.5 and 11.4.4.
This section summarizes aspects of generating and using the Fortran 90/95 bindings associated with software wrapped with Babel’s language interoperability middleware. The bindings generation process is presented first. 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.
The following is an example of invoking Babel to create the Fortran 90/95 stubs for a SIDL file1:
% babel --exclude-external --client=f90 file.sidl
or simply
% babel -E -c=f90 file.sidl
As a result, a makefile fragment called babel.make, numerous C header and source files, and some Fortran 90/95 files will be created. Files ending in _fStub.c (i. e., STUBSRCS in babel.make) are called by the Fortran 90/95 module which in turn allow Fortran 90/95 to call SIDL methods. Files ending in _type.F90 (i. e., STUBMODULESRCS in babel.make) contain derived type definitions for classes and interfaces. The remaining files ending in .F90 (i. e., TYPEMODULESRCS in babel.make) are Fortran 90/95 modules containing methods. All of these files need to be compiled and linked into the application.
Normally, IOR files (i. e., those ending in _IOR.c) are linked together with the implementation file, so probably don’t need to be compiled.
SIDL-specified objects are managed through explicit creation and reference counting. Babel automatically generates a new() method for concrete classes. The method is used to instantiate the class and return the associated reference. The following example illustrates the instantiation and casting of an object to an interface:
use sidl_BaseClass use sidl_BaseInterface type(sidl_BaseClass_t) :: object type(sidl_BaseInterface_t) :: interface type(sidl_BaseInterface_t) :: exception ! perhaps other code here call new(object, exception) call cast(object, interface, exception) |
The owner of the instance is responsible for its proper disposal. In other words, when processing with the object is done, the owner must invoke deleteRef() on it. Similarly, any object references returned by a subroutine call must be deleted or given to another part of the code that will take ownership of and, therefore, responsibility for deleteRef’ing it. The following example illustrates calling deleteRef() using the sidl.BaseInterface method:
use sidl_BaseInterface type(sidl_BaseInterface_t) :: interface1, interface2 type(sidl_BaseInterface_t) :: exception logical :: areSame ! ! code to initialize interface1 & interface 2 here ! call deleteRef(interface1, exception) |
When it is necessary to determine if two references point to the same object, the built-in isSame method can be used. For example, the following attempts to determine if interface1 and interface2 point to the same object:
use sidl_BaseInterface ! later in the code call isSame(interface1, interface2, areSame, exception) ! areSame holds the return value |
Below is an example illustrating a call to addSearchPath(), which is a static method in the sidl.Loader class.
use sidl_Loader use sidl_BaseInterface type(sidl_BaseInterface_t) :: exception ! later call addSearchPath('/try/looking/here', exception) |
Note the function is invoked directly, without an object reference argument.
Examples of calls to SIDL overloaded methods are 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 takes an integer argument, and the third takes a boolean. Each is called in the following code snippet:
use sidl use Overload_Sample type(Overload_Sample_t) :: t type(sidl_BaseInterface_t) :: exception logical :: b1, bretval integer (kind=sidl_int) :: i1, iretval call new(t, exception) call getValue (t, iretval, exception) call getValueInt (t, i1, iretval, exception) call getValueBool (t, b1, bretval, exception) |
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, the exception argument type is sidl.BaseInterface, while the base exception class is sidl.SIDLException. The exception argument, which behaves like an out parameter, will appear after the return value when both occur in a method. After the call, the client should test this argument using is_null or not_null. If it is not_null_null, an exception was thrown by the method so the caller should respond appropriately. When an exception is thrown, the values of all other arguments are undefined. So the best course of action is to ignore them. If the code does not check the exception argument after each call (that can throw one), any exceptions that are thrown will be utterly ignored as a result of not being automatically propagated to higher level routines.
It is possible to determine which exception was thrown through casting the argument. A successful cast indicates the type of exception that occurred. An example of this process is illustrated below. Package ExceptionTest has a class named Fib with a getFib 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; |
The code to catch specified exception types is:
use sidl use ExceptionTest_Fib use ExceptionTest_FibException use ExceptionTest_NegativeValueException use sidl_BaseInterface type(ExceptionTest_Fib_t) :: fib type(sidl_BaseInterface_t) :: except, except2 type(ExceptionTest_FibException_t) :: fibexcept type(ExceptionTest_NegativeValueException_t) :: nvexcept integer (kind=sidl_int) :: index, maxdepth, maxval, depth, result call new(fib, except) index = 4 maxdepth = 100 maxvalue = 32000 depth = 0 call getFib(fib, index, maxdepth, maxvalue, depth, result, except) if (not_null(except)) then call cast(except, fibexcept, except2) if (not_null(fibexcept)) then ! do something here with the FibException call deleteRef(fibexcept, except2) else call cast(except, nvexcept, except2) ! do something here with the NegativeValueException call deleteRef(nvexcept, except2) endif call deleteRef(except, except2) else write (*,*) 'getFib for ', index, ' returned ', result endif call deleteRef(fib, except2) |
NOTE: Any caller of a method that returns an exception should ignore the values of out and inout parameters. Anything not freed becomes a reference and memory leak.
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. Due to unresolved method overloading problems, the processes for enabling and disabling execution of the implementation-specific hooks are currently dependent on use of fully-qualified functions, as illustrated below.
use sidl use hooks_Basics type(hooks_Basics_t) :: obj type(sidl_BaseInterface_t) :: exception call new(obj, exception) ! ! Enable hooks execution (enabled by default) ! ...for static methods ! (until method overloading issue can be resolved)... ! call hooks_Basics__set_hooks_static_m(1, exception) ! ! ...for non-static methods ! (until method overloading issue can be resolved)... ! call hooks_Basics__set_hooks_m(obj, 1, exception) ! ! ...do something important... ! ! ! Disable hooks execution ! ...for static methods ! call hooks_Basics__set_hooks_static_m(0, exception) ! ! ...for non-static methods ! call hooks_Basics__set_hooks_m(obj, 0, exception) ! ! ...do something important... ! |
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 11.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 Fortran 90/95 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 Fortran 90/95 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.
use sidl use sidl_ContractClass use sidl_double_array use sidl_BaseInterface use sidl_EnfPolicy use vect_Utils implicit none ! ... type (sidl_BaseInterface_t) :: exc, tae type (sidl_double_1d) :: u, v, x call createDouble(MAX_SIZE, u) call createDouble(MAX_SIZE, v) ! Initialize u and v. ! Enable FULL contract enforcement. call sidl_EnfPolicy_setEnforceAll_m(ALLCLASSES, .true., exc) if (.not. is_null(exc)) then ! Handle the exception endif ! Do something meaningful before executing the method. call vect_Utils_vuSum_m(u, v, x, exc) if (is_null(exc)) then ! Do something meaningful with the result, x. else ! Handle the exception endif ! ... call deleteRef(u) call deleteRef(v) if (.not. is_null(x)) then call deleteRef(x) endif |
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 Fortran 90/95 calls associated with the traditional options of enabling only precondition enforcement, enabling postcondition enforcement, or completely disabling contract enforcement.
use sidl use sidl_ContractClass use sidl_double_array use sidl_BaseInterface use sidl_EnfPolicy use vect_Utils implicit none ! ... ! ! Enable only precondition contract enforcement. ! (Useful when only need to ensure callers comply with contract.) ! call sidl_EnfPolicy_setEnforceAll_m(PRECONDS, .false., exception) if (.not. is_null(exc)) then ! Handle the exception endif ! ! Enable only postcondition contract enforcement. ! (Useful when only need to ensure implementation(s) comply with contract.) ! call sidl_EnfPolicy_setEnforceAll_m(POSTCONDS, .false., exception) if (.not. is_null(exc)) then ! Handle the exception endif ! ! Disable contract enforcement. ! (Should only be used when have confidence in caller AND implementation.) ! call sidl_EnfPolicy_setEnforceNone_m(.false., exception) if (.not. is_null(exc)) then ! Handle the exception endif |
This section illustrates the basic interfaces and processes for traditional interface contract enforcement for a Fortran 90/95 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 Fortran 90/95. The bindings generation and basic implementation processes are presented first. Since access to object state requires special steps in Fortran 90/95, 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.
Much of the information associated with generating client-side bindings is pertinent to implementing a SIDL class in Fortran 90/95. The mapping of SIDL types to language constructs was given in Table 11.1. If the implementation calls other SIDL methods, client-side caller rules must be followed.
To create the implementation bindings for a set of SIDL classes in Fortran 90/95, Babel is invoked as follows:
% babel --exclude-external --server=f90 file.sidl
or simply
% babel -E -s=f90 file.sidl
As a result, a makefile fragment called babel.make, numerous C header and source files, and some Fortran 90/95 source files will be created. The SUBROUTINE and END SUBROUTINE statements are automatically generated and the types of arguments declared. Implementation details must be added to the Fortran 90/95 “Impl” files, whose names end with _Impl.F90 and _Mod.F90. More on this matter is provided in Subsection 11.4.2.
Implementation details must be added to the “Impl” files generated in Subsection 11.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 the standard, automatically generated code splicer pairs.
! DO-NOT-DELETE splicer.begin(_miscellaneous_code_start) ! Insert-Code-Here {_miscellaneous_code_start} (extra code) ! DO-NOT-DELETE splicer.end(_miscellaneous_code_start) . . . recursive subroutine Pkg_Class_name_mi(self, arg, exception) use sidl use sidl_BaseInterface use sidl_RuntimeException use Pkg_Class use Pkg_Class_impl ! DO-NOT-DELETE splicer.begin(Pkg.Class.name.use) ! Insert-Code-Here {Pkg.Class.name.use} (use statements) ! DO-NOT-DELETE splicer.end(Pkg.Class.name.use) implicit none type(Pkg_Class_t) :: self ! in integer (kind=sidl_int) :: arg ! in type(sidl_BaseInterface_t) :: exception ! out ! DO-NOT-DELETE splicer.begin(Pkg.Class.name) ! Insert-Code-Here {Pkg.Class.name} (name method) ! DO-NOT-DELETE splicer.end(Pkg.Class.name) end subroutine Pkg_Class_name_mi |
The comment “Insert-Code-Here” associated with the “miscellaneous code start” splicer pair will need to be replaced with details such as additional abbreviation file(s) and any local, or private, subroutines. For the subroutine’s “use” splicer pair, the “Insert-Code-Here {Pkg.Class.name.use} (use statements)” comment must be replaced with any use statements needed by the subroutine. Finally, the implementation between the subroutine body’s splicer pairs must be added in place of the “Insert-Code-Here {Pkg.Class.name} (name method)” comment.
Any variables declared in the implementation source file will, by virtue of Babel’s encapsulation, be private. Special initialization procedures can be added to the built-in _load() method, which is guaranteed to be called exactly once per class to set global class data — before any user-defined methods can even be invoked.
The SIDL IOR keeps a pointer for each object that is intended to hold a pointer to the object’s internal data. Below is an excerpt from a _Mod.F90 file for an object whose state requires a single integer value.
#include "sort_SimpleCounter_fAbbrev.h" module sort_SimpleCounter_impl ! DO-NOT-DELETE splicer.begin(sort.SimpleCounter.use) use sidl ! DO-NOT-DELETE splicer.end(sort.SimpleCounter.use) type sort_SimpleCounter_priv sequence ! DO-NOT-DELETE splicer.begin(sort.SimpleCounter.private_data) integer(kind=sidl_int) :: count ! DO-NOT-DELETE splicer.end(sort.SimpleCounter.private_data) end type sort_SimpleCounter_priv type sort_SimpleCounter_wrap sequence type(sort_SimpleCounter_priv), pointer :: d_private_data end type sort_SimpleCounter_wrap end module sort_SimpleCounter_impl |
The derived type sort_SimpleCounter_priv is the type in which the developer adds data to store the object’s state. The sort_SimpleCounter_wrap type exists simply to facilitate transferring the sort_SimpleCounter_priv pointer to and from the IOR.
Access to this data is provided by two built-in functions — referred to as set_data and get_data — whose full names are derived from the fully qualified type name. In both cases, the first argument is the object pointer (i. e., self), and the second is a derived type defined in the _Mod.F90 file. The developer is responsible for managing the memory associated with the private data.
As illustrated in the constructor below, the basic process to initialize private data involves allocating memory then setting the data pointer.
recursive subroutine sort_SimpleCounter__ctor_mi(self, exception) use sidl use sidl_BaseInterface use sidl_RuntimeException use sort_SimpleCounter use sort_SimpleCounter_impl ! DO-NOT-DELETE splicer.begin(sort.SimpleCounter._ctor.use) ! Insert-Code-Here {sort.SimpleCounter._ctor.use} (use statements) ! DO-NOT-DELETE splicer.end(sort.SimpleCounter._ctor.use) implicit none type(sort_SimpleCounter_t) :: self ! in type(sidl_BaseInterface_t) :: exception ! out ! DO-NOT-DELETE splicer.begin(sort.SimpleCounter._ctor) type(sort_SimpleCounter_wrap) :: dp allocate(dp%d_private_data) dp%d_private_data%count = 0 call sort_SimpleCounter__set_data_m(self, dp) ! DO-NOT-DELETE splicer.end(sort.SimpleCounter._ctor) end subroutine sort_SimpleCounter__ctor_mi |
Note the use of allocate(pd%d_private_data) in the constructor, _ctor, to allocate the memory for the sort_SimpleCounter_priv derived type and the fully qualified name for get_data.
Similarly, the destructor is responsible for freeing the data’s memory as follows:
recursive subroutine sort_SimpleCounter__dtor_mi(self, exception) use sidl use sidl_BaseInterface use sidl_RuntimeException use sort_SimpleCounter use sort_SimpleCounter_impl ! DO-NOT-DELETE splicer.begin(sort.SimpleCounter._dtor.use) ! Insert-Code-Here {sort.SimpleCounter._dtor.use} (use statements) ! DO-NOT-DELETE splicer.end(sort.SimpleCounter._dtor.use) implicit none type(sort_SimpleCounter_t) :: self ! in type(sidl_BaseInterface_t) :: exception ! out ! DO-NOT-DELETE splicer.begin(sort.SimpleCounter._dtor) type(sort_SimpleCounter_wrap) :: dp call sort_SimpleCounter__get_data_m(self, dp) deallocate(dp%d_private_data) ! DO-NOT-DELETE splicer.end(sort.SimpleCounter._dtor) end subroutine sort_SimpleCounter__dtor_mi |
In this case, deallocate(pd%d_private_data) is used to free the memory allocated in the constructor for the sort_SimpleCounter_priv derived type.
Below is an example of an implementation subroutine that throws an exception. The returned exception object pointer must be cast into the exception out parameter. This example also utilizes two methods, inherited from sidl.BaseException and implemented in sidl.SIDLException, that aid client-side debugging. The first, setNote, allows the developer to provide a useful error message. The second, add, provides a multi-language traceback capability — assuming each layer of the call stack invokes add before it propagates the exception.
recursive subroutine ExceptionTest_Fib_getFib_mi(self, n, max_depth, & max_value, depth, retval, exception) use sidl use sidl_BaseInterface use ExceptionTest_Fib use ExceptionTest_NegativeValueException use ExceptionTest_FibException use ExceptionTest_Fib_impl ! DO-NOT-DELETE splicer.begin(ExceptionTest.Fib.getFib.use) use ExceptionTest_TooBigException use ExceptionTest_TooDeepException ! DO-NOT-DELETE splicer.end(ExceptionTest.Fib.getFib.use) implicit none type(ExceptionTest_Fib_t) :: self integer (kind=sidl_int) :: n, max_depth, max_value integer (kind=sidl_int) :: retval, depth type(sidl_BaseInterface_t) :: exception type(sidl_BaseInterface_t) :: except2 ! DO-NOT-DELETE splicer.begin(ExceptionTest.Fib.getFib) type(ExceptionTest_NegativeValueException_t) :: negexc ! ...lines deleted... character (len=*) myfilename parameter(myfilename='ExceptionTest_Fib_Impl.f') retval = 0 if (n .lt. 0) then call new(negexc, except2) if (not_null(negexc)) then call setNote(negexc, & 'called with negative n', except2) call add(negexc, myfilename, 57, & 'ExceptionTest_Fib_getFib_impl', except2) call cast(negexc, exception, except2) call deleteRef(negexc, except2) return endif else ! ...numerous lines deleted.... ! DO-NOT-DELETE splicer.end(ExceptionTest.Fib.getFib) end subroutine ExceptionTest_Fib_getFib_mi |
When an exception is thrown, the implementation should deleteRef any references it was planning to return to its caller. In general, when throwing an exception, it is good practice to call set_null on all out and inout array, class, and interface arguments before returning. 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 11.3.6, 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 11.3.6, the generated _pre method
implementation is:
recursive subroutine hooks_Basics_aStaticMeth_pre_mi(i, io, exception) use sidl use sidl_NotImplementedException use sidl_BaseInterface use sidl_RuntimeException use hooks_Basics use hooks_Basics_impl ! DO-NOT-DELETE splicer.begin(hooks.Basics.aStaticMeth_pre.use) ! Insert implementation use details ! DO-NOT-DELETE splicer.end(hooks.Basics.aStaticMeth_pre.use) implicit none integer (kind=sidl_int) :: i ! in integer (kind=sidl_int) :: io ! in type(sidl_BaseInterface_t) :: exception ! out ! 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) end subroutine hooks_Basics_aStaticMeth_pre_mi |
while that of the _post method is:
recursive subroutine B_aStaticMeth_postywgp49zzy2_mi(i, o, io, retval, & exception) use sidl use sidl_NotImplementedException use sidl_BaseInterface use sidl_RuntimeException use hooks_Basics use hooks_Basics_impl ! DO-NOT-DELETE splicer.begin(hooks.Basics.aStaticMeth_post.use) ! Insert implementation use details ! DO-NOT-DELETE splicer.end(hooks.Basics.aStaticMeth_post.use) implicit none integer (kind=sidl_int) :: i ! in integer (kind=sidl_int) :: o ! in integer (kind=sidl_int) :: io ! in integer (kind=sidl_int) :: retval ! in type(sidl_BaseInterface_t) :: exception ! out ! DO-NOT-DELETE splicer.begin(hooks.Basics.aStaticMeth_post) ! ! Add instrumentation here to be executed immediately after ! return from dispatch to aStaticMeth(). ! return ! DO-NOT-DELETE splicer.end(hooks.Basics.aStaticMeth_post) end subroutine B_aStaticMeth_postywgp49zzy2_mi |
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.