Fortran¶
This section discusses Fortran specific wrapper details. This will also include some C wrapper details since some C wrappers are created specificially to be called by Fortran.
Names¶
There are several options to mangle the C++ library names into Fortran
names. By default, names are mangled to convert camel case into snake
case. For example, StructAsClass
into struct_as_class
. Since
Fortran is case insensitive, StructAsClass
and structasclass
are equivalent. By using snake case, the identifier should be easier
for a reader to parse regardless of the case.
The behavior is controlled by the option F_api_case which may have the values lower, upper, underscore, or preserve. This option is used to set the format field F_name_api which in turn is used in several options used to define names consistently: F_C_name_template, F_name_impl_template, F_name_function_template, F_name_generic_template, F_abstract_interface_subprogram_template, F_derived_name_template, F_typedef_name_template.
A Fortran module will be created for the library. This allows the compiler to do it’s own mangling so it is unnecessary to add an additional prefix to function names. In contrast, the C wrappers add a prefix to each wrapper since all names are global.
Wrapper¶
As each function declaration is parsed a format dictionary is created with fields to describe the function and its arguments. The fields are then expanded into the function wrapper.
The template for Fortran code showing names which may be controlled directly by the input YAML file:
module {F_module_name}
! use_stmts
implicit none
abstract interface
subprogram {F_abstract_interface_subprogram_template}
type :: {F_abstract_interface_argument_template}
end subprogram
end interface
interface
{F_C_pure_clause} {F_C_subprogram} {F_C_name}
{F_C_result_clause} bind(C, name="{C_name}")
! arg_f_use
implicit none
! arg_c_decl
end {F_C_subprogram} {F_C_name}
end interface
interface {F_name_generic}
module procedure {F_name_impl}
end interface {F_name_generic}
contains
{F_subprogram} {F_name_impl}
arg_f_use
arg_f_decl
! splicer begin
declare ! local variables
pre_call
call {arg_c_call}
post_call
! splicer end
end {F_subprogram} {F_name_impl}
end module {F_module_name}
Class¶
Use of format fields for creating class wrappers.
type, bind(C) :: {F_capsule_data_type}
type(C_PTR) :: addr = C_NULL_PTR ! address of C++ memory
integer(C_INT) :: idtor = 0 ! index of destructor
end type {F_capsule_data_type}
type {F_derived_name}
type({F_capsule_data_type}) :: {F_derived_member}
contains
procedure :: {F_name_function} => {F_name_impl}
generic :: {F_name_generic} => {F_name_function}, ...
! F_name_getter, F_name_setter, F_name_instance_get as underscore_name
procedure :: [F_name_function_template] => [F_name_impl_template]
end type {F_derived_name}
Standard type-bound procedures¶
Several type bound procedures can be created to make it easier to use class from Fortran.
Usually the F_derived_name is constructed from wrapped C++ constructor. It may also be useful to take a pointer to a C++ struct and explicitly put it into a the derived type. The functions F_name_instance_get and F_name_instance_set can be used to access the pointer directly.
Two predicate function are generated to compare derived types:
interface operator (.eq.)
module procedure class1_eq
module procedure singleton_eq
end interface
interface operator (.ne.)
module procedure class1_ne
module procedure singleton_ne
end interface
contains
function {F_name_scope}eq(a,b) result (rv)
use iso_c_binding, only: c_associated
type({F_derived_name}), intent(IN) ::a,b
logical :: rv
if (c_associated(a%{F_derived_member}%addr, b%{F_derived_member}%addr)) then
rv = .true.
else
rv = .false.
endif
end function {F_name_scope}eq
function {F_name_scope}ne(a,b) result (rv)
use iso_c_binding, only: c_associated
type({F_derived_name}), intent(IN) ::a,b
logical :: rv
if (.not. c_associated(a%{F_derived_member}%addr, b%{F_derived_member}%addr)) then
rv = .true.
else
rv = .false.
endif
end function {F_name_scope}ne
Generic Interfaces¶
Shroud has the ability to create generic interfaces for the routines that are being wrapped. The generic intefaces groups several functions under a common name. The compiler will then call the corresponding function based on the argument types used to call the generic function.
In several cases generic interfaces are automatically created. Function overloading and default arguments both create generic interfaces.
Assumed Rank¶
Assumed rank arguments allow a scalar or any rank array to be passed
as an argument. This is added as the attribute dimension(..). Think
of the ..
as a :
, used to separate lower and upper bounds,
which fell over. This feature is part of Fortran’s Further
interoperability with C. First as TS 29113, approved in 2012, then as
part of the Fortran 2018 standard.
Note
Shroud does not support Further Interoperability with C directly, yet.
Assumed-rank arguments are support by Shroud for older versions of Fortran by creating a generic interface. If there are multiple arguments with assumed-rank, Shroud will give each argument the same rank for each generic interface. This handles the common case and avoids the combinatoral explosion of mixing ranks in a single function interface.
The ranks used are controlled by the options F_assumed_rank_min and F_assumed_rank_max which default to 0, for scalar, and 7.
- decl: int SumValues(const int *values+dimension(..), int nvalues)
options:
F_assumed_rank_max: 2
The generated generic interface can be used to pass a scalar, 1d or 2d
array to the C function. In each case result
is 5.
result = sum_array(5, 1)
result = sum_array([1,1,1,1,1], 5)
Grouping Functions Together¶
The first case allows multiple C wrapper routines to be called by the same name. This is done by setting the F_name_generic format field.
- decl: void UpdateAsFloat(float arg)
options:
F_force_wrapper: True
format:
F_name_generic: update_real
- decl: void UpdateAsDouble(double arg)
options:
F_force_wrapper: True
format:
F_name_generic: update_real
This allows the correct functions to be called based on the argument type.
Note
In this example F_force_wrapper is set to True since by
default Shroud will not create explicit wrappers for the
functions since only native types are used as arguments.
The generic interface is using module procedure`
which
requires the Fortran wrapper. This should be changed in a
future version of Shroud.
call update_real(22.0_C_FLOAT)
call update_real(23.0_C_DOUBLE)
Or more typically as:
call update_real(22.0)
call update_real(23.0d0)
Argument Coercion¶
The C compiler will coerce arguments in a function call to the type of
the argument in the prototype. This makes it very easy to pass an
float
to a function which is expecting a double
. Fortran,
which defaults to pass by reference, does not have this feature since
it is passing the address of the argument. This corresponds to C’s
behavior since it cannot coerce a float *
to a double *
. When
passing a literal 0.0
as a float
argument it is necessary to
use 0.0_C_DOUBLE
.
Shroud can create a generic interface for function which will
coerce arguments similar to C’s behavior.
The fortran_generic section variations of arguments which will be
used to create a generic interface. For example, when wrapping a function
which takes a double
, the float
variation can also be created.
- decl: void GenericReal(double arg)
fortran_generic:
- decl: (float arg)
function_suffix: _float
- decl: (double arg)
function_suffix: _double
This will create a generic interface generic_real
with two module
procedures generic_real_float
and generic_real_double
.
interface generic_real
module procedure generic_real_float
module procedure generic_real_double
end interface generic_real
This can be used as
call generic_real(0.0)
call generic_real(0.0d0)
call generic_real_float(0.0)
call generic_real_double(0.0d0)
When adding decl entries to the fortran_generic list the original
declaration must also be included, double arg
in this case. When
there are multiple arguments only the arguments which vary need to be
declared. The other arguments will be the same as the original
decl line.
The function_suffix line will be used to add a unique string to the generated Fortran wrappers. Without function_suffix each function will have an integer suffix which is increment for each function.
Scalar and Array Arguments¶
Shroud can produce a generic interface which allows an argument to be
passed as a scalar or an array. This can help generalize some function
calls where a scalar can be used instead of an array of length one.
This was often used in Fortran code before interfaces were introduced
in Fortran 90. But now when using an interface the compiler will
report an error when passing a scalar where an array is expected.
Likewise, a C function with a pointer argument such as int *
has
no way of knowing how long the array is without being told explicitly.
Thus in C it is easy to pass a pointer to a scalar.
In the fortran_generic section one of the declarations can be given the rank attribute which causes the interface to expect an array. Note that the declaration for the C function does not include the rank attribute.
- decl: int SumArray(int *values, int nvalues)
fortran_generic:
- decl: (int *values)
function_suffix: _scalar
- decl: (int *values+rank(1))
function_suffix: _array
The generated generic interface can be used to pass a scalar or array to the C function.
integer scalar, result
integer array(5)
scalar = 5
result = sum_array(scalar, 1)
array = [1,2,3,4,5]
result = sum_array(array, 5)