Generating C++ code from SysML v2 textual models#
New: For guidance on structuring models for simulation and testing see the Modeling Guide for Simulation and Testing.

SysML® v2 is a newer version of SysML that is more flexible and powerful. An important aspect is that it now allows you to write textual models. This lowers the barrier to model-based systems engineering considerably, because no special tools are needed to write the models. For example, a Visual Studio Code plugin is available to write SysML v2 models. The Sinelabore Code Generator supports code generation from SysML v2 models. Unlike the current Sinelabore capabilities, the code generation also supports the generation of structural code and not just the generation of state machine code.
Code for parts, interactions between parts, and so on is also generated. This can be used to quickly generate mock-ups and simulations of an intended system.
To generate C++ code from a SysML model call the code generator with the
command line flag -p Sysml2Text.

A fully functional example is available on our GitHub site.
Introduction#
The SysML v2 backend focuses on executable models: state machines in parts, ports and connections between parts, and action behavior you can simulate in C++. Not every SysML v2 language feature is covered yet; support grows with each release. For how to structure models for simulation and testing see the Modeling Guide.
To execute the generated code a small header-only runtime environment is provided. It contains some base classes and implements inter-part communication via ports and timeout handling — the minimum needed to run a model.
In case a part contains a state machine the code generator also generates additional files
for the state machine. This code follows the structure that is already used by
the cppx backend.
Supported Features#
- Parts: Parts are the main building blocks of a system. They can contain other parts, attributes, a state machine, actions, enums, connections and ports. Parts are realized as C++ classes in the generated code. Part usages may have multiplicity (e.g.
part a[4] : A;). If a part contains other parts you are responsible to create them in the init method. Create a subclass of the part class and override the init method. Then call the base class init method. Example:part def P1{part p2:P3;} - Items: An item definition is a kind of occurrence definition for identifiable objects that may flow through a system or be acted on over time, such as data, signals, or fluids. A part is a specialized kind of item. Item definitions are translated to C++ structs or classes in the generated code. Item and attribute usages may use multiplicity (e.g.
item scenario : WayPoint[0..5];). Example:item def Fuel { attribute volume : Integer; }See Items. - Attributes: Parts can have attributes. Attributes can have a base type, a default value, and multiplicity. Attributes can contain other attributes. Attributes are translated to C++ member variables. Depending on the type of the attribute it is realized as a basic data type or a class. Example:
attribute a:bool; - Enums: Basic enums with their enum values can be defined. Enums are directly translated to C++ enums in the generated code. Example:
enum def Color{RED; GREEN; BLUE;} - Ports: Ports are communication points between parts. The framework has some helper classes to realize ports. A port marked as in has a receive method, a port marked as out has a send method in the generated C++ code. Example:
port def p {attribute x : Integer;} - Connections: Connections are the way to connect ports of parts in order to share data. Connections are realized as queues in the generated code. Only ports of the same type can be connected. One part must be marked as in, one as out. If you need bidirectional communication you can add two other ports in opposite direction. All attributes in a port share the same direction. Example:
connect p1.sendPort to p2.recvPort; - State Machines: A part can have a state machine. Supported features include hierarchical states, timed and guarded transitions, history, final states, and parallel (orthogonal) regions. Write the machine as a state usage —
state sm { ... }— so that attributes and ports of the part remain accessible from inside. See States. - Actions on other parts: An action can perform an action that belongs to another part, addressed through a feature chain:
perform redLamp.setOn;. See Actions. - Packages and imports: Packages map to C++ namespaces. Several packages may live in one model file, and a model may also be split across files — imports are resolved against sibling files and followed recursively. See Packages.
- Specialization: Parts and attributes can specialize and redefine features (
:>,:>>/redefines). Usages may useref,abstract, andsubsetswhere needed for structure and collections. - Units: Attribute values may carry units (
[SI::W],[SI::kilo*SI::metre]). Units are normalized for comparison but never converted. See Attributes. - Diagnostics: Problems in the model are reported with stable codes (
E3101,W3115, …). See Diagnostics.
Documentation#
In the sub-pages that describe the supported features of the code generator text references are made to: OMG Systems Modeling Language™ (SysML®), Version 2.0 Part 1: Language Specification. OMG Document Number: formal/2026-03-02 Date: March 2026, Standard document URL: https://www.omg.org/spec/SysML/2.0/
The following listing shows an example for a small traffic light control system model. It consists of three parts with some attributes and actions. The TrafficManagementCenter part represents a central control system that can control TrafficLightController parts connected via ports.
In this simple model the TrafficManagementCenter just enables traffic light controllers to operate by sending a start event. The TrafficLightController then changes state from OutOfService to Operational and performs expected traffic light operations. In a real system, this simulation would certainly be much more complex. But the example is rich enough to show the available possibilities. The TrafficLightSystem part weaves together the TrafficManagementCenter and the TrafficLightController parts.

