Получение 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, соответственно). Общие комментарии также приветствуются.
Ответы
Ответ 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
, но должно быть ясно, что интерфейсы и структуры должны иметь это четко определенное. Программистам слишком сложно делать предположения (которые часто неверны!) О его размере, и результаты, как правило, катастрофичны, когда они идут не так - и, что еще хуже, они часто не ошибаются в тестировании, где вы можете легко найти и исправить их.