Previous Up Next

Chapter 12  Fortran 2003/2008 Bindings


12.1  Introduction

This chapter provides an overview of the Fortran 2003/2008 bindings for SIDL. Common aspects of the bindings, such as the mapping of SIDL data types to their native Fortran 2003/2008 representatives, are presented in Section 12.2. Issues of concern to Fortran 2003/2008 callers are addressed in the client-side bindings discussion in Section 12.3, while issues of interest to callees of Fortran 2003/2008 appear in the implementation-side discussion in Section 12.4.

12.1.1  Compatibility with Fortran compilers

Fortran 2003/2008 is a relatively new standard and at the time of writing no compiler supports the full feature set. Babel’s configure script therefore tests for several features (including type extensions and C-compatible function pointers) before it enables the f03 backend. The configure script will output an explanation, if one of the tests should fail. As of October 2011, only GNU gfortran 4.6.1 and Intel ifort 12.1.233 and IBM XL Fortran for Blue Gene 11.1 were up to the task.1 It is advisable to run the regression tests (make check) in the build tree to confirm whether the f03 backend is working.

12.2  Basics

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 12.2.2, while those associated with the generation of subroutines from methods are given in Subsection 12.2.3. Translations between SIDL and native Fortran 2003/2008 constructs are described in Subsection 12.2.4. Finally, the process of casting between different types is illustrated in Subsection 12.2.5.

12.2.1  File extension

The default file extension for preprocessed Fortran 2003/2008 sources is .f03. Some compilers (e. g., Intel’s) do not recognize this extension; in these cases the configure process detects this and switches to .f90 instead. The capital version .F03 is used for files that are meant to be run through the C preprocessor cpp. If your compiler’s cpp has troubles with the extensions, it may help to pass CPP=’gcc -E’ to configure to enforce the use of GCC’s preprocessor instead.

12.2.2  Name space

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.F03. Defined in the file sidl_SIDLException_type.F03, the types for sidl.SIDLException are called sidl_SIDLException_t and, for the array, sidl_SIDLException_a.

12.2.3  Method signatures

To maintain compatibility with the Fortran 90/95 implementation (cf. Section 11.2.2), all SIDL methods are implemented as Fortran 2003/2008 subroutines regardless of whether they have a return value. However, in most cases, Babel will also generate an alternative stub using a function signature. 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 12.3.5 and 12.4.4.

12.2.4  Data types


Table 12.1: SIDL to Fortran 2003/2008 Type Mappings
SIDL typeReal Fortran 2003/2008 typeAlternative name
intinteger(c_int32_t)integer(kind=sidl_int)
long integer(c_int64_t)integer(kind=sidl_long)
float real(c_float)real (kind=sidl_float)
double real(c_double)real (kind=sidl_double)
bool integer(c_int)logical
char integer(c_signed_char)character (len=1)
string type(c_ptr) character (len=*)
fcomplexcomplex(c_float_complex) COMPLEX (kind=sidl_fcomplex)
dcomplexcomplex(c_double_complex) COMPLEX (kind=sidl_dcomplex)
enum integer(c_int64_t) integer (kind=sidl_enum)
opaque type(c_ptr)integer (kind=sidl_opaque)
interfacetype(c_ptr) derived type
class type(c_ptr) derived type
array type(c_ptr) derived type

The mapping for simple SIDL types to Fortran 2003/2008 is given in Table 12.1. The kind parameters, given in the sidl F03 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.

Strings

The SIDL string type mapping is currently identical to that of the fortran 77 mapping. That is, all Fortran 2003/2008 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_F03_STR_MINSIZE in runtime/sidl/babel_config.h prior to configuring Babel can be used to change the string size limitation.

Pointers

Pointer types are: opaque, interface, class, and array. All pointer types are mapped to type(c_ptr). The convenience functions set_null and is_null are generated to perform assignments and checks for unassociated pointers.

Enumerations

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:

