防止误用仅用于运输的结构

时间:2011-03-16 18:00:58

标签: c++ enforcement misuse

我研发的产品有多个组件,其中大部分是用C ++编写的,但其中一个用C语言编写。我们经常会遇到一段信息,其中一条信息通过IPC流经每个组件。

我们使用结构定义这些消息,因此我们可以将它们打包成消息并通过消息队列发送它们。这些结构仅用于“运输”目的,并以仅用于此目的的方式编写。我遇到的问题是:程序员持有结构并将其用作信息的长期容器。

在我看来,这是一个问题,因为:

1)如果我们改变传输结构,他们的所有代码都会被破坏。这里应该有封装,以便我们不会遇到这种情况。

2)消息结构非常笨拙并且仅用于传输信息......这个结构似乎也不太可能成为访问这些数据(长期)的最方便的形式,用于这些其他组件

我的问题是:我如何以编程方式防止这种误用?我想强制说这些结构只能用于运输。

编辑:我会尽力提供一个例子:

struct smallElement {
    int id;
    int basicFoo;
};

struct mediumElement {
    int id;
    int basicBar;
    int numSmallElements;
    struct smallElement smallElements[MAX_NUM_SMALL];
};

struct largeElement {
    int id;
    int basicBaz;
    int numMediumElements;
    struct mediumElement[MAX_NUM_MEDIUM];
};

效果是人们只是坚持使用'largeElement'而不是从largeElement中提取他们需要的数据并将其放入满足其需求的类中。

5 个答案:

答案 0 :(得分:2)

当我定义消息结构时(在C ++中,在C中无效)我确保:

  1. 邮件对象是可复制的
  2. 邮件对象只能构建一次
  3. 构建后无法更改消息对象
  4. 我不确定这些消息是否仍然是pod,但我认为从内存的角度来看它是等效的。

    要实现这一目标的事情:

    1. 拥有一个设置每个成员的唯一构造函数
    2. 让所有成员都私下
    3. 拥有const成员访问者
    4. 例如你可以这样:

      struct Message
      {
         int id;
         long data;
         Info info;
      };
      

      然后你应该有这个:

      class Message // struct or whatever, just make sure public/private are correctly set
      {
      public:
         Message( int id, long data, long info ) : m_id( id ), m_data( data ), m_info( info ) {}
      
         int id() const { return m_id; }
         long data() const { return m_data; }
         Info info() const { return m_info; }
      
      private:
      
         int m_id;
         long m_data;
         Info m_info;
      
      };
      

      现在,用户将能够构建消息,从中读取消息,但不能长期更改消息,使其无法用作数据容器。然而,它们可以存储一条消息,但以后无法更改它,因此它仅对内存有用。


      或....您可以使用“黑匣子”

      1. 分隔库中的消息层(如果尚未分离)。
      2. 不应将客户端代码暴露给消息结构定义。所以不要提供标题,或隐藏它们或其他东西。
      3. 现在,提供发送消息的功能,这些功能将(内部)构建消息并发送消息。这甚至有助于阅读代码。
      4. 接收消息时,提供通知客户端代码的方法。但是不要直接提供这些消息!只是把它们放在某个地方(可能是暂时的或使用终生规则或其他东西)在你的图书馆里面,也许是一种经理,无论如何,但要把它们放在黑盒子里面。只需提供一种消息标识符。
      5. 提供函数以从消息中获取信息而不暴露结构。要实现这一目标,您有几种方法。在这种情况下,我会提供在命名空间中收集的函数。这些函数会将消息标识符作为第一个参数,并且只返回消息中的一个数据(如果需要,可以是完整的对象)。
      6. 这样,用户就不能将结构体用作数据容器,因为它们没有定义。他们可以访问数据。

        这有两个问题:明显的性能成本,写入和更改显然更重。也许使用一些代码生成器会更好。 Google Protobuf在这个领域充满了好主意。


        但最好的方法是让他们明白为什么他们的行为方式会很快或晚些时候破裂。

答案 1 :(得分:2)

程序员这样做是因为它是获得所需功能的最简单阻力的最简单途径。如果它们在具有适当访问器的类中,则它们可能更容易访问数据,但是他们必须编写该类并编写转换函数。

利用他们的懒惰,让他们做最简单的道路就是做正确的事。对于您创建的每个消息结构,创建一个相应的类,用于使用带有转换方法的漂亮接口来存储和访问数据,以使它们成为将消息放入类中的一个线程。由于该类具有更好的访问器方法,因此使用它比使用错误的东西更容易。例如:

msg_struct inputStruct = receiveMsg();
MsgClass msg(inputStruct);
msg.doSomething()
...
msg_struct outputStruct = msg.toStruct();

而不是找到迫使他们不采取简单方法的方法,而是让你希望他们以最简单的方式使用代码。多个程序员正在使用这个反模式这一事实让我觉得库中缺少一块应该由库来提供以容纳这个。您正在将这个必要组件的创建推回到代码的用户身上,而不是喜欢他们提出的解决方案。

答案 2 :(得分:1)

您可以根据const引用实现它们,以便服务器端构造传输结构,但客户端使用只允许对它们进行const引用,并且实际上无法实例化或构造它们以强制执行您想要的用法。

不幸的是,如果没有您的消息,包装,正确使用和错误使用的代码片段,我无法真正提供有关如何在您的情况下实现这一点的更多细节,但我们在数据模型中使用类似的东西来防止不当使用。我还导出并提供模板存储类,以便在他们想要存储检索到的数据时,从客户端使用的消息中轻松填充。

答案 3 :(得分:0)

通常将传输消息定义为结构是个坏主意。最好为它定义“normal”(对程序员有用)struct和serializer / deserializer。要自动化序列化器/解串器编码,可以在单独的文件中定义带有宏的结构,并自动生成typedef结构和序列化器/解串器(boost预处理器llibrary也可能有帮助)

答案 4 :(得分:0)

我不能说比这更简单:使用protocol buffers。从长远来看,它会让你的生活变得如此简单。