Межпроцессное взаимодействие: передача структур в стиле C против C ++ - объекты - PullRequest
0 голосов
/ 20 декабря 2018

Предупреждение / Отказ от ответственности:

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

У этого вопроса нет кода.Просто технические запросы.

Справочная информация:

У меня есть устаревшее приложение, которое использует структуры в стиле C, передаваемые между процессами для межпроцессного взаимодействия .И это работает довольно хорошо и работает много-много лет, задолго до того, как я даже был на этой планете.

Я должен был написать новый процесс, который станет частью этого приложения.Я невольно написал это на C ++, предполагая, что какой бы IPC мы ни использовали, мы могли бы справиться с этим.К сожалению, тогда я узнал (от коллег), что существующая инфраструктура может передавать только структуры в стиле C.

"Непроверенные" утверждения / заявления:

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

  1. C ++ объекты имеют vtables.Структуры в стиле C - это просто переменные и значения.Поэтому структуры в стиле C можно передавать вокруг процессов, в то время как объекты C ++ не могут быть такими.

  2. Со структурами в стиле C мы можем встраивать информацию, такую ​​как размер структуры, так что обастороны знают, чего ожидать и что отправлять, но для объектов C ++ это невозможно, поскольку «размер таблицы может варьироваться» .

  3. «Если мы поменяем компиляторы, то это еще хуже.У нас было бы еще больше перестановок для случая объектов C ++. '

Исследование претензий:

Нет необходимостискажем, у этого коллеги есть небольшая предвзятость к Си, но он намного опытнее меня и, вероятно, знает, о чем говорит. Я не зависимый от языка .Но это сразу заставило меня задуматься.Как может быть, что мы не можем осуществлять межпроцессное взаимодействие с C ++?Я погуглил, и первые попадания были неизменно из-за переполнения стека, например:

Рекомендация по межпроцессному взаимодействию

И я посмотрел на различные методы IPC, перечисленные здесь,https://en.wikipedia.org/wiki/Inter-process_communication#Approaches

Я имею в виду, я следил за каждым из этих методов в списке, таких как каналы или разделяемая память и т. Д., И единственное предостережение, на которое все продолжают указывать, это то, что указатели (да! Конечно,) не может быть передано таким образом, и некоторые проблемы с синхронизацией могут всплыть - je nachdem.

НО я нигде не смог найти то, что могло бы опровергнуть или подтвердить его «утверждения».(Конечно, я мог бы продолжить копать до конца дня.: P)

Вопросы:

  1. Действительно ли его три утверждениятак или это был просто ФУД?Учитывая это, все, что у меня есть в тех объектах, которые я хотел обойти, это также только переменные POD и некоторые контейнеры STL, такие как std::vector и std::pair и их значения (без указателей или чего-либо еще), и средства получения для этих объектов.переменные. Виртуальных функций нет , кроме виртуального деструктора, который существует, поскольку я унаследовал все сообщения от одного базового класса сообщений, поскольку в то время я думал, что могут существовать некоторые общие базовые функции.(Теперь я могу довольно легко избавиться от этого базового класса, поскольку до сих пор там нет ничего общего! К счастью, я почему-то сохранил разбор и форматирование сообщений в отдельном классе. Удача или предвидение?: D)

  2. Меня также удивляет, как компилятор узнает, когда структура является структурой в стиле C, так как мы в любом случае используем компилятор g ++ для всего проекта?Используется ли ключевое слово «virtual»?

  3. Я не прошу решения для моего случая.Я могу обернуть результаты этих объектов в структуры и передать их через IPC, или я могу избавиться от базового класса и виртуального деструктора, как указано в «моем» пункте 1 выше.

Повышение или любой другой материал C ++ 11 или любая библиотека, которая обрабатывает это, не требуется.Любые предложения в этом отношении имеют отношение к рассматриваемому вопросу.

(ps. Теперь, когда я отправил и перечитал то, что я написал, я хочу пресечь мысль в зародыше, которая может появиться у любого читателя.Глава, который читает это, что ... Я спрашиваю это для моих знаний, а не для споров с этим коллегой. Скептицизм хорош, но было бы хорошо для сообщества, если бы мы все предполагали, что у других есть хорошие намерения. :))

Ответы [ 3 ]

0 голосов
/ 20 декабря 2018