Fortran 2003/2008
! File: enums_car_type.F03 ! Symbol: enums.car-v1.0 ! Symbol Type: enumeration ! Babel Version: 2.0.0 (Revision: 7140 trunk) ! Description: Client-side module for enums.car ! ! WARNING: Automatically generated; changes will be lost ! #include "enums_car_fAbbrev.h" ! ! This file contains a FORTRAN 2003 derived type for the ! sidl type enums.car. ! module enums_car use sidl use, intrinsic :: iso_c_binding 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

Arrays

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.F03. 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 2003/2008 arrays or normal SIDL arrays can be used when calling a Fortran 2003/2008 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:

Fortran 2003/2008
module sidl_double_array use sidl use sidl_array_type use, intrinsic :: iso_c_binding type sidl_double_1d type(c_ptr) :: d_array = c_null_ptr real (kind=sidl_double), pointer, & dimension(:) :: d_data end type sidl_double_1d type sidl_double_2d type(c_ptr) :: d_array = c_null_ptr real (kind=sidl_double), pointer, & dimension(:,:) :: d_data end type sidl_double_2d

For the other types, the array API must be used to access elements. In this case, the array can be accessed with the bind(C) array pointer d_data just like any other Fortran array. However, the Fortran 2003/2008 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 2003/2008 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 2003/2008 bind(C) arrays. Index variables do not need to be included because the values are determined from the Fortran 2003/2008 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 2003/2008 function with the following overloaded interface:

Fortran 2003/2008
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.

Fortran 2003/2008
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.

12.2.5  Type casting

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 12.3.5 and 12.4.4.

12.3  Client-side

This section summarizes aspects of generating and using the Fortran 2003/2008 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.

12.3.1  Bindings generation

The following is an example of invoking Babel to create the Fortran 2003/2008 stubs for a SIDL file2:

% babel --exclude-external --client=f03 file.sidl

or simply

% babel -E -c=f03 file.sidl

As a result, a Makefile fragment called babel.make, numerous C header and source files, and some Fortran 2003/2008 files will be created. Files ending in _fStub.c (i. e., STUBSRCS in babel.make) are called by the Fortran 2003/2008 module which in turn allow Fortran 2003/2008 to call SIDL methods. Files ending in [_type].F03 (i. e., STUBMODULESRCS in babel.make) contain derived type definitions for classes and interfaces. The remaining files ending in .F03 (i. e., TYPEMODULESRCS in babel.make) are Fortran 2003/2008 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 they probably don’t need to be compiled.

12.3.2  Object management

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:

Fortran 2003/2008
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:

Fortran 2003/2008
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:

Fortran 2003/2008
use sidl_BaseInterface ! later in the code call isSame(interface1, interface2, areSame, exception) ! areSame holds the return value

12.3.3  Static methods

Below is an example illustrating a call to addSearchPath(), which is a static method in the sidl.Loader class.

Fortran 2003/2008
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.

12.3.4  Overloaded methods

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:

Fortran 2003/2008
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)