The picture above is the structure of the full example on GitHub, where one management center supervises two traffic lights: control events flow from tmc.controlPort to each recvPort, service requests flow back over servicePort. Both lights are instances of BasicTrafficLightController, which specializes the TrafficLightController template.
It was not drawn by hand — it is rendered from the very same model text, which is one of the practical benefits of a textual notation. See Tooling and Validation.
private import ScalarValues::*;
package TrafficLight {
// definition of events for the traffic light controller
enum def TLCEvent {
evOperational;
evError;
}
// the payload carried by the port
item def ControlPortData {
attribute msg : TLCEvent;
}
port def ControlPort {
in item data : ControlPortData;
}
// traffic management center
part def TrafficManagementCenter{
out port sendPort : ControlPort;
state tmcStateMachine {
entry; then PreOperational;
state PreOperational;
accept after 2[SI::second] do send TLCEvent::evOperational via sendPort then Operational;
state Operational;
}
}
part def TrafficLightController{
attribute msg : TLCEvent;
in port recvPort : ~ControlPort;
// the lamps this controller drives; wired from outside
ref part redLamp : Lamp;
ref part yellowLamp : Lamp;
// read the port into 'msg' so transitions can guard on it
action getMsg {
action accept m : ControlPortData via recvPort {
assign msg := m.msg;
}
}
// switch a lamp by performing an action that belongs to the lamp
action setRed { first start; then perform redLamp.setOn; then done; }
action setYellow { first start; then perform yellowLamp.setOn; then done; }
state tlcStateMachine {
do action getMsg;
...
}
}
// Main system composition
part def TrafficLightSystem {
doc /*
* Main system that contains the parts
*/
part tmc : TrafficManagementCenter;
part tlc : TrafficLightController;
// Connect the parts
connect tmc.sendPort to tlc.recvPort;
}
}Two details in the listing are worth pointing out, because they are the ones most often written differently:
- The state machine is a state usage (
state tmcStateMachine { … }), not astate def. A state definition is owned by the part but not featured by it, somsgandsendPortwould not be accessible from inside it. perform redLamp.setOnperforms an action that belongs to another part. The lamp keeps its own behavior; the controller only triggers it.
The SysML code generation is growing step by step#
SysML v2 is a very rich modeling language. The features listed in the introduction and on the sub-pages are supported for C++ generation. The following are still limited or unsupported (list is not exhaustive). Take a look at the GitHub examples to see what is used in practice.
- Absolute-time transitions (
accept at …) — timed transitions useaccept afteronly - Timed transitions are evaluated in seconds; other time units are not converted (use
0.001[SI::second]for 1 ms) - Inner transitions (not supported by sysml)
- Unit conversion and dimensional analysis — units are normalized so that different spellings of the same unit compare equal, but values are never rescaled (see Attributes)
- The OMG standard library — name-compatible stubs for
ScalarValuesandISQare bundled so thatsubsets ISQ::lengthbinds and editors stay quiet, but they are a small subset and are not emitted into the generated code - Keywords such as
ordered/uniqueon attributes are not fully tracked for codegen - Additional enum features beyond a basic definition
- Inter-part communication beyond ports and connections as shown above
- Many other SysML features not shown in the examples
Language areas outside the executable subset#
The backend deliberately covers the part of SysML v2 that can be turned into running code.
Whole language areas that describe intent rather than behavior are therefore not implemented
yet — among them use case, requirement, constraint, assert, calc, analysis,
allocation and view / viewpoint.
These keywords are not silently ignored. The parser does not accept them, so a model that uses them stops with a syntax error and no code is generated:
usecase.sysml:3:6: no viable alternative at input 'usecase'
Error: SysML parse failed with 3 syntax error(s)That is intentional. Skipping unknown constructs would produce code that quietly omits part of the model — a far worse outcome than a clear error. If you keep requirements or use cases in the same model, put them in a separate file that you do not pass to the code generator.
See also the Modeling Guide supported-features summary.