【问题标题】:C++ Structures with different members - decided at runtime具有不同成员的 C++ 结构 - 在运行时决定
【发布时间】:2011-09-09 11:22:36
【问题描述】:

我有以下情况。下面附上一个伪代码。我有一个 A 类,它有一个 D 或 E 类型的对象 c 并且这会有所不同(实际上是随机决定的)。它使用 b 作为与远程计算机通信的消息。

  1. 那么,我应该如何让结构 B 具有不同的变量(在这种情况下为浮点数或双精度数)?
  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
  }
};

我希望这能解释我的问题。如果不清楚或模棱两可,请告诉我。我将提供更多细节和解释。提前致谢。

【问题讨论】:

    标签: c++ class sockets inheritance struct


    【解决方案1】:

    使用 factory pattern 在运行时创建对象。

    或者,使用模板:

    template <class T>
    class A {
       T a;
    }
    
    
    A<int> a = new A<int>();
    A<double> b = new A<double>();
    

    第二部分很简单。
    在您的发送-接收协议中,在 sizeof (int) 的开头保留 4 个字节......然后用 sizeof(a) 填充它

    【讨论】:

    • 感谢您的回复。您所说的将解决问题的第 1 部分,但不能解决问题的第二部分。我需要一个适用于这两个部分的解决方案。
    【解决方案2】:

    由于 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++)。

    【讨论】:

    • 感谢您的回复。我无法分批发送数据包,这是我的限制。我需要将其作为单个数据包发送。知道我该怎么做吗?
    【解决方案3】:

    在纯 C++ 和原始套接字上工作真的很忙,许多人最终通过套接字传递结构,而不考虑其他问题因此而上升..

    1. 您需要注意 big-endian 和 host-endian 转换
    2. 并非所有编译器都按原样处理结构。您经常需要使用#pragma 包来强制处理结构的大小。一些编译器不支持这个。

    如果您不担心性能,我建议您将数据作为易于解析的 XML/ini 内容发送。INI 阅读器附带方便您可以将参数读取为浮点或双精度..

    如果您仍然喜欢二进制内容,我建议您学习 ASN.1 符号,但至少您需要 2 周的时间来完全练习您的项目,然后您将停止使用现有协议或使用您的自定义协议..

    因此,您的问题没有直接的解决方案。您可以要求您的类 C 或 D 通过套接字发送序列化数据,而不是在内存中发送结构的副本,对象的类型作为第一个字节,接下来 2/4 字节中的数据长度,实际数据到继续休息。然后您实现一个读取器类,该类读取第一个字节,然后它决定对象的类型,然后将调用委托给相应的类以读取其余数据。

    【讨论】:

    • 感谢您的回复。但是由于我有一定的限制,我没有将数据包分成几部分的选项,因此我需要将它作为单个数据包发送。有什么想法吗?
    【解决方案4】:

    首先,您的类层次结构看起来很可疑。从您所展示的内容来看,BC 之间没有任何关系,除了存在第三种类型A,它包含一个B 对象和一个C 指针。然而,B 中的变量类型应该取决于A 对象中的指针指向的C 的哪个子类?

    这似乎是不必要的耦合。为什么不将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_type1message_type2 可以通过作为同一基类的子类或同一模板的实例化等来关联,以避免在两种消息类型有一些共同点时重复。

    编辑:您在 cmets 中提到其他答案,您无法将消息拆分为不同的数据包。你能澄清一下这是什么意思吗? “数据包”不是一个精确的术语:TCP 有,UDP 和IP 有数据报,以太网有。这些都与您在套接字上调用send() 的次数无关(即两次调用send() 确实 一定意味着发送了两个TCP 段;相反,发出一个调用到send()保证您的数据将在单个段中传输)。

    【讨论】:

    • 基本上,我有一个队列,我将这些数据包推送到其中,并且一个线程单独运行,不断从队列中传输这些数据包。如果我希望传输不同大小的数据包,那么我也需要更改代码的其他部分,如果可以避免的话,我不想带来很多更改。此外,在我的系统中,通信是实时性能的障碍。调用更多的发送语法会使性能变差。但如果没有其他出路,那我就只能按照你说的去做了。
    • @Neel:我不确定您所说的“调用更多发送语法”是什么意思,但让我再说一遍,两次调用 send() 不会比调用一次慢额外的调用只包含几个字节的数据。如果这仍然困扰您,您可以自己将这两个部分连接到一个缓冲区中,并在一次调用中将缓冲区发送到 send()
    • 其实我有一个队列,所以如果我可以将对象推入队列,那么我会很高兴,因为我不必对代码进行任何更改,否则它会给我带来很多其他问题因为队列也用于发送数据包以外的应用程序。即使我连接两个缓冲区,我也无法让它运行,因为接收端不会有关于连接缓冲区大小的信息。
    【解决方案5】:

    Union 可以用来解决问题。

    struct B
    {
      int a;
      bool d; //d = 0 for D and 1 for E
      union
      {
        float b;
        double c;
      }
    };
    

    【讨论】:

      猜你喜欢
      • 2013-12-14
      • 1970-01-01
      • 2016-09-09
      • 1970-01-01
      • 2012-03-02
      • 2019-06-29
      • 1970-01-01
      • 2016-12-31
      • 1970-01-01
      相关资源
      最近更新 更多