Handling different datatypes in a single structure

2020-03-24 05:52发布

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?

标签: c++
6条回答
干净又极端
2楼-- · 2020-03-24 06:16

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

查看更多
我想做一个坏孩纸
3楼-- · 2020-03-24 06:16

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.

查看更多
淡お忘
4楼-- · 2020-03-24 06:19

You can use boost::variant for this.

查看更多
Deceive 欺骗
5楼-- · 2020-03-24 06:22

+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
查看更多
够拽才男人
6楼-- · 2020-03-24 06:35

Try QVariant in Qt

查看更多
Ridiculous、
7楼-- · 2020-03-24 06:37

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.

查看更多
登录 后发表回答