Playing around with it a bit, I came up with a fairly messy way to imlement this by passing a pointer to the pwork vector in the model outputs function. It seems like there must be a safer solution in Simulink though...
#define S_FUNCTION_NAME sfun_RandomStream
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
#include <random>
#include "RandomNumberStream.h"
enum params {SEED_idx, NPARAMS};
enum outputs {STREAMPTR_idx, NOUT};
static int tmp = 0;
static void mdlInitializeSizes(SimStruct *S)
{
int_T status;
DTypeId id;
tmp = 0;
ssSetNumSFcnParams(S, params::NPARAMS); //seed
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch will be reported by Simulink */
}
if (!ssSetNumInputPorts(S, 0)) return;
if (!ssSetNumOutputPorts(S,outputs::NOUT)) return;
ssSetOutputPortWidth(S, outputs::STREAMPTR_idx, 1);
id = ssRegisterDataType(S, "voidPtr");
if(id == INVALID_DTYPE_ID) return;
status = ssSetDataTypeSize(S, id, sizeof(voidPtr));
if(status == 0) return;
ssSetOutputPortDataType(S, outputs::STREAMPTR_idx, id);
ssSetNumSampleTimes(S, 1);
ssSetNumPWork(S,1);
ssSetOperatingPointCompliance(S, USE_DEFAULT_OPERATING_POINT);
ssSetOptions(S,
SS_OPTION_WORKS_WITH_CODE_REUSE |
SS_OPTION_EXCEPTION_FREE_CODE);
}
#define MDL_START
static void mdlStart(SimStruct *S)
{
double seed = (double) mxGetScalar(ssGetSFcnParam(S, SEED_idx));
//printf("SEED: %i\n", seed);
RandomGenerator *generator = new RandomGenerator(seed);
(ssGetPWork(S))[0] = (void *)(generator);
}
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}
static void mdlOutputs(SimStruct *S, int_T tid)
{
voidPtr pwork = ssGetPWork(S)[0];
voidPtr *out = (voidPtr *)ssGetOutputPortSignal(S, 0);
//void *tmp = (void *) out[0];
out[0] = pwork;
// printf("randStream mdlOutputs address: %p\n", out[0]);
}
static void mdlTerminate(SimStruct *S)
{
delete ( RandomGenerator *)ssGetPWorkValue(S,0);
}
#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */
#include "simulink.c" /* MEX-file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif
%implements "sfun_RandomStream" "C"
%function BlockTypeSetup(block, system) void
%% The Target Language must be C
%if ::GenCPP!=1 && !IsModelReferenceSimTarget()
%<LibReportFatalError("Random Number Generator must be implemented in c++.")>
%endif
%<LibAddToCommonIncludes("RandomNumberStream.h")>
%endfunction %% BlockTypeSetup
%function Start(block, system) Output
/* %<Type> Block: %<Name> */
%assign seed = LibBlockParameterValue(block.Parameter[0], 0)
RandomGenerator *stream = new RandomGenerator((double) %<seed>);
%<LibBlockPWork(0, "", "", 0)> = stream;
%endfunction
%function Outputs(block, system) Output
/* %<Type> Block: %<Name> */
%assign pwork = LibBlockPWork(0, "", "", 0)
%assign out = LibBlockOutputSignal(0, "", "", 0)
%<out> = %<pwork>;
%endfunction
%function Terminate(block, system) Output
%switch SFunctionType
%case "TLC"
/* %<Type> Block: %<Name> */
%assign u = LibBlockPWork(0, "", "", 0)
RandomGenerator *stream = (RandomGenerator *)%<u>;
delete stream;
%break
%endswitch
%endfunction
#define S_FUNCTION_NAME sfun_NormalDist
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
#include <random>
#include "RandomNumberStream.h"
enum params {TS_idx, SIGMA_idx, MU_idx, NPARAMS};
enum outputs {VAL_idx, NOUT};
enum inputs {STREAM_idx, NINP};
static void mdlInitializeSizes(SimStruct *S)
{
int_T status;
DTypeId id;
ssSetNumSFcnParams(S, params::NPARAMS); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {return; }
ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 0);
if (!ssSetNumInputPorts(S, inputs::NINP)) return;
ssSetInputPortWidth(S, inputs::STREAM_idx, 1);
//DTypeId id = ssGetDataTypeId(S, "voidPtr");
id = ssRegisterDataType(S, "voidPtr");
if(id == INVALID_DTYPE_ID) return;
status = ssSetDataTypeSize(S, id, sizeof(voidPtr));
if(status == 0) return;
if(id != ssSetInputPortDataType(S, inputs::STREAM_idx, id)) return;
ssSetInputPortDirectFeedThrough(S, inputs::STREAM_idx, 1);
if (!ssSetNumOutputPorts(S, outputs::NOUT)) return;
ssSetOutputPortWidth(S, outputs::VAL_idx, 1);
ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);
ssSetOperatingPointCompliance(S, USE_DEFAULT_OPERATING_POINT);
//ssSetRuntimeThreadSafetyCompliance(S, RUNTIME_THREAD_SAFETY_COMPLIANCE_TRUE);
//ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
ssSupportsMultipleExecInstances(S, true);
}
static void mdlInitializeSampleTimes(SimStruct *S)
{
double ts = (double) mxGetScalar(ssGetSFcnParam(S, TS_idx));
ssSetSampleTime(S, 0, ts);
ssSetOffsetTime(S, 0, 0.0);
}
static void mdlOutputs(SimStruct *S, int_T tid)
{
voidPtr **inPtr = ((voidPtr**) ssGetInputPortSignal(S, inputs::STREAM_idx));
if(inPtr == NULL)
return;
voidPtr in = inPtr[0][0];
RandomGenerator *engine = static_cast<RandomGenerator*>(in);
if(engine == NULL) //deal with sample time issues...
return;
double sigma = (double) mxGetScalar(ssGetSFcnParam(S, SIGMA_idx));
double mu = (double) mxGetScalar(ssGetSFcnParam(S, MU_idx));
std::normal_distribution<double> distribution(mu, sigma);
double val = distribution(engine->getEngine());
double *out = (double*) ssGetOutputPortSignal(S, outputs::VAL_idx);
out[0] = val;
}
static void mdlTerminate(SimStruct *S)
{
}
/*=============================*
* Required S-function trailer *
*=============================*/
#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */
#include "simulink.c" /* MEX-file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif
%implements "sfun_NormalDist" "C"
%function BlockTypeSetup(block, system) void
%% The Target Language must be C
%if ::GenCPP!=1 && !IsModelReferenceSimTarget()
%<LibReportFatalError("Random Number Generator must be implemented in c++.")>
%endif
%<LibAddToCommonIncludes("RandomNumberStream.h")>
%endfunction %% BlockTypeSetup
%function Outputs(block, system) Output
/* %<Type> Block: %<Name> */
%assign pu = LibBlockInputSignalAddr(0, "", "", 0)
%assign y = LibBlockOutputSignal(0, "", "", 0)
RandomGenerator *engine = (RandomGenerator *)(*%<pu>);
%assign sigma = LibBlockParameterValue(block.Parameter[1], 0)
%assign mu = LibBlockParameterValue(block.Parameter[2], 0)
std::normal_distribution<double> distribution((double) %<mu>, (double) %<sigma>);
double val = distribution(engine->getEngine());
%<y> = val;
%endfunction