Sending different objects to a queue

oahmad2 wrote on Friday, May 28, 2010:

I am looking for the best way to pass different objects into the same queue for my lcd task.  I am using C.

As an example, I am faced with sending different objects such as strings, bmps, buttons, etc to an lcdTask’s queue.

Is there an elegant and efficient way to do this, or is this a no-no in practice?

Here are my thoughts on one approach for doing this:

I was thinking of using a struct that contains

struct object
int dataType; //string, bmp, etc
void * data; //pointer to the string, bmp, etc

and then the lcd task will get the dataType from this **object **struct and then use that information to properly dereference the data.  Is this a good way to send different datatypes to the same queue or is there a better way?  The one question I have about this approach is how do the tasks putting this data into the lcdQueue find out the type of datatypes available (string, bmp, etc).  If I define these datatype structs in lcd.h (where the lcd task is), then what is the best way for functions residing in other modules to find out the datatypes and how to populate them…right now, the way I was thinking to handle this is to store these datatype structs in the lcd.h file to share the datatype (string, bmp) directly.  Is there a better way to do this, for example, by utilizing a public function in lcd.c that the callee residing in another module can ask for the different types of datatypes (string, bmp) available?

Thanks for your insight and help.

richard_damon wrote on Friday, May 28, 2010:

Let me give a few suggestions to help here.

First, change the int dataType to and enum. This has the advantage that it clearly documents the list of types and the value to be used. Also some compilers when you switch on an enum will generate an error if there is an unhandled enum balue.

Second, change the void* to a union of pointer types. This again makes it clear what types are allowed, and also allows for an int parm (or other simple type like long or float) if some message could use that, this may not apply to your case, but in general for a multi-type message might be useful.

This would make your structure look something like this:

enum lcd_msg_type { LCD_String, LCD_Bmp };
struct lcd_message {
  enum lcd_msg_type type;
  union {
    char* stringptr;
    struct bmp* bmpptr;
  } data;

(add more enum and pointers as needed)

I would also not put this definition in lcd.h, but instead provide a set of functions like lcd_string(char*) that take the various things to be sent to the lcd, and add them to the queue itself. That way there is less code to review to make sure the type and data are consistent. The definition would then just reside in lcd.c (or in lcd_impl.h if lcd.c grows too big and needs to be split, or if style guides request that all structures be defined in a header.)

oahmad2 wrote on Friday, May 28, 2010:

Thanks Richard,

This is the exact information that I was looking.

I’m curious, can void pointers be utilized in addition to your suggestion to improve things further.  Do you generally use void pointers in your work and if so, do you have suggestions where they are best utilized? 

Also, I have a question about style.  I tend to put my private functions, #defines of private scope, and other data types that are not public in the .c file, the .h file only contains public functions, #defines, etc.  Is this how you generally approach your files? If I understand correctly, you store all your private stuff in the _impl.h file, the public in .h and the code in .c.

These design answers are a great help to me.

richard_damon wrote on Friday, May 28, 2010:

I personally try to avoid void pointers if reasonably possible, but I do realize that there are times when it just isn’t possible or adds significant clutter to avoid them. One of the reasons to avoid void* pointers is that they can mask a number of errors (sending something of the wrong type, or wrong level of indirection if sending pointers to pointers). Another is if you want to inter-operate with C++ you need to realize they work differently. The main time I see them being useful is when the routine really doesn’t care about the format of the memory being passed, but is going to treat it as raw bytes (and internally will probably convert the pointer to a unsigned char*, but the interface is made void* so a cast isn’t needed at the call).

Of course a void* pointer could be used as one of the options.

As to style, I also tend to place private information at the top of the .c file, as long as I don’t start getting too big of a file trying to keep it all together.  If the file starts to get larger, then the design gets partitioned, and stuff that is needed to interconnect the pieces goes into a “private” header (which is what the _impl designates).

I do know of some organizations which have established code style guidelines that require all structure definitions to be in a header file (and sometimes each structure gets its own header file unless it is very tightly coupled with another structure). The advantage of this, particularly for a large code base, is that if you want to look up the definition/documentation for a structure, you only need to search through the .h files (and with the 1 struct per file rule, the name of the file is the name of the struct). These guidelines also sometimes have significant documentation rules for adding comments to the .h file to fully document the use and members of the struct.

Basic rule, you follow the customers guidelines.

oahmad2 wrote on Friday, May 28, 2010:

Thanks for the insight into these issues.