-
Notifications
You must be signed in to change notification settings - Fork 63
BMI Cpp
You can implement a model in C++ by writing an object which implements the BMI C++ interface.
For C++ models, the model should be packaged as a pre-compiled shared library. Support for loading of C++ modules/libraries is always enabled, so no build system flags are required.
As noted above, the path to the shared library must be provided in the configuration so that the module can be loaded at runtime.
BMI models written in C++ should implement two C functions declared with extern "C"
. These functions instantiate and destroy a C++ BMI model object. By default, these functions are expected to be named bmi_model_create
and bmi_model_destroy
, and have signatures like the following:
extern "C"
{
/**
* @brief Construct this BMI instance as a normal C++ object, to be returned to the framework.
* @return A pointer to the newly allocated instance.
*/
MyBmiModelClass *bmi_model_create()
{
/* You can do anything necessary to set up a model instance here, but do NOT call `Initialize()`. */
return new MyBmiModelClass(/* e.g. any applicable constructor parameters */);
}
/**
* @brief Destroy/free an instance created with @see bmi_model_create
* @param ptr
*/
void bmi_model_destroy(MyBmiModelClass *ptr)
{
/* You can do anything necessary to dispose of a model instance here, but note that `Finalize()`
* will already have been called!
*/
delete ptr;
}
}
It is possible to configure different names for the functions within the NGen realization config by using the keys create_function
and destroy_function
, but the return types and parameters must be as shown above.
An example of implementing these functions can be found in the test harness implementation at /extern/test_bmi_cpp/include/test_bmi_cpp.hpp.
Counterintuitively, loading C++ shared libraries into a C++ executable (such as the NextGen framework) requires the use of standard C functions. This is because all C++ compilers "mangle" the names of C++ functions and classes in order to support polymorphism and other scenarios where C++ symbols are allowed to have the same name (which is not possible in standard C). This "mangling" algorithm is not specified or defined so different compilers may use different methods--and even different versions of the same compiler can vary--such that it is not possible to predict the symbol name for any C++ class or function in a compiled shared library. Only by using extern "C"
will the compiler produce a library with a predictable symbol name (and no two functions having the extern "C"
declaration may have the same name!), so this mechanism is used whenever dynamic loading of C++ library classes is needed.
Similarly, different compilers (or different compiler versions) may implement delete
differently, or layout private memory of an object differently. This is why the bmi_model_destroy
function should be implemented in the library where the object was instantiated: to prevent compiler behavior differences from potentially freeing memory incorrectly.
An example implementation for an appropriate BMI model as a C++ shared library is provided in the project here.
Tutorial
Getting Started
Configuration
Technical References