Create Model
A model is a piece of code with inputs, outputs and a cyclic function. It can be the controller under test or a plant model, meaning a simulated physical mechanism such as a motor, valve or tank that the controller acts on. A simulation calls the models' cyclic functions once per step and connects their inputs and outputs.
CppModel does not require models to use any special base class. Most existing embedded code can be simulated as it is. This guide shows the recommended structure for models in C and C++.
If you use Claude Code, the CppModel Claude Code plugin can build a plant model for you (cppmodel:plant-model). It can also decouple existing controller code from vendor BSW, RTOS or HAL calls so it can run under CppModel (cppmodel:decouple-component).
Creating a C model
Keep the model's inputs, outputs and state in a context struct, and pass it to a cyclic function. You can then run several instances of the model side by side, and the same code can run unchanged on the target.
Example: Doubler
Doubler.h
#pragma once
typedef struct Doubler_s
{
/* Inputs */
int in;
/* Outputs */
int out;
} Doubler_ts;
void Doubler_RunCyclic(Doubler_ts *const ctx);
Doubler.c
#include "Doubler.h"
void Doubler_RunCyclic(Doubler_ts *const ctx)
{
ctx->out = ctx->in * 2;
}
Using the model in a simulation
The simulation writes the model's inputs, calls its cyclic function and records the values it wants to see in the workspace:
#include <cppmodel/CModel.h>
#include "Doubler.h"
static Doubler_ts doubler;
void RunCyclic(CModelSimulation_ts *self, unsigned long currentTime_ms)
{
doubler.in = CppModel_getInputI32(self, "in", (int32_t)(currentTime_ms / 1000));
Doubler_RunCyclic(&doubler);
CppModel_setOutputI32(self, "out", doubler.out);
}
int main(void)
{
CModelSimulation_ts *simulation = CppModel_create("Doubler", 10000, 100);
if (simulation == NULL)
return 1;
CppModel_setRunStepFunction(simulation, RunCyclic);
CppModel_Simulate(simulation);
int result = CppModel_getSimulationResult(simulation);
CppModel_destroy(simulation);
return result;
}
If a model needs to know the cycle time, for example to integrate a physical quantity, read it from self->stepTime_ms in the simulation and pass it to the model.
Using C models from C++
To call a C model from a C++ simulation, wrap its header in extern "C":
extern "C"
{
#include "Doubler.h"
}
Alternatively, add the guard to the header itself:
#ifdef __cplusplus
extern "C" {
#endif
/* declarations */
#ifdef __cplusplus
}
#endif
Creating a C++ model
Plain class
A plain class with typed members is the simplest option, and the one closest to most production code:
Doubler.h
#pragma once
class Doubler
{
public:
int in = 0;
int out = 0;
void RunCyclic();
};
Doubler.cpp
#include "Doubler.h"
void Doubler::RunCyclic()
{
out = in * 2;
}
Deriving from cppmodel::Model
If you want named, dynamically typed ports, derive from cppmodel::Model. Ports are accessed by name with operator[] and hold any JSON-compatible value. The class is part of the header-only C++ API and lives entirely in your code:
Doubler.h
#pragma once
#include <cppmodel/cppmodel.hpp>
class Doubler : public cppmodel::Model
{
public:
void RunCyclic(unsigned long long simulationTime_ms) override;
};
Doubler.cpp
#include "Doubler.h"
void Doubler::RunCyclic(unsigned long long simulationTime_ms)
{
int in = (*this)["input1"];
(*this)["output1"] = in * 2;
}
From the simulation:
doubler["input1"] = 21;
doubler.RunCyclic(simulationTime_ms);
int out = doubler["output1"]; // 42
cppmodel::Model also has virtual Init() and Finalize() methods. Call them from the simulation's own Init() and Finalize() to run setup and teardown code for the model.