I am programming C in an embedded target. Due to the increasing complexity and testability issues, modularity is a must.
At a glance, the program is a control loop. Read physical inputs using internal hardware, apply some calculations, and apply calculated outputs. However, the control is quite sophisticated, and has many internal states and changing variables.
This control is divided into different modules, bundling the functionalities of the different states. Common tasks/calculations are provided in separate modules and are invoked in the different modules, in order to keep myself DRY. For enum and type consistency across the entire project, a top .H file is used (as OO strategies such as inheritance are not an option in my framework, as far as I am concerned).
My problem arises when deciding how to pass variables to and from modules.
My initial approach is:
mymodule.H:
struct Inputs{
int input1;
...
int inputN;
}
struct Outputs{
int output1;
...
int outputN;
}
void mymodule(Inputs in,Outputs* out);
And in the main function (or the module that invokes this module) have "Inputs" and "Outputs" type structs created.
Then, the variables are copied to the Inputs struct, the function is invoked (referencing the Outputs struct), and once completed, the content of this struct is used for further calculations.
However, this would result in a big memory footprint, as each module requires instances of InputType and OutputType to be created in the calling module. It is not an ellegant solution in my opinion. Dynamic allocation, by the way, is not allowed in my project.
Could you provide me some guidelines and/or alternate ideas to get to a good solution?
Thank you.
Added
One of the solutions could be passing InputStruct also as pointer, but as they are effectively inputs to the module, how could I assure that they are not modified along the code?
Added
By the way,another problem that arises is the fact that not all the modules receive the same variables, and with no inheritance mechanism available (as this is C), each module's structure has to be loaded with the proper values. I am quite obfuscated...
You don't have to accept a large memory footprint from passing parameters in and out of functions. The key is to pass the parameters by reference, and to use the const
keyword to ensure that inputs are not modified. A well-known example is:
int strcpy(char *destination, const char *source);
in which only the pointers to the source and destination character buffers are passed in, not a copy of the buffers, but the const
keyword prevents strcpy() from modifying the content of the source buffer.
If you have so many parameters that passing each individually is impractical then by all means pass pointers to structs into your function instead, again, using the const
keyword to guarantee that inputs are not modified:
int myFunc(struct myFuncOut *outputs, const struct myFuncIn *inputs);
Because the struct is passed by reference, myFunc() will operate on the same memory that the calling function uses (but it will not be able to write to the memory pointed to by inputs
thanks to the const
keyword), so the memory overhead of passing it to the function is only that of the pointer, which on a typical embedded system is four bytes, and there is no copying overhead.
As for your second implied problem, that the outputs from one function need to be passed as inputs to another function, but the parameter lists are not identical, there might not be much that you can do other than copy from one struct to another. If you are lucky you might be able to do something like this:
struct myFunc1Out
{
int common1;
int common2;
int common3;
};
struct myFunc2In
{
int common1;
int common2;
int common3;
int special1;
int special2;
}
struct myFunc2In data;
myFunc1((struct myFunc1Out *)(*data), *inputs);
data.special1 = 1;
data.special2 = 2;
myFunc2(*outputs, *data);
Do you see what's happening here? The first part of struct myFunc2In is the same as struct myFunc1Out, so you can just cast the former to the latter and the extra fields will be ignored. You can think of it as a (very) poor man's polymorphism.
Another, perhaps less obscure, way would be to pass a struct myFunc1Out to the second function along with a second struct for the additional parameters. The additional memory cost is one pointer. Perhaps you can organise your data into logical groups, each represented by a struct, such that there are not too many structs yet no struct contains fields that are not always required wherever the rest of that struct is used?
By the way, one of your comments seemed to imply that you expect that a definition of a struct has a memory overhead in the executable. This is not true. Memory is used only when an instance of the struct is allocated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With