分解通信:传递C风格结构与C 对象

Interprocess communication: passing C-style structs vs C++-objects

本文关键字:结构 对象 风格 分解 传递 通信      更新时间:2023-10-16

警告/免责声明:

这个问题包含HERESAY,但是在过去半小时左右的我的小研究中,我找不到下面所述主张的答案。我很好奇,如果这里有人已经知道这一点。

这个问题没有代码。只是技术查询。

背景:

我有一个旧应用程序,该应用程序使用 cransCess Communication 的过程之间传递的C风格结构。而且这种运作良好,并且已经工作了很多年,即使我在这个星球上也很早就:p。

我应该写一个将成为本应用程序的一部分的新过程。不知不觉地,我在C 上写了它,假设Whaterver IPC,我们正在使用它可以处理的。不幸的是,然后我(来自同事)发现现有的基础架构只能通过C风格的结构。

'未验证'索赔/语句:

此外,其中一个同事列出了C 在这种情况下是不良选择的以下原因。

  1. c 对象具有VTABLES。C风格的结构只是变量和值。因此,C型结构可以围绕进程传递,而C 对象则不能。

  2. 使用C风格的结构,我们可以嵌入诸如结构的大小之类的信息,以便双方都知道期望什么和发送什么,但是对于C 对象,这是不可能的VTable可以变化'。

  3. '如果我们更改编译器,那么情况甚至更糟。对于C 对象,我们将需要更多的排列。'

调查索赔:

不必说,这个同事对C有一点偏见,但他比我经验丰富,并且可能知道他在说什么。我是语言 - 敏捷。但这立即让我思考。我们怎么不能与C 进行反学交流?我搜索了谷歌搜索,最初的命中总是来自Stackoverflow,就像这样:

过程间通信建议

我查看了此处列出的IPC的不同方法。https://en.wikipedia.org/wiki/inter-process_communication#apphaches

我的意思是,我跟进了列表中的每种方法,例如管道或共享内存等,而每个人都一直指出的唯一警告是,指示器(当然是duh!)不能像这样传递同步的某些问题可能会蔓延 - je nachdem。

但是,我找不到可以反驳或证实他的"主张"的东西。(当然,我可以继续挖掘一天的剩余时间。:P)

问题:

  1. 他的三个主张真的如此还是只是FUD?考虑到这一点,我想要传递的那些对象中所拥有的所有内容也只有POD变量和某些STL容器(例如std::vectorstd::pair)及其值及其值(没有指针或任何东西),以及这些变量的Getters。没有虚拟函数除了虚拟驱动器,它存在于我从一个基本消息类中继承所有消息以来存在,因为当时我认为可能有一些常见的基本功能。(我现在可以很容易地摆脱这个基础,因为到目前为止,没有真正常见的东西!值得庆幸的是,出于某种原因,我在单独的班级中保留了消息的解析和格式。运气或远见?:D)

  2. 它实际上也让我感到奇怪,编译器如何知道struct何时是C风格的结构,因为我们正在为整个项目使用G 编译器?是"虚拟"关键字的使用?

  3. 我不是要为我的案件提供解决方案。我可以将这些对象的结果包裹在结构中,并通过IPC将它们传递给它们,或者我可以摆脱基础类别和虚拟破坏者,如上面的"我的"点1"中所述。

提升或任何C 11的内容或任何处理此操作的库。这方面的任何建议都与手头的问题相切。

(P.S。现在我发布并重新阅读了我的发布的内容,我想在任何阅读此书的读者的头脑中逐渐蔓延的想法,而不是因为与那个同事争论。怀疑是好的,但是如果我们都假设其他人有良好的意图,对社区会很好。:))

只有每个人都一直指出的只是警告,这是,当然不能像这样的指针(当然)

指针值(以及对内存和资源的其他引用)确实在整个过程中都毫无意义。这显然是虚拟内存的结果。

另一个警告是,当C标准指定结构的精确(平台特定)内存布局,C 标准不能保证一般类的特定内存布局。例如,一个过程不一定与成员之间的填充量(即使在同一系统中)的另一个过程一致。C 仅保证标准布局类型的内存布局 - 这可以保证与C结构匹配。


...还有一些STL容器,例如STD :: vector ...(没有指针或其他任何东西)

std::array外,所有标准容器都在内部使用指针。他们之所以这样做是因为它们的大小是动态的,因此必须动态分配数据结构。另外,这些都不是标准布局类。此外,不能保证一个标准库实现的类定义与另一个实现匹配,并且两个过程可以使用不同的标准库 - 这在Linux上根本不常见,在Linux上,某些过程可能会使用libstdc (来自GNU),而另一些过程可能会使用libc (来自clang)。

没有虚拟函数除了虚拟驱动器

换句话说:至少有一个虚拟函数(destructor),因此有一个指针 vtable。而且由于具有虚拟函数的类绝不是标准布局类,因此也无法保证内存布局。


所以回答问题:

  1. 主要不是FUD,尽管从技术上讲有些说法有点不准确:

    1. C 对象 May 具有vtables;并非所有人都这样做。C结构可以有指针,因此并非所有C结构都可以共享。某些C 对象可以共享累积过程。具体来说,可以共享标准布局类(假设没有指针)。
    2. 实际上无法共享带有VTABLES的对象。
    3. 标准布局类具有保证的内存布局。只要您将自己限制在标准布局类中,更改编译器就不是问题。如果您不幸的话,试图分享其他课程可能会显得有效,但是当您开始混合编译器时,您可能会遇到问题。
  2. C Stadard定义了类是标准布局的确切条件。所有C结构定义都是C 中的标准布局类。编译器知道这些规则。

  3. 这不是一个问题。


