Opened 6 years ago

Closed 6 years ago

Last modified 6 years ago

#5152 closed defect (fixed)

FMI fmi2CallbackFunctions is incompatible with C++

Reported by: federico.terraneo@… Owned by: Lennart Ochel
Priority: high Milestone: Future
Component: FMI Version: v1.13.0-dev-nightly
Keywords: Cc:

Description

OpenModelica when exporting FMU produces a fmi2FunctionTypes.h
with the following code

typedef struct {

const fmi2CallbackLogger logger;
const fmi2CallbackAllocateMemory allocateMemory;
const fmi2CallbackFreeMemory freeMemory;
const fmi2StepFinished stepFinished;
const fmi2ComponentEnvironment componentEnvironment;

} fmi2CallbackFunctions;

where all fields are declared const. However in C++ const fields in structs/classes can only be initialized in a constructor's intialization list, and the struct has no constructor. I think const should be removed to make this code interoperable with C++.

This is what g++ produces:

fmitest.cpp: In function ‘int main()’:
fmitest.cpp:14:27: error: use of deleted function ‘fmi2CallbackFunctions::fmi2CallbackFunctions()’

fmi2CallbackFunctions callbacks;


In file included from FmiTest.Model/sources/include/fmi2/fmi2Functions.h:148:0,

from fmitest.cpp:8:

FmiTest.Model/sources/include/fmi2/fmi2FunctionTypes.h:135:3: note: ‘fmi2CallbackFunctions::fmi2CallbackFunctions()’ is implicitly deleted because the default definition would be ill-formed:

} fmi2CallbackFunctions;


FmiTest.Model/sources/include/fmi2/fmi2FunctionTypes.h:135:3: error: uninitialized const member in ‘struct fmi2CallbackFunctions’
FmiTest.Model/sources/include/fmi2/fmi2FunctionTypes.h:130:37: note: ‘void (* const fmi2CallbackFunctions::logger)(fmi2ComponentEnvironment, fmi2String, fmi2Status, fmi2String, fmi2String, ...)’ should be initialized

const fmi2CallbackLogger logger;

~

FmiTest.Model/sources/include/fmi2/fmi2FunctionTypes.h:135:3: error: uninitialized const member in ‘struct fmi2CallbackFunctions’

} fmi2CallbackFunctions;


FmiTest.Model/sources/include/fmi2/fmi2FunctionTypes.h:131:37: note: ‘void* (* const fmi2CallbackFunctions::allocateMemory)(size_t, size_t)’ should be initialized

const fmi2CallbackAllocateMemory allocateMemory;

~

FmiTest.Model/sources/include/fmi2/fmi2FunctionTypes.h:135:3: error: uninitialized const member in ‘struct fmi2CallbackFunctions’

} fmi2CallbackFunctions;


FmiTest.Model/sources/include/fmi2/fmi2FunctionTypes.h:132:37: note: ‘void (* const fmi2CallbackFunctions::freeMemory)(void*)’ should be initialized

const fmi2CallbackFreeMemory freeMemory;

~

FmiTest.Model/sources/include/fmi2/fmi2FunctionTypes.h:135:3: error: uninitialized const member in ‘struct fmi2CallbackFunctions’

} fmi2CallbackFunctions;


FmiTest.Model/sources/include/fmi2/fmi2FunctionTypes.h:133:37: note: ‘void (* const fmi2CallbackFunctions::stepFinished)(fmi2ComponentEnvironment, fmi2Status)’ should be initialized

const fmi2StepFinished stepFinished;

~

FmiTest.Model/sources/include/fmi2/fmi2FunctionTypes.h:135:3: error: uninitialized const member in ‘struct fmi2CallbackFunctions’

} fmi2CallbackFunctions;


FmiTest.Model/sources/include/fmi2/fmi2FunctionTypes.h:134:37: note: ‘void* const fmi2CallbackFunctions::componentEnvironment’ should be initialized

const fmi2ComponentEnvironment componentEnvironment;

Attachments (1)

test.zip (1.3 KB ) - added by federico.terraneo@… 6 years ago.

Download all attachments as: .zip

Change History (6)

by federico.terraneo@…, 6 years ago

Attachment: test.zip added

