C and C++

A C API is created for a C++ library. Wrapper functions are within an extern "C" block so they may be called by C or Fortran. But the file must be compiled with the C++ compiler since it is wrapping a C++ library.

When wrapping a C library, additional functions may be created which pass meta-data arguments. When called from Fortran, its wrappers will provide the meta-data. When called directly by a C application, the meta-data must be provided by the user.

Names

Shroud will flatten scoped C++ library names to create the C API. Since C does not support scopes such as classes and namespaces, a name such as ns1::function must be flattened into ns1_function to avoid conflict with a similarly named function ns2::function.

Names are also contolled by the C_api_case option. It can be set to lower, upper, underscore or preserve. This option is used to set the format field C_name_api which in turn is used in the option C_name_template. The default is preserve. This creates a stronger correlation between the C API and the C++ API.

To further help control the scope of C names, all externals add a prefix. It defaults to the first three letters of the library but may be changed by setting the format C_prefix:

format:
  C_prefix: NEW_

Wrapper

As each function declaration is parsed a format dictionary is created with fields to describe the function and its arguments. The fields are then expanded into the function wrapper.

C wrapper:

extern "C" {

{C_return_type} {C_name}({C_prototype})
{
    {C_code}
}

}

The wrapper is within an extern "C" block so that C_name will not be mangled by the C++ compiler.

C_return_code can be set from the YAML file to override the return value:

-  decl: void vector_string_fill(std::vector< std::string > &arg+intent(out))
   format:
     C_return_type: int
     C_return_code: return SH_arg.size();

The C wrapper (and the Fortran wrapper) will return int instead of void using C_return_code to compute the value. In this case, the wrapper will return the size of the vector. This is useful since C and Fortran convert the vector into an array.

Struct Type

While C++ considers a struct and a class to be similar, Shroud assumes a struct is intended to be a C compatible data structure. It has no methods which will cause a v-table to be created. This will cause an array of structs to be identical in C and C++.

The main use of wrapping a struct for C is to provide access to the name. If the struct is defined within a namespace, then a C application will be unable to access the struct. Shroud creates an identical struct as the one defined in the YAML file but at the global level.

Class Types

A C++ class is represented by the C_capsule_data_type. This struct contains a pointer to the C++ instance allocated and an index passed to generated C_memory_dtor_function used to destroy the memory:

struct s_{C_capsule_data_type} {
    void *addr;     /* address of C++ memory */
    int idtor;      /* index of destructor */
};
typedef struct s_{C_capsule_data_type} {C_capsule_data_type};

In addition, an identical struct is created for each class. Having a unique struct and typedef for each class add a measure of type safety to the C wrapper:

struct s_{C_type_name} {
    void *addr;   /* address of C++ memory */
    int idtor;    /* index of destructor */
};
typedef struct s_{C_type_name} {C_type_name};

idtor is the index of the destructor code. It is used with memory managerment and discussed in Memory Management.

The C wrapper for a function which returns a class instance will return a C_capsule_data_type by value. Functions which take a class instance will receive a pointer to a C_capsule_data_type.