结论:您 can 将C 用于IPC,但您仅限于该接口中的标准布局类。这使您不包括许多C 功能,例如虚拟函数,访问说明器等。但不是全部:您仍然可以具有成员功能。

确实要注意,使用C 功能可能会导致相互处理接口仅与C 一起使用。许多语言可以与C接口,但几乎没有任何语言可以与C 接口。

此外:如果您的"分解"通信超出了系统的边界 - 跨网络 - 即使是C结构或标准布局类也不是一个好的表示。在这种情况下,您需要序列化。

  1. 他的三个主张真的是如此还是只是FUD?考虑到这一点,我想要传递的那些物体中所拥有的一切也只有POD变量和某些STL容器(例如STD :: vector :: vector and std :: Pair及其值及其值(没有指针或任何东西)以及Getters对于这些变量。除了虚拟驱动器之外,没有虚拟函数,该功能是因为我从一个基本消息类中继承了所有消息以来存在,因为当时我认为可能存在一些共同的基本功能。(我现在可以很容易地摆脱这个基础,因为到目前为止,没有真正常见的东西!值得庆幸的是,出于某种原因,我保留了单独的班级解析和格式化。运气或远见?:D)

否,一旦STL容器在您的结构中,就无法像Pod数据一样传递它们。未指定STL容器的实现,它们可能(在大多数情况下)包含用于内部目的的指针。

  1. 它实际上也让我感到奇怪,编译器如何知道构成何时是C风格的结构,因为我们正在为整个项目使用G 编译器?是"虚拟"关键字的使用?

只要您的struct/class只有pod数据,并且没有虚拟功能,它将被存储为pod,但是如果与另一个编译器和/或不同的编译器一起编译了IPC的另一侧,则对齐差异可能是一个问题。设置或不同的对齐指令(例如#Pragma Pack等)。

  1. 我不是要求解决我的案子的解决方案。我可以将这些对象的结果包裹到结构中,并通过IPC将它们传递给它们,或者我可以摆脱基础类别和虚拟破坏者,如上面的"我的"点1"中所述。

将这些对象的结果包裹到结构中,并通过IPC传递它们对我来说听起来不错,这就是我要做的。另一个解决方案也不错,很难说出哪个没有上下文。

'未验证'索赔/语句:

C 对象具有VTABLES。C风格的结构只是变量和值。因此,C型结构可以围绕过程传递,而C 对象则不能。

此陈述部分是正确的,但以误导性的方式施放。

并非所有的C 对象都具有VTABLES(从技术上讲,C 标准根本不需要VTABLES,尽管它是用于支持虚拟函数调度的常见实现技术,因为它提供了各种优势)。

如果您查找了这个问题,并且各种答案,您将在C 中找到有关聚合和POD类型的讨论。捕获的是,C 标准之间的定义(如该问题的各种答案所反映)。在C 11中,更改了POD类型的概念,并有效地用微不足道和标准的类型的概念代替。

pod类型(C 11之前)和标准layout类型(C 11及更高版本)可以在C 和C之间互换(即,从用一种语言编写的代码传递给另一种语言的代码,因为内存布局兼容)。

的确,具有任何虚拟函数的C 对象是不能与C互换的C 对象,通常无法复制Pointer,这阻止了(大多数)C 标准容器的使用。

使用C风格结构,我们可以嵌入信息,例如大小 结构,以便双方都知道期望什么和发送什么,但是 对于C 对象,这是不可能的 可能会有所不同。

此语句是错误的,因为有一些类型没有VTable,并且可以在C 和C。

之间互换的类型

如果我们更改编译器,那就更糟了。对于C 对象,我们将有更多的排列要处理。

再次,只要在C 代码中正确选择类型,就可以与c。

互换

此语句 - 与C与C 互操作的情况也是如此。对于C,C类型的大小正式在C中正式实现,就像在C 中一样。intlongfloatdouble之类的类型不能保证具有不同编译器的大小相同。有一些编译器的设置会改变某些或所有基本类型的大小(例如,具有不同浮点选项的编译器,具有设置的编译器会影响int是16还是32位,等等)。

struct c中的类型在成员之间也可能具有填充,填充物在C编译器之间可能会有所不同。许多编译器具有影响填充的编译选项,这会影响struct类型的大小。这可以在c。

中引入相同struct类型的布局不兼容

那么这里可能发生了什么?

概要通信的设计可能是在使用相同(或兼容)编译器构建的C代码之间的假设。IPC机制可能很简单:例如,一个过程在指定的内存位置在管道下挤出一定数量的数据,而接收器将该管道另一端接收到的数据复制到等效的数据结构中。p>隐含的假设是可以直接复制数据。这依赖于两个程序中兼容的数据类型的布局。

问题在于,由于IPC机制是用兼容C编译器的假设设计的,因此您现在被告知这是由于C优于C (或其他语言)的优势。它不是。这是如何完成IPC的人工制品。

IPC方法可能非常有限,但是您的C 代码可以通过IPC机制发送和接收数据,只要您将数据包装在C 代码中的适当类型(例如标准layout)中。而其他过程是用C或C 编写的。它可能需要在C 中进行更多的工作(例如,将C 类的数据包装成标准的结构结构,并向另一个过程喷压到该结构 - 或者如果接收到数据的相反),但这肯定是可能的。

>

无论如何,您都需要使用兼容的编译器。

这是假设您不能更改分解通信的手段(例如,设计用于在过程之间进行交谈的协议,而不是盲目地从内存位置沿线复制数据到另一个过程,然后接收过程复制数据回到兼容的数据结构中)。有一些方法可以做IPC,如果需要,可以更好地支持一系列编程语言 - 尽管具有不同的权衡取舍(例如,通信的带宽,代码可以转换数据,以便将其发送,以及代码以接收数据并转动数据返回数据结构)。