Statements

The statement data structure is used to define code snippets that will be used to create the wrapper. Combinations of language, type and attributes are used to select a statement name based on a function’s argument declaration in the YAML input. Shroud provides defaults for most standard wrappings. Statements provides a way for users to have more control over wrapping. Each aspect of the wrapping, argument names, argument declarations, and passing to the next layer, must be supplied by the statements in a consistent manner.

Details for C and Fortran are provided in other sections.

The command line option --write-statements can be used to create a file which will contain all of the statements that Shroud knows.

The option debug in the YAML file will add additional comments into the wrapper to identify which statement names were used to wrap an argument.

options:
  debug: True

Shared fields

All statement groups, independent of the wrapper language, share common fields. These fields are used to identify the group and to build up complete groups from shared features.

name

The name is a underscore delimited list of parts which are used to find the group. The first part is the language being wrapped and the second part is the intent of the group.

Names which start with a # are ignored. This provides a way to add comments into the JSON file. (which does not support comments)

{
    "name":"##### enum #################################################"
},
{
    "name":"f_in_native"
},

alias

The most common way of reusing a group is to create additional names for the group via an alias. An alias field can be used with or without the name field.

If there are C and Fortran group, make the first alias a Fortran name. C is a subset of Fortran and the first alias determines the defaults.

alias:
- f_out_string**_cdesc_allocatable
- c_out_string**_cdesc_allocatable

Names which start with a # are ignored. This provides a way to add comments into the JSON file. (which does not support comments)

mixin

When the group has the intent mixin in its name it is can be used to add subsets of fields to other groups. The fields will be “mixed in” to another group to avoid repeating fields. After the language and intent parts of the name, any words can be appended to the name to help identify the group.

When multiple mixins are used, scalar fields are assigned, replacing any existing value in the group being defined. A field which is a list will be appended to the group being defined making it possible to build up a field from several mixins. Dictionaries are recursively appended.

After the mixins are used to initialize a group, if a field name is reused it will replace the value from the mixin group. This allows a group to use mixins yet still be customized as needed.

A mixin group must not contain alias, append or base

- name: f_mixin_one
  f_dummy_decl:
  - integer arg1
  f_arg_call:
  -  arg1
  f_need_wrapper: True
- name: f_mixin_two
  f_dummy_decl:
  - integer arg2
  f_arg_call:
  -  arg2
  f_need_wrapper: False
- name: f_in_type
  mixin:
  -  f_mixin_one
  -  f_mixin_two
  f_arg_call:
  -  arg1 + arg2

The final group will be:

- name: f_in_type
  f_dummy_decl:
  - integer arg1
  - integer arg2
  f_arg_call:
  -  arg1 + arg2
  f_need_wrapper: False

While this example uses twice as many lines to create the f_in_type group, the real benefit is when each mixin group contains several declarations and is mixed into many other groups.

Names which start with a # are ignored. This provides a way to add comments into the JSON file. (which does not support comments)

comments

Comments list the steps used by the wrapper. The final group’s comment will be a collection of all of the mixin’s comments. Each mixin can contribute a step in the wrapping process. Comments are appended as part of the mixin process. Note that if the final non-mixin group also defines comments, it will replace the comments created from the mixins. notes is intended for text which provides additional information specific to a group.

notes

Notes are for information about how the group works or when it should be used. Unlike comments, the notes are not mixed into groups and are intended to be more detailed than comments.

usage

Documents a typical declaration which will use this group.

name: "f_out_char**_cdesc_pointer"
usage: [
    "char **arg +intent(out)"
]

fmtdict

A dictionary to replace default values An entry may be list. It will be joined with newlines into a single string before being used. This allows several lines to be used in a more natural manner.

The fmtdict is ordered allowing earlier entries to be used by later entries.

name: f_mixin_getter_argname
fmtdict:
    f_var: "val"
    i_var: "val"
    c_var: "val"

    cnamefunc: "{C_memory_dtor_function}"
    cnameproto: "void {cnamefunc}\t({c_capsule_data_type} *cap)"
name: c_mixin_function-assign-to-new
fmtdict:
    cxx_addr: ""
    cxx_member: "->"
name: c_mixin_operator_assignment_fmtdict_swig
fmtdict:
    capture_code:
    - // Capture pointer from RHS, clear 'moving' flag.
    - // additional line here

Examples

name: f_mixin_one
f_pre_call:
- "! comment f_mixin_one"

name: f_mixin_two
mixin:
- f_mixin_one
f_pre_call:
- "! comment f_mixin_two"    # appends

name: f_function_one
mixin:
- f_mixin_one
f_pre_call:
- "! comment two"            # replaces

Lookup statements

