C++ s-function with dynamically sized output, not dependent on input size

14 views (last 30 days)
I am trying to create a C++ s-function that has an output of a variable sized vector. I have found some examples that have a variable sized output, where the output is dependent on the input, such as sfcndemo_varsize from:
However, my function does not set the size based on the inputs. In the mdlOutputs() function, I call a c++ function from another file which outputs a vector, and then get the size of that vector.
static void mdlOutputs(SimStruct *S, int_T tid) {
// Call the function
// result is a structure that includes vector 'headings'
result = update();
const std::vector headings = result.headings;
int_T headingSize = static_cast<int_T>(headings.size());
if (headingSize > 0) {
for (size_t i = 0; i < headings.size(); i++) {
y2[i] = static_cast<real_T>(headings[i]); // Store each heading
}
}
else {
y2[0] = 0.0; // Default if no heading data
}
}
Is it possible to set the s-function output size from mdlOutputs? This code does not appear to ever trigger the mdlSetOutputPortDimensionInfo() which I have also defined.

Answers (2)

Harsh
Harsh on 26 Mar 2025
Hi @David
You cannot change the output port dimensions from the “mdlOutputs” function. In Simulink, any changes to signal dimensions must be performed during the dimension propagation phase and not during runtime execution in “mdlOutputs. To have a dynamically sized output, you need to declare the port as “DYNAMICALLY_SIZED” in mdlInitializeSizes”.
By default, the Simulink engine calls “mdlSetOutputPortDimensionInfo” only if it can fully determine the dimensionality of port from the port to which it is connected. If the engine cannot completely determine the dimensionality from port connectivity, it invokes mdlSetDefaultPortDimensionInfo”. Therefore, you can set the output port dimensions info there by calling the “update” method and then call “update” function again in “mdlOutputs” to set the values also.
Below is the sample code snippet to achieve this task -
  • “update” function to return a vector of headings
static std::vector<double> update(int &size) {
size = 3 ;
std::vector<double> headings(size); // Allocate vector of 'size' elements
for (int i = 0; i < size; i++) {
headings[i] = i;
}
return headings;
}
  • Set the output port width to “DYNAMICALLY_SIZED” in “”mdlInitializeSizes
static void mdlInitializeSizes(SimStruct *S) {
ssSetNumSFcnParams(S, 0);
if (!ssSetNumInputPorts(S, 0)) return;
if (!ssSetNumOutputPorts(S, 1)) return;
ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);
ssSetNumSampleTimes(S, 1);
ssSetOptions(S, 0);
}
  • Set the output port dimensions to [1xn] where n is the size of headings vector in the "mdlSetDefaultPortDimensionInfo"
// mdlSetDefaultPortDimensionInfo: Set default port size based on update() output.
#define MDL_SET_DEFAULT_PORT_DIMENSION_INFO
void mdlSetDefaultPortDimensionInfo(SimStruct *S) {
int headingSize = 0;
std::vector<double> headings = update(headingSize);
if (ssGetOutputPortWidth(S, 0) == DYNAMICALLY_SIZED) {
ssSetOutputPortMatrixDimensions(S, 0, 1, headingSize);
}
}
  • set the output port value in the "mdlOutputs" function
static void mdlOutputs(SimStruct *S, int_T tid) {
int headingSize = 0;
std::vector<double> headings = update(headingSize);
real_T *y = ssGetOutputPortRealSignal(S, 0);
if (!headings.empty()) {
for (int i = 0; i < headingSize; i++) {
y[i] = headings[i];
}
} else {
y[0] = 0.0;
}
}
  • Below is the snapshot of Simulink model and the output visualized via a “scope” block
  1 Comment
