views:

185

answers:

5

I am working on an embedded system that has different output capabilities (digital out, serial, analog, etc). I am trying to figure out a clean way to pass many of the variables that will control those functions.

I don't need to pass ALL of them too often, but I was hoping to have a function that would read the input data (in this case from a TCP network), and then parse the data (IE, the 3rd byte contains the states of 8 of the digital outputs (according to which bit in that byte is high or low)), and put that into a variable where I can then use elsewhere in the program.

I wanted that function to be separate from the main() function, but to do so would require passing pointers to some 20 or so variables that it would be writing to. I know I could make the variables global, but I am trying to make it easier to debug by making it obvious when a function is allowed to edit that variable, by passing it to the function.

My best idea was a struct, and just pass a pointer to it, but wasn't sure if there was a more efficient way, especially since there is only really 1 function that would need to access all of them at once, while most others only require parts of the information that will be stored in this bunch of state variables.

So anyway, is there a clean way to send many variables between functions at once that need to be edited?

+5  A: 

Using a struct with a pointer to it is really a good bet. The code may be a little longer to write, but it will look nice. You can pass a struct by value, but pass by reference will avoid copying the data.

Another alternative is to create a struct composed of structs. Then you can be more selective in what data you pass to each function by passing the whole thing, just one or two of them, or an individual element of a struct.

typedef struct a {
  struct b {
    int b1;
    int b2;
  } b_s;
  struct c {
    int c1;
    int c2;
  } c_s;
} a_s;
WhirlWind
A: 

I would say that a struct is your best bet. You can chose to pass just one of the members to a function if you don't need all the variables in a particular function. You might also benefit from a bitfield for that 3rd byte if you need to set individual bits. Of course, this bitfield can also be a member of your struct.

Fred
A: 

What I would do is write them all to accept the struct. After the program is functioning if efficiency is an issue it should be easy to go back and change those functions that only use one or two of the variables to only accept those to variables as parameters.

Spencer Ruport
A: 

Passing a pointer to a structure is usually the "clean" way to go. Defining the data structure intelligently can give this method both power and flexibility. For example take the data structure:

struct func_params {
    int type;
    union {
        struct {
            int    port;
            int    direction;
            long   target_addr;
        } digital_io;
        struct {
            int    channel;
            long   sample_rate;
        } analog_in;
        struct {
            int    channel;
            int    async;
            int    hold_output;
        } analog_out;
        struct {
            int    port;
            int    direction;
            int    encoding;
        } serial_io;
    };
};

Define your function with something like

my_function (struct func_params* pStruct, size_t length)

When using the function, first create a buffer large enough to contain the data structure plus the size of any potentially variable-length data that you will be sending to the function (data to send out a port, for instance). The first part of the data buffer will consist of the structure you defined. The rest of the buffer (length - sizeof(struct func_params) bytes) will be where you store your data. Your function reads the type structure member and determines how to interpret the rest of the structure, and after processing the structure it can determine how to interpret the variable-length data section.

This may be a bit more involved than what you had in mind. In the embedded world, many communications protocols (SCSI, for example) communicate by sending a header followed by a variable-length data section like this. It gives the functions a clean interface, does a good job bundling related information together, and makes it easier to change the function in the future without changing the calling parameters.

bta
+1  A: 

This is a classic use of a "parameter block" -- just a pointer to a well-known struct.

The upsides include: Efficient, since any access to a given paramter is an adderess plus an offset; Ease of debugging (you can see all the parameters in one 'print'); Locality (caches nicely); Easy to turn into a stackable set of calls, where you don't easily know what parameters any given function down the stack might need.

The downsides include: You cannot tell by looking at a function call which parameters are important to the callee -- you have to know, or go digging in the source of that function; It's easier to cause side effects, especially of you allow the called functions to modify the block (bad, bad, bad!); Increases coupling (every function can see every parameter, which can lead to temptations...)

Kevin Little