Functions¶
What files are created¶
Shroud will create multiple output file which must be compiled with C++ or Fortran compilers.
One C++ file will be created for the library and one file for each C++ class.
Fortran creates a file for the library and one per additional namespace. Since Fortran does not support forward referencing of derived types, it is necessary to add all classes from a namespace into a single module.
Each Fortran file will only contain one module to make it easier to create makefile dependencies using pattern rules:
%.o %.mod : %.f
File names for the header and implementation files can be set explicitly by setting variables in the format of the global or class scope:
format:
C_header_filename: top.h
C_impl_filename: top.cpp
F_impl_filename: top.f
declarations:
- decl: class Names
format:
C_header_filename: foo.h
C_impl_filename: foo.cpp
F_impl_filename: foo.f
The default file names are controlled by global options. The option values can be changed to avoid setting the name for each class file explicitly. It’s also possible to change just the suffix of files:
options:
YAML_type_filename_template: {library_lower}_types.yaml
C_header_filename_suffix: h
C_impl_filename_suffix: cpp
C_header_filename_library_template: wrap{library}.{C_header_filename_suffix}
C_impl_filename_library_template: wrap{library}.{C_impl_filename_suffix}
C_header_filename_namespace_template: wrap{file_scope}.{C_header_file_suffix}
C_impl_filename_namespace_template: wrap{file_scope}.{C_impl_filename_suffix}
C_header_filename_class_template: wrap{cxx_class}.{C_header_file_suffix}
C_impl_filename_class_template: wrap{cxx_class}.{C_impl_filename_suffix}
F_filename_suffix: f
F_impl_filename_library_template: wrapf{library_lower}.{F_filename_suffix}
F_impl_filename_namespace_template: wrapf{file_scope}.{F_filename_suffix}
How names are created¶
Shroud attempts to provide user control of names while providing reasonable defaults. Each name is based on the library, class, function or argument name in the current scope. Most names have a template which may be used to control how the names are generated on a global scale. Many names may also be explicitly specified by a field.
For example, a library has an initialize
function which is
in a namespace. In C++ it is called as:
#include "library.hpp"
library::initialize()
By default this will be a function in a Fortran module and can be called as:
use library
call initialize
Since initialize
is a rather common name for a function, it may
be desirable to rename the Fortran wrapper to something more specific.
The name of the Fortran implementation wrapper can be changed
by setting F_name_impl:
library: library
declarations:
- decl: namespace library
declarations:
- decl: void initialize
format:
F_name_impl: library_initialize
To rename all functions, set the template in the toplevel options:
library: library
options:
F_name_impl_template: "{library}_{underscore_name}{function_suffix}"
declarations:
- decl: namespace library
declarations:
- decl: void initialize
C++ allows allows overloaded functions and will mangle the names behind the scenes. With Fortran, the mangling must be explicit. To accomplish this Shroud uses the function_suffix format string. By default, Shroud will use a sequence number. By explicitly setting function_suffix, a more meaningful name can be provided:
- decl: void Function6(const std::string& name)
format:
function_suffix: _from_name
- decl: void Function6(int indx)
format:
function_suffix: _from_index
This will create the Fortran functions function6_from_name
and
function6_from_index
. A generic interface named function6
will also be created which will include the two generated functions.
Likewise, default arguments will produce several Fortran wrappers and a generic interface for a single C++ function. The format dictionary only allows for a single function_default per function. Instead the field default_arg_suffix can be set. It contains a list of function_suffix values which will be applied from the minimum to the maximum number of arguments:
- decl: int overload1(int num,
int offset = 0, int stride = 1)
default_arg_suffix:
- _num
- _num_offset
- _num_offset_stride
Finally, multiple Fortran wrappers can be generated from a single templated function. Each instantiation will generate an additional Fortran Wrapper and can be distinguished by the template_suffix entry of the format dictionary.
If there is a single template argument, then template_suffix will be
set to the flat_name field of the instantiated argument. For
example, <int>
defaults to _int
. This works well for POD types.
The entire qualified name is used. For <std::string>
this would be
std_string
. Classes which are deeply nested can produce very long
values for template_suffix. To deal with this, the
function_template field can be set on Class declarations:
- decl: namespace internal
declarations:
- decl: class ImplWorker1
format:
template_suffix: instantiation3
By default internal_implworker1
would be used for the
template_suffix. But in this case instantiation3
will be used.
For multiple template arguments, template_suffix defaults to a sequence number to avoid long function names. In this case, specifying an explicit template_suffix can produce a more user friendly name:
- decl: template<T,U> void FunctionTU(T arg1, U arg2)
cxx_template:
- instantiation: <int, long>
format:
template_suffix: instantiation1
- instantiation: <float, double>
format:
template_suffix: instantiation2
- The Fortran functions will be named
function_tu_instantiation1
and function_tu_instantiation2
.
Additional Wrapper Functions¶
Functions can be created in the Fortran wrapper which have no
corresponding function in the C++ library. This may be necessary to
add functionality which may unnecessary in C++. For example, a
library provides a function which returns a string reference to a
name. If only the length is desired no extra function is required in
C++ since the length is extracted used a std::string
method:
ExClass1 obj("name")
int len = obj.getName().length();
Calling the Fortran getName
wrapper will copy the string into a
Fortran array but you need the length first to make sure there is
enough room. You can create a Fortran wrapper to get the length
without adding to the C++ library:
declarations:
- decl: class ExClass1
declarations:
- decl: int GetNameLength() const
format:
C_code: |
{C_pre_call}
return {CXX_this}->getName().length();
The generated C wrapper will use the C_code provided for the body:
int AA_exclass1_get_name_length(const AA_exclass1 * self)
{
const ExClass1 *SH_this = static_cast<const ExClass1 *>(
static_cast<const void *>(self));
return SH_this->getName().length();
}
The C_pre_call format string is generated by Shroud to convert the
self
argument into CXX_this and must be included in C_code
to get the definition.
Helper functions¶
Shroud provides some additional file static function which are inserted at the beginning of the wrapped code.
C helper functions
ShroudStrCopy(char *dest, int ndest, const char *src, int nsrc)
- Copy src into dest, blank fill to ndest characters Truncate if dest is too short to hold all of src. dest will not be NULL terminated.
int ShroudLenTrim(const char *src, int nsrc)
- Returns the length of character string src with length nsrc, ignoring any trailing blanks.
Header Files¶
The header files for the library are included by the generated C++ source files.
The library source file will include the global cxx_header field. Each class source file will include the class cxx_header field unless it is blank. In that case the global cxx_header field will be used.
To include a file in the implementation list it in the global or class options:
cxx_header: global_header.hpp
declarations:
- decl: class Class1
cxx_header: class_header.hpp
- decl: typedef int CustomType
c_header: type_header.h
cxx_header : type_header.hpp
The c_header field will be added to the header file of contains functions which reference the type. This is used for files which are not part of the library but which contain code which helps map C++ constants to C constants
Local Variable¶
SH_ prefix on local variables which are created for a corresponding argument. For example the argument char *name, may need to create a local variable named std::string SH_name.
Shroud also generates some code which requires local variables such as loop indexes. These are prefixed with SHT_. This name is controlled by the format variable c_temp.
Results are named from fmt.C_result or fmt.F_result.
Format variable which control names are
- c_temp
- C_local
- C_this
- CXX_local
- CXX_this
- C_result
- F_pointer -
SHT_pointer
- F_result -
SHT_rv
(return value) - F_this -
obj
- LUA_result
- PY_result
C Preprocessor¶
It is possible to add C preprocessor conditional compilation
directives to the generated source. For example, if a function should
only be wrapped if USE_MPI
is defined the cpp_if
field can be
used:
- decl: void testmpi(MPI_Comm comm)
format:
function_suffix: _mpi
cpp_if: ifdef HAVE_MPI
- decl: void testmpi()
format:
function_suffix: _serial
cpp_if: ifndef HAVE_MPI
The function wrappers will be created within #ifdef
/#endif
directives. This includes the C wrapper, the Fortran interface and
the Fortran wrapper. The generated Fortran interface will be:
interface testmpi
#ifdef HAVE_MPI
module procedure testmpi_mpi
#endif
#ifndef HAVE_MPI
module procedure testmpi_serial
#endif
end interface testmpi
Class generic type-bound function will also insert conditional compilation directives:
- decl: class ExClass3
cpp_if: ifdef USE_CLASS3
declarations:
- decl: void exfunc()
cpp_if: ifdef USE_CLASS3_A
- decl: void exfunc(int flag)
cpp_if: ifndef USE_CLASS3_A
The generated type will be:
type exclass3
type(SHROUD_capsule_data), private :: cxxmem
contains
procedure :: exfunc_0 => exclass3_exfunc_0
procedure :: exfunc_1 => exclass3_exfunc_1
#ifdef USE_CLASS3_A
generic :: exfunc => exfunc_0
#endif
#ifndef USE_CLASS3_A
generic :: exfunc => exfunc_1
#endif
end type exclass3
A cpp_if
field in a class will add a conditional directive around
the entire class.
Finally, cpp_if
can be used with types. This would be required in
the first example since mpi.h
should only be included when
USE_MPI
is defined:
typemaps:
- type: MPI_Comm
fields:
cpp_if: ifdef USE_MPI
When using cpp_if
, it is useful to set the option
F_filename_suffix
to F
. This will cause most compilers to
process the Fortran souce with cpp
before compilation.
The typemaps
field can only appear at the outermost layer
and is used to augment existing typemaps.
Debugging¶
Shroud generates a JSON file with all of the input from the YAML and all of the format dictionaries and type maps. This file can be useful to see which format keys are available and how code is generated.