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_no_return_no_arguments()
{
    // splicer begin function.no_return_no_arguments
    tutorial::NoReturnNoArguments();
    return;
    // splicer end function.no_return_no_arguments
}

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, 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 +dimension(:)+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:

void POI_sum(int len, int * values, int * result)
{
    // splicer begin function.sum
    Sum(len, values, result);
    return;
    // splicer end function.sum
}

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:

subroutine sum(values, result)
    use iso_c_binding, only : C_INT
    integer(C_INT) :: SH_len
    integer(C_INT), intent(IN) :: values(:)
    integer(C_INT), intent(OUT) :: result
    ! splicer begin function.sum
    SH_len = size(values,kind=C_INT)
    call c_sum(SH_len, values, result)
    ! splicer end function.sum
end subroutine sum

Example usage:

integer(C_INT) rv_int
call sum([1,2,3,4,5], rv_int)
call assert_true(rv_int .eq. 15, "sum")

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_get_min_max(int * min, int * max)
{
    // splicer begin function.get_min_max
    tutorial::getMinMax(*min, *max);
    return;
    // splicer end function.get_min_max
}

Fortran calls C via the following interface:

interface
    subroutine get_min_max(min, max) &
            bind(C, name="TUT_get_min_max")
        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")

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) SH_arg1
    logical(C_BOOL) SH_arg2
    logical(C_BOOL) SH_arg3
    SH_arg1 = arg1  ! coerce to C_BOOL
    SH_arg3 = arg3  ! coerce to C_BOOL
    call c_check_bool(SH_arg1, SH_arg2, SH_arg3)
    arg2 = SH_arg2  ! coerce to logical
    arg3 = SH_arg3  ! 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_return_one_name_bufferify(char * name1, int Nname1)
{
    // splicer begin function.return_one_name_bufferify
    returnOneName(name1);
    ShroudStrBlankFill(name1, Nname1);
    return;
    // splicer end function.return_one_name_bufferify
}

Fortran calls C via the following interface:

