Sample Fortran Wrappers¶
This chapter gives details of the generated code. It’s intended for users who want to understand the details of how the wrappers are created.
All of these examples are derived from tests in the regression
directory.
No Arguments¶
C library function in clibrary.c:
void NoReturnNoArguments(void)
{
strncpy(last_function_called, "Function1", MAXLAST);
return;
}
clibrary.yaml:
- decl: void NoReturnNoArguments()
Fortran calls C via the following interface:
interface
subroutine no_return_no_arguments() &
bind(C, name="NoReturnNoArguments")
implicit none
end subroutine no_return_no_arguments
end interface
If wrapping a C++ library, a function with a C API will be created that Fortran can call.
void TUT_NoReturnNoArguments(void)
{
// splicer begin function.NoReturnNoArguments
tutorial::NoReturnNoArguments();
// splicer end function.NoReturnNoArguments
}
Fortran usage:
use tutorial_mod
call no_return_no_arguments
The C++ usage is similar:
#include "tutorial.hpp"
tutorial::NoReturnNoArguments();
Numeric Types¶
PassByValue¶
C library function in clibrary.c:
double PassByValue(double arg1, int arg2)
{
strncpy(last_function_called, "PassByValue", MAXLAST);
return arg1 + arg2;
}
clibrary.yaml:
- decl: double PassByValue(double arg1, int arg2)
Both types are supported directly by the iso_c_binding module
so there is no need for a Fortran function.
The C function can be called directly by the Fortran interface
using the bind(C) keyword.
Fortran calls C via the following interface:
interface
function pass_by_value(arg1, arg2) &
result(SHT_rv) &
bind(C, name="PassByValue")
use iso_c_binding, only : C_DOUBLE, C_INT
implicit none
real(C_DOUBLE), value, intent(IN) :: arg1
integer(C_INT), value, intent(IN) :: arg2
real(C_DOUBLE) :: SHT_rv
end function pass_by_value
end interface
Fortran usage:
real(C_DOUBLE) :: rv_double
rv_double = pass_by_value(1.d0, 4)
call assert_true(rv_double == 5.d0)
PassByReference¶
C library function in clibrary.c:
void PassByReference(double *arg1, int *arg2)
{
strncpy(last_function_called, "PassByReference", MAXLAST);
*arg2 = *arg1;
}
clibrary.yaml:
- decl: void PassByReference(double *arg1+intent(in), int *arg2+intent(out))
Fortran calls C via the following interface:
interface
subroutine pass_by_reference(arg1, arg2) &
bind(C, name="PassByReference")
use iso_c_binding, only : C_DOUBLE, C_INT
implicit none
real(C_DOUBLE), intent(IN) :: arg1
integer(C_INT), intent(OUT) :: arg2
end subroutine pass_by_reference
end interface
Example usage:
integer(C_INT) var
call pass_by_reference(3.14d0, var)
call assert_equals(3, var)
Sum¶
C++ library function from pointers.cpp:
void Sum(int len, const int *values, int *result)
{
int sum = 0;
for (int i=0; i < len; i++) {
sum += values[i];
}
*result = sum;
return;
}
pointers.yaml:
- decl: void Sum(int len +implied(size(values)),
int *values +rank(1)+intent(in),
int *result +intent(out))
The POI prefix to the function names is derived from
the format field C_prefix which defaults to the first three letters
of the library field, in this case pointers.
This is a C++ file which provides a C API via extern "C".
wrappointers.cpp:
int POI_sumFixedArray(void)
{
// splicer begin function.sumFixedArray
int SHC_rv = sumFixedArray();
return SHC_rv;
// splicer end function.sumFixedArray
}
Fortran calls C via the following interface:
interface
subroutine c_sum(len, values, result) &
bind(C, name="POI_Sum")
use iso_c_binding, only : C_INT
implicit none
integer(C_INT), value, intent(IN) :: len
integer(C_INT), intent(IN) :: values(*)
integer(C_INT), intent(OUT) :: result
end subroutine c_sum
end interface
The Fortran wrapper:
interface
function sum_fixed_array() &
result(SHT_rv) &
bind(C, name="POI_sumFixedArray")
use iso_c_binding, only : C_INT
implicit none
integer(C_INT) :: SHT_rv
end function sum_fixed_array
end interface
Example usage:
integer(C_INT) rv_int
call sum([1,2,3,4,5], rv_int)
call assert_true(rv_int .eq. 15, "sum")
truncate_to_int¶
Sometimes it is more convenient to have the wrapper allocate an
intent(out) array before passing it to the C++ function. This can
be accomplished by adding the deref(allocatable) attribute.
C++ library function from pointers.c:
void truncate_to_int(double *in, int *out, int size)
{
int i;
for(i = 0; i < size; i++) {
out[i] = in[i];
}
}
pointers.yaml:
- decl: void truncate_to_int(double * in +intent(in) +rank(1),
int * out +intent(out)
+deref(allocatable)+dimension(size(in)),
int sizein +implied(size(in)))
Fortran calls C via the following interface:
interface
subroutine c_truncate_to_int(in, out, sizein) &
bind(C, name="truncate_to_int")
use iso_c_binding, only : C_DOUBLE, C_INT
implicit none
real(C_DOUBLE), intent(IN) :: in(*)
integer(C_INT), intent(OUT) :: out(*)
integer(C_INT), value, intent(IN) :: sizein
end subroutine c_truncate_to_int
end interface
The Fortran wrapper:
subroutine truncate_to_int(in, out)
use iso_c_binding, only : C_DOUBLE, C_INT
real(C_DOUBLE), intent(IN) :: in(:)
integer(C_INT), intent(OUT) :: out(:)
integer(C_INT) :: SH_sizein
! splicer begin function.truncate_to_int
SH_sizein = size(in,kind=C_INT)
call c_truncate_to_int(in, out, SH_sizein)
! splicer end function.truncate_to_int
end subroutine truncate_to_int
Example usage:
integer(c_int), allocatable :: out_int(:)
call truncate_to_int([1.2d0, 2.3d0, 3.4d0, 4.5d0], out_int)
Numeric Pointers¶
getRawPtrToFixedArray¶
C++ library function from pointers.c:
void getRawPtrToFixedArray(int **count)
{
*count = (int *) &global_fixed_array;
}
pointers.yaml:
- decl: void getRawPtrToFixedArray(int **count+intent(out)+deref(raw))
Fortran calls C via the following interface:
interface
subroutine get_raw_ptr_to_fixed_array(count) &
bind(C, name="getRawPtrToFixedArray")
use iso_c_binding, only : C_PTR
implicit none
type(C_PTR), intent(OUT) :: count
end subroutine get_raw_ptr_to_fixed_array
end interface
Example usage:
type(C_PTR) :: cptr_array
call get_raw_ptr_to_fixed_array(cptr_array)
getPtrToScalar¶
C++ library function from pointers.c:
void getPtrToScalar(int **nitems)
{
*nitems = &global_int;
}
pointers.yaml:
- decl: void getPtrToScalar(int **nitems+intent(out))
This is a C file which provides the bufferify function.
wrappointers.c:
void POI_getPtrToScalar_bufferify(POI_SHROUD_array *SHT_nitems_cdesc)
{
// splicer begin function.getPtrToScalar_bufferify
int *nitems;
getPtrToScalar(&nitems);
SHT_nitems_cdesc->base_addr = nitems;
SHT_nitems_cdesc->type = SH_TYPE_INT;
SHT_nitems_cdesc->elem_len = sizeof(int);
SHT_nitems_cdesc->rank = 0;
SHT_nitems_cdesc->size = 1;
// splicer end function.getPtrToScalar_bufferify
}
Fortran calls C via the following interface:
interface
subroutine c_get_ptr_to_scalar_bufferify(SHT_nitems_cdesc) &
bind(C, name="POI_getPtrToScalar_bufferify")
import :: POI_SHROUD_array
implicit none
type(POI_SHROUD_array), intent(OUT) :: SHT_nitems_cdesc
end subroutine c_get_ptr_to_scalar_bufferify
end interface
The Fortran wrapper:
subroutine get_ptr_to_scalar(nitems)
use iso_c_binding, only : C_INT, c_f_pointer
integer(C_INT), intent(OUT), pointer :: nitems
! splicer begin function.get_ptr_to_scalar
type(POI_SHROUD_array) :: SHT_nitems_cdesc
call c_get_ptr_to_scalar_bufferify(SHT_nitems_cdesc)
call c_f_pointer(SHT_nitems_cdesc%base_addr, nitems)
! splicer end function.get_ptr_to_scalar
end subroutine get_ptr_to_scalar
Assigning to iscalar will modify the C++ variable.
Example usage:
integer(C_INT), pointer :: iscalar
call get_ptr_to_scalar(iscalar)
iscalar = 0
getPtrToDynamicArray¶
C++ library function from pointers.c:
void getPtrToDynamicArray(int **count, int *len)
{
*count = (int *) &global_fixed_array;
*len = sizeof(global_fixed_array)/sizeof(int);
}
pointers.yaml:
- decl: void getPtrToDynamicArray(int **count+intent(out)+dimension(ncount),
int *ncount+intent(out)+hidden)
This is a C file which provides the bufferify function.
wrappointers.c:
void POI_getPtrToDynamicArray_bufferify(
POI_SHROUD_array *SHT_count_cdesc)
{
// splicer begin function.getPtrToDynamicArray_bufferify
int *count;
int ncount;
getPtrToDynamicArray(&count, &ncount);
SHT_count_cdesc->base_addr = count;
SHT_count_cdesc->type = SH_TYPE_INT;
SHT_count_cdesc->elem_len = sizeof(int);
SHT_count_cdesc->rank = 1;
SHT_count_cdesc->shape[0] = ncount;
SHT_count_cdesc->size = SHT_count_cdesc->shape[0];
// splicer end function.getPtrToDynamicArray_bufferify
}
Fortran calls C via the following interface:
interface
subroutine c_get_ptr_to_dynamic_array_bufferify(SHT_count_cdesc) &
bind(C, name="POI_getPtrToDynamicArray_bufferify")
import :: POI_SHROUD_array
implicit none
type(POI_SHROUD_array), intent(OUT) :: SHT_count_cdesc
end subroutine c_get_ptr_to_dynamic_array_bufferify
end interface
The Fortran wrapper:
subroutine get_ptr_to_dynamic_array(count)
use iso_c_binding, only : C_INT, c_f_pointer
integer(C_INT), intent(OUT), pointer :: count(:)
! splicer begin function.get_ptr_to_dynamic_array
type(POI_SHROUD_array) :: SHT_count_cdesc
call c_get_ptr_to_dynamic_array_bufferify(SHT_count_cdesc)
call c_f_pointer(SHT_count_cdesc%base_addr, count, &
SHT_count_cdesc%shape(1:1))
! splicer end function.get_ptr_to_dynamic_array
end subroutine get_ptr_to_dynamic_array
Assigning to iarray will modify the C++ variable.
Example usage:
integer(C_INT), pointer :: iarray(:)
call get_ptr_to_dynamic_array(iarray)
iarray = 0
getRawPtrToInt2d¶
global_int2d is a two dimensional array of non-contiguous rows.
C stores the address of each row.
Shroud can only deal with this as a type(C_PTR) and expects the
user to dereference the address.
C++ library function from pointers.c:
static int global_int2d_1[] = {1,2,3};
static int global_int2d_2[] = {4,5};
static int *global_int2d[] = {global_int2d_1, global_int2d_2};
void getRawPtrToInt2d(int ***arg)
{
*arg = (int **) global_int2d;
}
pointers.yaml:
- decl: void getRawPtrToInt2d(int ***arg +intent(out))
Fortran calls C via the following interface:
interface
subroutine get_raw_ptr_to_int2d(arg) &
bind(C, name="getRawPtrToInt2d")
use iso_c_binding, only : C_PTR
implicit none
type(C_PTR), intent(OUT) :: arg
end subroutine get_raw_ptr_to_int2d
end interface
Example usage:
type(C_PTR) :: addr
type(C_PTR), pointer :: array2d(:)
integer(C_INT), pointer :: row1(:), row2(:)
integer total
call get_raw_ptr_to_int2d(addr)
! Dereference the pointers into two 1d arrays.
call c_f_pointer(addr, array2d, [2])
call c_f_pointer(array2d(1), row1, [3])
call c_f_pointer(array2d(2), row2, [2])
total = row1(1) + row1(2) + row1(3) + row2(1) + row2(2)
call assert_equals(15, total)
checkInt2d¶
Example of using the type(C_PTR) returned
getRawPtrToInt2d.
pointers.yaml:
- decl: int checkInt2d(int **arg +intent(in))
Fortran calls C via the following interface.
Note the use of VALUE attribute.
interface
function check_int2d(arg) &
result(SHT_rv) &
bind(C, name="checkInt2d")
use iso_c_binding, only : C_INT, C_PTR
implicit none
type(C_PTR), intent(IN), value :: arg
integer(C_INT) :: SHT_rv
end function check_int2d
end interface
Example usage:
type(C_PTR) :: addr
integer total
call get_raw_ptr_to_int2d(addr)
total = check_int2d(addr)
call assert_equals(15, total)
getMinMax¶
No Fortran function is created. Only an interface to a C wrapper which dereference the pointers so they can be treated as references.
C++ library function in tutorial.cpp:
void getMinMax(int &min, int &max)
{
min = -1;
max = 100;
}
tutorial.yaml:
- decl: void getMinMax(int &min +intent(out), int &max +intent(out))
The C wrapper:
void TUT_getMinMax(int *min, int *max)
{
// splicer begin function.getMinMax
tutorial::getMinMax(*min, *max);
// splicer end function.getMinMax
}
Fortran calls C via the following interface:
interface
subroutine get_min_max(min, max) &
bind(C, name="TUT_getMinMax")
use iso_c_binding, only : C_INT
implicit none
integer(C_INT), intent(OUT) :: min
integer(C_INT), intent(OUT) :: max
end subroutine get_min_max
end interface
Fortran usage:
call get_min_max(minout, maxout)
call assert_equals(-1, minout, "get_min_max minout")
call assert_equals(100, maxout, "get_min_max maxout")
returnIntPtrToScalar¶
pointers.yaml:
- decl: int *returnIntPtrToScalar(void)
Fortran calls C via the following interface:
interface
function c_return_int_ptr_to_scalar_bufferify() &
result(SHT_rv) &
bind(C, name="POI_returnIntPtrToScalar_bufferify")
use iso_c_binding, only : C_PTR
implicit none
type(C_PTR) :: SHT_rv
end function c_return_int_ptr_to_scalar_bufferify
end interface
The Fortran wrapper:
function return_int_ptr_to_scalar() &
result(SHT_rv)
use iso_c_binding, only : C_INT, C_PTR, c_f_pointer
integer(C_INT), pointer :: SHT_rv
! splicer begin function.return_int_ptr_to_scalar
type(C_PTR) :: SHC_rv_ptr
SHC_rv_ptr = c_return_int_ptr_to_scalar_bufferify()
call c_f_pointer(SHC_rv_ptr, SHT_rv)
! splicer end function.return_int_ptr_to_scalar
end function return_int_ptr_to_scalar
Example usage:
integer(C_INT), pointer :: irvscalar
irvscalar => return_int_ptr_to_scalar()
returnIntPtrToFixedArray¶
pointers.yaml:
- decl: int *returnIntPtrToFixedArray(void) +dimension(10)
This is a C file which provides the bufferify function.
wrappointers.c:
void POI_returnIntPtrToFixedArray_bufferify(
POI_SHROUD_array *SHT_rv_cdesc)
{
// splicer begin function.returnIntPtrToFixedArray_bufferify
int *SHC_rv = returnIntPtrToFixedArray();
SHT_rv_cdesc->base_addr = SHC_rv;
SHT_rv_cdesc->type = SH_TYPE_INT;
SHT_rv_cdesc->elem_len = sizeof(int);
SHT_rv_cdesc->rank = 1;
SHT_rv_cdesc->shape[0] = 10;
SHT_rv_cdesc->size = SHT_rv_cdesc->shape[0];
// splicer end function.returnIntPtrToFixedArray_bufferify
}
Fortran calls C via the following interface:
interface
subroutine c_return_int_ptr_to_fixed_array_bufferify( &
SHT_rv_cdesc) &
bind(C, name="POI_returnIntPtrToFixedArray_bufferify")
import :: POI_SHROUD_array
implicit none
type(POI_SHROUD_array), intent(OUT) :: SHT_rv_cdesc
end subroutine c_return_int_ptr_to_fixed_array_bufferify
end interface
The Fortran wrapper:
function return_int_ptr_to_fixed_array() &
result(SHT_rv)
use iso_c_binding, only : C_INT, c_f_pointer
integer(C_INT), pointer :: SHT_rv(:)
! splicer begin function.return_int_ptr_to_fixed_array
type(POI_SHROUD_array) :: SHT_rv_cdesc
call c_return_int_ptr_to_fixed_array_bufferify(SHT_rv_cdesc)
call c_f_pointer(SHT_rv_cdesc%base_addr, SHT_rv, &
SHT_rv_cdesc%shape(1:1))
! splicer end function.return_int_ptr_to_fixed_array
end function return_int_ptr_to_fixed_array
Example usage:
integer(C_INT), pointer :: irvarray(:)
irvarray => return_int_ptr_to_fixed_array()
returnIntScalar¶
pointers.yaml:
- decl: int *returnIntScalar(void) +deref(scalar)
This is a C file which provides the bufferify function.
wrappointers.c:
int POI_returnIntScalar_bufferify(void)
{
// splicer begin function.returnIntScalar_bufferify
int *SHC_rv = returnIntScalar();
return *SHC_rv;
// splicer end function.returnIntScalar_bufferify
}
Fortran calls C via the following interface:
interface
function return_int_scalar() &
result(SHT_rv) &
bind(C, name="POI_returnIntScalar_bufferify")
use iso_c_binding, only : C_INT
implicit none
integer(C_INT) :: SHT_rv
end function return_int_scalar
end interface
Example usage:
integer :: ivalue
ivalue = return_int_scalar()
returnIntPtrDimPointer¶
Return a Fortran pointer to an array.
The length of the array is returned from C++ in the len argument.
This argument sets the hidden attribute since it is not needed in
the Fortran wrapper. It will be used in the c_f_pointer call to
set the length of the array.
The input is in file ownership.yaml.
- decl: int * ReturnIntPtrDimPointer(int *len+intent(out)+hidden)
fattrs:
deref: pointer
dimension: len
The C wrapper calls the C++ function from an extern C wrapper.
In does not hide the len argument.
This function does not use the deref attribute.
int * OWN_ReturnIntPtrDimPointer(int *len)
{
// splicer begin function.ReturnIntPtrDimPointer
int *SHC_rv = ReturnIntPtrDimPointer(len);
return SHC_rv;
// splicer end function.ReturnIntPtrDimPointer
}
The bufferify function passes an argument to contain the meta data of the array.
It is written to wrapownership.cpp.
void OWN_ReturnIntPtrDimPointer_bufferify(
OWN_SHROUD_array *SHT_rv_cdesc)
{
// splicer begin function.ReturnIntPtrDimPointer_bufferify
int len;
int *SHC_rv = ReturnIntPtrDimPointer(&len);
SHT_rv_cdesc->base_addr = SHC_rv;
SHT_rv_cdesc->type = SH_TYPE_INT;
SHT_rv_cdesc->elem_len = sizeof(int);
SHT_rv_cdesc->rank = 1;
SHT_rv_cdesc->shape[0] = len;
SHT_rv_cdesc->size = SHT_rv_cdesc->shape[0];
// splicer end function.ReturnIntPtrDimPointer_bufferify
}
Fortran calls the bufferify function in wrapfownership.f.
function return_int_ptr_dim_pointer() &
result(SHT_rv)
use iso_c_binding, only : C_INT, c_f_pointer
integer(C_INT), pointer :: SHT_rv(:)
! splicer begin function.return_int_ptr_dim_pointer
type(OWN_SHROUD_array) :: SHT_rv_cdesc
call c_return_int_ptr_dim_pointer_bufferify(SHT_rv_cdesc)
call c_f_pointer(SHT_rv_cdesc%base_addr, SHT_rv, &
SHT_rv_cdesc%shape(1:1))
! splicer end function.return_int_ptr_dim_pointer
end function return_int_ptr_dim_pointer
Fortran usage:
integer(C_INT), pointer :: ivalue(:)
integer len
ivalue => return_int_ptr_dim_pointer()
len = size(ivalue)
returnIntPtrDimAlloc¶
Convert a pointer returned from C++ into a Fortran allocatable array. To do this, memory is allocated in Fortran then the C++ values are copied into it. The advantage is that the user does not have to worry about releasing the C++ memory. The length of the array is returned from C++ in the len argument. This argument sets the hidden attribute since it is not needed in the Fortran wrapper.
The input is in file ownership.yaml.
- decl: int * ReturnIntPtrDimAlloc(int *len+intent(out)+hidden)
fattrs:
deref: allocatable
dimension: len
The C wrapper calls the C++ function from an extern C wrapper.
In does not hide the len argument.
This function does not use the deref attribute.
int * OWN_ReturnIntPtrDimAlloc(int *len)
{
// splicer begin function.ReturnIntPtrDimAlloc
int *SHC_rv = ReturnIntPtrDimAlloc(len);
return SHC_rv;
// splicer end function.ReturnIntPtrDimAlloc
}
The bufferify function passes an argument to contain the meta data of the array.
It is written to wrapownership.cpp.
void OWN_ReturnIntPtrDimAlloc_bufferify(OWN_SHROUD_array *SHT_rv_cdesc,
OWN_SHROUD_capsule_data *SHT_rv_capsule)
{
// splicer begin function.ReturnIntPtrDimAlloc_bufferify
int len;
int *SHC_rv = ReturnIntPtrDimAlloc(&len);
SHT_rv_cdesc->base_addr = SHC_rv;
SHT_rv_cdesc->type = SH_TYPE_INT;
SHT_rv_cdesc->elem_len = sizeof(int);
SHT_rv_cdesc->rank = 1;
SHT_rv_cdesc->shape[0] = len;
SHT_rv_cdesc->size = SHT_rv_cdesc->shape[0];
SHT_rv_capsule->addr = SHC_rv;
SHT_rv_capsule->idtor = 0;
SHT_rv_capsule->cmemflags = SWIG_MEM_RVALUE;
// splicer end function.ReturnIntPtrDimAlloc_bufferify
}
Fortran calls the bufferify function in wrapfownership.f.
function return_int_ptr_dim_alloc() &
result(SHT_rv)
use iso_c_binding, only : C_INT, C_LOC, C_SIZE_T
integer(C_INT), allocatable, target :: SHT_rv(:)
! splicer begin function.return_int_ptr_dim_alloc
type(OWN_SHROUD_array) :: SHT_rv_cdesc
type(OWN_SHROUD_capsule_data) :: SHT_rv_capsule
call c_return_int_ptr_dim_alloc_bufferify(SHT_rv_cdesc, &
SHT_rv_capsule)
allocate(SHT_rv(SHT_rv_cdesc%shape(1)))
call OWN_SHROUD_copy_array(SHT_rv_cdesc, C_LOC(SHT_rv), &
size(SHT_rv, kind=C_SIZE_T))
call OWN_SHROUD_capsule_dtor(SHT_rv_capsule)
! splicer end function.return_int_ptr_dim_alloc
end function return_int_ptr_dim_alloc
Fortran usage:
integer(C_INT), allocatable :: ivalue(:)
integer len
ivalue = return_int_ptr_dim_alloc()
len = size(ivalue)
Bool¶
checkBool¶
Assignments are done in the Fortran wrapper to convert between
logical and logical(C_BOOL).
C library function in clibrary:
void checkBool(const bool arg1, bool *arg2, bool *arg3)
{
strncpy(last_function_called, "checkBool", MAXLAST);
*arg2 = ! arg1;
*arg3 = ! *arg3;
return;
}
clibrary.yaml:
- decl: void checkBool(const bool arg1,
bool *arg2+intent(out),
bool *arg3+intent(inout))
Fortran calls C via the following interface:
interface
subroutine c_check_bool(arg1, arg2, arg3) &
bind(C, name="checkBool")
use iso_c_binding, only : C_BOOL
implicit none
logical(C_BOOL), value, intent(IN) :: arg1
logical(C_BOOL), intent(OUT) :: arg2
logical(C_BOOL), intent(INOUT) :: arg3
end subroutine c_check_bool
end interface
The Fortran wrapper:
subroutine check_bool(arg1, arg2, arg3)
use iso_c_binding, only : C_BOOL
logical, value, intent(IN) :: arg1
logical, intent(OUT) :: arg2
logical, intent(INOUT) :: arg3
! splicer begin function.check_bool
logical(C_BOOL) :: SHT_arg1_cxx
logical(C_BOOL) :: SHT_arg2_cxx
logical(C_BOOL) :: SHT_arg3_cxx
SHT_arg1_cxx = arg1 ! coerce to C_BOOL
SHT_arg3_cxx = arg3 ! coerce to C_BOOL
call c_check_bool(SHT_arg1_cxx, SHT_arg2_cxx, SHT_arg3_cxx)
arg2 = SHT_arg2_cxx ! coerce to logical
arg3 = SHT_arg3_cxx ! coerce to logical
! splicer end function.check_bool
end subroutine check_bool
Fortran usage:
logical rv_logical, wrk_logical
rv_logical = .true.
wrk_logical = .true.
call check_bool(.true., rv_logical, wrk_logical)
call assert_false(rv_logical)
call assert_false(wrk_logical)
Character¶
acceptName¶
Pass a NULL terminated string to a C function.
The string will be unchanged.
C library function in clibrary.c:
void acceptName(const char *name)
{
strncpy(last_function_called, "acceptName", MAXLAST);
}
clibrary.yaml:
- decl: void acceptName(const char *name)
Fortran calls C via the following interface:
interface
subroutine c_accept_name(name) &
bind(C, name="acceptName")
use iso_c_binding, only : C_CHAR
implicit none
character(kind=C_CHAR), intent(IN) :: name(*)
end subroutine c_accept_name
end interface
The Fortran wrapper:
subroutine accept_name(name)
use iso_c_binding, only : C_NULL_CHAR
character(len=*), intent(IN) :: name
! splicer begin function.accept_name
call c_accept_name(trim(name)//C_NULL_CHAR)
! splicer end function.accept_name
end subroutine accept_name
No C wrapper is required since the Fortran wrapper creates a NULL
terminated string by calling the Fortran intrinsic function trim
and concatenating C_NULL_CHAR (from iso_c_binding). This can
be done since the argument name is const which sets the
attribute intent(in).
Fortran usage:
call accept_name("spot")
returnOneName¶
Pass the pointer to a buffer which the C library will fill. The
length of the string is implicitly known by the library to not exceed
the library variable MAXNAME.
C library function in clibrary.c:
void returnOneName(char *name1)
{
strcpy(name1, "bill");
}
clibrary.yaml:
- decl: void returnOneName(char *name1+intent(out)+charlen(MAXNAME))
The C wrapper:
void CLI_returnOneName_bufferify(char *name1, int SHT_name1_len)
{
// splicer begin function.returnOneName_bufferify
returnOneName(name1);
ShroudCharBlankFill(name1, SHT_name1_len);
// splicer end function.returnOneName_bufferify
}
Fortran calls C via the following interface:
interface
subroutine c_return_one_name_bufferify(name1, SHT_name1_len) &
bind(C, name="CLI_returnOneName_bufferify")
use iso_c_binding, only : C_CHAR, C_INT
implicit none
character(kind=C_CHAR), intent(OUT) :: name1(*)
integer(C_INT), value, intent(IN) :: SHT_name1_len
end subroutine c_return_one_name_bufferify
end interface
The Fortran wrapper:
subroutine return_one_name(name1)
use iso_c_binding, only : C_INT
character(len=*), intent(OUT) :: name1
! splicer begin function.return_one_name
integer(C_INT) SHT_name1_len
SHT_name1_len = len(name1, kind=C_INT)
call c_return_one_name_bufferify(name1, SHT_name1_len)
! splicer end function.return_one_name
end subroutine return_one_name
Fortran usage:
name1 = " "
call return_one_name(name1)
call assert_equals("bill", name1)
passCharPtr¶
The function passCharPtr(dest, src) is equivalent to the Fortran
statement dest = src:
C++ library function in char.c:
void passCharPtr(char *dest, const char *src)
{
strcpy(dest, src);
}
strings.yaml:
- decl: void passCharPtr(char * dest+intent(out)+charlen(40),
const char *src)
The intent of dest must be explicit. It defaults to intent(inout)
since it is a pointer.
src is implied to be intent(in) since it is const.
This single line will create five different wrappers.
The native C version.
The only feature this provides to Fortran is the ability
to call a C++ function by wrapping it in an extern "C" function.
The user is responsible for providing the NULL termination.
The result in str will also be NULL terminated instead of
blank filled.:
void CHA_passCharPtr(char *dest, const char *src)
{
// splicer begin function.passCharPtr
passCharPtr(dest, src);
// splicer end function.passCharPtr
}
The C wrapper:
void CHA_passCharPtr_bufferify(char *dest, int SHT_dest_len,
const char *src)
{
// splicer begin function.passCharPtr_bufferify
passCharPtr(dest, src);
ShroudCharBlankFill(dest, SHT_dest_len);
// splicer end function.passCharPtr_bufferify
}
Fortran calls C via the following interface:
interface
subroutine c_pass_char_ptr(dest, src) &
bind(C, name="CHA_passCharPtr")
use iso_c_binding, only : C_CHAR
implicit none
character(kind=C_CHAR), intent(OUT) :: dest(*)
character(kind=C_CHAR), intent(IN) :: src(*)
end subroutine c_pass_char_ptr
end interface
interface
subroutine c_pass_char_ptr_bufferify(dest, SHT_dest_len, src) &
bind(C, name="CHA_passCharPtr_bufferify")
use iso_c_binding, only : C_CHAR, C_INT
implicit none
character(kind=C_CHAR), intent(OUT) :: dest(*)
integer(C_INT), value, intent(IN) :: SHT_dest_len
character(kind=C_CHAR), intent(IN) :: src(*)
end subroutine c_pass_char_ptr_bufferify
end interface
The Fortran wrapper:
subroutine pass_char_ptr(dest, src)
use iso_c_binding, only : C_INT, C_NULL_CHAR
character(len=*), intent(OUT) :: dest
character(len=*), intent(IN) :: src
! splicer begin function.pass_char_ptr
integer(C_INT) SHT_dest_len
SHT_dest_len = len(dest, kind=C_INT)
call c_pass_char_ptr_bufferify(dest, SHT_dest_len, &
trim(src)//C_NULL_CHAR)
! splicer end function.pass_char_ptr
end subroutine pass_char_ptr
The function can be called without the user aware that it is written in C++:
character(30) str
call pass_char_ptr(dest=str, src="mouse")
ImpliedTextLen¶
Pass the pointer to a buffer which the C library will fill. The
length of the buffer is passed in ltext. Since Fortran knows the
length of CHARACTER variable, the Fortran wrapper does not need to
be explicitly told the length of the variable. Instead its value can
be defined with the implied attribute.
This can be used to emulate the behavior of most Fortran compilers
which will pass an additional, hidden argument which contains the
length of a CHARACTER argument.
C library function in clibrary.c:
void ImpliedTextLen(char *text, int ltext)
{
strncpy(text, "ImpliedTextLen", ltext);
strncpy(last_function_called, "ImpliedTextLen", MAXLAST);
}
clibrary.yaml:
- decl: void ImpliedTextLen(char *text+intent(out)+charlen(MAXNAME),
int ltext+implied(len(text)))
The C wrapper:
void CLI_ImpliedTextLen_bufferify(char *text, int SHT_text_len,
int ltext)
{
// splicer begin function.ImpliedTextLen_bufferify
ImpliedTextLen(text, ltext);
ShroudCharBlankFill(text, SHT_text_len);
// splicer end function.ImpliedTextLen_bufferify
}
Fortran calls C via the following interface:
interface
subroutine c_implied_text_len_bufferify(text, SHT_text_len, &
ltext) &
bind(C, name="CLI_ImpliedTextLen_bufferify")
use iso_c_binding, only : C_CHAR, C_INT
implicit none
character(kind=C_CHAR), intent(OUT) :: text(*)
integer(C_INT), value, intent(IN) :: SHT_text_len
integer(C_INT), value, intent(IN) :: ltext
end subroutine c_implied_text_len_bufferify
end interface
The Fortran wrapper:
subroutine implied_text_len(text)
use iso_c_binding, only : C_INT
character(len=*), intent(OUT) :: text
integer(C_INT) :: SH_ltext
! splicer begin function.implied_text_len
integer(C_INT) SHT_text_len
SHT_text_len = len(text, kind=C_INT)
SH_ltext = len(text,kind=C_INT)
call c_implied_text_len_bufferify(text, SHT_text_len, SH_ltext)
! splicer end function.implied_text_len
end subroutine implied_text_len
Fortran usage:
character(MAXNAME) name1
call implied_text_len(name1)
call assert_equals("ImpliedTextLen", name1)
acceptCharArrayIn¶
Arguments of type char ** are assumed to be a list of NULL
terminated strings. In Fortran this pattern would be an array of
CHARACTER where all strings are the same length. The Fortran
variable is converted into the the C version by copying the data then
releasing it at the end of the wrapper.
char.yaml:
- decl: void acceptCharArrayIn(char **names +intent(in))
This is a C file which provides the bufferify function.
wrapchar.cpp:
int CHA_acceptCharArrayIn_bufferify(const char *names,
size_t SHT_names_size, int SHT_names_len)
{
// splicer begin function.acceptCharArrayIn_bufferify
char **SHC_names_cxx = ShroudStrArrayAlloc(names, SHT_names_size,
SHT_names_len);
int SHC_rv = acceptCharArrayIn(SHC_names_cxx);
ShroudStrArrayFree(SHC_names_cxx, SHT_names_size);
return SHC_rv;
// splicer end function.acceptCharArrayIn_bufferify
}
Most of the work is done by the helper function. This converts the Fortran array into NULL terminated strings by copying all of the values:
// helper char_array_alloc
// Copy src into new memory and null terminate.
// char **src +size(nsrc) +len(len)
// CHARACTER(len) src(nsrc)
static char **ShroudStrArrayAlloc(const char *src, int nsrc, int len)
{
char **rv = static_cast<char **>(std::malloc(sizeof(char *) *nsrc));
const char *src0 = src;
for(int i=0; i < nsrc; ++i) {
int ntrim = ShroudCharLenTrim(src0, len);
char *tgt = static_cast<char *>(std::malloc(ntrim+1));
std::memcpy(tgt, src0, ntrim);
tgt[ntrim] = '\0';
rv[i] = tgt;
src0 += len;
}
return rv;
}
Fortran calls C via the following interface:
interface
function c_accept_char_array_in(names) &
result(SHT_rv) &
bind(C, name="CHA_acceptCharArrayIn")
use iso_c_binding, only : C_INT, C_PTR
implicit none
type(C_PTR), intent(IN) :: names(*)
integer(C_INT) :: SHT_rv
end function c_accept_char_array_in
end interface
The Fortran wrapper:
function accept_char_array_in(names) &
result(SHT_rv)
use iso_c_binding, only : C_INT, C_SIZE_T
character(len=*), intent(IN) :: names(:)
integer(C_INT) :: SHT_rv
! splicer begin function.accept_char_array_in
SHT_rv = c_accept_char_array_in_bufferify(names, &
size(names, kind=C_SIZE_T), len(names, kind=C_INT))
! splicer end function.accept_char_array_in
end function accept_char_array_in
Example usage:
character(10) :: in(3) = [ &
"dog ", &
"cat ", &
"monkey " &
]
call accept_char_array_in(in)
fetchCharPtrLibrary¶
The argument will return the address of a character array which
is owned by the library. The user will not need to release the
memory. The Fortran wrapper will use a variable with the
POINTER attribute which points to the same memory. The length
of the string is based on the strlen of the argument.
char.yaml:
- decl: void fetchCharPtrLibrary(char **outstr+intent(out))
This is a C++ file which provides the bufferify function.
wrapchar.cpp:
void CHA_fetchCharPtrLibrary_bufferify(
CHA_SHROUD_array *SHT_outstr_cdesc)
{
// splicer begin function.fetchCharPtrLibrary_bufferify
char *outstr;
fetchCharPtrLibrary(&outstr);
SHT_outstr_cdesc->base_addr = const_cast<char *>(outstr);
SHT_outstr_cdesc->type = SH_TYPE_CHAR;
SHT_outstr_cdesc->elem_len = outstr == nullptr ? 0 : std::strlen(outstr);
SHT_outstr_cdesc->size = 1;
SHT_outstr_cdesc->rank = 0;
// splicer end function.fetchCharPtrLibrary_bufferify
}
Fortran calls C via the following interface:
interface
subroutine c_fetch_char_ptr_library(outstr) &
bind(C, name="CHA_fetchCharPtrLibrary")
use iso_c_binding, only : C_PTR
implicit none
type(C_PTR), intent(OUT) :: outstr
end subroutine c_fetch_char_ptr_library
end interface
The Fortran wrapper:
subroutine fetch_char_ptr_library(outstr)
character(len=:), intent(OUT), pointer :: outstr
! splicer begin function.fetch_char_ptr_library
type(CHA_SHROUD_array) :: SHT_outstr_cdesc
call c_fetch_char_ptr_library_bufferify(SHT_outstr_cdesc)
call CHA_SHROUD_pointer_string(SHT_outstr_cdesc, outstr)
! splicer end function.fetch_char_ptr_library
end subroutine fetch_char_ptr_library
The helper function is required to set the length of the character pointer.
! helper pointer_string
! Assign context to an assumed-length character pointer
subroutine CHA_SHROUD_pointer_string(context, var)
use iso_c_binding, only : c_associated, c_f_pointer, C_PTR
implicit none
type(CHA_SHROUD_array), intent(IN) :: context
character(len=:), pointer, intent(OUT) :: var
character(len=context%elem_len), pointer :: fptr
if (c_associated(context%base_addr)) then
call c_f_pointer(context%base_addr, fptr)
var => fptr
else
nullify(var)
endif
end subroutine CHA_SHROUD_pointer_string
Example usage:
character(len=:), pointer :: outptr
call fetch_char_ptr_library(outptr)
print *, outptr
std::string¶
acceptStringReference¶
C++ library function in strings.c:
void acceptStringReference(std::string & arg1)
{
arg1.append("dog");
}
strings.yaml:
- decl: void acceptStringReference(std::string & arg1)
A reference defaults to intent(inout) and will add both the len and len_trim annotations.
Both generated functions will convert arg into a std::string,
call the function, then copy the results back into the argument.
Which will call the C wrapper:
void STR_acceptStringReference(char *arg1)
{
// splicer begin function.acceptStringReference
std::string SHC_arg1_cxx(arg1);
acceptStringReference(SHC_arg1_cxx);
std::strcpy(arg1, SHC_arg1_cxx.c_str());
// splicer end function.acceptStringReference
}
The C wrapper:
void STR_acceptStringReference_bufferify(char *arg1, int SHT_arg1_len)
{
// splicer begin function.acceptStringReference_bufferify
int SHC_arg1_trim = ShroudCharLenTrim(arg1, SHT_arg1_len);
std::string SHC_arg1_cxx(arg1, SHC_arg1_trim);
acceptStringReference(SHC_arg1_cxx);
ShroudCharCopy(arg1, SHT_arg1_len, SHC_arg1_cxx.data(),
SHC_arg1_cxx.size());
// splicer end function.acceptStringReference_bufferify
}
An interface for the native C function is also created:
interface
subroutine c_accept_string_reference(arg1) &
bind(C, name="STR_acceptStringReference")
use iso_c_binding, only : C_CHAR
implicit none
character(kind=C_CHAR), intent(INOUT) :: arg1(*)
end subroutine c_accept_string_reference
end interface
Fortran calls C via the following interface:
interface
subroutine c_accept_string_reference_bufferify(arg1, &
SHT_arg1_len) &
bind(C, name="STR_acceptStringReference_bufferify")
use iso_c_binding, only : C_CHAR, C_INT
implicit none
character(kind=C_CHAR), intent(INOUT) :: arg1(*)
integer(C_INT), value, intent(IN) :: SHT_arg1_len
end subroutine c_accept_string_reference_bufferify
end interface
The Fortran wrapper:
subroutine accept_string_reference(arg1)
use iso_c_binding, only : C_INT
character(len=*), intent(INOUT) :: arg1
! splicer begin function.accept_string_reference
integer(C_INT) SHT_arg1_len
SHT_arg1_len = len(arg1, kind=C_INT)
call c_accept_string_reference_bufferify(arg1, SHT_arg1_len)
! splicer end function.accept_string_reference
end subroutine accept_string_reference
The important thing to notice is that the pure C version could do very bad things since it does not know how much space it has to copy into. The bufferify version knows the allocated length of the argument. However, since the input argument is a fixed length it may be too short for the new string value:
Fortran usage:
character(30) str
str = "cat"
call accept_string_reference(str)
call assert_true( str == "catdog")
char functions¶
getConstCharPtrAlloc¶
Return a pointer and convert into an ALLOCATABLE CHARACTER
variable. The Fortran application is responsible for releasing the
memory. However, this may be done automatically by the Fortran
runtime.
C++ library function in char.c:
const char *getConstCharPtrAlloc(void)
{
return static_char;
}
strings.yaml:
- decl: const char *getConstCharPtrAlloc()
The C wrapper copies all of the metadata into a SHROUD_array
struct which is used by the Fortran wrapper:
void CHA_getConstCharPtrAlloc_bufferify(CHA_SHROUD_array *SHT_rv_cdesc)
{
// splicer begin function.getConstCharPtrAlloc_bufferify
const char *SHC_rv = getConstCharPtrAlloc();
SHT_rv_cdesc->base_addr = const_cast<char *>(SHC_rv);
SHT_rv_cdesc->type = SH_TYPE_CHAR;
SHT_rv_cdesc->elem_len = SHC_rv == nullptr ? 0 : std::strlen(SHC_rv);
SHT_rv_cdesc->size = 1;
SHT_rv_cdesc->rank = 0;
// splicer end function.getConstCharPtrAlloc_bufferify
}
Fortran calls C via the following interface:
interface
subroutine c_get_const_char_ptr_alloc_bufferify(SHT_rv_cdesc) &
bind(C, name="CHA_getConstCharPtrAlloc_bufferify")
import :: CHA_SHROUD_array
implicit none
type(CHA_SHROUD_array), intent(OUT) :: SHT_rv_cdesc
end subroutine c_get_const_char_ptr_alloc_bufferify
end interface
The Fortran wrapper uses the metadata in SHT_rv_cdesc to allocate
a CHARACTER variable of the correct length.
The helper function SHROUD_copy_string_and_free will copy
the results of the C++ function into the return variable:
function get_const_char_ptr_alloc() &
result(SHT_rv)
character(len=:), allocatable :: SHT_rv
! splicer begin function.get_const_char_ptr_alloc
type(CHA_SHROUD_array) :: SHT_rv_cdesc
call c_get_const_char_ptr_alloc_bufferify(SHT_rv_cdesc)
allocate(character(len=SHT_rv_cdesc%elem_len):: SHT_rv)
call CHA_SHROUD_copy_string(SHT_rv_cdesc, SHT_rv, &
SHT_rv_cdesc%elem_len)
! splicer end function.get_const_char_ptr_alloc
end function get_const_char_ptr_alloc
Fortran usage:
character(len=:), allocatable :: str
str = get_const_char_ptr_alloc()
getConstCharPtrLen¶
If you know the maximum size of string that you expect the function to
return, then the len attribute is used to declare the length. The
explicit ALLOCATE is avoided but any result which is longer than
the length will be silently truncated.
C++ library function in char.c:
const char *getConstCharPtrLen(void)
{
return static_char;
}
strings.yaml:
- decl: const char *getConstCharPtrLen() +len(30)
The C wrapper:
void CHA_getConstCharPtrLen_bufferify(char *SHC_rv, int SHT_rv_len)
{
// splicer begin function.getConstCharPtrLen_bufferify
const char *SHC_rv_cxx = getConstCharPtrLen();
ShroudCharCopy(SHC_rv, SHT_rv_len, SHC_rv_cxx, -1);
// splicer end function.getConstCharPtrLen_bufferify
}
Fortran calls C via the following interface:
interface
subroutine c_get_const_char_ptr_len_bufferify(SHT_rv, &
SHT_rv_len) &
bind(C, name="CHA_getConstCharPtrLen_bufferify")
use iso_c_binding, only : C_CHAR, C_INT
implicit none
character(kind=C_CHAR), intent(OUT) :: SHT_rv(*)
integer(C_INT), value, intent(IN) :: SHT_rv_len
end subroutine c_get_const_char_ptr_len_bufferify
end interface
The Fortran wrapper:
function get_const_char_ptr_len() &
result(SHT_rv)
use iso_c_binding, only : C_INT
character(len=30) :: SHT_rv
! splicer begin function.get_const_char_ptr_len
integer(C_INT) SHT_rv_len
SHT_rv_len = len(SHT_rv, kind=C_INT)
call c_get_const_char_ptr_len_bufferify(SHT_rv, SHT_rv_len)
! splicer end function.get_const_char_ptr_len
end function get_const_char_ptr_len
Fortran usage:
character(30) str
str = get_const_char_ptr_len()
getConstCharPtrAsCopyArg¶
Create a Fortran subroutine with an additional CHARACTER
argument for the C function result. Any size character string can
be returned limited by the size of the Fortran argument. The
argument is defined by the +funcarg(output) format string.
If no name is provided with +funcarg then option F_result_as_arg
is used.
C++ library function in char.c:
const char *getConstCharPtrAsCopyArg(void)
{
return static_char;
}
strings.yaml:
- decl: const char *getConstCharPtrAsCopyArg()
+funcarg(output)+deref(copy)
The C wrapper:
void CHA_getConstCharPtrAsCopyArg_bufferify(char *SHC_rv, int nSHC_rv)
{
// splicer begin function.getConstCharPtrAsCopyArg_bufferify
const char *SHC_rv_cxx = getConstCharPtrAsCopyArg();
ShroudCharCopy(SHC_rv, nSHC_rv, SHC_rv_cxx, -1);
// splicer end function.getConstCharPtrAsCopyArg_bufferify
}
Fortran calls C via the following interface:
interface
subroutine c_get_const_char_ptr_as_copy_arg_bufferify(output, &
noutput) &
bind(C, name="CHA_getConstCharPtrAsCopyArg_bufferify")
use iso_c_binding, only : C_CHAR, C_INT
implicit none
character(kind=C_CHAR), intent(OUT) :: output(*)
integer(C_INT), value, intent(IN) :: noutput
end subroutine c_get_const_char_ptr_as_copy_arg_bufferify
end interface
The Fortran wrapper:
subroutine get_const_char_ptr_as_copy_arg(output)
use iso_c_binding, only : C_INT
character(len=*), intent(OUT) :: output
! splicer begin function.get_const_char_ptr_as_copy_arg
integer(C_INT) noutput
noutput = len(output, kind=C_INT)
call c_get_const_char_ptr_as_copy_arg_bufferify(output, noutput)
! splicer end function.get_const_char_ptr_as_copy_arg
end subroutine get_const_char_ptr_as_copy_arg
Fortran usage:
character(30) str
call get_const_char_ptr_as_copy_arg(str)
string functions¶
getConstStringRefAlloc¶
C++ library function in strings.cpp:
const std::string& getConstStringRefAlloc()
{
return static_str;
}
strings.yaml:
- decl: const string& getConstStringRefAlloc()
The C wrapper:
void STR_getConstStringRefAlloc_bufferify(
STR_SHROUD_array *SHT_rv_cdesc,
STR_SHROUD_capsule_data *SHT_rv_capsule)
{
// splicer begin function.getConstStringRefAlloc_bufferify
const std::string &SHC_rv_cxx = getConstStringRefAlloc();
ShroudStringToCdesc(SHT_rv_cdesc, &SHC_rv_cxx);
SHT_rv_capsule->addr = const_cast<std::string *>(&SHC_rv_cxx);
SHT_rv_capsule->idtor = 0;
SHT_rv_capsule->cmemflags = SWIG_MEM_RVALUE;
// splicer end function.getConstStringRefAlloc_bufferify
}
The native C wrapper:
const char * STR_getConstStringRefAlloc(void)
{
// splicer begin function.getConstStringRefAlloc
const std::string &SHC_rv_cxx = getConstStringRefAlloc();
const char *SHC_rv = SHC_rv_cxx.c_str();
return SHC_rv;
// splicer end function.getConstStringRefAlloc
}
Fortran calls C via the following interface:
interface
subroutine c_get_const_string_ref_alloc_bufferify(SHT_rv_cdesc, &
SHT_rv_capsule) &
bind(C, name="STR_getConstStringRefAlloc_bufferify")
import :: STR_SHROUD_array, STR_SHROUD_capsule_data
implicit none
type(STR_SHROUD_array), intent(OUT) :: SHT_rv_cdesc
type(STR_SHROUD_capsule_data), intent(OUT) :: SHT_rv_capsule
end subroutine c_get_const_string_ref_alloc_bufferify
end interface
The Fortran wrapper:
function get_const_string_ref_alloc() &
result(SHT_rv)
character(len=:), allocatable :: SHT_rv
! splicer begin function.get_const_string_ref_alloc
type(STR_SHROUD_array) :: SHT_rv_cdesc
type(STR_SHROUD_capsule_data) :: SHT_rv_capsule
call c_get_const_string_ref_alloc_bufferify(SHT_rv_cdesc, &
SHT_rv_capsule)
allocate(character(len=SHT_rv_cdesc%elem_len):: SHT_rv)
call STR_SHROUD_copy_string(SHT_rv_cdesc, SHT_rv, &
SHT_rv_cdesc%elem_len)
call STR_SHROUD_capsule_dtor(SHT_rv_capsule)
! splicer end function.get_const_string_ref_alloc
end function get_const_string_ref_alloc
Fortran usage:
character(:), allocatable :: astr
astr = get_const_string_ref_alloc()
call assert_true(astr == static_str, "getConstStringRefAlloc")
The wrappers use the statement group f_function_string&_cdesc_allocatable.
name: f_function_string&_cdesc_allocatable
intent: function
comments:
- Call the C wrapper as a subroutine.
- Pass cdesc as argument to C wrapper.
- Pass capsule as argument to C wrapper.
- Create a local Fortran capsule.
- Call function and assign to a local C++ variable.
- Fill cdesc from std::string using helper string_to_cdesc
- in the C wrapper.
- Assign capsule values to capsule argument in C wrapper.
- Allocate Fortran CHARACTER scalar, then fill from cdesc
- using helper copy_string.
- Release memory from capsule.
mixin_names:
- ' f_mixin_call-cwrapper-as-subroutine'
- ' c_mixin_as-intent-out'
- ' f_mixin_cdesc_pass_to_cwrapper'
- ' f_mixin_capsule_pass'
- ' c_mixin_capsule_pass'
- ' c_mixin_function-assign-to-local'
- ' c_mixin_cdesc_function_string'
- ' c_mixin_capsule_fill_arg'
- ' f_mixin_cdesc_char_allocate'
- ' f_mixin_capsule_dtor'
f_dummy_decl:
- 'character(len=:){f_intent_attr}{f_deref_attr} :: {f_var}'
f_local_decl:
- 'type({F_array_type}) :: {f_var_cdesc}'
- 'type({f_capsule_data_type}) :: {f_var_capsule}'
f_arg_call:
- '{f_var_cdesc}'
- '{f_var_capsule}'
f_call:
- call {f_call_function}({F_arg_c_call})
f_post_call:
- 'allocate(character(len={f_var_cdesc}%elem_len):: {f_var})'
- "call {f_helper_copy_string}(\t{f_var_cdesc},\t {f_var},\t {f_var_cdesc}%elem_len)"
- call {f_helper_capsule_dtor}({f_var_capsule})
f_temps:
- cdesc
- capsule
f_need_wrapper: true
i_dummy_arg:
- '{i_var_cdesc}'
- '{i_var_capsule}'
i_dummy_decl:
- 'type({F_array_type}), intent(OUT) :: {i_var_cdesc}'
- 'type({f_capsule_data_type}), intent(OUT) :: {i_var_capsule}'
i_import:
- '{F_array_type}'
- '{f_capsule_data_type}'
c_return_type: void
c_prototype:
- '{C_array_type} *{c_var_cdesc}'
- '{c_capsule_data_type} *{c_var_capsule}'
c_call:
- "{gen.cxxresult.c_local_cxx} =\t {C_call_function};"
c_post_call:
- "{c_helper_string_to_cdesc}(\t{c_var_cdesc},\t {cxx_addr}{cxx_var});"
- '{c_var_capsule}->addr = {cxx_nonconst_ptr};'
- '{c_var_capsule}->idtor = {idtor};'
- '{c_var_capsule}->cmemflags = {c_cmemflags_or}SWIG_MEM_RVALUE;'
c_temps:
- cdesc
- capsule
c_local:
- cxx
fmtdict:
i_intent: OUT
i_intent_attr: ', intent(OUT)'
helper:
- array_context
- capsule_data
- array_context
- string_to_cdesc
- copy_string
- capsule_dtor
std::vector¶
vector_sum¶
C++ library function in vectors.cpp:
int vector_sum(const std::vector<int> &arg)
{
int sum = 0;
for(std::vector<int>::const_iterator it = arg.begin(); it != arg.end(); ++it) {
sum += *it;
}
return sum;
}
vectors.yaml:
- decl: int vector_sum(const std::vector<int> &arg)
intent(in) is implied for the vector_sum argument since it is
const. The Fortran wrapper passes the array and the size to C.
The C wrapper:
int VEC_vector_sum(int *arg, size_t SHT_arg_size)
{
// splicer begin function.vector_sum
const std::vector<int> SHC_arg_cxx(arg, arg + SHT_arg_size);
int SHC_rv = vector_sum(SHC_arg_cxx);
return SHC_rv;
// splicer end function.vector_sum
}
Fortran calls C via the following interface:
interface
function c_vector_sum(arg, SHT_arg_size) &
result(SHT_rv) &
bind(C, name="VEC_vector_sum")
use iso_c_binding, only : C_INT, C_SIZE_T
implicit none
integer(C_INT), intent(IN) :: arg(*)
integer(C_SIZE_T), intent(IN), value :: SHT_arg_size
integer(C_INT) :: SHT_rv
end function c_vector_sum
end interface
The Fortran wrapper:
function vector_sum(arg) &
result(SHT_rv)
use iso_c_binding, only : C_INT, C_SIZE_T
integer(C_INT), intent(IN) :: arg(:)
integer(C_INT) :: SHT_rv
! splicer begin function.vector_sum
SHT_rv = c_vector_sum(arg, size(arg, kind=C_SIZE_T))
! splicer end function.vector_sum
end function vector_sum
Fortran usage:
integer(C_INT) intv(5)
intv = [1,2,3,4,5]
irv = vector_sum(intv)
call assert_true(irv .eq. 15)
vector_iota_out¶
C++ library function in vectors.cpp accepts an empty vector
then fills in some values.
In this example, a Fortran array is passed in and will be filled.
void vector_iota_out(std::vector<int> &arg)
{
for(unsigned int i=0; i < 5; i++) {
arg.push_back(i + 1);
}
return;
}
vectors.yaml:
- decl: void vector_iota_out(std::vector<int> &arg+intent(out))
The C wrapper allocates a new std::vector instance which will be
returned to the Fortran wrapper.
Variable Darg will be filled with the meta data for the std::vector
in a form that allows Fortran to access it.
The value of Darg->cxx.idtor is computed by Shroud and used
to release the memory (index of destructor).
void VEC_vector_iota_out_bufferify(VEC_SHROUD_array *SHT_arg_cdesc)
{
// splicer begin function.vector_iota_out_bufferify
std::vector<int> *SHC_arg_cxx = new std::vector<int>;
vector_iota_out(*SHC_arg_cxx);
SHT_arg_cdesc->base_addr = SHC_arg_cxx->empty() ? nullptr : &SHC_arg_cxx->front();
SHT_arg_cdesc->type = SH_TYPE_INT;
SHT_arg_cdesc->elem_len = sizeof(int);
SHT_arg_cdesc->size = SHC_arg_cxx->size();
SHT_arg_cdesc->rank = 1;
SHT_arg_cdesc->shape[0] = SHT_arg_cdesc->size;
// splicer end function.vector_iota_out_bufferify
}
Fortran calls C via the following interface:
interface
subroutine c_vector_iota_out_bufferify(SHT_arg_cdesc) &
bind(C, name="VEC_vector_iota_out_bufferify")
import :: VEC_SHROUD_array
implicit none
type(VEC_SHROUD_array), intent(OUT) :: SHT_arg_cdesc
end subroutine c_vector_iota_out_bufferify
end interface
The Fortran wrapper passes a SHROUD_array instance which will be
filled by the C wrapper.
subroutine vector_iota_out(arg)
use iso_c_binding, only : C_INT, C_LOC, C_SIZE_T
integer(C_INT), intent(OUT), target :: arg(:)
! splicer begin function.vector_iota_out
type(VEC_SHROUD_array) :: SHT_arg_cdesc
call c_vector_iota_out_bufferify(SHT_arg_cdesc)
call VEC_SHROUD_copy_array(SHT_arg_cdesc, C_LOC(arg), &
size(arg, kind=C_SIZE_T))
! splicer end function.vector_iota_out
end subroutine vector_iota_out
Function SHROUD_copy_array_int copies the values
into the user’s argument.
If the argument is too short, not all values returned by
the library function will be copied.
// helper copy_array
// Copy std::vector into array c_var(c_var_size).
// Then release std::vector.
// Called from Fortran.
void VEC_ShroudCopyArray(VEC_SHROUD_array *data, void *c_var,
size_t c_var_size)
{
const void *cxx_var = data->base_addr;
int n = c_var_size < data->size ? c_var_size : data->size;
n *= data->elem_len;
std::memcpy(c_var, cxx_var, n);
}
Finally, the std::vector is released based on the value of idtor:
// Release library allocated memory.
void VEC_SHROUD_memory_destructor(VEC_SHROUD_capsule_data *cap)
{
void *ptr = cap->addr;
switch (cap->idtor) {
case 0: // --none--
{
// Nothing to delete
break;
}
case 1: // std_vector_int
{
std::vector<int> *cxx_ptr =
reinterpret_cast<std::vector<int> *>(ptr);
delete cxx_ptr;
break;
}
case 2: // std_vector_double
{
std::vector<double> *cxx_ptr =
reinterpret_cast<std::vector<double> *>(ptr);
delete cxx_ptr;
break;
}
default:
{
// Unexpected case in destructor
break;
}
}
cap->addr = nullptr;
cap->idtor = 0; // avoid deleting again
cap->cmemflags = cap->cmemflags & ~SWIG_MEM_OWN;
}
Fortran usage:
integer(C_INT) intv(5)
intv(:) = 0
call vector_iota_out(intv)
call assert_true(all(intv(:) .eq. [1,2,3,4,5]))
vector_iota_out_alloc¶
C++ library function in vectors.cpp accepts an empty vector
then fills in some values.
In this example, the Fortran argument is ALLOCATABLE and will
be sized based on the output of the library function.
void vector_iota_out_alloc(std::vector<int> &arg)
{
for(unsigned int i=0; i < 5; i++) {
arg.push_back(i + 1);
}
return;
}
The attribute +deref(allocatable) will cause the argument to be an
ALLOCATABLE array.
vectors.yaml:
- decl: void vector_iota_out_alloc(std::vector<int> &arg+intent(out)+deref(allocatable))
The C wrapper:
void VEC_vector_iota_out_alloc_bufferify(
VEC_SHROUD_array *SHT_arg_cdesc)
{
// splicer begin function.vector_iota_out_alloc_bufferify
std::vector<int> *SHC_arg_cxx = new std::vector<int>;
vector_iota_out_alloc(*SHC_arg_cxx);
SHT_arg_cdesc->base_addr = SHC_arg_cxx->empty() ? nullptr : &SHC_arg_cxx->front();
SHT_arg_cdesc->type = SH_TYPE_INT;
SHT_arg_cdesc->elem_len = sizeof(int);
SHT_arg_cdesc->size = SHC_arg_cxx->size();
SHT_arg_cdesc->rank = 1;
SHT_arg_cdesc->shape[0] = SHT_arg_cdesc->size;
// splicer end function.vector_iota_out_alloc_bufferify
}
Fortran calls C via the following interface:
interface
subroutine c_vector_iota_out_alloc_bufferify(SHT_arg_cdesc) &
bind(C, name="VEC_vector_iota_out_alloc_bufferify")
import :: VEC_SHROUD_array
implicit none
type(VEC_SHROUD_array), intent(OUT) :: SHT_arg_cdesc
end subroutine c_vector_iota_out_alloc_bufferify
end interface
The Fortran wrapper passes a SHROUD_array instance which will be
filled by the C wrapper.
After the function returns, the allocate statement allocates an
array of the proper length.
subroutine vector_iota_out_alloc(arg)
use iso_c_binding, only : C_INT, C_LOC, C_SIZE_T
integer(C_INT), intent(OUT), allocatable, target :: arg(:)
! splicer begin function.vector_iota_out_alloc
type(VEC_SHROUD_array) :: SHT_arg_cdesc
call c_vector_iota_out_alloc_bufferify(SHT_arg_cdesc)
allocate(arg(SHT_arg_cdesc%size))
call VEC_SHROUD_copy_array(SHT_arg_cdesc, C_LOC(arg), &
size(arg, kind=C_SIZE_T))
! splicer end function.vector_iota_out_alloc
end subroutine vector_iota_out_alloc
inta is intent(out), so it will be deallocated upon entry to vector_iota_out_alloc.
Fortran usage:
integer(C_INT), allocatable :: inta(:)
call vector_iota_out_alloc(inta)
call assert_true(allocated(inta))
call assert_equals(5 , size(inta))
call assert_true( all(inta == [1,2,3,4,5]), &
"vector_iota_out_alloc value")
vector_iota_inout_alloc¶
C++ library function in vectors.cpp:
void vector_iota_inout_alloc(std::vector<int> &arg)
{
for(unsigned int i=0; i < 5; i++) {
arg.push_back(i + 11);
}
return;
}
vectors.yaml:
- decl: void vector_iota_out_alloc(std::vector<int> &arg+intent(inout)+deref(allocatable))
The C wrapper creates a new std::vector and initializes it to the
Fortran argument.
void VEC_vector_iota_inout_alloc_bufferify(int *arg,
size_t SHT_arg_size, VEC_SHROUD_array *SHT_arg_cdesc)
{
// splicer begin function.vector_iota_inout_alloc_bufferify
std::vector<int> *SHC_arg_cxx = new std::vector<int>(
arg, arg + SHT_arg_size);
vector_iota_inout_alloc(*SHC_arg_cxx);
SHT_arg_cdesc->base_addr = SHC_arg_cxx->empty() ? nullptr : &SHC_arg_cxx->front();
SHT_arg_cdesc->type = SH_TYPE_INT;
SHT_arg_cdesc->elem_len = sizeof(int);
SHT_arg_cdesc->size = SHC_arg_cxx->size();
SHT_arg_cdesc->rank = 1;
SHT_arg_cdesc->shape[0] = SHT_arg_cdesc->size;
// splicer end function.vector_iota_inout_alloc_bufferify
}
Fortran calls C via the following interface:
interface
subroutine c_vector_iota_inout_alloc_bufferify(arg, &
SHT_arg_size, SHT_arg_cdesc) &
bind(C, name="VEC_vector_iota_inout_alloc_bufferify")
use iso_c_binding, only : C_INT, C_SIZE_T
import :: VEC_SHROUD_array
implicit none
integer(C_INT), intent(IN) :: arg(*)
integer(C_SIZE_T), intent(IN), value :: SHT_arg_size
type(VEC_SHROUD_array), intent(OUT) :: SHT_arg_cdesc
end subroutine c_vector_iota_inout_alloc_bufferify
end interface
The Fortran wrapper will deallocate the argument after returning
since it is intent(inout). The in values are now stored in
the std::vector. A new array is allocated to the current size
of the std::vector. Fortran has no reallocate statement.
Finally, the new values are copied into the Fortran array and
the std::vector is released.
subroutine vector_iota_inout_alloc(arg)
use iso_c_binding, only : C_INT, C_LOC, C_SIZE_T
integer(C_INT), intent(INOUT), allocatable, target :: arg(:)
! splicer begin function.vector_iota_inout_alloc
type(VEC_SHROUD_array) :: SHT_arg_cdesc
call c_vector_iota_inout_alloc_bufferify(arg, &
size(arg, kind=C_SIZE_T), SHT_arg_cdesc)
if (allocated(arg)) deallocate(arg)
allocate(arg(SHT_arg_cdesc%size))
call VEC_SHROUD_copy_array(SHT_arg_cdesc, C_LOC(arg), &
size(arg, kind=C_SIZE_T))
! splicer end function.vector_iota_inout_alloc
end subroutine vector_iota_inout_alloc
inta is intent(inout), so it will NOT be deallocated upon
entry to vector_iota_inout_alloc.
Fortran usage:
call vector_iota_inout_alloc(inta)
call assert_true(allocated(inta))
call assert_equals(10 , size(inta))
call assert_true( all(inta == [1,2,3,4,5,11,12,13,14,15]), &
"vector_iota_inout_alloc value")
deallocate(inta)
Void Pointers¶
passAssumedType¶
C library function in clibrary.c:
int passAssumedType(void *arg)
{
strncpy(last_function_called, "passAssumedType", MAXLAST);
return *(int *) arg;
}
clibrary.yaml:
- decl: int passAssumedType(void *arg+assumedtype)
Fortran calls C via the following interface:
interface
function pass_assumed_type(arg) &
result(SHT_rv) &
bind(C, name="passAssumedType")
use iso_c_binding, only : C_INT
implicit none
type(*), intent(IN) :: arg
integer(C_INT) :: SHT_rv
end function pass_assumed_type
end interface
Fortran usage:
use iso_c_binding, only : C_INT
integer(C_INT) rv_int
rv_int = pass_assumed_type(23_C_INT)
As a reminder, 23_C_INT creates an integer(C_INT) constant.
Note
Assumed-type was introduced in Fortran 2018.
passAssumedTypeRank¶
C library function in clibrary.c:
void passAssumedTypeRank(void *arg)
{
strncpy(last_function_called, "passAssumedTypeRank", MAXLAST);
}
clibrary.yaml:
- decl: int passAssumedTypeRank(void *arg+assumedtype+rank(1))
Fortran calls C via the following interface:
interface
subroutine pass_assumed_type_rank(arg) &
bind(C, name="passAssumedTypeRank")
implicit none
type(*), intent(IN) :: arg(*)
end subroutine pass_assumed_type_rank
end interface
Example usage:
use iso_c_binding, only : C_INT, C_DOUBLE
integer(C_INT) int_array(10)
real(C_DOUBLE) double_array(2,5)
call pass_assumed_type_rank(int_array)
call pass_assumed_type_rank(double_array)
Note
Assumed-type was introduced in Fortran 2018.
passVoidStarStar¶
C library function in clibrary.c:
void passVoidStarStar(void *in, void **out)
{
strncpy(last_function_called, "passVoidStarStar", MAXLAST);
*out = in;
}
clibrary.yaml:
- decl: void passVoidStarStar(void *in+intent(in), void **out+intent(out))
Fortran calls C via the following interface:
interface
subroutine pass_void_star_star(in, out) &
bind(C, name="passVoidStarStar")
use iso_c_binding, only : C_PTR
implicit none
type(C_PTR), value, intent(IN) :: in
type(C_PTR), intent(OUT) :: out
end subroutine pass_void_star_star
end interface
Example usage:
use iso_c_binding, only : C_INT, C_NULL_PTR, c_associated
integer(C_INT) int_var
cptr1 = c_loc(int_var)
cptr2 = C_NULL_PTR
call pass_void_star_star(cptr1, cptr2)
call assert_true(c_associated(cptr1, cptr2))
Function Pointers¶
callback1¶
C++ library function in tutorial.cpp:
int callback1(int in, int (*incr)(int))
{
return incr(in);
}
tutorial.yaml:
- decl: int callback1(int in, int (*incr)(int));
The C wrapper:
int TUT_callback1(int in, int (*incr)(int))
{
// splicer begin function.callback1
int SHC_rv = tutorial::callback1(in, incr);
return SHC_rv;
// splicer end function.callback1
}
Creates the abstract interface:
abstract interface
function callback1_incr(arg0) &
result(SHT_rv) bind(C)
use iso_c_binding, only : C_INT
implicit none
integer(C_INT), value :: arg0
integer(C_INT) :: SHT_rv
end function callback1_incr
end interface
Fortran calls C via the following interface:
interface
function callback1(in, incr) &
result(SHT_rv) &
bind(C, name="TUT_callback1")
use iso_c_binding, only : C_INT
import :: callback1_incr
implicit none
integer(C_INT), value, intent(IN) :: in
procedure(callback1_incr) :: incr
integer(C_INT) :: SHT_rv
end function callback1
end interface
Fortran usage:
module worker
use iso_c_binding
contains
subroutine userincr(i) bind(C)
integer(C_INT), value :: i
! do work of callback
end subroutine user
subroutine work
call callback1(1, userincr)
end subroutine work
end module worker
callback1_funptr¶
C library function in funptr.c. The actual function would need
some way to know the interface/prototype of the function that was
passed in. Perhaps by another argument or some other state:
void callback1_funptr(void (*incr)(void))
{
incr();
}
funptr.yaml:
- decl: void callback1_funptr(void (*incr)(void)+funptr)
The Fortran wrapper.
By using funptr no abstract interface is used:
subroutine callback1_funptr(incr1_funptr) &
bind(C, name="callback1_funptr")
use iso_c_binding, only : C_FUNPTR
implicit none
type(C_FUNPTR), value :: incr1_funptr
end subroutine callback1_funptr
Fortran usage:
module worker
use iso_c_binding
contains
subroutine userincr_int() bind(C)
! do work of callback
end subroutine user_int
subroutine userincr_double() bind(C)
! do work of callback
end subroutine user_int
subroutine work
call callback1_funptr(c_funloc(userincr_int))
call callback1_funptr(c_funloc(userincr_double))
end subroutine work
end module worker
Struct¶
Struct creating is described in Fortran Structs.
passStruct1¶
C library function in struct.c:
int passStruct1(const Cstruct1 *s1)
{
strncpy(last_function_called, "passStruct1", MAXLAST);
return s1->ifield;
}
struct.yaml:
- decl: int passStruct1(Cstruct1 *s1)
Fortran calls C via the following interface:
interface
function pass_struct1(arg) &
result(SHT_rv) &
bind(C, name="passStruct1")
use iso_c_binding, only : C_INT
import :: cstruct1
implicit none
type(cstruct1), intent(IN) :: arg
integer(C_INT) :: SHT_rv
end function pass_struct1
end interface
Fortran usage:
type(cstruct1) str1
str1%ifield = 12
str1%dfield = 12.6
call assert_equals(12, pass_struct1(str1), "passStruct1")
passStructByValue¶
C library function in struct.c:
int passStructByValue(Cstruct1 arg)
{
int rv = arg.ifield * 2;
// Caller will not see changes.
arg.ifield += 1;
return rv;
}
struct.yaml:
- decl: double passStructByValue(struct1 arg)
Fortran calls C via the following interface:
interface
function pass_struct_by_value(arg) &
result(SHT_rv) &
bind(C, name="passStructByValue")
use iso_c_binding, only : C_INT
import :: cstruct1
implicit none
type(cstruct1), value, intent(IN) :: arg
integer(C_INT) :: SHT_rv
end function pass_struct_by_value
end interface
Fortran usage:
type(cstruct1) str1
str1%ifield = 2_C_INT
str1%dfield = 2.0_C_DOUBLE
rvi = pass_struct_by_value(str1)
call assert_equals(4, rvi, "pass_struct_by_value")
! Make sure str1 was passed by value.
call assert_equals(2_C_INT, str1%ifield, "pass_struct_by_value ifield")
call assert_equals(2.0_C_DOUBLE, str1%dfield, "pass_struct_by_value dfield")
Class Type¶
constructor and destructor¶
The C++ header file from classes.hpp.
class Class1
{
public:
int m_flag;
int m_test;
Class1() : m_flag(0), m_test(0) {};
Class1(int flag) : m_flag(flag), m_test(0) {};
};
classes.yaml:
declarations:
- decl: class Class1
declarations:
- decl: Class1()
format:
function_suffix: _default
- decl: Class1(int flag)
format:
function_suffix: _flag
- decl: ~Class1() +name(delete)
A C wrapper function is created for each constructor and the destructor.
The C wrappers:
CLA_Class1 * CLA_Class1_ctor_default(CLA_Class1 *SHC_rv)
{
// splicer begin class.Class1.method.ctor_default
classes::Class1 *SHCXX_rv = new classes::Class1();
SHC_rv->addr = static_cast<void *>(SHCXX_rv);
SHC_rv->idtor = 1;
SHC_rv->cmemflags = SWIG_MEM_RVALUE | SWIG_MEM_OWN;
return SHC_rv;
// splicer end class.Class1.method.ctor_default
}
CLA_Class1 * CLA_Class1_ctor_flag(int flag, CLA_Class1 *SHC_rv)
{
// splicer begin class.Class1.method.ctor_flag
classes::Class1 *SHCXX_rv = new classes::Class1(flag);
SHC_rv->addr = static_cast<void *>(SHCXX_rv);
SHC_rv->idtor = 1;
SHC_rv->cmemflags = SWIG_MEM_RVALUE | SWIG_MEM_OWN;
return SHC_rv;
// splicer end class.Class1.method.ctor_flag
}
void CLA_Class1_delete(CLA_Class1 * self)
{
classes::Class1 *SH_this = static_cast<classes::Class1 *>
(self->addr);
// splicer begin class.Class1.method.delete
if (self->cmemflags & SWIG_MEM_OWN) {
delete SH_this;
}
self->addr = nullptr;
self->idtor = 0;
self->cmemflags = 0;
// splicer end class.Class1.method.delete
}
The corresponding Fortran interfaces:
interface
function c_class1_ctor_default(SHT_rv) &
result(SHT_rv_ptr) &
bind(C, name="CLA_Class1_ctor_default")
use iso_c_binding, only : C_PTR
import :: CLA_SHROUD_capsule_data
implicit none
type(CLA_SHROUD_capsule_data), intent(OUT) :: SHT_rv
type(C_PTR) :: SHT_rv_ptr
end function c_class1_ctor_default
end interface
interface
function c_class1_ctor_flag(flag, SHT_rv) &
result(SHT_rv_ptr) &
bind(C, name="CLA_Class1_ctor_flag")
use iso_c_binding, only : C_INT, C_PTR
import :: CLA_SHROUD_capsule_data
implicit none
integer(C_INT), value, intent(IN) :: flag
type(CLA_SHROUD_capsule_data), intent(OUT) :: SHT_rv
type(C_PTR) :: SHT_rv_ptr
end function c_class1_ctor_flag
end interface
interface
subroutine c_class1_delete(self) &
bind(C, name="CLA_Class1_delete")
import :: CLA_SHROUD_capsule_data
implicit none
type(CLA_SHROUD_capsule_data), intent(INOUT) :: self
end subroutine c_class1_delete
end interface
And the Fortran wrappers:
function class1_ctor_default() &
result(SHT_rv)
type(class1) :: SHT_rv
! splicer begin class.Class1.method.ctor_default
call c_class1_ctor_default_bufferify(SHT_rv%cxxmem)
! splicer end class.Class1.method.ctor_default
end function class1_ctor_default
function class1_ctor_flag(flag) &
result(SHT_rv)
use iso_c_binding, only : C_INT
integer(C_INT), value, intent(IN) :: flag
type(class1) :: SHT_rv
! splicer begin class.Class1.method.ctor_flag
call c_class1_ctor_flag_bufferify(flag, SHT_rv%cxxmem)
! splicer end class.Class1.method.ctor_flag
end function class1_ctor_flag
subroutine class1_delete(obj)
class(class1), intent(INOUT) :: obj
! splicer begin class.Class1.method.delete
call c_class1_delete(obj%cxxmem)
! splicer end class.Class1.method.delete
end subroutine class1_delete
The Fortran shadow class adds the type-bound method for the destructor:
type, bind(C) :: SHROUD_class1_capsule
type(C_PTR) :: addr = C_NULL_PTR ! address of C++ memory
integer(C_INT) :: idtor = 0 ! index of destructor
end type SHROUD_class1_capsule
type class1
type(SHROUD_class1_capsule) :: cxxmem
contains
procedure :: delete => class1_delete
end type class1
The constructors are not type-bound procedures. But they are combined into a generic interface.
interface class1
module procedure class1_ctor_default
module procedure class1_ctor_flag
end interface class1
A class instance is created and destroy from Fortran as:
use classes_mod
type(class1) obj
obj = class1()
call obj%delete
Corresponding C++ code:
include <classes.hpp>
classes::Class1 * obj = new classes::Class1;
delete obj;
Getter and Setter¶
The C++ header file from classes.hpp.
class Class1
{
public:
int m_flag;
int m_test;
};
classes.yaml:
declarations:
- decl: class Class1
declarations:
- decl: int m_flag +readonly;
- decl: int m_test +name(test);
A C wrapper function is created for each getter and setter.
If the readonly attribute is added, then only a getter is created.
In this case m_ is a convention used to designate member variables.
The Fortran attribute is renamed as test to avoid cluttering
the Fortran API with this convention.
The C wrappers:
int CLA_Class1_get_m_flag(CLA_Class1 * self)
{
classes::Class1 *SH_this = static_cast<classes::Class1 *>
(self->addr);
// splicer begin class.Class1.method.get_m_flag
return SH_this->m_flag;
// splicer end class.Class1.method.get_m_flag
}
int CLA_Class1_get_test(CLA_Class1 * self)
{
classes::Class1 *SH_this = static_cast<classes::Class1 *>
(self->addr);
// splicer begin class.Class1.method.get_test
return SH_this->m_test;
// splicer end class.Class1.method.get_test
}
void CLA_Class1_set_test(CLA_Class1 * self, int val)
{
classes::Class1 *SH_this = static_cast<classes::Class1 *>
(self->addr);
// splicer begin class.Class1.method.set_test
SH_this->m_test = val;
// splicer end class.Class1.method.set_test
}
The corresponding Fortran interfaces:
interface
function c_class1_get_m_flag(self) &
result(SHT_rv) &
bind(C, name="CLA_Class1_get_m_flag")
use iso_c_binding, only : C_INT
import :: CLA_SHROUD_capsule_data
implicit none
type(CLA_SHROUD_capsule_data), intent(IN) :: self
integer(C_INT) :: SHT_rv
end function c_class1_get_m_flag
end interface
interface
function c_class1_get_test(self) &
result(SHT_rv) &
bind(C, name="CLA_Class1_get_test")
use iso_c_binding, only : C_INT
import :: CLA_SHROUD_capsule_data
implicit none
type(CLA_SHROUD_capsule_data), intent(IN) :: self
integer(C_INT) :: SHT_rv
end function c_class1_get_test
end interface
interface
subroutine c_class1_set_test(self, val) &
bind(C, name="CLA_Class1_set_test")
use iso_c_binding, only : C_INT
import :: CLA_SHROUD_capsule_data
implicit none
type(CLA_SHROUD_capsule_data), intent(IN) :: self
integer(C_INT), value, intent(IN) :: val
end subroutine c_class1_set_test
end interface
And the Fortran wrappers:
function class1_get_m_flag(obj) &
result(SHT_rv)
use iso_c_binding, only : C_INT
class(class1), intent(INOUT) :: obj
integer(C_INT) :: SHT_rv
! splicer begin class.Class1.method.get_m_flag
SHT_rv = c_class1_get_m_flag(obj%cxxmem)
! splicer end class.Class1.method.get_m_flag
end function class1_get_m_flag
function class1_get_test(obj) &
result(SHT_rv)
use iso_c_binding, only : C_INT
class(class1), intent(INOUT) :: obj
integer(C_INT) :: SHT_rv
! splicer begin class.Class1.method.get_test
SHT_rv = c_class1_get_test(obj%cxxmem)
! splicer end class.Class1.method.get_test
end function class1_get_test
subroutine class1_set_test(obj, val)
use iso_c_binding, only : C_INT
class(class1), intent(INOUT) :: obj
integer(C_INT), value, intent(IN) :: val
! splicer begin class.Class1.method.set_test
call c_class1_set_test(obj%cxxmem, val)
! splicer end class.Class1.method.set_test
end subroutine class1_set_test
The Fortran shadow class adds the type-bound methods:
type class1
type(SHROUD_class1_capsule) :: cxxmem
contains
procedure :: get_m_flag => class1_get_m_flag
procedure :: get_test => class1_get_test
procedure :: set_test => class1_set_test
end type class1
The class variables can be used as:
use classes_mod
type(class1) obj
integer iflag
obj = class1()
call obj%set_test(4)
iflag = obj%get_test()
Corresponding C++ code:
include <classes.hpp>
classes::Class1 obj = new * classes::Class1;
obj->m_test = 4;
int iflag = obj->m_test;
Struct as a Class¶
While C does not support object-oriented programming directly, it can be
emulated by using structs. The ‘base class’ struct is Cstruct_as_clss.
It is subclassed by Cstruct_as_subclass which explicitly duplicates
the members of C_struct_as_class.
The C header file from struct.h.
struct Cstruct_as_class {
int x1;
int y1;
};
typedef struct Cstruct_as_class Cstruct_as_class;
/* The first members match Cstruct_as_class */
struct Cstruct_as_subclass {
int x1;
int y1;
int z1;
};
typedef struct Cstruct_as_subclass Cstruct_as_subclass;
The C ‘constructor’ returns a pointer to an instance of the object.
Cstruct_as_class *Create_Cstruct_as_class(void);
Cstruct_as_class *Create_Cstruct_as_class_args(int x, int y);
The ‘methods’ pass an instance of the class as an explicit this object.
int Cstruct_as_class_sum(const Cstruct_as_class *point);
The methods are wrapped in classes.yaml:
declarations:
- decl: struct Cstruct_as_class {
int x1;
int y1;
};
options:
wrap_struct_as: class
- decl: Cstruct_as_class *Create_Cstruct_as_class(void)
options:
class_ctor: Cstruct_as_class
- decl: Cstruct_as_class *Create_Cstruct_as_class_args(int x, int y)
options:
class_ctor: Cstruct_as_class
- decl: int Cstruct_as_class_sum(const Cstruct_as_class *point +pass)
options:
class_method: Cstruct_as_class
format:
F_name_function: sum
- decl: struct Cstruct_as_subclass {
int x1;
int y1;
int z1;
};
options:
wrap_struct_as: class
class_baseclass: Cstruct_as_class
- decl: Cstruct_as_subclass *Create_Cstruct_as_subclass_args(int x, int y, int z)
options:
wrap_python: False
class_ctor: Cstruct_as_subclass
This uses several options to creates the class features for the struct: wrap_struct_as, class_ctor, class_method.
type cstruct_as_class
type(STR_SHROUD_capsule_data) :: cxxmem = &
STR_SHROUD_capsule_data()
! splicer begin class.Cstruct_as_class.component_part
! splicer end class.Cstruct_as_class.component_part
contains
procedure :: get_x1 => cstruct_as_class_get_x1
procedure :: set_x1 => cstruct_as_class_set_x1
procedure :: get_y1 => cstruct_as_class_get_y1
procedure :: set_y1 => cstruct_as_class_set_y1
procedure :: sum => cstruct_as_class_sum
! splicer begin class.Cstruct_as_class.type_bound_procedure_part
! splicer end class.Cstruct_as_class.type_bound_procedure_part
end type cstruct_as_class
The subclass is created using the Fortran EXTENDS keyword. No
additional members are added. The cxxmem field from
cstruct_as_class will now point to an instance of the C struct
Cstruct_as_subclass.
type, extends(cstruct_as_class) :: cstruct_as_subclass
! splicer begin class.Cstruct_as_subclass.component_part
! splicer end class.Cstruct_as_subclass.component_part
contains
procedure :: get_x1 => cstruct_as_subclass_get_x1
procedure :: set_x1 => cstruct_as_subclass_set_x1
procedure :: get_y1 => cstruct_as_subclass_get_y1
procedure :: set_y1 => cstruct_as_subclass_set_y1
procedure :: get_z1 => cstruct_as_subclass_get_z1
procedure :: set_z1 => cstruct_as_subclass_set_z1
! splicer begin class.Cstruct_as_subclass.type_bound_procedure_part
! splicer end class.Cstruct_as_subclass.type_bound_procedure_part
end type cstruct_as_subclass
The C wrapper to construct the struct-as-class. It calls the C function and fills in the fields for the shadow struct.
void STR_Create_Cstruct_as_class_bufferify(STR_Cstruct_as_class *SHC_rv)
{
// splicer begin function.Create_Cstruct_as_class_bufferify
Cstruct_as_class *SHC_rv_cxx = Create_Cstruct_as_class();
SHC_rv->addr = SHC_rv_cxx;
SHC_rv->idtor = 1;
SHC_rv->cmemflags = SWIG_MEM_OWN | SWIG_MEM_RVALUE;
// splicer end function.Create_Cstruct_as_class_bufferify
}
A Fortran generic interface is created for the class:
interface cstruct_as_class
module procedure create_cstruct_as_class
module procedure create_cstruct_as_class_args
end interface cstruct_as_class
And the Fortran constructor call the C wrapper function.
function create_cstruct_as_class() &
result(SHT_rv)
type(cstruct_as_class) :: SHT_rv
! splicer begin function.create_cstruct_as_class
call c_create_cstruct_as_class_bufferify(SHT_rv%cxxmem)
! splicer end function.create_cstruct_as_class
end function create_cstruct_as_class
The class can be used as:
type(cstruct_as_class) point1, point2
type(cstruct_as_subclass) subpoint1
call set_case_name("test_struct_class")
! F_name_associated is blank so the associated function is not created.
! Instead look at pointer directly.
! call assert_false(point1%associated())
call assert_false(c_associated(point1%cxxmem%addr))
point1 = Cstruct_as_class()
call assert_equals(0, point1%get_x1())
call assert_equals(0, point1%get_y1())
point2 = Cstruct_as_class(1, 2)
call assert_equals(1, point2%get_x1())
call assert_equals(2, point2%get_y1())
call assert_equals(3, cstruct_as_class_sum(point2))
call assert_equals(3, point2%sum())
subpoint1 = Cstruct_as_subclass(1, 2, 3)
call assert_equals(1, subpoint1%get_x1())
call assert_equals(2, subpoint1%get_y1())
call assert_equals(3, subpoint1%get_z1())
call assert_equals(3, subpoint1%sum())
Default Value Arguments¶
The default values are provided in the function declaration.
C++ library function in tutorial.cpp:
double UseDefaultArguments(double arg1 = 3.1415, bool arg2 = true);
tutorial.yaml:
- decl: double UseDefaultArguments(double arg1 = 3.1415, bool arg2 = true)
default_arg_suffix:
-
- _arg1
- _arg1_arg2
A C++ wrapper is created which calls the C++ function with no arguments with default values and then adds a wrapper with an explicit argument for each default value argument. In this case, three wrappers are created. Since the C++ compiler provides the default value, it is necessary to create each wrapper.
wrapTutorial.cpp:
double TUT_UseDefaultArguments(void)
{
// splicer begin function.UseDefaultArguments
double SHC_rv = tutorial::UseDefaultArguments();
return SHC_rv;
// splicer end function.UseDefaultArguments
}
double TUT_UseDefaultArguments_arg1(double arg1)
{
// splicer begin function.UseDefaultArguments_arg1
double SHC_rv = tutorial::UseDefaultArguments(arg1);
return SHC_rv;
// splicer end function.UseDefaultArguments_arg1
}
double TUT_UseDefaultArguments_arg1_arg2(double arg1, bool arg2)
{
// splicer begin function.UseDefaultArguments_arg1_arg2
double SHC_rv = tutorial::UseDefaultArguments(arg1, arg2);
return SHC_rv;
// splicer end function.UseDefaultArguments_arg1_arg2
}
This creates three corresponding Fortran interfaces:
interface
function c_use_default_arguments() &
result(SHT_rv) &
bind(C, name="TUT_UseDefaultArguments")
use iso_c_binding, only : C_DOUBLE
implicit none
real(C_DOUBLE) :: SHT_rv
end function c_use_default_arguments
end interface
interface
function c_use_default_arguments_arg1(arg1) &
result(SHT_rv) &
bind(C, name="TUT_UseDefaultArguments_arg1")
use iso_c_binding, only : C_DOUBLE
implicit none
real(C_DOUBLE), value, intent(IN) :: arg1
real(C_DOUBLE) :: SHT_rv
end function c_use_default_arguments_arg1
end interface
interface
function c_use_default_arguments_arg1_arg2(arg1, arg2) &
result(SHT_rv) &
bind(C, name="TUT_UseDefaultArguments_arg1_arg2")
use iso_c_binding, only : C_BOOL, C_DOUBLE
implicit none
real(C_DOUBLE), value, intent(IN) :: arg1
logical(C_BOOL), value, intent(IN) :: arg2
real(C_DOUBLE) :: SHT_rv
end function c_use_default_arguments_arg1_arg2
end interface
In many case the interfaces would be enough to call the routines. However, in order to have a generic interface, there must be explicit Fortran wrappers:
function use_default_arguments() &
result(SHT_rv)
use iso_c_binding, only : C_DOUBLE
real(C_DOUBLE) :: SHT_rv
! splicer begin function.use_default_arguments
SHT_rv = c_use_default_arguments()
! splicer end function.use_default_arguments
end function use_default_arguments
function use_default_arguments_arg1(arg1) &
result(SHT_rv)
use iso_c_binding, only : C_DOUBLE
real(C_DOUBLE), value, intent(IN) :: arg1
real(C_DOUBLE) :: SHT_rv
! splicer begin function.use_default_arguments_arg1
SHT_rv = c_use_default_arguments_arg1(arg1)
! splicer end function.use_default_arguments_arg1
end function use_default_arguments_arg1
function use_default_arguments_arg1_arg2(arg1, arg2) &
result(SHT_rv)
use iso_c_binding, only : C_BOOL, C_DOUBLE
real(C_DOUBLE), value, intent(IN) :: arg1
logical, value, intent(IN) :: arg2
real(C_DOUBLE) :: SHT_rv
! splicer begin function.use_default_arguments_arg1_arg2
logical(C_BOOL) :: SHT_arg2_cxx
SHT_arg2_cxx = arg2 ! coerce to C_BOOL
SHT_rv = c_use_default_arguments_arg1_arg2(arg1, SHT_arg2_cxx)
! splicer end function.use_default_arguments_arg1_arg2
end function use_default_arguments_arg1_arg2
The Fortran generic interface adds the ability to call any of the functions by the C++ function name:
interface use_default_arguments
module procedure use_default_arguments
module procedure use_default_arguments_arg1
module procedure use_default_arguments_arg1_arg2
end interface use_default_arguments
Usage:
real(C_DOUBLE) rv
rv = use_default_arguments()
rv = use_default_arguments(1.d0)
rv = use_default_arguments(1.d0, .false.)
Generic Real¶
C library function in clibrary.c:
void GenericReal(double arg)
{
global_double = arg;
return;
}
generic.yaml:
- decl: void GenericReal(double arg)
fortran_generic:
- decl: (float arg)
function_suffix: float
- decl: (double arg)
function_suffix: double
Fortran calls C via the following interface:
interface
subroutine c_generic_real(arg) &
bind(C, name="GenericReal")
use iso_c_binding, only : C_DOUBLE
implicit none
real(C_DOUBLE), value, intent(IN) :: arg
end subroutine c_generic_real
end interface
There is a single interface since there is a single C function. A generic interface is created for each declaration in the fortran_generic block.
interface generic_real
module procedure generic_real_float
module procedure generic_real_double
end interface generic_real
A Fortran wrapper is created for each declaration in the fortran_generic block.
The argument is explicitly converted to a C_DOUBLE before calling the C function
in generic_real_float. There is no conversion necessary in generic_real_double.
subroutine generic_real_float(arg)
use iso_c_binding, only : C_DOUBLE, C_FLOAT
real(C_FLOAT), value, intent(IN) :: arg
! splicer begin function.generic_real_float
call c_generic_real(real(arg, C_DOUBLE))
! splicer end function.generic_real_float
end subroutine generic_real_float
subroutine generic_real_double(arg)
use iso_c_binding, only : C_DOUBLE
real(C_DOUBLE), value, intent(IN) :: arg
! splicer begin function.generic_real_double
call c_generic_real(arg)
! splicer end function.generic_real_double
end subroutine generic_real_double
The function can be called via the generic interface with either type. If the specific function is called, the correct type must be passed.
call generic_real(0.0)
call generic_real(0.0d0)
call generic_real_float(0.0)
call generic_real_double(0.0d0)
In C, the compiler will promote the argument.
GenericReal(0.0f);
GenericReal(0.0);