This tutorial will walk through the steps required to create a Fortran or Python wrapper for a simple C++ library.


The simplest item to wrap is a function in the file tutorial.hpp:

namespace tutorial {
  void NoReturnNoArguments(void);

This is wrapped using a YAML input file tutorial.yaml:

library: Tutorial
cxx_header: tutorial.hpp

- decl: namespace tutorial
  - decl: void NoReturnNoArguments()

library is used to name output files and name the Fortran module. cxx_header is the name of a C++ header file which contains the declarations for functions to be wrapped. declarations is a sequence of mappings which describe the functions to wrap.

Process the file with Shroud:

% shroud tutorial.yaml
Wrote wrapTutorial.h
Wrote wrapTutorial.cpp
Wrote wrapftutorial.f

Wrote pyClass1type.cpp
Wrote pyTutorialmodule.hpp
Wrote pyTutorialmodule.cpp
Wrote pyTutorialutil.cpp

The C++ code to call the function:

#include "tutorial.hpp"

using namespace tutorial;

And the Fortran version:

use tutorial_mod
call no_return_no_arguments


rename module to just tutorial.

The generated code is listed at NoReturnNoArguments.


Integer and Real

Integer and real types are handled using the iso_c_binding module which match them directly to the corresponding types in C++. To wrap PassByValue:

double PassByValue(double arg1, int arg2)
    return arg1 + arg2;

Add the declaration to the YAML file:

- decl: double PassByValue(double arg1, int arg2)


use tutorial_mod
real(C_DOUBLE) result
result = pass_by_value(1.d0, 4)
import tutorial
result = tutorial.PassByValue(1.0, 4)

Pointer Functions

Functions which return a pointer will create a Fortran wrapper with the POINTER attribute:

- decl: int * ReturnIntPtrDim(int *len+intent(out)+hidden) +dimension(len)

The C++ routine returns a pointer to an array and the length of the array in argument len. The Fortran API does not need to pass the argument since the returned pointer will know its length. The hidden attribute will cause len to be omitted from the Fortran API, but still passed to the C API.

It can be used as:

integer(C_INT), pointer :: intp(:)

intp => return_int_ptr()

Pointer arguments

When a C++ routine accepts a pointer argument it may mean several things

