Получение bool от C до С++ и обратно - программирование
Подтвердить что ты не робот

Получение bool от C до С++ и обратно

При проектировании структур данных, которые должны быть переданы через API C, который соединяет код C и С++, безопасно ли использовать bool? То есть, если у меня есть struct, как это:

struct foo {
  int bar;
  bool baz;
};

гарантировано, что размер и значение baz, а также его позиция внутри foo интерпретируются одинаково с помощью C (где a _Bool) и С++?

Мы планируем сделать это на одной платформе (GCC для Debian 8 на Beaglebone) с кодом C и С++, скомпилированным той же версией GCC (как C99 и С++ 11, соответственно). Общие комментарии также приветствуются.

4b9b3361

Ответ 1

C и С++ bool - разные, но, если вы придерживаетесь одного и того же компилятора (в вашем случае gcc), он должен быть безопасным, так как это разумный общий сценарий.

В С++ bool всегда было ключевым словом. C не было до C99, где они ввели ключевое слово _Bool (поскольку люди использовали для typedef или #define bool как int или char в коде C89, поэтому прямое добавление bool в качестве ключевого слова нарушит существующий код); есть заголовок stdbool.h, который должен в C иметь typedef или #define от _Bool до bool. Взгляните на себя; Выполнение GCC выглядит следующим образом:

/*
 * ISO C Standard:  7.16  Boolean type and values  <stdbool.h>
 */

#ifndef _STDBOOL_H
#define _STDBOOL_H

#ifndef __cplusplus

#define bool        _Bool
#define true        1
#define false        0

#else /* __cplusplus */

/* Supporting <stdbool.h> in C++ is a GCC extension.  */
#define _Bool        bool
#define bool        bool
#define false        false
#define true        true

#endif /* __cplusplus */

/* Signal that all the definitions are present.  */
#define __bool_true_false_are_defined        1

#endif        /* stdbool.h */

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

Также стоит отметить Itanium ABI, который используется GCC и большинством других компиляторов (кроме Visual Studio, как отметил Маттиу М. в комментариях ниже) на многих платформах, указывает, что _Bool и bool следуют тем же правилам. Это серьезная гарантия. Третий намек, который мы можем получить, приведен в справочном руководстве Objective-C, в котором говорится, что для Objective-C и Objective-C ++, которые относятся к соглашениям на C и С++, соответственно, bool и _Bool эквивалентны; поэтому я бы сказал, что, хотя стандарты не гарантируют этого, вы можете предположить, что да, они эквивалентны.

Изменить:

Если стандарт не гарантирует совместимость _Bool и bool (по размеру, выравниванию и дополнению), что делает?

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

Поскольку OP задал вопрос о Beaglebone, мы должны проверить ARM ABI, в частности, GNU ARM EABI, используемую Debian. Как отмечает Justin Time в комментариях, ARM ABI действительно заявляет, что С++ ABI расширяет C и что _Bool и bool совместимы, оба имеют размер 1, выравнивание 1, представляющее машинный знак без знака. Таким образом, ответ на вопрос, на Beaglebone, да, _Bool и bool совместимы.

Ответ 2

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

Например, amd64 ABI document имеет сноску для типа _Bool, который гласит:

Этот тип называется bool в С++.

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

Также, просто размышляя об этом. Конечно, это сработает. Компиляторы генерируют код, который соответствует ABI и поведению самого большого компилятора для платформы (если это поведение выходит за пределы ABI). Большая вещь о С++ заключается в том, что он может ссылаться на библиотеки, написанные на C, и что касается библиотек, это то, что они могут быть скомпилированы любым компилятором на той же платформе (вот почему у нас есть документы ABI в первую очередь). Может ли быть какая-то незначительная несовместимость в какой-то момент? Конечно, но это то, что вам лучше решить, сообщая отчет об ошибке разработчику компилятора, а не обходному в своем коде. Я сомневаюсь, что в bool будет что-то, что создатели компиляторов будут испорчены.

Ответ 3

Единственное, что говорит стандарт C на _Bool:

Объект, объявленный как тип _Bool, достаточно велик, чтобы хранить значения 0 и 1.

Это означало бы, что _Bool не менее sizeof(char) или больше (поэтому true/false гарантируется сохранение).

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

Ответ 4

Как говорит Гилл Бейтс, у вас есть проблема, что sizeof(bool) зависит от компилятора. Там нет гарантии, что один и тот же компилятор будет рассматривать его одинаково. У компиляторов часто есть возможность уменьшить длину перечислений, если значения, определенные для перечисления, могут быть установлены в меньшее количество бит, и для bool было бы непредсказуемым. Компилятор даже в пределах своих прав (в соответствии со стандартом C) должен представлять это как отдельный бит в битовом поле, если он захочет.

Я лично испытал это при работе с процессором TI OMAP-L138, который сочетает в себе 32-битное ядро ​​ARM и 32-разрядное ядро ​​DSP на одном и том же устройстве с доступной для них общей памятью. Ядро ARM представляло bool как int (32-бит здесь), тогда как DSP представлял bool как char (8-бит). Чтобы решить эту проблему, я определил свой собственный тип bool32_t для использования с интерфейсом общей памяти, зная, что 32-битное значение будет работать для обеих сторон. Конечно, я мог бы определить его как 8-битное значение, но я считал, что он менее подвержен влиянию производительности, если я сохранил его как собственный целочисленный размер.

Если вы делаете то же самое, что и я, то вы можете 100% гарантировать двоичную совместимость между вашим кодом C и С++. Если вы этого не сделаете, вы не сможете. Это действительно так просто. С тем же компилятором ваши шансы очень хороши - но нет никакой гарантии, и изменение параметров компилятора может неожиданно привести вас в норму.

В отношении связанного объекта ваш int также должен использовать int16_t, int32_t или другое целое число определенного размера. (Вы должны включить stdint.h для этих определений типов.) На той же платформе маловероятно, что это будет отличаться для C и С++, но это запах кода для прошивки для использования int. Исключение составляет то место, где вам действительно не важно, как долго работает int, но должно быть ясно, что интерфейсы и структуры должны иметь это четко определенное. Программистам слишком сложно делать предположения (которые часто неверны!) О его размере, и результаты, как правило, катастрофичны, когда они идут не так - и, что еще хуже, они часто не ошибаются в тестировании, где вы можете легко найти и исправить их.