A frame read out from external device is stored in shared memory (in a struct), to be used both by main (C++) application, and an ANSI C library.
For reasons a bit too broad to explain here the library must remain pure ANSI C, and must retain access to the structure in its "pure ANSI C" form. But the main application uses the data in a lot of places, and it's a nuisance to do this the "ANSI C" way, treating it as a dumb data container. It would be much nicer if it was a class - if I could add constructors, copy constructor, comparison operators, a neater 'is valid' method (currently checked as absence of a magic number in one of the struct fields), generally a lot of stuff that a C++ class can do, but an ANSI C struct can't. Except if I replace the struct with the class in the shared memory, it will break compatibility with the library.
What's a neat way to achieve this? Create a class that inherits from the struct? Inheritance through composition? A separate class with a set of conversion methods? Something else I didn't think about? Some transparent way to keep the data visible to C unchanged, but enhanced with class features for C++?
Note: both C++ and C operate on the same instance of the structure. Main app reads out the frame out of the device, writes to the structure in shared memory, then calls the library functions to do their magic on the frame (possibly, but not necessarily modifying it; business logic), then performs own operations on it (display, logging, re-broadcasting on other media etc.
I have full control over the C++ code, but C code is mostly out of my control; I can create a local copy of the structure, if that's beneficial, but my copy and the 'business logic instance' should remain in sync, or at least be synced before and after each library function call (operation under my control, but timing dictated by system requirements.)
edit: some extra details, as requested:
the "business logic" is implemented in the C library, customized C code generated by an external application (for PC) from input from user (graphical interface for drafting the logic; think "block diagram"), varies per device (many users, even more devices). The device requires cross-compiling; only an ANSI C cross-compiler is available in a form that can be easily bundled with the PC application; C++ cross-compiler is available only on the system developer's (my) PCs ; its installation process and license make it impossible to bundle with the (sold) generator app.
the library and the C++ application on the device use shared memory as storage of about all input and output data, for two reasons:
- primarily because the volume and variety of that data would make it extremely difficult to provide it as parameters in function calls (over 20 wildly varying external systems the device can cooperate with, each with own communication protocol, each providing inputs and/or accepting outputs which may be utilized in business logic). C++ app handles all communication and converts the data back and forth between the various interfaces and an "easily digestible" data format stored in the shared memory for consumption by the library as required (by the particular instance of business logic).
- But there are other applications running on the device - a WWW server, a debug app, etc - which can peer into the shared memory too, to display current state, allow real-time parameter tweaks etc. While such "centralized storage"/"superglobal" may be considered an anti-pattern, considering the wild variety of interactions between the systems (internal, and external, where the C++ serves as a central hub for connecting them) and makes for a much clearer structure than what kind of byzantine web would arise if I tried to connect every data provider with every data consumer directly.
- The main app handles synchronization (timing, locking) reads from shared memory for all interfaces where it matters; others can just peer into the shared memory and pick what they need whenever they want (read-only); resulting race condition errors will result in a perfectly acceptable momentary glitch that will be corrected come next 'tick'. All writes are synchronized.
- because the C library is the primary, most important consumer and provider for the shared memory, the structure in shared memory must remain C compatible.