How to design generic backward compatible API for embedded software application library interface in C? - c

I am tasked to assist with the design of a dynamic library (exposed with a C interface) aimed to be used in embed software application on various embed platform (Android,Windows,Linux).
Main requirements are speed , and decoupling.
For the decoupling part : one of our requirement is to be able to facilitate integration and so permit backward compatibility and resilience.
My library have some entry points that should be called by the integrating software (like an initialize constructor to provide options as where to log, how to behave etc...) and could also call some callback in the application (an event to inform when task is finished).
So I have come with several propositions but as each of one not seems great I am searching advice on a better or standard ways to achieve decoupling an d backward compatibility than this 3 ways that I have come up :
First an option that I could think of is to have a generic interface call for my exposed entry points for example with a hashmap of key/values for the parameters of my functions so in pseudo code it gives something like :
myLib.Initialize(Key_Value_Option_Array_Here);
Another option is to provide a generic function to provide all the options to the library :
myLib.SetOption(Key_Of_Option, Value_OfOption);
myLib.SetCallBack(Key_Of_Callbak, FunctionPointer);
When presenting my option my collegue asked me why not use a google protobuf argument as interface between the library and the embed software : but it seems weird to me, as their will be a performance hit on each call for serialization and deserialization.
Are there any more efficient or standard way that you coud think of?

You could have a struct for optional arguments:
typedef struct {
uint8_t optArg1;
float optArg2;
} MyLib_InitOptArgs_T;
void MyLib_Init(int16_t arg1, uint32_t arg2, MyLib_InitOptArgs_T const * optionalArgs);
Then you could use compound literals on function call:
MyLib_Init(1, 2, &(MyLib_InitOptArgs_T){ .optArg2=1.2f });
All non-specified values would have zero-ish value (0, NULL, NaN), and would be considered unused. Similarly, when passing NULL for struct pointer, all optional arguments would be considered unused.
Downside with this method is that if you expect to have many new arguments in the future, structure could grow too big. But whether that is an issue, depends on what your limits are.
Another option is to simply have multiple smaller initialization functions for initializating different subsystems. This could be combined with the optional arguments system above.

Related

How to create FMU slave and initialise FMU in C using Modelica's fmi headers

I'm creating a simple FMI demo system to try out FMI where I have 1 simulator connected to an FMU which computes the state of the system (represented as a number calculated from a closed-form equation) and another FMU that controls the system via a parameter in the closed-form equation. So the system looks something like
FMU-system <--> Simulator <--> FMU-control
In every iteration, I'm updating the system state based on 1 equation, and passing it to the control, which returns a parameter to be passed to the system.
I'm using FMI 2.0.3, and have read the specification. Right now I have 3 files, 1 to act as a simulator and 2 to act as the FMUs. But I'm having difficulties with the implementation of the FMUs and the initialisation of the simulator.
To initialise the FMU, my understanding is I need to call fmi2Instantiate which has this signature.
fmi2Component fmi2Instantiate(fmi2String instanceName, fmi2Type fmuType, fmi2String fmuGUID, fmi2String fmuResourceLocation, const fmi2CallbackFunctions* functions, fmi2Boolean visible, fmi2Boolean loggingOn);
But I don't know what to pass in the function for the GUID, resource location and callback function. How should I implement the callback function and initialisation?
Then to implement the FMU, my understanding is I need to implement fmi2SetReal, fmi2GetReal and fmi2DoStep, but I can't figure out how to implement them in terms of code. These are the signatures
fmi2Status setReal(fmi2Component c, fmi2ValueReference vr[], size_t nvr, fmi2Real value[])
fmi2Status getReal(fmi2Component c, fmi2ValueReference vr[], size_t nvr, fmi2Real value[])
fmi2Status doStep(fmi2Component c, fmi2Real currentCommunicationPoint, fmi2Real communicationStepSize, fmi2Boolean noSetFMUStatePriorToCurrentPoint)
But I can't figure out how to implement these functions. Is fmi2Component c meaningless here? And I suppose I have to do the system state computation for the FMU-system in doStep. How should I update the state and pass the code here?
Sorry if this is too many questions but I was trying to look for a tutorial too and I couldn't find any.
https://github.com/traversaro/awesome-fmi
This is a curated list of Functional Mock-up Interface (FMI) libraries, tools and resources.
There are non commercial tools available. Check them out, you will get idea to implement these functions for your application.
A good starting point to implement FMI support are the open source Reference FMUs (which recently also got a simple FMU simulator) and fmpy:
https://github.com/CATIA-Systems/FMPy
https://github.com/modelica/Reference-FMUs/tree/main/fmusim

Difference between Data_Wrap_Struct and TypedData_Wrap_Struct?

I'm wrapping a C struct in a Ruby C extension but I can't find the differente between Data_Wrap_Struct and TypedData_Wrap_Struct in the docs, what's the difference between the two functions?
It's described pretty well in the official documentation.
The tl;dr is that Data_Wrap_Struct is deprecated and just lets you set the class and the mark/free functions for the wrapped data. TypedData_Wrap_Struct instead lets you set the class and then takes a pointer to a rb_data_type_struct structure that allows for more advanced options to be set for the wrapping:
the mark/free functions as before, but also
an internal label to identify the wrapped type
a function for calculating memory consumption
arbitrary data (basically letting you wrap data at a class level)
additional flags for garbage collection optimization
Check my unofficial documentation for a couple examples of how this is used.