interface
    subroutine c_return_one_name_bufferify(name1, Nname1) &
            bind(C, name="CLI_return_one_name_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) :: Nname1
    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
    call c_return_one_name_bufferify(name1, len(name1, kind=C_INT))
    ! 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 strings.cpp:

void passCharPtr(char *dest, const char *src)
{
    std::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 STR_pass_char_ptr(char * dest, const char * src)
{
    // splicer begin function.pass_char_ptr
    passCharPtr(dest, src);
    return;
    // splicer end function.pass_char_ptr
}

The C wrapper:

void STR_pass_char_ptr_bufferify(char * dest, int Ndest,
    const char * src)
{
    // splicer begin function.pass_char_ptr_bufferify
    passCharPtr(dest, src);
    ShroudStrBlankFill(dest, Ndest);
    return;
    // splicer end function.pass_char_ptr_bufferify
}

Fortran calls C via the following interface:

interface
    subroutine c_pass_char_ptr(dest, src) &
            bind(C, name="STR_pass_char_ptr")
        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, Ndest, src) &
            bind(C, name="STR_pass_char_ptr_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) :: Ndest
        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
    call c_pass_char_ptr_bufferify(dest, len(dest, kind=C_INT), &
        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_implied_text_len_bufferify(char * text, int Ntext, int ltext)
{
    // splicer begin function.implied_text_len_bufferify
    ImpliedTextLen(text, ltext);
    ShroudStrBlankFill(text, Ntext);
    return;
    // splicer end function.implied_text_len_bufferify
}

Fortran calls C via the following interface:

interface
    subroutine c_implied_text_len_bufferify(text, Ntext, ltext) &
            bind(C, name="CLI_implied_text_len_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) :: Ntext
        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
    SH_ltext = len(text,kind=C_INT)
    call c_implied_text_len_bufferify(text, len(text, kind=C_INT), &
        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)

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_accept_string_reference(char * arg1)
{
    // splicer begin function.accept_string_reference
    std::string SHCXX_arg1(arg1);
    acceptStringReference(SHCXX_arg1);
    strcpy(arg1, SHCXX_arg1.c_str());
    return;
    // splicer end function.accept_string_reference
}

The C wrapper:

void STR_accept_string_reference_bufferify(char * arg1, int Larg1,
    int Narg1)
{
    // splicer begin function.accept_string_reference_bufferify
    std::string SHCXX_arg1(arg1, Larg1);
    acceptStringReference(SHCXX_arg1);
    ShroudStrCopy(arg1, Narg1, SHCXX_arg1.data(), SHCXX_arg1.size());
    return;
    // splicer end function.accept_string_reference_bufferify
}

An interface for the native C function is also created:

interface
    subroutine c_accept_string_reference(arg1) &
            bind(C, name="STR_accept_string_reference")
        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, Larg1, &
            Narg1) &
            bind(C, name="STR_accept_string_reference_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) :: Larg1
        integer(C_INT), value, intent(IN) :: Narg1
    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
    call c_accept_string_reference_bufferify(arg1, &
        len_trim(arg1, kind=C_INT), len(arg1, kind=C_INT))
    ! 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

getCharPtr1

Return a pointer and convert into an ALLOCATABLE CHARACTER variable. The Fortran application is responsible to release the memory. However, this may be done automatically by the Fortran runtime.

C++ library function in strings.cpp:

const char * getCharPtr1()
{
    return static_char;
}

strings.yaml:

- decl: const char * getCharPtr1()

The C wrapper copies all of the metadata into a SHROUD_array struct which is used by the Fortran wrapper:

void STR_get_char_ptr1_bufferify(STR_SHROUD_array *DSHF_rv)
{
    // splicer begin function.get_char_ptr1_bufferify
    const char * SHC_rv = getCharPtr1();
    DSHF_rv->cxx.addr = static_cast<void *>(const_cast<char *>(SHC_rv));
    DSHF_rv->cxx.idtor = 0;
    DSHF_rv->addr.ccharp = SHC_rv;
    DSHF_rv->type = SH_TYPE_OTHER;
    DSHF_rv->elem_len = SHC_rv == NULL ? 0 : std::strlen(SHC_rv);
    DSHF_rv->size = 1;
    return;
    // splicer end function.get_char_ptr1_bufferify
}

Fortran calls C via the following interface:

interface
    subroutine c_get_char_ptr1_bufferify(DSHF_rv) &
            bind(C, name="STR_get_char_ptr1_bufferify")
        import :: SHROUD_array
        implicit none
        type(SHROUD_array), intent(OUT) :: DSHF_rv
    end subroutine c_get_char_ptr1_bufferify
end interface

The Fortran wrapper uses the metadata in DSHF_rv 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_char_ptr1() &
        result(SHT_rv)
    type(SHROUD_array) :: DSHF_rv
    character(len=:), allocatable :: SHT_rv
    ! splicer begin function.get_char_ptr1
    call c_get_char_ptr1_bufferify(DSHF_rv)
    allocate(character(len=DSHF_rv%elem_len):: SHT_rv)
    call SHROUD_copy_string_and_free(DSHF_rv, SHT_rv, DSHF_rv%elem_len)
    ! splicer end function.get_char_ptr1
end function get_char_ptr1

Fortran usage:

character(len=:), allocatable :: str
str = get_char_ptr1()

getCharPtr2

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 strings.cpp:

const char * getCharPtr2()
{
    return static_char;
}

strings.yaml:

- decl: const char * getCharPtr2() +len(30)

The C wrapper:

void STR_get_char_ptr2_bufferify(char * SHF_rv, int NSHF_rv)
{
    // splicer begin function.get_char_ptr2_bufferify
    const char * SHC_rv = getCharPtr2();
    ShroudStrCopy(SHF_rv, NSHF_rv, SHC_rv, -1);
    return;
    // splicer end function.get_char_ptr2_bufferify
}

Fortran calls C via the following interface:

interface
    subroutine c_get_char_ptr2_bufferify(SHF_rv, NSHF_rv) &
            bind(C, name="STR_get_char_ptr2_bufferify")
        use iso_c_binding, only : C_CHAR, C_INT
        implicit none
        character(kind=C_CHAR), intent(OUT) :: SHF_rv(*)
        integer(C_INT), value, intent(IN) :: NSHF_rv
    end subroutine c_get_char_ptr2_bufferify
end interface

The Fortran wrapper:

function get_char_ptr2() &
        result(SHT_rv)
    use iso_c_binding, only : C_INT
    character(len=30) :: SHT_rv
    ! splicer begin function.get_char_ptr2
    call c_get_char_ptr2_bufferify(SHT_rv, len(SHT_rv, kind=C_INT))
    ! splicer end function.get_char_ptr2
end function get_char_ptr2

Fortran usage:

character(30) str
str = get_char_ptr2()

getCharPtr3

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 F_string_result_as_arg format string.

C++ library function in strings.cpp:

const char * getCharPtr3()
{
    return static_char;
}

strings.yaml:

- decl: const char * getCharPtr3()
  format:
    F_string_result_as_arg: output

The C wrapper:

void STR_get_char_ptr3_bufferify(char * output, int Noutput)
{
    // splicer begin function.get_char_ptr3_bufferify
    const char * SHC_rv = getCharPtr3();
    ShroudStrCopy(output, Noutput, SHC_rv, -1);
    return;
    // splicer end function.get_char_ptr3_bufferify
}

Fortran calls C via the following interface:

interface
    subroutine c_get_char_ptr3_bufferify(output, Noutput) &
            bind(C, name="STR_get_char_ptr3_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_char_ptr3_bufferify
end interface

The Fortran wrapper:

subroutine get_char_ptr3(output)
    use iso_c_binding, only : C_INT
    character(len=*), intent(OUT) :: output
    ! splicer begin function.get_char_ptr3
    call c_get_char_ptr3_bufferify(output, len(output, kind=C_INT))
    ! splicer end function.get_char_ptr3
end subroutine get_char_ptr3

Fortran usage:

character(30) str
call get_char_ptrs(str)

string functions

getConstStringRefPure

C++ library function in strings.cpp:

const std::string& getConstStringRefPure()
{
    return static_str;
}

strings.yaml:

- decl: const string& getConstStringRefPure()

The C wrapper:

void STR_get_const_string_ref_pure_bufferify(STR_SHROUD_array *DSHF_rv)
{
    // splicer begin function.get_const_string_ref_pure_bufferify
    const std::string & SHCXX_rv = getConstStringRefPure();
    ShroudStrToArray(DSHF_rv, &SHCXX_rv, 0);
    return;
    // splicer end function.get_const_string_ref_pure_bufferify
}

The native C wrapper:

const char * STR_get_const_string_ref_pure()
{
    // splicer begin function.get_const_string_ref_pure
    const std::string & SHCXX_rv = getConstStringRefPure();
    const char * SHC_rv = SHCXX_rv.c_str();
    return SHC_rv;
    // splicer end function.get_const_string_ref_pure
}

Fortran calls C via the following interface:

interface
    subroutine c_get_const_string_ref_pure_bufferify(DSHF_rv) &
            bind(C, name="STR_get_const_string_ref_pure_bufferify")
        import :: SHROUD_array
        implicit none
        type(SHROUD_array), intent(OUT) :: DSHF_rv
    end subroutine c_get_const_string_ref_pure_bufferify
end interface

The Fortran wrapper:

function get_const_string_ref_pure() &
        result(SHT_rv)
    type(SHROUD_array) :: DSHF_rv
    character(len=:), allocatable :: SHT_rv
    ! splicer begin function.get_const_string_ref_pure
    call c_get_const_string_ref_pure_bufferify(DSHF_rv)
    allocate(character(len=DSHF_rv%elem_len):: SHT_rv)
    call SHROUD_copy_string_and_free(DSHF_rv, SHT_rv, DSHF_rv%elem_len)
    ! splicer end function.get_const_string_ref_pure
end function get_const_string_ref_pure

Fortran usage:

str = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
str = get_const_string_ref_pure()
call assert_true( str == static_str, "getConstStringRefPure")

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_bufferify(const int * arg, long Sarg)
{
    // splicer begin function.vector_sum_bufferify
    const std::vector<int> SHCXX_arg(arg, arg + Sarg);
    int SHC_rv = vector_sum(SHCXX_arg);
    return SHC_rv;
    // splicer end function.vector_sum_bufferify
}

Fortran calls C via the following interface:

interface
    function c_vector_sum_bufferify(arg, Sarg) &
            result(SHT_rv) &
            bind(C, name="VEC_vector_sum_bufferify")
        use iso_c_binding, only : C_INT, C_LONG
        implicit none
        integer(C_INT), intent(IN) :: arg(*)
        integer(C_LONG), value, intent(IN) :: Sarg
        integer(C_INT) :: SHT_rv
    end function c_vector_sum_bufferify
end interface

The Fortran wrapper:

function vector_sum(arg) &
        result(SHT_rv)
    use iso_c_binding, only : C_INT, C_LONG
    integer(C_INT), intent(IN) :: arg(:)
    integer(C_INT) :: SHT_rv
    ! splicer begin function.vector_sum
    SHT_rv = c_vector_sum_bufferify(arg, size(arg, kind=C_LONG))
    ! 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 *Darg)
{
    // splicer begin function.vector_iota_out_bufferify
    std::vector<int> *SHCXX_arg = new std::vector<int>;
    vector_iota_out(*SHCXX_arg);
    Darg->cxx.addr  = static_cast<void *>(SHCXX_arg);
    Darg->cxx.idtor = 1;
    Darg->addr.base = SHCXX_arg->empty() ? NULL : &SHCXX_arg->front();
    Darg->type = SH_TYPE_INT;
    Darg->elem_len = sizeof(int);
    Darg->size = SHCXX_arg->size();
    return;
    // splicer end function.vector_iota_out_bufferify
}

Fortran calls C via the following interface:

interface
    subroutine c_vector_iota_out_bufferify(Darg) &
            bind(C, name="VEC_vector_iota_out_bufferify")
        import :: SHROUD_array
        implicit none
        type(SHROUD_array), intent(INOUT) :: Darg
    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_SIZE_T
    integer(C_INT), intent(OUT) :: arg(:)
    type(SHROUD_array) :: Darg
    ! splicer begin function.vector_iota_out
    call c_vector_iota_out_bufferify(Darg)
    call SHROUD_copy_array_int(Darg, 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.

// 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->addr.base;
    int n = c_var_size < data->size ? c_var_size : data->size;
    n *= data->elem_len;
    std::memcpy(c_var, cxx_var, n);
    VEC_SHROUD_memory_destructor(&data->cxx); // delete data->cxx.addr
}

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 = NULL;
    cap->idtor = 0;  // avoid deleting again
}

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 *Darg)
{
    // splicer begin function.vector_iota_out_alloc_bufferify
    std::vector<int> *SHCXX_arg = new std::vector<int>;
    vector_iota_out_alloc(*SHCXX_arg);
    Darg->cxx.addr  = static_cast<void *>(SHCXX_arg);
    Darg->cxx.idtor = 1;
    Darg->addr.base = SHCXX_arg->empty() ? NULL : &SHCXX_arg->front();
    Darg->type = SH_TYPE_INT;
    Darg->elem_len = sizeof(int);
    Darg->size = SHCXX_arg->size();
    return;
    // splicer end function.vector_iota_out_alloc_bufferify
}

Fortran calls C via the following interface:

interface
    subroutine c_vector_iota_out_alloc_bufferify(Darg) &
            bind(C, name="VEC_vector_iota_out_alloc_bufferify")
        import :: SHROUD_array
        implicit none
        type(SHROUD_array), intent(INOUT) :: Darg
    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_SIZE_T
    integer(C_INT), intent(OUT), allocatable :: arg(:)
    type(SHROUD_array) :: Darg
    ! splicer begin function.vector_iota_out_alloc
    call c_vector_iota_out_alloc_bufferify(Darg)
    allocate(arg(Darg%size))
    call SHROUD_copy_array_int(Darg, 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, long Sarg,
    VEC_SHROUD_array *Darg)
{
    // splicer begin function.vector_iota_inout_alloc_bufferify
    std::vector<int> *SHCXX_arg = new std::vector<int>(arg, arg + Sarg);
    vector_iota_inout_alloc(*SHCXX_arg);
    Darg->cxx.addr  = static_cast<void *>(SHCXX_arg);
    Darg->cxx.idtor = 1;
    Darg->addr.base = SHCXX_arg->empty() ? NULL : &SHCXX_arg->front();
    Darg->type = SH_TYPE_INT;
    Darg->elem_len = sizeof(int);
    Darg->size = SHCXX_arg->size();
    return;
    // splicer end function.vector_iota_inout_alloc_bufferify
}

Fortran calls C via the following interface:

interface
    subroutine c_vector_iota_inout_alloc_bufferify(arg, Sarg, Darg) &
            bind(C, name="VEC_vector_iota_inout_alloc_bufferify")
        use iso_c_binding, only : C_INT, C_LONG
        import :: SHROUD_array
        implicit none
        integer(C_INT), intent(INOUT) :: arg(*)
        integer(C_LONG), value, intent(IN) :: Sarg
        type(SHROUD_array), intent(INOUT) :: Darg
    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_LONG, C_SIZE_T
    integer(C_INT), intent(INOUT), allocatable :: arg(:)
    type(SHROUD_array) :: Darg
    ! splicer begin function.vector_iota_inout_alloc
    call c_vector_iota_inout_alloc_bufferify(arg, &
        size(arg, kind=C_LONG), Darg)
    if (allocated(arg)) deallocate(arg)
    allocate(arg(Darg%size))
    call SHROUD_copy_array_int(Darg, 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(*) :: 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.

passAssumedTypeDim

C library function in clibrary.c:

void passAssumedTypeDim(void *arg)
{
    strncpy(last_function_called, "passAssumedTypeDim", MAXLAST);
}

clibrary.yaml:

- decl: int passAssumedTypeDim(void *arg+assumedtype+dimension)

Fortran calls C via the following interface:

interface
    subroutine pass_assumed_type_dim(arg) &
            bind(C, name="passAssumedTypeDim")
        implicit none
        type(*) :: arg(*)
    end subroutine pass_assumed_type_dim
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_dim(int_array)
call pass_assumed_type_dim(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) bind(C)
        use iso_c_binding, only : C_INT
        implicit none
        integer(C_INT), value :: arg0
        integer(C_INT) :: callback1_incr
    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

callback1c

C library function in clibrary.c:

void callback1(int type, void (*incr)(void))
{
  // Use type to decide how to call incr
}

clibrary.yaml:

- decl: int callback1(int type, void (*incr)()+external)

Creates the abstract interface:

abstract interface
    subroutine callback1_incr() bind(C)
        implicit none
    end subroutine callback1_incr
end interface

Fortran calls C via the following interface:

interface
    function c_callback1(type, incr) &
            result(SHT_rv) &
            bind(C, name="callback1")
        use iso_c_binding, only : C_INT
        import :: callback1_incr
        implicit none
        integer(C_INT), value, intent(IN) :: type
        procedure(callback1_incr) :: incr
        integer(C_INT) :: SHT_rv
    end function c_callback1
end interface

The Fortran wrapper. By using external no abstract interface is used:

function callback1(type, incr) &
        result(SHT_rv)
    use iso_c_binding, only : C_INT
    integer(C_INT), value, intent(IN) :: type
    external :: incr
    integer(C_INT) :: SHT_rv
    ! splicer begin function.callback1
    SHT_rv = c_callback1(type, incr)
    ! splicer end function.callback1
end function callback1

Fortran usage:

module worker
  use iso_c_binding
contains
  subroutine userincr_int(i) bind(C)
    integer(C_INT), value :: i
    ! do work of callback
  end subroutine user_int

  subroutine userincr_double(i) bind(C)
    real(C_DOUBLE), value :: i
    ! do work of callback
  end subroutine user_int

  subroutine work
    call callback1c(1, userincr_int)
    call callback1c(1, userincr_double)
  end subrouine work
end module worker

Struct

Struct creating is described in Fortran Structs.

passStruct1

C library function in struct.c:

int passStruct1(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()         +name(new)
    format:
      function_suffix: _default
  - decl: Class1(int flag) +name(new)
    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_new_default(CLA_Class1 * SHC_rv)
{
    // splicer begin class.Class1.method.new_default
    classes::Class1 *SHCXX_rv = new classes::Class1();
    SHC_rv->addr = static_cast<void *>(SHCXX_rv);
    SHC_rv->idtor = 0;
    return SHC_rv;
    // splicer end class.Class1.method.new_default
}
CLA_Class1 * CLA_Class1_new_flag(int flag, CLA_Class1 * SHC_rv)
{
    // splicer begin class.Class1.method.new_flag
    classes::Class1 *SHCXX_rv = new classes::Class1(flag);
    SHC_rv->addr = static_cast<void *>(SHCXX_rv);
    SHC_rv->idtor = 0;
    return SHC_rv;
    // splicer end class.Class1.method.new_flag
}
void CLA_Class1_delete(CLA_Class1 * self)
{
    classes::Class1 *SH_this =
        static_cast<classes::Class1 *>(self->addr);
    // splicer begin class.Class1.method.delete
    delete SH_this;
    self->addr = NULL;
    return;
    // splicer end class.Class1.method.delete
}

The corresponding Fortran interfaces:

interface
    function c_class1_new_default(SHT_crv) &
            result(SHT_rv) &
            bind(C, name="CLA_Class1_new_default")
        use iso_c_binding, only : C_PTR
        import :: SHROUD_class1_capsule
        implicit none
        type(SHROUD_class1_capsule), intent(OUT) :: SHT_crv
        type(C_PTR) SHT_rv
    end function c_class1_new_default
end interface
interface
    function c_class1_new_flag(flag, SHT_crv) &
            result(SHT_rv) &
            bind(C, name="CLA_Class1_new_flag")
        use iso_c_binding, only : C_INT, C_PTR
        import :: SHROUD_class1_capsule
        implicit none
        integer(C_INT), value, intent(IN) :: flag
        type(SHROUD_class1_capsule), intent(OUT) :: SHT_crv
        type(C_PTR) SHT_rv
    end function c_class1_new_flag
end interface
interface
    subroutine c_class1_delete(self) &
            bind(C, name="CLA_Class1_delete")
        import :: SHROUD_class1_capsule
        implicit none
        type(SHROUD_class1_capsule), intent(IN) :: self
    end subroutine c_class1_delete
end interface

And the Fortran wrappers:

function class1_new_default() &
        result(SHT_rv)
    use iso_c_binding, only : C_PTR
    type(class1) :: SHT_rv
    ! splicer begin class.Class1.method.new_default
    type(C_PTR) :: SHT_prv
    SHT_prv = c_class1_new_default(SHT_rv%cxxmem)
    ! splicer end class.Class1.method.new_default
end function class1_new_default
function class1_new_flag(flag) &
        result(SHT_rv)
    use iso_c_binding, only : C_INT, C_PTR
    integer(C_INT), value, intent(IN) :: flag
    type(class1) :: SHT_rv
    ! splicer begin class.Class1.method.new_flag
    type(C_PTR) :: SHT_prv
    SHT_prv = c_class1_new_flag(flag, SHT_rv%cxxmem)
    ! splicer end class.Class1.method.new_flag
end function class1_new_flag
subroutine class1_delete(obj)
    class(class1) :: 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_new
    module procedure class1_new_default
    module procedure class1_new_flag
end interface class1_new

A class instance is created and destroy from Fortran as:

use classes_mod
type(class1) obj

obj = class1_new()
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;
    return;
    // 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 :: SHROUD_class1_capsule
        implicit none
        type(SHROUD_class1_capsule), 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 :: SHROUD_class1_capsule
        implicit none
        type(SHROUD_class1_capsule), 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 :: SHROUD_class1_capsule
        implicit none
        type(SHROUD_class1_capsule), 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) :: 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) :: 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) :: 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_new()
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;

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_use_default_arguments()
{
    // splicer begin function.use_default_arguments
    double SHC_rv = tutorial::UseDefaultArguments();
    return SHC_rv;
    // splicer end function.use_default_arguments
}
double TUT_use_default_arguments_arg1(double arg1)
{
    // splicer begin function.use_default_arguments_arg1
    double SHC_rv = tutorial::UseDefaultArguments(arg1);
    return SHC_rv;
    // splicer end function.use_default_arguments_arg1
}
double TUT_use_default_arguments_arg1_arg2(double arg1, bool arg2)
{
    // splicer begin function.use_default_arguments_arg1_arg2
    double SHC_rv = tutorial::UseDefaultArguments(arg1, arg2);
    return SHC_rv;
    // splicer end function.use_default_arguments_arg1_arg2
}

This creates three corresponding Fortran interfaces:

interface
    function c_use_default_arguments() &
            result(SHT_rv) &
            bind(C, name="TUT_use_default_arguments")
        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_use_default_arguments_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_use_default_arguments_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) SH_arg2
    SH_arg2 = arg2  ! coerce to C_BOOL
    SHT_rv = c_use_default_arguments_arg1_arg2(arg1, SH_arg2)
    ! 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);