Types

Numeric Types

The numeric types usually require no conversion. In this case the type map is mainly used to generate declaration code for wrappers:

type: int
fields:
    c_type: int
    cxx_type: int
    f_type: integer(C_INT)
    f_kind: C_INT
    f_module:
        iso_c_binding:
        - C_INT
    f_cast: int({f_var}, C_INT)

One case where a conversion is required is when the Fortran argument is one type and the C++ argument is another. This may happen when an overloaded function is generated so that a C_INT or C_LONG argument may be passed to a C++ function function expecting a long. The f_cast field is used to convert the argument to the type expected by the C++ function.

Bool

The first thing to notice is that f_c_type is defined. This is the type used in the Fortran interface for the C wrapper. The type is logical(C_BOOL) while f_type, the type of the Fortran wrapper argument, is logical.

The f_statements section describes code to add into the Fortran wrapper to perform the conversion. c_var and f_var default to the same value as the argument name. By setting c_local_var, a local variable is generated for the call to the C wrapper. It will be named SH_{f_var}.

There is no Fortran intrinsic function to convert between default logical and logical(C_BOOL). The pre_call and post_call sections will insert an assignment statement to allow the compiler to do the conversion.

If a function returns a bool result then a wrapper is always needed to convert the result. The result section sets need_wrapper to force the wrapper to be created. By default a function with no argument would not need a wrapper since there will be no pre_call or post_call code blocks. Only the C interface would be required since Fortran could call the C function directly.

See example checkBool.

Char

Any C++ function which has char or std::string arguments or result will create an additional C function which include additional arguments for the length of the strings. Most Fortran compiler use this convention when passing CHARACTER arguments. Shroud makes this convention explicit for two reasons:

  • It allows an interface to be used. Functions with an interface will not pass the hidden, non-standard length argument, depending on compiler.
  • Returning character argument from C to Fortran is non-portable.

The C wrapper will create a NULL terminated copy a string with the intent(in) attribute. The assumption is that the trailing blanks are not part of the data but only padding. Return values and intent(out) arguments add a len annotation with the assumption that the wrapper will copy the result and blank fill the argument so it need to know the declared length.

A buffer for intent(out) arguments is also create which is one longer than the Fortran string length. This allows space for a C terminating NULL. This buffer is passed to the C library which will copy into it. Upon return, the buffer is copied and blank filled into the user’s argument and the intermediate buffer released.

Library functions which return a scalar char have a wrapper generated which pass a char * argument to the C wrapper where the first element is assigned ( *arg a.k.a arg[0]). Returning a char proved to be non-portable while passing the result by reference works on the tested compilers.

The bufferify function will be named the same as the original function with the option C_bufferify_suffix appended to the end. The Fortran wrapper will use the original function name, but call the C wrapper which accepts the length arguments.

Python wrappers may need an additional attribute for intent(out) strings to let Shroud know how much space to pass to the function. A function may pass a char * argument which the C library will copy into. While this is not a recommened practice since it’s easy to overwrite memory, Shoud can deal with it by setting the +charlen(n) attribute where n is the number of character in the array passed to the function. This is required for Python since strings are inmutable. The buffer will be converted into a Python str object then returned to the user. This is not an issue in Fortran since the output buffer is passed in by the caller and will have a known size.

By default, a Fortran blank input string will be converted to an empty string before being passed to the C library. i.e. " " in Fortran is converted to '\0' in C. This behavior can be changed to convert the empty string into a NULL pointer by setting the +blanknull attribute. This is often more natural for the C library to indicate the absence of a value. The option F_blanknull can be used to make this the default for all const char * arguments.

On some occasions the copy and null terminate behavior is not wanted. For example, to avoid copying a large buffer or the memory must be operated on directly. In this case using the attribute +api(capi) will use the native C API instead of the bufferify API for the argument. The library will need some way to determine the length of the string since it will not be passed to the C wrapper. As an alternative the bufferify function can be avoided altogether by setting the F_create_bufferify_function option to false.

The character type maps use the c_statements section to define code which will be inserted into the C wrapper. These actions vary depending on the intent of in, out, inout and result.

MPI_Comm

MPI_Comm is provided by Shroud and serves as an example of how to wrap a non-native type. MPI provides a Fortran interface and the ability to convert MPI_comm between Fortran and C. The type map tells Shroud how to use these routines:

type: MPI_Comm
fields:
    cxx_type: MPI_Comm
    c_header: mpi.h
    c_type: MPI_Fint
    f_type: integer
    f_kind: C_INT
    f_c_type: integer(C_INT)
    f_c_module:
        iso_c_binding:
          - C_INT
    cxx_to_c: MPI_Comm_c2f({cxx_var})
    c_to_cxx: MPI_Comm_f2c({c_var})

This mapping makes the assumption that integer and integer(C_INT) are the same type.