CPPFMU is a set of interfaces and helper functions for writing FMI-compliant model/slave code in C++.
CPPFMU lets you write object-oriented C++ code, using high-level features such as exceptions and automatic memory management, rather than implement the low-level C functions specified by FMI.
Currently, only FMI for Co-simulation is supported. Support for Model Exchange is planned. (Therefore, we'll continue to write "model/slave code", even though it's only the latter which is relevant for the time being.)
CPPFMU was developed as part of the R&D project Virtual Prototyping of Maritime Systems and Operations (ViProMa), and is currently maintained by SINTEF Ocean.
If you have a question, a bug report or an enhancement request, please use the GitHub issue tracker. We appreciate if you do a quick search first to see if anyone has already brought the issue up, and we will also be very happy if you label your issue appropriately.
Contributions are very welcome, and should be submitted as pull requests on GitHub.
CPPFMU does not come with any makefiles or other build scripts.
You just include the headers in your model/slave code and compile
.cpp files together with yours, and you're good to go.
A good tip is to include the CPPFMU repository as a Git submodule
in your own repository.
Note that this repository does not contain the FMI header files; you'll have to get them yourself and make sure your compiler can find them.
You'll also need a compiler with decent C++11 support. The following are known to work (and newer versions of these ought to work as well):
- Microsoft Visual C++ 12.0 (Visual Studi0 2013)
- GCC 4.9
How it works
It's simple: We have already implemented all the FMI C functions
for you in
fmi_functions.cpp. These forward to the C++ functions
defined by you. They also ensure that exceptions are caught,
logged and turned into the appropriate error codes.
To implement a co-simulation slave, this is what you have to do:
cppfmu_cs.hppheader in your sources.
Create a class that publicly derives from
cppfmu::SlaveInstance, and override the latter's virtual member functions as required. Look at the class definition in the header file for information about them.
Define the function
CppfmuInstantiateSlave()so that it creates and returns instances of your slave class. This function is declared in the header file, in the global namespace, and you must define it with the exact same signature. You'll find more information about this in the header too.
That's more or less it. Read on below to learn how to deal with errors, memory management, and logging.
CPPFMU uses exceptions to signal errors, and expects the same of
model/slave code. Any CPPFMU function which is not marked with
CPPFMU_NOEXCEPT may throw. All exceptions are required to derive
If an exception is thrown from within model/slave code, the model/slave instance in question is considered unusable, and no further calls will be made to any of its member functions except the destructor.
The message associated with the exception will be logged using the
mechanism provided by the simulation environment (the
in the C API), and the currently executing FMI function will return an
error code. For most exception types this will be
If the nature of the error is such that all other instances have also
become unusable, an exception of type
cppfmu::FatalError (or a derived
type) must be thrown instead. This will result in an
cppfmu::FatalError are both defined in
FMI 1.0 specifies that all memory allocations and deallocations
must be performed using functions provided by the simulation
environment. This is a tall order for some high-level programming
languages, so it was relaxed in FMI 2.0, which lets you specify the
canNotUseMemoryManagementFunctions flag in
In C++ it is certainly possible to use custom memory allocation
mechanisms, but in high-level code that uses containers,
smart pointers and so on, it is somewhat of a hassle, as they all use
the built-in operator
new by default. CPPFMU aims to make this a
cppfmu_common.hpp header contains a set of classes and functions
that wrap the low-level FMI memory management callbacks in a
higher-level C++ interface. These are:
cppfmu::Memory, which simply bundles the two FMI callbacks (
freeMemory) in a single object. This is passed to
CppfmuInstantiateSlave(), and must be further passed on to any code that needs to allocate memory. All of the following classes and functions need to be passed a
cppfmu::Allocator, a class which satisfies the C++ Allocator concept, and which can therefore be used to manage memory for the standard container classes, among others.
cppfmu::String, which is a type alias for
std::basic_stringusing the above allocator, and the convenience function
cppfmu::CopyString(), which creates a
Stringfrom a C-style character array.
cppfmu::Delete, which are more or less equivalent to the built-in operators
cppfmu::UniquePtr, which is a type alias for
std::unique_ptrwith a custom deleter, and
cppfmu::AllocateUnique, which allocates and constructs an object managed by a
FMI includes a logging mechanism which model/slave code can use to
pass messages to the simulation environment. Mostly, this is used
for error messages, but those are handled automatically by CPPFMU's
exception handling mechanism. For other cases, such as debugging
information, you can use
An object of this type is passed to
must be passed on to any code that is to perform logging.
Logger class is defined and documented in