Структуры C ++ с различными членами - решаются во время выполнения - PullRequest
4 голосов
/ 06 июня 2011

У меня следующая ситуация. Псевдокод прилагается ниже. У меня есть класс A, у которого есть объект c типа D или E, и это варьируется (на самом деле это случайное решение). В качестве сообщения для связи с удаленным компьютером используется b.

  1. Итак, как мне заставить структуру B иметь разные переменные (в данном случае float или double)?
  2. Кроме того, когда я открываю сокет и передаю объект, объект теперь будет иметь различный размер. Удаленный компьютер не знает, имеет ли объект размер, соответствующий sizeof (int) + sizeof (float) ИЛИ sizeof (int) + sizeof (double). Мне нужен размер в качестве параметра для получения пакетов, так как я могу решить это?

Код:

class C 
{
  ...
};

class D: public C
{
  ...
};

class E: public C
{
  ...
};

struct B
{
  int a;
  // If A->c is of type D
  float b;
  // If A->c is of type E
  double b;
};

class A
{
  B b;
  C *c;
  A()
  {
    c = (C*) new D;
    //c = (C*) new E;
  }
  ...
  ...
  void transmit()
  {
    //b has some attributes depending on whether c is of type D or E
    //Open a socket and send packets via UDP
    //The remote host receives the packets
  }
};

Надеюсь, это объясняет мою проблему. Если это не ясно или неоднозначно, пожалуйста, дайте мне знать. Я предоставлю более подробную информацию и объяснение. Заранее спасибо.

Ответы [ 5 ]

2 голосов
/ 06 июня 2011

Либо используйте фабричный шаблон для создания объектов во время выполнения.

Или используйте шаблон:

template <class T>
class A {
   T a;
}


A<int> a = new A<int>();
A<double> b = new A<double>();

Вторая частьэто просто.
В вашем протоколе отправки-получения вначале оставьте 4 байта для sizeof (int) ... Затем заполните его sizeof(a)

1 голос
/ 06 июня 2011

Прежде всего, ваша иерархия классов выглядит подозрительно.Из того, что вы показали, нет никакой связи между B и C, кроме существования третьего типа, A, который содержит объект B и указатель C.И все же, тип переменной внутри B должен зависеть от того, на какой подкласс C указывает указатель внутри объекта A?

Это кажется ненужной связью.Почему бы просто не включить это поле B в соответствующие подклассы C?

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

enum message_type {type1, type2};

class message_type1
{
    ...
};

class message_type2
{
    ...
};

// when sending message (pseudocode)
write message_type variable
write message_type1 object OR message_type2 object

// when reading message (pseudocode)
read message_type variable
if type1
    read message_type1 object
else
    read message_type2 object

Конечно, message_type1 и message_type2 могут быть связаны, будучи подклассами одного и того же базового класса, или экземплярами одного и того же шаблона и т. Д., Чтобы избежать повторения, если два сообщенияу типов есть некоторые общие черты.

РЕДАКТИРОВАТЬ : Вы упомянули в комментариях к другим ответам, что вы не можете разбить свое сообщение на разные пакеты.Можете ли вы уточнить, что это значит?«Пакет» - это не точный термин: TCP имеет сегментов , UDP и IP имеют дейтаграмм , а Ethernet имеет кадров .Ни один из них не имеет никакого отношения к тому, сколько раз вы вызываете send() в сокете (т.е. два вызова send() не не обязательно означают, что два сегмента TCP отправлены; наоборот, размещение одного вызова наsend() не не гарантирует, что ваши данные будут передаваться в одном сегменте).

1 голос
/ 06 июня 2011

Работа на простом C ++ и сыром сокете действительно беспокойна, и многие заканчивают тем, что пропускают структуру через сокет, не думая, что из-за этого возникают другие проблемы.

  1. Вам нужно знать о старшем порядкеи endian-преобразования в узлы
  2. Не все компиляторы обрабатывают структуру как есть. Вам часто нужно использовать пакет #pragma для принудительной обработки размера структуры.Некоторые компиляторы не поддерживают это.

Если вы не беспокоитесь о производительности, я бы порекомендовал отправлять данные в виде XML / ini-контента, который легко анализировать.удобно, что вы можете читать аргументы как float или double ..

Если вы все еще предпочитаете двоичный контент, я бы порекомендовал изучить нотации ASN.1, но, по крайней мере, вам нужно 2 недели, чтобы полностью практиковаться для вашего проекта, тогдапрекратит использовать существующий протокол или использовать ваш собственный протокол ..

Так что прямого решения вашего вопроса не существует.Вместо того, чтобы отправлять в память копию структуры, вы можете попросить ваш класс C или D отправить сериализованные данные через сокет с типом объекта в качестве первого байта, длиной данных в следующих 2/4 байтах, фактическими данными вfollow in rest. Затем вы реализуете класс читателя, который читает первый байт, а затем решает тип объекта, затем делегирует вызов соответствующему классу для чтения остальных данных.

1 голос
/ 06 июня 2011

Поскольку D и E являются производными от C, ваша реализация выглядит правильно:

c = (C*) new D;

Хотя я бы удалил C-Cast (это не обязательно, если вам нужны приведения, вы должны использовать варианты C ++).

c = new D;

Как вы будете передавать данные, будет зависеть. Но обычно вам необходимо добавить к этой части информации префикс с информацией о типе, чтобы получатель понимал, как декодировать следующий поток.

send(a->a);
send("1") if a->c is D
send("2") if a->c is E
send(<Conditional Part>);

Как примечание. Ищите умные указатели. Использование необработанного указателя в вашем классе - плохая идея (и не очень хороший C ++).

0 голосов
/ 09 июня 2011

Союз может быть использован для решения проблемы.

struct B
{
  int a;
  bool d; //d = 0 for D and 1 for E
  union
  {
    float b;
    double c;
  }
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...