Единственное предостережение, на которое все продолжают указывать, состоит в том, что указатели (да! Конечно) не могут передаваться следующим образом

Значения указателей (а также другие ссылки на память иресурсы) действительно бессмысленно во всех процессах.Это, очевидно, является следствием виртуальной памяти.

Другое предостережение заключается в том, что в то время как стандарт C определяет точную (зависящую от платформы) структуру памяти для структур, стандарт C ++ не гарантирует конкретной структуры памяти для классов в целом.Один процесс не обязательно согласен с другим процессом, например, относительно количества заполнения между членами - даже в пределах одной системы.C ++ гарантирует макет памяти только для стандартных типов макетов - и этот гарантированный макет совпадает со структурами C.


... и некоторыми контейнерами STL, такими как std :: vector ... (без указателей иличто угодно)

Все стандартные контейнеры, кроме std::array, используют указатели внутри.Они должны, потому что их размер является динамическим, и, следовательно, должны динамически распределять структуры данных.Кроме того, ни один из этих классов не является стандартным.Более того, определения классов одной реализации стандартной библиотеки не гарантируют совпадения с другой реализацией, и два процесса могут использовать разные стандартные библиотеки - это не редкость в Linux, где некоторые процессы могут использовать libstdc ++ (из GNU), в то время как другие могут использовать libc ++ (от Clang).

Нет виртуальных функций , кроме виртуального деструктора

Другими словами: существует хотя бы одна виртуальная функция (деструктор), и, следовательно, существует указатель на vtable.А также нет гарантированного размещения памяти, поскольку классы с виртуальными функциями никогда не являются стандартными классами размещения.


Итак, чтобы ответить на вопросы:

  1. В основном нет FUD, хотя некоторыеутверждения технически немного неточны:

    1. объекты C ++ могут иметь vtables;не все из них делают.Структуры C могут иметь указатели, поэтому не все структуры C также могут быть общими.Некоторые объекты C ++ могут быть разделены между процессами.В частности, стандартные классы макетов могут использоваться совместно (при условии, что указателей нет).
    2. Объекты с виртуальными таблицами действительно не могут совместно использоваться.
    3. Стандартные классы макетов имеют гарантированный макет памяти.Смена компилятора не является проблемой, если вы ограничиваете себя стандартными классами макетов.Попытка поделиться другими классами может показаться трудной, если вам не повезло, но вы, вероятно, столкнетесь с проблемами, когда начнете смешивать компиляторы.
  2. Стандарт C ++ определяет точные условия, в которыхкласс - это стандартное расположение.Все определения структуры C являются стандартными классами компоновки в C ++.Компилятор знает эти правила.

  3. Это не вопрос.


Вывод: Вы можете использоватьC ++ для IPC, но вы ограничены стандартными классами макета в этом интерфейсе.Это исключает вас из многих функций C ++, таких как виртуальные функции, спецификаторы доступа и т. Д. Но не все: например, у вас могут быть функции-члены.

Обратите внимание, что использование функций C ++ может привести к тому, что интерфейс межпроцессного взаимодействияработать только с C ++.Многие языки могут взаимодействовать с C, но вряд ли кто-либо может взаимодействовать с C ++.

Более того: если ваше «межпроцессное» взаимодействие выходит за пределы системы - через сеть, то есть - даже структуру C или стандартный класс компоновкине очень хорошее представление.В этом случае вам нужна сериализация.

0 голосов
/ 20 декабря 2018

«Непроверенные» утверждения / утверждения:

Объекты C ++ имеют vtables.Структуры в стиле C - это просто переменные и значения.Поэтому структуры в стиле C могут передаваться вокруг процессов, а объекты C ++ не могут быть.

Это утверждение частично верно, но приводится в заблуждение.

Не все объекты C ++иметь vtables (Технически стандарты C ++ вообще не требуют vtables, хотя это распространенная техника реализации, используемая для поддержки диспетчеризации виртуальных функций, поскольку она предлагает различные преимущества).

Если вы посмотрите , этоТАК вопрос и различные ответы вы найдете обсуждение агрегатных и POD-типов в C ++.Суть в том, что определения развивались между стандартами C ++ (как отражено в различных ответах на этот вопрос).В C ++ 11 понятие типов POD было изменено и фактически заменено понятиями тривиальных типов и типов стандартной компоновки.

