psvaibhav
psvaibhav

Reputation: 279

Handling different datatypes in a single structure

I need to send some information on a VxWorks message queue. The information to be sent is decided at runtime and may be of different data types. I am using a structure for this -

struct structData
{
  char m_chType;    // variable to indicate the data type - long, float or string
  long m_lData;     // variable to hold long value
  float m_fData;    // variable to hold float value
  string m_strData; // variable to hold string value
};

I am currently sending an array of structData over the message queue.

structData arrStruct[MAX_SIZE];

The problem here is that only one variable in the structure is useful at a time, the other two are useless. The message queue is therefore unneccessarily overloaded. I can't use unions because the datatype and the value are required. I tried using templates, but it doesn't solve the problem.I can only send an array of structures of one datatype at a time.

template <typename T>
struct structData
{
  char m_chType;
  T m_Data;
}

structData<int> arrStruct[MAX_SIZE];

Is there a standard way to hold such information?

Upvotes: 4

Views: 2754

Answers (6)

MadH
MadH

Reputation: 1508

Try QVariant in Qt

Upvotes: 0

bltxd
bltxd

Reputation: 9113

+1 for 1800 and Ylisar.

Using an union for this kind of things is probably the way to go. But, as others pointed out, it has several drawbacks:

  • inherently error prone.
  • not safely extensible.
  • can't handle members with constructors (although you can use pointers).

So unless you can built a nice wrapper, going the boost::variant way is probably safer.


This is a bit offtopic, but this issue is one of the reasons why languages of the ML family have such a strong appeal (at least for me). For example, your issue is elegantly solved in OCaml with:

(*
 * LData, FData and StrData are constructors for this sum type,
 * they can have any number of arguments
 *)
type structData = LData of int | FData of float | StrData of string

(*
 * the compiler automatically infers the function signature
 * and checks the match exhaustiveness.
 *)
let print x =
    match x with
      | LData(i) -> Printf.printf "%d\n" i 
      | FData(f) -> Printf.printf "%f\n" f
      | StrData(s) -> Printf.printf "%s\n" s

Upvotes: 0

Ylisar
Ylisar

Reputation: 4291

You can use boost::variant for this.

Upvotes: 6

neuro
neuro

Reputation: 15180

There are many ways to handle different datatypes. Besides the union solution you can use a generic struct like :

typedef struct
{
    char m_type;
    void* m_data;
} 
structData;

This way you know the type and you can cast the void* pointer into the right type. This is like the union solution a more C than C++ way of doing things. The C++ way would be something using inheritance. You define a base "Data" class an use inheritance to specialize the data. You can use RTTI to check for type if needed.

But as you stated, you need to send your data over a VxWork queue. I'm no specialist but if those queues are OS realtime queue, all the previous solutions are not good ones. Your problem is that your data have variable length (in particular string) and you need to send them through a queue that probably ask for something like a fixed length datastruct and the actual length of this datastruct.

In my experience, the right way to handle this is to serialize the data into something like a buffer class/struct. This way you can optimize the size (you only serialize what you need) and you can send your buffer through your queue.

To serialize you can use something like 1 byte for type then data. To handle variable length data, you can use 1 to n bytes to encode data length, so you can deserialize the data.

For a string : 1 byte to code the type (0x01 = string, ...) 2 bytes to code the string length (if you need less than 65536 bytes) n data bytes

So the string "Hello" will be serialized as :

0x00 0x00 0x07 0x65 0x48 0x6c 0x6c

You need a buffer class and a serializer/deserializer class. Then you do something like :

serialize data
send serialized data into queue

and on the other side

receive data
deserialize data

I hope it helps and that I have not misunderstood your problem. The serialization part is overkill if the VxWorks queues are not what I think ...

Upvotes: 5

1800 INFORMATION
1800 INFORMATION

Reputation: 135285

I don't see why you cannot use a union. This is the standard way:

struct structData
{
  char m_chType;    // variable to indicate the data type - long, float or string
  union
  {
    long m_lData;         // variable to hold long value
    float m_fData;    // variable to hold float value
    char *m_strData; // variable to hold string value
  }
};

Normally then, you switch on the data type, and then access on the field which is valid for that type.

Note that you cannot put a string into a union, because the string type is a non-POD type. I have changed it to use a pointer, which could be a C zero-terminated string. You must then consider the possibility of allocating and deleting the string data as necessary.

Upvotes: 8

Roddy
Roddy

Reputation: 68033

Be very careful with the "string" member in the message queue. Under the hood, it's a pointer to some malloc'd memory that contains the actual string characters, so you're only passing the 'pointer' in your queue, not the real string.

The receiving process may potentially not be able to access the string memory, or -worse - it may have already been destroyed by the time your message reader tries to get it.

Upvotes: 2

Related Questions