The statements for an argument are looked up by converting the type and attributes into an underscore delimited string.

  • language - c

  • intent - in, out, inout, function, subroutine, ctor, dtor, getter, setter, operator

  • Abstract declaration. For example, native, native* or native**. May include template arguments vector<native>. Uses the typemap field sgroup.

  • api - from attribute buf, capsule, capptr, cdesc and cfi.

  • funcarg - from attribute. Uses funcarg and not the value of the attribute.

  • deref - from attribute allocatable, pointer, raw, scalar

  • owner caller, library If set, it replaces the attribute and any default computed by Shroud. Used with allocating temporary memory. The capsule is allocated in C, returned to Fortran which copies the value, then the Fortran wrapper releases the memory.

  • operator ex. assignment

  • custom ex. weakptr

Passing function result as an argument

This section explains how statements are used to generate code for functions which return a struct.

Compiler ABI do not agree on how some function results should be returned. To ensure portablity, some function results must be passed as an additional argument. This is typically more complicated types such as struct or complex.

        "alias": [
            "f_function_struct",
            "c_function_struct"
        ],
        "mixin": [
            "f_mixin_call-cwrapper-as-subroutine",
            "f_mixin_declare-interface-arg",
            "f_mixin_declare-local-variable"
        ],
        "f_arg_call": [
            "{f_var}"
        ],
        "c_prototype": [
            "{c_type} *{c_var}"
        ],
        "c_call": [
            "*{c_var} = {C_call_function};"
        ],
        "c_need_wrapper": true,

Classes and Structs

The default behavior for classes and structs is to pass them through from Fortran to C++ without looking inside them. The statements are selected based on the typemap’s sgroup field which is either shadow or struct.

In some cases, it can be beneficial to look inside a compound type. By setting the option typemap_sgroup, a statement group can be used which is specific for the type. A prime example of this is std::vector. This maps naturally to a Fortran array. The C wrapper accepts a pointer to the array along with a length argument. The C wrapper is then responsible for creating the std::vector. If the std::vector was created in the Fortran code it could be passed opaquely; however, it would be very inconvenient to access elements in Fortran requiring the use of the vector.at method instead of Fortran array subscripting.

See the sgroup.yaml test.

Format fields

Each statement group is evaluated in the context of a format dictionary created for the function result or argument.

  • c_arglist, f_arglist

    An array of format fields for all arguments to the function. Entry 0 is the function, 1 is the first argument, and so on. This allows a statement group to access other variable using a value such as c_arglist[1].c_local_cxx.

Several format fields are defined to help use a set of statements with both pointers and references.

  • cxx_member

"c_post_call": [
    "{c_const}char *{c_var} = {cxx_var}{cxx_member}c_str();"
]

Some attributes can be used to generate strings based on the declaration and attributes.

Dimension

Since the rank of arrays can vary, the attribute can create a varying number of lines.

  • gen.f_allocate_shape

    Shape to use with ALLOCATE statement from cdesc variable. Blank if scalar.

  • gen.c_f_pointer

    Shape for C_F_POINTER intrinsic from cdesc variable. Blank for scalars.

  • gen.f_cdesc_shape

    Assign variable shape to cdesc in Fortran using SHAPE intrinsic. This will be passed to C wrapper. Blank for scalars.

  • gen.c_dimension_size

    Compute size of array from dimension attribute. 1 if scalar.

  • gen.c_array_shape

    Assign array shape to a cdesc variable in C. Blank if scalar.

  • gen.c_array_size

    Return expression to compute the size of an array. *c_array_shape* must be used first to define ``c_var_cdesc->shape`. 1 if scalar.

  • gen.c_extents_decl

    Define the shape in local variable extents in a CFI_index_t variable. Blank if scalar.

  • gen.c_extents_use

    Return variable name of extents of CFI array. NULL if scalar.

  • gen.c_lower_use

    Return variable name of lower bounds of CFI array from helper lower_bounds_CFI. NULL if scalar.

Examle with CFI arrays.

"name": "c_mixin_cfi_native_allocatable",
"comments": [
    "Allocate copy of C pointer (requires +dimension)."
],
"mixin": [
    "c_mixin_header_cstring"
],
"c_post_call": [
    "if ({c_local_cxx} != {nullptr}) {{+",
    "{gen.c_extents_decl}int SH_ret = CFI_allocate({c_var_cfi}, \t{gen.c_lower_use}, \t{gen.c_extents_use}, \t0);",
    "if (SH_ret == CFI_SUCCESS) {{+",
    "{stdlib}memcpy({c_var_cfi}->base_addr, \t{c_local_cxx}, \t{c_var_cfi}->elem_len);",
    "-}}",
    "-}}"
],
"c_local": [
    "extents",
    "lower"
],
"helper": [
    "lower_bounds_CFI"
],