comment:1 by Rüdiger Franke, 6 years ago

There are a couple of misunderstandings here. First, OpenModelica does not "produce" fmi2CallbackFunctions.h. The file is part of the FMI standard and OpenModelica just redistributes it. Second, the FMI interface is a C interface, not a C++ interface. You might consider writing the direct interface code in C and call that code from your C++ program.

Also, consider placing the ticket in the FMI issue tracker (https://trac.modelica.org/fmi).

comment:2 by federico.terraneo@…, 6 years ago

Resolution: fixed
Status: newclosed

Thanks for the comment.
The file contains a

#ifdef cplusplus
extern "C" {
#endif

which in my opinion means that it is a C file meant to be interoperable with C++. I thus still consider it a bug.
I didn't know that OpenModelica just redistributes that file. I will place a ticket in the FMI issue tracker as you said. Thanks.

comment:3 by pmai@…, 6 years ago

I've commented on the migrated FMI issue (https://github.com/modelica/fmi-standard/issues/443) to indicate that this is IMHO not really an FMI issue itself: The FMI headers are written from the PoV of the FMU, not from the PoV of the calling environment, hence the inability to initialize the contents of fmi2CallbackFunctions is not a problem (calling environments need to use different mechanisms to populate a struct that can then be cast to fmi2CallbackFunctions, e.g. by using their own headers or struct definitions).

While this might (or might not) be changed in FMI 3.0 (by dropping the const protections in favour of ease of use for importers, or not, depending on consensous opinion), it is the way it is for FMI 2.0.

It is hard to tell for sure without access to the generated code, but it seems to me that the code in question is trying to instantiate a variable of type fmi2CallbackFunctions itself, which an FMU has no need to do, and a calling environment needs to handle in some other fashion. So if fmitest.cpp is OpenModelica-generated code, then this would seem to be an OpenModelica bug, and if fmitest.cpp is user code, than this is a bug in user code instead.

comment:4 by Adrian Pop, 6 years ago

You can do something like this in fmitest.cpp:

#include <iostream>
#include <cstdlib>

using namespace std;

extern "C"
{

#define FMI2_FUNCTION_PREFIX
#include "fmi2Functions.h"

void logfunc(fmi2ComponentEnvironment, fmi2String, fmi2Status, fmi2String, fmi2String, ...)
{
    cout<<"Something should be logged"<<endl;
}

void stepFinished(fmi2ComponentEnvironment, fmi2Status)
{
    cout<<"stepFinished called"<<endl;
}
  
fmi2CallbackFunctions* callbacks(void)
{
  fmi2CallbackFunctions *c = (fmi2CallbackFunctions*)malloc(sizeof(fmi2CallbackFunctions));
  fmi2CallbackFunctions cc = { logfunc, calloc, free, stepFinished };
  memcpy(c, &cc, sizeof(fmi2CallbackFunctions));
}

} // extern C


int main()
{
    cout<<fmi2GetVersion()<<endl;
    
    fmi2Component model=fmi2Instantiate(
        "test",                                   //fmi2String instanceName,
        fmi2CoSimulation,                         //fmi2Type fmuType,
        "{78daf3ce-5bc6-4c3e-8e37-44ca0d416ef3}", //fmi2String fmuGUID,
        "FmiTest.Model/resources",                //fmi2String fmuResourceLocation,
        callbacks(),                               //const fmi2CallbackFunctions* functions,
        true,                                     //fmi2Boolean visible,
        true                                      //fmi2Boolean loggingOn
    );

    cout<<model<<endl;
}

comment:5 by federico.terraneo@…, 6 years ago

Thank you for your answers, however I reported the issue to the Modelica FMI issue tracker (​https://trac.modelica.org/fmi) and received the suggestion that C++11 uniform initialization could be used in this context.

This is both convenient and elegant, as the struct initialization would look like:

fmi2CallbackFunctions callbacks{

&logfunc, logger
calloc,
allocateMemory
free, freenMemory
&stepFinished,
stepFinished
nullptr componentEnvironment

};

However, I can't find the reply post, maybe it got lost when they moved the issue tracker to github.

From my perspective, now that I have a good way of initializing the struct from C++, the issue is fixed.

Note: See TracTickets for help on using tickets.