Question

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...

Was it helpful?

Solution

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.

OTHER TIPS

module.c:

#include "module.h"

struct Inputs *getInput() { 
    static struct Inputs inputs;
    return &inputs;
}

struct Outputs *getOutput() { 
    static struct Outputs outputs;
    return &outputs;
}

struct Outputs *mymodule() {
    struct Outputs *o = getOutput();
    struct Inputs  *i = getInput();
    o->output[0] = i->input[0];
    return o;
}

module.h:

#define N 10

struct Inputs {
    int input[N];
};

struct Outputs {
    int output[N];
};


struct Inputs *getInputs();
struct Inputs *getOutputs();
void mymodule();

One possibility is to hide the variables behind setter/getter functions and then use the preprocessor to control visibility of those functions in the modules.

iovars.c:

/* iovars.c */

static int s_input1;
static int s_input2;
static int s_output1;
static int s_output2;

int GetIn1(void)  { return s_input1; }
int GetIn2(void)  { return s_input2; }
void SetIn1(int value) { s_input1 = value; }
void SetIn2(int value) { s_input2 = value; }
int GetOut1(void)  { return s_output1; }
int GetOut2(void)  { return s_output2; }
void SetOut1(int value) { s_output1 = value; }
void SetOut2(int value) { s_ouput2 = value; }

iovars.h:

/* iovars.h */

#ifdef USING_IN1_READONLY || USING_IN1_READWRITE
int GetIn1(void);
#ifdef USING_IN1_READWRITE
void SetIn1(int value);
#endif
#endif

#ifdef USING_IN2_READONLY || USING_IN2_READWRITE
int GetIn2(void);
#ifdef USING_IN2_READWRITE
void SetIn2(int value);
#endif
#endif

#ifdef USING_OUT1_READONLY || USING_OUT1_READWRITE
int GetOut1(void);
#ifdef USING_OUT1_READWRITE
void SetOut1(int value);
#endif
#endif

#ifdef USING_OUT2_READONLY || USING_OUT2_READWRITE
int GetOut2(void);
#ifdef USING_OUT2_READWRITE
void SetOut2(int value);
#endif
#endif

OK that was a bit tedious but you can now control visibility and writability on a variable by variable basis in your modules:

/* moduleA.c */

#define USING_IN1_READONLY
#define USING_OUT1_READWRITE
#include "iovars.h"

/* code in this module can only see functions to read input 1
   and to read or write output 1 */

BTW this answer builds on responses to this question which also contain some discussion of getting all those functions to inline.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top