Generated CLib API#
Caution
The generated CLib API is an experimental part of Cantera and may be changed without notice.
In CLib, Cantera objects are stored and referenced by integers - no pointers are passed to or from the calling application. Further, the contents of arrays and variables are copied, which separates internal C++ memory management from the application using CLib.
Example: Cantera implements a method to retrieve molecular weights in C++ as the
Phase::getMolecularWeights() class method, which is accessible from the derived C++
ThermoPhase class. The CLib source generator expands a corresponding
getMolecularWeights entry:
- name: getMolecularWeights
uses: nSpecies
in the Header Specification File ctthermo.yaml
into a CLib header within a generated ctthermo.h file:
/**
* Copy the vector of molecular weights into array weights.
*
* Wraps C++ getter: `void Phase::getMolecularWeights(double*)`
*
* Uses:
* - `size_t Phase::nSpecies()`
*
* @param handle Handle to queried Phase object.
* @param[in] weightsLen Length of array reserved for weights.
* @param weights Output array of molecular weights (kg/kmol)
*/
int32_t thermo_getMolecularWeights(int32_t handle, int32_t weightsLen, double* weights);
and an associated CLib implementation within a generated ctthermo.cpp file:
int32_t thermo_getMolecularWeights(int32_t handle, int32_t weightsLen, double* weights)
{
// getter: void Phase::getMolecularWeights(double*)
try {
auto obj = ThermoPhaseCabinet::as<Phase>(handle);
if (weightsLen < obj->nSpecies()) {
throw ArraySizeError("thermo_getMolecularWeights", weightsLen, obj->nSpecies());
}
obj->getMolecularWeights(weights);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
When generating code, the CLib source generator uses the docstring of the original C++ code in combination with “crosswalks” of type information as described in the Implementation Details.
Building the Generated CLib Interface#
Compilation of the generated CLib interface is fully integrated into the build process, and is available after building the main Cantera library with default options. The CLib test suite is invoked by running:
scons test-clib
CLib Code Generation#
Source generation for the CLib interface is fully integrated into the build process. Files used by the CLib API can be generated manually by running the following command from the root folder of the Cantera source code:
sourcegen --api=clib --output=interfaces/clib
Generated files are placed in the output folder interfaces/clib, which is the
same as for the automated build process. Note that this step requires installation of
sourcegen via python -m pip install -e interfaces/sourcegen.
CLib Source Generator Overview#
The CLib source generator follows the generic layout of sourcegen’s
automated code generation, with all code located in
the interfaces/sourcegen/src/sourcegen/clib folder. While the overall configuration
follows available Generated CLib Headers, the CLib source generator introduces additional
configuration options.
Configuration#
The YAML file config.yaml within the clib folder contains configuration options
specific to the CLib interface. Configuration options are static unless new CLib
modules need to be implemented (see section Extending the CLib API).
Generic Options: As the CLib interface follows directly from Header Specification Files, override options defined by
ignore_filesandignore_funcsare not needed. While the options are available, they should only be used for testing purposes and otherwise be left at their default values (empty).Type Crosswalks: These fields map C++ types to their CLib equivalents.
ret_type_crosswalk: Specifies the types returned by CLib functions and methods.par_type_crosswalk: Specifies the types passed as parameters in CLib functions and methods.
CLib-Specific Options: These fields define options specific to the CLib interface and use base class names defined within the C++ Cantera namespace. Functions defined within Cantera’s root namespace use
""as the class name:preambles: A mapping of class names to associated text blocks for preambles (headers).includes: A mapping of class names to lists of C++ includes defining base classes and associated specializations.
Implementation Details#
Templates for Scaffolding: Templates, powered by Jinja, are used to scaffold elements of the CLib API. The following files define these templates:
templates.yaml: Defines code blocks within the header and implementation files.template_header.h.j2: Defines the template for header files.template_source.cpp.j2: Defines the template for implementation files.
Source Code: The implementation of the CLib source generator is contained in
generator.py.
Extending the CLib API#
Sourcegen uses a one-to-one correspondence of YAML configuration files to C++ base classes; derived classes are handled by the same configuration as the base class.
New Methods for Existing Configurations: Add the name of the method as a new recipe; the new CLib function will become available once CLib is regenerated and Cantera is recompiled/reinstalled.
YAML Configuration for a C++ Base Class: The CLib source generator implements templates for C++ interface patterns commonly used by Cantera.
Follow the following steps for new classes and associated methods:
Add a new YAML configuration file as described in Generated CLib Headers.
Add include files specifying base class and specializations to the
includesmapping inconfig.yaml.Regenerate the CLib interface and recompile/reinstall Cantera.
Add new unit tests in
test/clibto ensure that the new feature is working properly.
Any new functionality should be implemented in C++ first and broken out using the
steps outlined above. On rare occasions, this is not possible, and custom code needs
to be implemented. The following example illustrates how a C++ CanteraError is
retrieved in CLib (as defined in ct.yaml):
- name: getCanteraError
brief: Get Cantera error.
what: function
declaration: int32_t ct_getCanteraError(int32_t bufLen, char* buf)
parameters:
bufLen: Length of reserved array.
buf: String containing Cantera error.
returns: Actual length of string or -1 for exception handling.
code: |-
string err = Application::Instance()->lastErrorMessage();
copyString(err, buf, bufLen);
return static_cast<int32_t>(err.size());
This results in the following generated header (in ct.h):
/**
* Get Cantera error.
*
* Wraps C++ function: `custom code`
*
* @param bufLen Length of reserved array.
* @param buf String containing Cantera error.
* @returns Actual length of string or -1 for exception handling.
*/
int32_t ct_getCanteraError(int32_t bufLen, char* buf);
/**
And corresponding generated implementation (in ct.cpp):
int32_t ct_getCanteraError(int32_t bufLen, char* buf)
{
// function: custom code
try {
// *************** begin custom code ***************
string err = Application::Instance()->lastErrorMessage();
copyString(err, buf, bufLen);
return static_cast<int32_t>(err.size());
// **************** end custom code ****************
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
Troubleshooting#
The sourcegen utility uses a logging module to provide feedback. Add the verbose
-v option to generate additional feedback.
Missing XML tree: The sourcegen utility requires a valid Doxygen tag file and an associated XML tree, which are generated by running
scons doxygen.[CRITICAL] Tag file does not exist at expected location: <...>/cantera/build/doc/Cantera.tag Run 'scons doxygen' to generate.
Invalid function/method name: The function/method name is not known to Doxygen; check spelling and/or re-run
scons doxygenif the function/method was recently created.[CRITICAL] Could not find '...' in Doxygen tag file.
Missing docstring: Cantera’s Doxygen configuration skips undocumented functions and methods; thus, they are not part of the XML tree and cannot be resolved by sourcegen.
[CRITICAL] Unable to resolve recipe type for '...'
Ambiguous Signature: Functions and methods that have overloads and/or define default arguments require disambiguation via the
wrapsfield.[CRITICAL] Need argument list to disambiguate '...'. Possible matches are: - (double, double, const double*) - (double, double, const Composition&) - (double, double, const string&)
Missing Crosswalk: The CLib code generator is limited to type crosswalks defined
config.yaml.[CRITICAL] Failed crosswalk for argument type '...'. [CRITICAL] Failed crosswalk for return type '...'.
A resolution will require one of the two options:
Create alternative C++ functions/methods with signatures that are compatible with existing crosswalks.
Add new crosswalks to
config.yaml, which may require updates of templates as well as the CLib source generator source code itself.