How can I parametrize a callback function that I submit to an external library

Say I have an external library that computes the optima, say minima, of a given function. Say its headers give me a function
double[] minimizer(ObjFun f)
where the headers define
typedef double (*ObjFun)(double x[])
and "minimizer" returns the minima of the function f of, say, a two dimensional vector x.
Now, I want to use this to minimize a parameterized function. I don't know how to express this in code exactly, but say if I am minimizing quadratic forms (just a silly example, I know these have closed form minima)
double quadraticForm(double x[]) {
return x[0]*x[0]*q11 + 2*x[0]*x[1]*q12 + x[1]*x[1]*q22
}
which is parameterized by the constants (q11, q12, q22). I want to write code where the user can input (q11, q12, q22) at runtime, I can generate a function to give to the library as a callback, and return the optima.
What is the recommended way to do this in C?
I am rusty with C, so asking about both feasibility and best practices. Really I am trying to solve this using C/Cython code. I was using python bindings to the library so far and using "inner functions" it was really obvious how to do this in python:
def getFunction(q11, q12, q22):
def f(x):
return x[0]*x[0]*q11 + 2*x[0]*x[1]*q12 + x[1]*x[1]*q22
return f
// now submit getFunction(/*user params*/) to the library
I am trying to figure out the C construct so that I can be better informed in creating a Cython equivalent.
The header defines the prototype of a function which can be used as a callback. I am assuming that you can't/won't change that header.
If your function has more parameters, they cannot be filled by the call.
Your function therefor cannot be called as callback, to avoid undefined behaviour or bogus values in parameters.
The function therefor cannot be given as callback; not with additional parameters.
Above means you need to drop the idea of "parameterizing" your function.
Your actual goal is to somehow allow the constants/coefficients to be changed during runtime.
Find a different way of doing that. Think of "dynamic configuration" instead of "parameterizing".
I.e. the function does not always expect those values at each call. It just has access to them.
(This suggests the configuration values are less often changed than the function is called, but does not require it.)
How:
I only can think of one simple way and it is pretty ugly and vulnerable (e.g. due to racing conditions, concurrent access, reentrance; you name it, it will hurt you ...):
Introduce a set of global variables, or better one struct-variable, for readability. (See recommendation below for "file-global" instead of "global".)
Set them at runtime to the desired values, using a separate function.
Initialise them to meaningful defaults, in case they never get written.
Read them at the start of the minimizing callback function.
Recommendation: Have everything (the minimizing function, the configuration variable and the function which sets the configuration at runtime) in one code file and make the configuration variable(s) static (i.e. restricts access to it this code file).
Note:
The answer is only the analysis that and why you should not try paraemeters.
The proposed method is not considered part of the answer; it is more simple than good.
I invite more holistic answers, which propose safer implementation.

Adding new data types (and arithmetic operators for new data types) in picoc

I have just stumbled across picoc and I am very impressed with what it can do - especially the fact that it can be extended by adding new functions etc. It saves me from going down the route of trying to "roll my own" interpreter.
However, I am wondering if there is anyway I can extend picoc by:
Adding new data types (for example, MySimpleDataType, MyPointerDataType)
Adding simple arithmetic operator functions (+,-,/, * etc) for my new data types.
Does anyone have any experience of doing this, or can someone provide pointers on how to go about adding new data types and their operator functions to picoc?
[[Edit]]
On further inspection of the code, I believe I have found how to add new data types (by modifying type.c). However, it is still not clear to me how to add arithmetic operators for new data types in picoc. Any help appreciated,
In general, C does not have operators overloading (while C++ does). Picoc is positioned as very tiny and having only the essentials, so I don't think it provides any extensions for it.
Adding new types can be done in the same manner that you can add new functions. A simple example of this can be had by examining picoc's source stdbool.c, where you'll find a typedef int bool; in the StdboolDefs element. You have to look elsewhere, include.c, to find the element in use; you'll find it as the "SetupCSource" parameter to an IncludeRegister() call.
Regarding adding new operators -- of course it's possible but only with a rather invasive change to the picoc library. As #yeputons said, the C language doesn't allow you to change or add operators, so there's no reason for picoc to directly support that.

What is a MsgPack 'zone'

I have seen references to 'zone' in the MsgPack C headers, but can find no documentation on what it is or what it's for. What is it? Furthermore, where's the function-by-function documentation for the C API?
msgpack_zone is an internal structure used for memory management & lifecycle at unpacking time. I would say you will never have to interact with it if you use the standard, high-level interface for unpacking or the alternative streaming version.
To my knowledge, there is no detailed documentation: instead you should refer to the test suite that provides convenient code samples to achieve the common tasks, e.g. see pack_unpack_c.cc and streaming_c.cc.
From what I could gather, it is a move-only type that stores the actual data of a msgpack::object. It very well might intended to be an implementation detail, but it actually leaks into users' code sometimes. For example, any time you want to capture a msgpack::object in a lambda, you have to capture the msgpack::zone object as well. Sometimes you can't use move capture (e.g. asio handlers in some cases will only take copyable handlers, or your compiler doesn't support the feature). To work around this, you can:
msgpack::unpacked r;
while (pac_.next(&r)) {
auto msg = result.get();
io_->post([this, msg, z = std::shared_ptr<msgpack::zone>(r.zone().release())]() {
// msg is valid here
}));
}

Resources