Типы POD (до C ++ 11) и типами стандартной компоновки (C +)+11 и позже) можно поменять местами между C ++ и C (т. Е. Передать из кода, написанного на одном языке, в код, написанный на другом, поскольку компоновка памяти совместима).

Это правда, что C ++ объекты с любымвиртуальные функции относятся к числу тех, которые нельзя взаимозаменять с C. Обычно указатели не могут быть скопированы, что препятствует использованию (большей части) стандартных контейнеров C ++.

Со структурами в стиле C мы можем встраиватьтакая информация, как размер структуры, так что обе стороны знают, чего ожидать и что отправлять, но для объектов C ++ это невозможно, поскольку «размер таблицы может варьироваться».

Это утверждениеимеет значение false, поскольку существуют типы, у которых нет vtable, и типы, которые можно взаимозаменять между C ++ и C.

Если мы изменим компиляторы, тогдаэто еще хуже.У нас было бы еще больше перестановок для случая объектов C ++.

Опять же, пока типы выбираются правильно в коде C ++, их можно обменять с C.

Это утверждение - где оно верно для C, взаимодействующего с C ++ - также верно для C. Размер типов C формально определяется реализацией, определенной в C, так же, как и в C ++.Такие типы, как int, long, float, double, не обязательно имеют одинаковый размер с разными компиляторами.Существуют компиляторы с настройками, которые изменяют размер некоторых или всех основных типов (например, компиляторы, которые имеют различные параметры с плавающей запятой, компиляторы, которые имеют настройки, влияющие на то, является ли int 16 или 32-битным и т. Д.).

struct типы в C также могут иметь заполнение между членами, и заполнение может варьироваться в зависимости от компиляторов C.У ряда компиляторов есть опция компиляции, которая влияет на заполнение, которое влияет на размер struct типов.Это может привести к несовместимости макета для того же типа struct в C.

Так что же здесь происходит?

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

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

Проблема заключается в том, что, поскольку механизм IPC был разработан с предположением о совместимости компиляторов C, теперь вам говорят, что это из-за преимуществC над C ++ (или другими языками).Это не.Это артефакт того, как проводится IPC.

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

Вынезависимо от того, нужно ли будет использовать совместимые компиляторы.

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

0 голосов
/ 20 декабря 2018
  1. Действительно ли три его утверждения таковы или это просто ФУД?Учитывая это, все, что у меня есть в тех объектах, которые я хотел обойти, это также только переменные POD и некоторые контейнеры STL, такие как std :: vector и std :: pair и их значения (без указателей или чего-либо еще), и получателидля этих переменных.Нет никаких виртуальных функций, кроме виртуального деструктора, который существует, поскольку я унаследовал все сообщения от одного базового класса сообщений, поскольку в то время я думал, что могут существовать некоторые общие базовые функции.(Я мог бы довольно легко избавиться от этого базового класса сейчас, так как до сих пор там нет ничего общего! К счастью, я почему-то сохранил разбор и форматирование сообщений в отдельном классе. Удача или предвидение?: D)

Нет, как только контейнеры stl будут в вашей структуре, вы не сможете передавать их, как данные POD.Реализация контейнеров stl не указана, и они могут (и в большинстве случаев содержат) указатели для внутренних целей.

Это также фактически заставляет меня задаться вопросом, как компилятор узнает, когда структура является структурой в стиле C, так как мы в любом случае используем компилятор g ++ для всего проекта?Используется ли ключевое слово «virtual»?

Пока ваш struct / class содержит только данные POD и не содержит виртуальных функций, он будет сохранен как POD, но различия в выравнивании могут быть проблемой, еслидругая сторона вашего IPC была скомпилирована с другим компилятором и / или другими настройками компилятора или другими директивами выравнивания (такими как #pragma pack и т. д.).

Я не прошу решения по моему делу.Я могу обернуть результаты этих объектов в структуры и передать их через IPC, или я могу избавиться от базового класса и виртуального деструктора, как указано в «моем» пункте 1 выше.

Обтекание результатовот этих объектов до структур и передачи их через МПК звучит хорошо для меня, лично я это сделаю.Другое решение тоже неплохое, трудно сказать, какое из них лучше без контекста.

...