Обработка разных типов данных в единой структуре - PullRequest
4 голосов
/ 09 июля 2009

Мне нужно отправить некоторую информацию в очередь сообщений VxWorks. Информация для отправки определяется во время выполнения и может быть разных типов данных. Я использую структуру для этого -

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
};

В настоящее время я отправляю массив structData через очередь сообщений.

structData arrStruct[MAX_SIZE];

Проблема здесь в том, что только одна переменная в структуре полезна за раз, а две другие бесполезны. Поэтому очередь сообщений перегружена излишне. Я не могу использовать союзы, потому что требуется тип данных и значение. Я пытался использовать шаблоны, но это не решает проблему. Я могу отправлять только массив структур одного типа данных за раз.

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

structData<int> arrStruct[MAX_SIZE];

Существует ли стандартный способ хранения такой информации?

Ответы [ 6 ]

7 голосов
/ 09 июля 2009

Я не понимаю, почему вы не можете использовать союз. Это стандартный способ:

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
  }
};

Обычно тогда вы включаете тип данных, а затем получаете доступ к полю, которое является допустимым для этого типа.

Обратите внимание, что вы не можете поместить string в объединение, потому что тип string не относится к POD. Я изменил его, чтобы использовать указатель, который может быть строкой с нулем в конце. Затем вы должны рассмотреть возможность выделения и удаления строковых данных по мере необходимости.

6 голосов
/ 09 июля 2009

Для этого вы можете использовать boost :: option .

5 голосов
/ 09 июля 2009

Существует много способов обработки разных типов данных. Помимо решения объединения вы можете использовать общую структуру, такую ​​как:

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

Таким образом, вы знаете тип и можете привести указатель void * к нужному типу. Это похоже на объединенное решение - это скорее C, чем C ++ способ ведения дел. Путь C ++ был бы чем-то, использующим наследование. Вы определяете базовый класс «Data», используя наследование, чтобы специализировать данные. При необходимости вы можете использовать RTTI для проверки типа.

Но, как вы заявили, вам нужно отправить данные через очередь VxWork. Я не специалист, но если эти очереди являются очередями ОС в реальном времени, все предыдущие решения не являются хорошими. Ваша проблема в том, что ваши данные имеют переменную длину (в частности, строку), и вам нужно отправить их через очередь, которая, вероятно, запрашивает что-то вроде фиксированного отрезка длины данных и фактическую длину этого отрезка времени.

По моему опыту, правильный способ справиться с этим - сериализовать данные в нечто вроде буферного класса / структуры. Таким образом, вы можете оптимизировать размер (вы только сериализуете то, что вам нужно), и вы можете отправить свой буфер через свою очередь.

Для сериализации вы можете использовать что-то вроде 1 байта для типа, а затем для данных. Для обработки данных переменной длины вы можете использовать от 1 до n байтов для кодирования длины данных, чтобы вы могли десериализовать данные.

Для строки: 1 байт для кодирования типа (0x01 = строка, ...) 2 байта для кодирования длины строки (если вам нужно меньше 65536 байтов) n байтов данных

Таким образом, строка «Hello» будет сериализована как:

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

Вам нужен класс буфера и класс сериализатора / десериализатора. Затем вы делаете что-то вроде:

serialize data
send serialized data into queue

и с другой стороны

receive data
deserialize data

Надеюсь, это поможет, и я не понял вашу проблему. Часть сериализации излишня, если очереди VxWorks не такие, как я думаю ...

2 голосов
/ 09 июля 2009

Будьте очень осторожны с элементом "string" в очереди сообщений. Под капотом находится указатель на некоторую память malloc, которая содержит действительные строковые символы, поэтому вы передаете только «указатель» в своей очереди, а не настоящую строку.

Процесс получения может быть не в состоянии получить доступ к строковой памяти, или, что еще хуже, он уже был уничтожен к тому времени, когда программа чтения сообщений пытается его получить.

0 голосов
/ 09 июля 2009

Попробуйте QVariant в Qt

0 голосов
/ 09 июля 2009

+ 1 за 1800 и Илизар.

Использование союза для такого рода вещей - это, вероятно, путь. Но, как отмечали другие, у него есть несколько недостатков:

  • изначально подвержен ошибкам.
  • не безопасно расширяется.
  • не может обрабатывать члены с помощью конструкторов (хотя вы можете использовать указатели).

Так что, если вы не можете создать красивую обертку, возможно, будет лучше использовать метод boost :: variable.


Это немного оффтоп, но эта проблема является одной из причин, почему языки семейства ML имеют такую ​​сильную привлекательность (по крайней мере, для меня) Например, ваша проблема элегантно решена в OCaml с помощью:

(*
 * 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...