12.3.5  Exception catching

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 (if 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:

SIDL
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:

Fortran 2003/2008
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 result = getFib(fib, index, maxdepth, maxvalue, depth, 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.

12.3.6  Hooks execution

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:

SIDL
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.

Fortran 2003/2008
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 12.4.5.

12.3.7  Contract enforcement

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 2003/2008 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.

SIDL
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 2003/2008 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.

Fortran 2003/2008
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 u = createDouble(MAX_SIZE) v = createDouble(MAX_SIZE) ! 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. x = vect_Utils_vuSum_m(u, v, 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 2003/2008 calls associated with the traditional options of enabling only precondition enforcement, enabling postcondition enforcement, or completely disabling contract enforcement.

Fortran 2003/2008
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 2003/2008 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.

12.4  Implementation-side

This section summarizes aspects of generating and wrapping software written in Fortran 2003/2008. The bindings generation and basic implementation processes are presented first. Since access to object state requires special steps in Fortran 2003/2008, 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.

12.4.1  Bindings generation

Much of the information associated with generating client-side bindings is pertinent to implementing a SIDL class in Fortran 2003/2008. The mapping of SIDL types to language constructs was given in Table 12.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 2003/2008, 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 2003/2008 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 2003/2008 “Impl” files, whose names end with _Impl.F90 and _Mod.F90. More on this matter is provided in Subsection 12.4.2.

12.4.2  Bindings implementation

Implementation details must be added to the “Impl” files generated in Subsection 12.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.

Fortran 2003/2008
! 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.

12.4.3  Private data

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 _Impl.F03 file for an object whose state requires a single integer value.

Fortran 2003/2008
type, extends(sort_SimpleCounter_t) :: sort_SimpleCounter_impl_t ! DO-NOT-DELETE splicer.begin(sort.SimpleCounter.private_data) ! insert code here (private data members) integer(kind=sidl_int) :: count ! DO-NOT-DELETE splicer.end(sort.SimpleCounter.private_data) end type sort_SimpleCounter_impl_t

As illustrated in the constructor below, the basic process to initialize private data involves allocating memory then setting the data pointer.

Fortran 2003/2008
subroutine ctor_impl(self, exception) ! DO-NOT-DELETE splicer.begin(sort.SimpleCounter._ctor.use) ! Insert use statements here... ! DO-NOT-DELETE splicer.end(sort.SimpleCounter._ctor.use) implicit none type(sort_SimpleCounter_impl_t) :: self type(sidl_BaseInterface_t), intent(out) :: exception ! DO-NOT-DELETE splicer.begin(sort.SimpleCounter._ctor) self%count = 0 ! DO-NOT-DELETE splicer.end(sort.SimpleCounter._ctor) end subroutine ctor_impl

12.4.4  Exception throwing

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.

Fortran 2003/2008
function getFib_impl(self, n, max_depth, max_value, depth, exception) & result(retval) ! DO-NOT-DELETE splicer.begin(Exceptions.Fib.getFib.use) use Exceptions_TooBigException use Exceptions_TooDeepException ! DO-NOT-DELETE splicer.end(Exceptions.Fib.getFib.use) implicit none type(Exceptions_Fib_impl_t) :: self integer (kind=sidl_int), intent(in) :: n integer (kind=sidl_int), intent(in) :: max_depth integer (kind=sidl_int), intent(in) :: max_value integer (kind=sidl_int), intent(in) :: depth type(sidl_BaseInterface_t), intent(out) :: exception ! function result integer (kind=sidl_int) :: retval ! DO-NOT-DELETE splicer.begin(Exceptions.Fib.getFib) type(Exceptions_NegativeValueException_t) :: negexc type(Exceptions_TooDeepException_t) :: deepexc type(Exceptions_TooBigException_t) :: toobigexc type(Exceptions_FibException_t) :: fibexc type(sidl_BaseInterface_t) :: throwaway character (len=*) myfilename parameter(myfilename='Exceptions_Fib_Impl.f') integer(kind=sidl_int) a, b retval = 0_sidl_int if (n .lt. 0_sidl_int) then call new(negexc, throwaway) if (not_null(negexc)) then call setNote(negexc, & 'called with negative n', throwaway) call add( & negexc, myfilename, 57_sidl_int, 'Exceptions_Fib_getFib_impl',& throwaway) call cast(negexc, exception,throwaway) call deleteRef(negexc,throwaway) return endif else if (depth .gt. max_depth) then ! ...numerous lines deleted.... ! DO-NOT-DELETE splicer.end(Exceptions.Fib.getFib) end function getFib_impl

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.

12.4.5  Hooks implementation

As discussed in Subsection 12.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 12.3.6, the generated _pre method implementation is:

Fortran 2003/2008
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:

Fortran 2003/2008
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.


1
configure tests for the features and not for the version numbers, so future versions of other compilers may also work.
2
For information on additional command line options, refer to Section 4.2.

Previous Up Next