David
David on 31 Mar 2025
Edited: David on 31 Mar 2025
Thanks for the reply. I created your example and made some modifications to headings() so it changes size each time step. However, it appears that the function mdlSetDefaultPortDimensionInfo is only called once, the first time, which then calls headings(). After that, it is never called. The output vector is always a 5-element vector.
// external_functions.cpp
#include "external_functions.h"
#include <stdlib.h>
#include <vector>
// Simulates a dynamic size that changes every step
int_T get_dynamic_size(void)
{
static int_T counter = 0;
counter++;
// Alternate between sizes 3 and 5
if (counter % 2 == 0) {
return 3;
} else {
return 5;
}
}
std::vector<double> update(int &size)
{
size = get_dynamic_size() ;
std::vector<double> headings(size); // Allocate vector of 'size' elements
for (int i = 0; i < size; i++) {
headings[i] = i;
}
mexPrintf("update: Headings created with %d elements.\n", size);
return headings;
}
// dynamic_resize_sfunction.cpp
#define S_FUNCTION_NAME dynamic_resize_sfunction
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
#include "external_functions.h"
// =========================
// mdlInitializeSizes - Set Ports and Sizes
// =========================
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0);
if (!ssSetNumInputPorts(S, 0)) return;
if (!ssSetNumOutputPorts(S, 1)) return;
ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);
ssSetNumSampleTimes(S, 1);
ssSetOptions(S, 0);
mexPrintf("mdlInitializeSizes initialized correctly.\n");
}
// =========================
// mdlInitializeSampleTimes - Define Sample Time
// =========================
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
}
// mdlSetDefaultPortDimensionInfo: Set default port size based on update() output.
#define MDL_SET_DEFAULT_PORT_DIMENSION_INFO
void mdlSetDefaultPortDimensionInfo(SimStruct *S) {
mexPrintf("mdlSetDefaultPortDimensionInfo called.\n");
int headingSize = 0;
std::vector<double> headings = update(headingSize);
if (ssGetOutputPortWidth(S, 0) == DYNAMICALLY_SIZED) {
ssSetOutputPortMatrixDimensions(S, 0, 1, headingSize);
}
mexPrintf("mdlSetDefaultPortDimensionInfo: Generated output with %d elements.\n", headingSize);
}
// =========================
// mdlOutputs - Generate Output with Updated Size
// =========================
static void mdlOutputs(SimStruct *S, int_T tid)
{
int headingSize = 0;
std::vector<double> headings = update(headingSize);
real_T *y = ssGetOutputPortRealSignal(S, 0);
if (!headings.empty()) {
for (int i = 0; i < headingSize; i++) {
y[i] = headings[i];
}
} else {
y[0] = 0.0;
}
mexPrintf("mdlOutputs: Generated output with %d elements.\n", headingSize);
}
// =========================
// mdlTerminate - Cleanup at End
// =========================
static void mdlTerminate(SimStruct *S)
{
mexPrintf("mdlTerminate: Simulation complete.\n");
}
// =========================
// Required S-Function Trailer
// =========================
#ifdef MATLAB_MEX_FILE
#include "simulink.c" // Required for MATLAB MEX
#else
#include "cg_sfun.h" // Required for code generation
#endif

Sign in to comment.


Aryan
Aryan on 8 Aug 2025
Edited: Aryan on 8 Aug 2025
Hi,
I understand you want to create a C++ S-Function (CPP FILE) in SIMULINK where the output vector size can change at each simulation step, based on the result of a function call in "mdlOutputs".
Even if we set the output port as "DYNAMICALLY_SIZED" and implement "mdlSetOutputPortDimensionInfo", SIMULINK doesn’t actually update the output size during simulation.
It’s because SIMULINK only negotiates signal dimensions during model compilation and dimension propagation—never at runtime. Once the simulation starts, the output port size is fixed.
Workaround is to define the output port with the maximum possible size you expect, and then provide a second output that tells you how many elements are valid at each time step. The unused elements in the output vector can be set to zero (or NaN, if you prefer). This approach works reliably with SIMULINK.
Here’s a basic example of how you might set this up in your S-Function (CPP FILE):
#define S_FUNCTION_NAME sample
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
#include <cstdlib> // for rand(), srand()
#include <ctime> // for time()
#define MAX_HEADINGS 10
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0);
// No input ports
if (!ssSetNumInputPorts(S, 0)) return;
// Two outputs: vector and count
if (!ssSetNumOutputPorts(S, 2)) return;
ssSetOutputPortWidth(S, 0, MAX_HEADINGS); // Headings vector (fixed size)
ssSetOutputPortDataType(S, 0, SS_DOUBLE);
ssSetOutputPortWidth(S, 1, 1); // Valid count (scalar)
ssSetOutputPortDataType(S, 1, SS_INT32);
ssSetNumSampleTimes(S, 1);
ssSetOptions(S, 0);
}
// Optional: Seed the RNG once at start
static void mdlStart(SimStruct *S)
{
srand(static_cast<unsigned int>(time(0)));
}
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, 1.0);
ssSetOffsetTime(S, 0, 0.0);
}
static void mdlOutputs(SimStruct *S, int_T tid)
{
// Generate n in [1, MAX_HEADINGS]
int_T n = (rand() % MAX_HEADINGS) + 1;
// Output port pointers
real_T *y_headings = ssGetOutputPortRealSignal(S, 0);
int_T *y_count = (int_T*)ssGetOutputPortSignal(S, 1);
// Fill output vector with 1, 2, ..., n
int_T i = 0;
for (; i < n; ++i) {
y_headings[i] = static_cast<real_T>(i + 1);
}
// Fill remaining with zeros
for (; i < MAX_HEADINGS; ++i) {
y_headings[i] = 0.0;
}
// Output the valid count
y_count[0] = n;
}
static void mdlTerminate(SimStruct *S) { }
#ifdef MATLAB_MEX_FILE
#include "simulink.c"
#else
#include "cg_sfun.h"
#endif
Below are some snapshots of how simulation will look like:
Kindly refer to the following MathWorks documentation links for better understanding the functions used above:
Hope it helps !

Products


Release

R2024a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!