  • output a scalar
  • input or output an array
  • pass-by-reference for a struct or class.

In this example, len and values are an input array and result is an output scalar:

void Sum(size_t len, const int *values, int *result)
    int sum = 0;
    for (size_t i=0; i < len; i++) {
      sum += values[i];
    *result = sum;

When this function is wrapped it is necessary to give some annotations in the YAML file to describe how the variables should be mapped to Fortran:

- decl: void Sum(size_t len  +implied(size(values)),
                 const int *values +rank(1),
                 int *result +intent(out))

In the BIND(C) interface only len uses the value attribute. Without the attribute Fortran defaults to pass-by-reference i.e. passes a pointer. The rank attribute defines the variable as a one dimensional, assumed-shape array. In the C interface this maps to an assumed-length array. C pointers, like assumed-length arrays, have no idea how many values they point to. This information is passed by the len argument.

The len argument defines the implied attribute. This argument is not part of the Fortran API since its presence is implied from the expression size(values). This uses the Fortran intrinsic size to compute the total number of elements in the array. It then passes this value to the C wrapper:

use tutorial_mod
integer(C_INT) result
call sum([1,2,3,4,5], result)
import tutorial
result = tutorial.Sum([1, 2, 3, 4, 5])

See example Sum for generated code.


Character variables have significant differences between C and Fortran. The Fortran interoperability with C feature treats a character variable of default kind as an array of character(kind=C_CHAR,len=1). The wrapper then deals with the C convention of NULL termination to Fortran’s blank filled.

C++ routine:

const std::string ConcatenateStrings(
    const std::string& arg1,
    const std::string& arg2)
    return arg1 + arg2;

YAML input:

- decl: const std::string ConcatenateStrings(
    const std::string& arg1,
    const std::string& arg2 )

The function is called as:

character(len=:), allocatable :: rv4c

rv4c = concatenate_strings("one", "two")


This function is just for demonstration purposes. Any reasonable person would just use the concatenation operator in Fortran.

Default Value Arguments

Each function with default value arguments will create a C and Fortran wrapper for each possible prototype. For Fortran, these functions are then wrapped in a generic statement which allows them to be called by the original name. A header files contains:

double UseDefaultArguments(double arg1 = 3.1415, bool arg2 = true)

and the function is defined as:

double UseDefaultArguments(double arg1, bool arg2)
    if (arg2) {
        return arg1 + 10.0;
    } else {
        return arg1;

Creating a wrapper for each possible way of calling the C++ function allows C++ to provide the default values:

- decl: double UseDefaultArguments(double arg1 = 3.1415, bool arg2 = true)
  -  _arg1
  -  _arg1_arg2

The default_arg_suffix provides a list of values of function_suffix for each possible set of arguments for the function. In this case 0, 1, or 2 arguments.

Fortran usage:

use tutorial_mod
print *, use_default_arguments()
print *, use_default_arguments(1.d0)
print *, use_default_arguments(1.d0, .false.)

Python usage:

>>> import tutorial
>>> tutorial.UseDefaultArguments()
>>> tutorial.UseDefaultArguments(1.0)
>>> tutorial.UseDefaultArguments(1.0, False)

The generated code is listed at UseDefaultArguments.


Fortran’s OPTIONAL attribute provides similar but different semantics. Creating wrappers for each set of arguments allows C++ to supply the default value. This is important when the default value does not map directly to Fortran. For example, bool type or when the default value is created by calling a C++ function.

Using the OPTIONAL keyword creates the possibility to call the C++ function in a way which is not supported by the C++ compilers. For example, function5(arg2=.false.)

Fortran has nothing similar to variadic functions.

Overloaded Functions

C++ allows function names to be overloaded. Fortran supports this by using a generic interface. The C and Fortran wrappers will generated a wrapper for each C++ function but must mangle the name to distinguish the names.


void OverloadedFunction(const std::string &name);
void OverloadedFunction(int indx);

By default the names are mangled by adding an index to the end. This can be controlled by setting function_suffix in the YAML file:

- decl: void OverloadedFunction(const std::string& name)
  function_suffix: _from_name
- decl: void OverloadedFunction(int indx)
  function_suffix: _from_index
call overloaded_function_from_name("name")
call overloaded_function_from_index(1)
call overloaded_function("name")
call overloaded_function(1)

Optional arguments and overloaded functions

Overloaded function that have optional arguments can also be wrapped:

- decl: int UseDefaultOverload(int num,
          int offset = 0, int stride = 1)
- decl: int UseDefaultOverload(double type, int num,
          int offset = 0, int stride = 1)

These routines can then be called as:

rv = use_default_overload(10)
rv = use_default_overload(1d0, 10)

rv = use_default_overload(10, 11, 12)
rv = use_default_overload(1d0, 10, 11, 12)


C++ template are handled by creating a wrapper for each instantiation of the function defined by the cxx_template field. The C and Fortran names are mangled by adding a type suffix to the function name.


template<typename ArgType>
void TemplateArgument(ArgType arg)


- decl: |
      template<typename ArgType>
      void TemplateArgument(ArgType arg)
  - instantiation: <int>
  - instantiation: <double>

Fortran usage:

call template_argument(1)
call template_argument(10.d0)

Python usage:


Likewise, the return type can be templated but in this case no interface block will be generated since generic function cannot vary only by return type.


template<typename RetType>
RetType TemplateReturn()
    return 0;


- decl: template<typename RetType> RetType TemplateReturn()
  - instantiation: <int>
  - instantiation: <double>

Fortran usage:

integer(C_INT) rv_integer
real(C_DOUBLE) rv_double
rv_integer = template_return_int()
rv_double = template_return_double()

Python usage:

rv_integer = TemplateReturn_int()
rv_double = TemplateReturn_double()

Generic Functions

C and C++ provide a type promotion feature when calling functions which Fortran does not support:

void FortranGeneric(double arg);


When FortranGeneric is wrapped in Fortran it may only be used with the correct arguments:

call fortran_generic(1.)
Error: Type mismatch in argument 'arg' at (1); passed REAL(4) to REAL(8)

It would be possible to create a version of the routine in C++ which accepts floats, but that would require changes to the library being wrapped. Instead it is possible to create a generic interface to the routine by defining which variables need their types changed. This is similar to templates in C++ but will only impact the Fortran wrapper. Instead of specify the Type which changes, you specify the argument which changes:

- decl: void FortranGeneric(double arg)
  - decl: (float arg)
    function_suffix: float
  - decl: (double arg)
    function_suffix: double

It may now be used with single or double precision arguments:

call fortran_generic(1.0)
call fortran_generic(1.0d0)

A full example is at GenericReal.



Sometimes a library will use a typedef to identify a specific use of a type:

typedef int TypeID;

int typefunc(TypeID arg);

Shroud must be told about user defined types in the YAML file:

- decl: typedef int TypeID;

This will map the C++ type TypeID to the predefined type int. The C wrapper will use int:

int TUT_typefunc(int arg)
    tutorial::TypeID SHC_rv = tutorial::typefunc(arg);
    return SHC_rv;


Enumeration types can also be supported by describing the type to shroud. For example:

namespace tutorial

enum EnumTypeID {

EnumTypeID enumfunc(EnumTypeID arg);

} /* end namespace tutorial */

This enumeration is within a namespace so it is not available to C. For C and Fortran the type can be describe as an int similar to how the typedef is defined. But in addition we describe how to convert between C and C++:

- decl: typedef int EnumTypeID
    c_to_cxx : static_cast<tutorial::EnumTypeID>({c_var})
    cxx_to_c : static_cast<int>({cxx_var})

The typename must be fully qualified (use tutorial::EnumTypeId instead of EnumTypeId). The C argument is explicitly converted to a C++ type, then the return type is explicitly converted to a C type in the generated wrapper:

int TUT_enumfunc(int arg)
    tutorial::EnumTypeID SHCXX_arg = static_cast<tutorial::EnumTypeID>(arg);
    tutorial::EnumTypeID SHCXX_rv = tutorial::enumfunc(SHCXX_arg);
    int SHC_rv = static_cast<int>(SHCXX_rv);
    return SHC_rv;

Without the explicit conversion you’re likely to get an error such as:

error: invalid conversion from ‘int’ to ‘tutorial::EnumTypeID’

A enum can also be fully defined to Fortran:

- decl: |
      enum Color {

In this case the type is implicitly defined so there is no need to add it to the types list. The C header duplicates the enumeration, but within an extern "C" block:

//  tutorial::Color
enum TUT_Color {

Fortran creates integer parameters for each value:

!  enum tutorial::Color
integer(C_INT), parameter :: tutorial_color_red = 0
integer(C_INT), parameter :: tutorial_color_blue = 1
integer(C_INT), parameter :: tutorial_color_white = 2


Fortran’s ENUM, BIND(C) provides a way of matching the size and values of enumerations. However, it doesn’t seem to buy you too much in this case. Defining enumeration values as INTEGER, PARAMETER seems more straightforward.


A structure in C++ can be mapped directly to a Fortran derived type using the bind(C) attribute provided by Fortran 2003. For example, the C++ code:

struct struct1 {
  int ifield;
  double dfield;

can be defined to Shroud with the YAML input:

- decl: |
    struct struct1 {
      int ifield;
      double dfield;

This will generate a C struct which is compatible with C++:

struct s_TUT_struct1 {
    int ifield;
    double dfield;
typedef struct s_TUT_struct1 TUT_struct1;

A C++ struct is compatible with C; however, its name may not be accessible to C since it may be defined within a namespace. By creating an identical struct in the C wrapper, we’re guaranteed visibility for the C API.


All fields must be defined in the YAML file in order to ensure that sizeof operator will return the same value for the C and C++ structs.

This will generate a Fortran derived type which is compatible with C++:

type, bind(C) :: struct1
    integer(C_INT) :: ifield
    real(C_DOUBLE) :: dfield
end type struct1

A function which returns a struct value can have its value copied into a Fortran variable where the fields can be accessed directly by Fortran. A C++ function which initialized a struct can be written as:

- decl: struct1 returnStructByValue(int i, double d);

The C wrapper casts the C++ struct to the C struct by using pointers to the struct then returns the value by dereferencing the C struct pointer.

TUT_struct1 TUT_return_struct_by_value(int i, double d)
    Cstruct1 SHCXX_rv = returnStructByValue(i, d);
    TUT_cstruct1 * SHC_rv = static_cast<TUT_cstruct1 *>(
        static_cast<void *>(&SHCXX_rv));
    return *SHC_rv;

This function can be called directly by Fortran using the generated interface:

function return_struct_by_value(i, d) &
        result(SHT_rv) &
        bind(C, name="TUT_return_struct_by_value")
    use iso_c_binding, only : C_DOUBLE, C_INT
    import :: struct1
    implicit none
    integer(C_INT), value, intent(IN) :: i
    real(C_DOUBLE), value, intent(IN) :: d
    type(struct1) :: SHT_rv
end function return_struct

To use the function:

type(struct1) var

var = return_struct(1, 2.5)
print *, var%ifield, var%dfield


Each class is wrapped in a Fortran derived type which shadows the C++ class by holding a type(C_PTR) pointer to an C++ instance. Class methods are wrapped using Fortran’s type-bound procedures. This makes Fortran usage very similar to C++.

Now we’ll add a simple class to the library:

class Class1
    void Method1() {};

To wrap the class add the lines to the YAML file:

- decl: class Class1
  - decl: Class1()  +name(new)
      function_suffix: _default
  - decl: ~Class1() +name(delete)
  - decl: int Method1()

The constructor and destructor have no method name associated with them. They default to ctor and dtor. The names can be overridden by supplying the +name annotation. These declarations will create wrappers over the new and delete C++ keywords.

The C++ code to call the function:

#include <tutorial.hpp>
tutorial::Class1 *cptr = new tutorial::Class1();


And the Fortran version:

use tutorial_mod
type(class1) cptr

cptr = class1_new()
call cptr%method1

Python usage:

import tutorial
obj = tutorial.Class1()

Class static methods

Class static methods are supported using the NOPASS keyword in Fortran. To wrap the method:

class Singleton {
    static Singleton& getReference();

Use the YAML input:

- decl: class Singleton
  - decl: static Singleton& getReference()

Called from Fortran as:

type(singleton) obj0
obj0 = obj0%get_reference()

Note that obj0 is not assigned a value before the function get_reference is called.