51

When designing data structures which are to be passed through a C API which connects C and C++ code, is it safe to use bool? That is, if I have a struct like this:

struct foo {
  int bar;
  bool baz;
};

is it guaranteed that the size and meaning of baz as well as its position within foo are interpreted in the same way by C (where it's a _Bool) and by C++?

We are considering to do this on a single platform (GCC for Debian 8 on a Beaglebone) with both C and C++ code compiled by the same GCC version (as C99 and C++11, respectively). General comments are welcome as well, though.

sbi
  • 219,715
  • 46
  • 258
  • 445
  • 12
    bool in C is different from bool in C++. bool in C is actually a typedef of the type _Bool that was introduced in C99 – martinkunev Oct 13 '16 at 12:02
  • 1
    @martinkunev Well, we certainly know that. Lemme add a statement to make this clear... – sbi Oct 13 '16 at 12:06
  • 8
    `sizeof(bool)` and `sizeof(_Bool)` (C99 boolean) are implementation dependant. If they are the same size on your platform with a given set of compilers, I'd consider it safe as long as you stick with that compilers. Concerning padding of structure layout, this may be another story even if the sizes of the boolean types are the same. – Jabberwocky Oct 13 '16 at 12:06
  • AFAIK if you use the same compiler on the same system and each corresponding type in the structs have the same size and same order then you should get the same layout for POD types. – NathanOliver Oct 13 '16 at 12:09
  • 2
    @sbi http://stackoverflow.com/questions/3529831/bool-and-bool-how-do-i-solve-the-problem-of-a-c-library-that-uses-bool I found this link, is it useful? – Pavel Oct 13 '16 at 12:13
  • @Pavel: Indeed, it is. For a moment I was contemplating closing my question as a dupe. But I find Anthony's answer rather vague both for my question about a specific platform (GCC) and general advice on the topic. "I would expect" and "you'll need to check for compatibility" seem rather hard to act on. – sbi Oct 13 '16 at 12:29
  • 3
    Visual C++ [changed the size of `bool`](http://stackoverflow.com/a/6935286/1956010) from 32 to 8 bits in version 5.0. So in theory you should make sure to never mix object code from different compiler versions. OTOH, it's highly unlikely that any modern compiler would ever use anything larger than 8 bits. – nwellnhof Oct 13 '16 at 12:30
  • 1
    You should probably add a bunch of *#if #error* preprosessor checks for *sizeof* and *offsetof* of struct fields, so compilation fails if there ever is a mismatch (due to platform change or whatever). Also use the integer types with specified bit size instead of plain *int* just to be safer. – hyde Oct 13 '16 at 13:09
  • 1
    IMO, "platform" implies the compiler/settings too. So "single platform" and 2 different compiler/settings (one for C another for C++) is not correct. That is 2 platforms which may be very similar or very different. I'd go with `int32_t` and `uint8_t` instead. – chux - Reinstate Monica Oct 13 '16 at 13:37
  • As far as I know, there's no telling if bool will even be binary compatible from C++ to C++ or from C to C. This is specified by the ABI and not by language standards. So I'm not quite sure why everyone started posting "bool is different in C and C++". – Lundin Oct 13 '16 at 14:27
  • @chux You are onto something there. Thanks for reminding me! – sbi Oct 13 '16 at 23:43
  • This is a compiler-specific question, the C and C++ standards make no guarantees about sizes of any types for interoperability – M.M Oct 14 '16 at 05:58
  • @hyde Hmmm... I'm all for checks; but the preprocessor commands will only prove things for one *exclusive-or* the other compiler, never for both in one run (how?). So perhaps one must initialize globals with the results and compare them *at run time* after linking the result of the different compilers. – Peter - Reinstate Monica Oct 14 '16 at 08:08
  • @PeterA.Schneider I meant, that you define the format to be whatever it is, with fixed sizes and offsets. Then if you compile it with incompatible compiler, you get compiler error, and then you need to tweak the struct *for that compiler* so it produces the same binary layout, staying compatible with the version produced by the original compiler. – hyde Oct 14 '16 at 08:24
  • @hyde You [can't use `sizeof`](http://stackoverflow.com/questions/4079243/how-can-i-use-sizeof-in-a-preprocessor-macro) in the preprocessor. – nwellnhof Oct 14 '16 at 22:21
  • @nwellnhof Aww, right. Then *offsetof* won't work either, I suppose. Fortunately that post you linked to also gives solutions to get compile time assert for these. – hyde Oct 15 '16 at 05:26

4 Answers4

50

C's and C++'s bool type are different, but, as long as you stick to the same compiler (in your case, gcc), it should be safe, as this is a reasonable common scenario.

In C++, bool has always been a keyword. C didn't have one until C99, where they introduced the keyword _Bool (because people used to typedef or #define bool as int or char in C89 code, so directly adding bool as a keyword would break existing code); there is the header stdbool.h which should, in C, have a typedef or #define from _Bool to bool. Take a look at yours; GCC's implementation looks like this:

/*
 * 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 */

Which leads us to believe that, at least in GCC, the two types are compatible (in both size and alignment, so that the struct layout will remain the same).

Also worth noting, the Itanium ABI, which is used by GCC and most other compilers (except Visual Studio; as noted by Matthieu M. in the comments below) on many platforms, specifies that _Bool and bool follow the same rules. This is a strong garantee. A third hint we can get is from Objective-C's reference manual, which says that for Objective-C and Objective-C++, which respect C's and C++'s conventions respectively, bool and _Bool are equivalent; so I'd pretty much say that, though the standards do not guarantee this, you can assume that yes, they are equivalent.

Edit:

If the standard does not guarantee that _Bool and bool will be compatible (in size, alignment, and padding), what does?

When we say those things are "architecture dependent", we actually mean that they are ABI dependent. Every compiler implements one or more ABIs, and two compilers (or versions of the same compiler) are said to be compatible if they implement the same ABI. Since it is expected to call C code from C++, as this is ubiquitously common, all C++ ABIs I've ever heard of extend the local C ABI.

Since OP asked about Beaglebone, we must check the ARM ABI, most specifically the GNU ARM EABI used by Debian. As noted by Justin Time in the comments, the ARM ABI indeed declares C++'s ABI to extend C's, and that _Bool and bool are compatible, both being of size 1, alignment 1, representing a machine's unsigned byte. So the answer to the question, on the Beaglebone, yes, _Bool and bool are compatible.

paulotorrens
  • 2,286
  • 20
  • 30
  • If this is GCC's implementation, it seems that they are indeed fully compatible on GCC. Thanks! – sbi Oct 13 '16 at 12:22
  • 26
    If I ever find a compiler that supports both C and C++ but implements `bool` and `_Bool` differently, I'll hunt its programmers down, I'll find them, and I'll sit them down and explain to them why they this is a huge mistake. – paulotorrens Oct 13 '16 at 12:49
  • 8
    gcc's implementation only offers compatibility if both the C code and C++ code are compiled as C++ (using g++). It does not offer compatibility between C code which is actually compiled as C, and C++ code. – Peter Oct 13 '16 at 13:12
  • 6
    I would emphasize the Itanium ABI more: this is the ABI implemented by all major C++ players except Visual Studio, which means that compatibility is not only guaranteed within a single version of GCC, it's actually guaranteed *across compilers and versions*, and it also *includes future versions*. It's hard to find a better guarantee. However... it would be worth linking to the specific part of the ABI mentioning this; I found https://mentorembedded.github.io/cxx-abi/abi.html#pod is this what you were thinking of? – Matthieu M. Oct 13 '16 at 13:16
  • You've provided good information to support the discussion. And, until your final statement, everything you said was sound, and made sense. But then you say: _though the standards do not garantee this, you can assume that yes, they are equivalent._ Is this not dangerous? When it is concluded that standards do not guarantee something, such as the definition of the 'bool' type', should we not assume that possibly that "they are not equivalent"? This small observation notwithstanding, you've provided a useful answer, which has led to some very informative comments (i.e. from @MatthieuM. ) – ryyker Oct 13 '16 at 13:51
  • 1
    MatthieuM., yes, that's the point I was thinking of. ryyker, I do not think it is dangerous _in this particular situation_. Though layout compatibility between C and C++ is not garanteed, it is heavely expected by programmers: it is not uncommon to use structs declared in C in C++ programs, as vendors would not reimplement the whole libc in C++ instead of using the existing libc, which depend on a few structs' layouts. Of course it's possible and it works this way on the [DeathStation 9000](http://www.statemaster.com/encyclopedia/DeathStation-9000), but it would be way too unexpected. – paulotorrens Oct 13 '16 at 14:15
  • 1
    Every computer is not a PC. There's a whole world out there, outside your PC desktop. Embedded systems will have all manner of ABIs. Essentially there will be one ABI for every known CPU: all the ARMs, all the Power PCs, all the Renesas, all the PICs, all the Freescales/NXPs and so on and so on. Hundreds of different cores. And we shouldn't even mention DSPs. – Lundin Oct 13 '16 at 14:32
  • 5
    Why do they bother with #define bool bool #define false false #define true true (in the #else part)? – Heshy Oct 13 '16 at 15:18
  • 2
    @Heshy That would appear to be a GCC modification, which is allowed but not necessary. Officially, C++11 header `cstdbool` (the C++ version of C's `stdbool.h`) only contains the macro `#define __bool_true_false_are_defined 1`, because `bool`, `true`, and `false` are already defined as keywords. So, it appears that for C++, GCC defines them as macros which expand to the identically-spelled keywords. [This also provides a measure of safety in case of any other C header that defines them as macros, by making the compiler give you a redefinition warning.] – Justin Time - Reinstate Monica Oct 13 '16 at 16:47
  • 13
    @Heshy, I assume that this is because some legacy programs will check `#ifndef bool /**/ typedef int bool; /**/ #endif` or something like that. Actually I've seem this on the wild already. If `bool` wasn't #defined to `bool`, such code would fail. – paulotorrens Oct 13 '16 at 17:10
  • 3
    The ARM ABI specifies that ["the C99 semantics for `bool` are intended to match those of C++."](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0375g/chr1359124241645.html), and other parts of the documentation indicate that `sizeof(bool) == sizeof(_Bool) == sizeof(char)` (8 bits) and `alignof(bool) == alignof(_Bool) == alignof(char)` (1 byte). From this, it's _probably_ safe to assume that C `_Bool` and C++ `bool` are equivalent on ARM. – Justin Time - Reinstate Monica Oct 13 '16 at 17:28
  • Testing with the most recent version of Visual Studio indicates that `sizeof(bool) == sizeof(_Bool)` (1 byte) and `alignof(bool) == _Alignof(bool)` (1 byte), suggesting bitwise compatibility between the two. Unfortunately, I can't find documentation for MSVC `_Bool` anywhere, even though Visual Studio supports it now; the closest I can find is a [generic page](https://msdn.microsoft.com/en-us/library/dn495456.aspx) that indicates that `cstdbool` and `stdbool.h` are supported, but says literally nothing of use. – Justin Time - Reinstate Monica Oct 13 '16 at 18:03
  • Tested by running [this program](http://ideone.com/Zirq0n) on the [MSVC online compiler](http://webcompiler.cloudapp.net/). Ran it once with compiler flag `/TC` (compile all files as C), and once with `/TP` (compile all files as C++). – Justin Time - Reinstate Monica Oct 13 '16 at 18:09
  • 2
    @paulotorrens _"I don't know who you are. I don't know what you want. If you are looking for ransom I can tell you I don't have money, but what I do have are a very particular set of skills. Skills I have acquired over a very long career. Skills that make me a nightmare for people like you. If you fix your compiler now that'll be the end of it. I will not look for you, I will not pursue you, but if you don't, I will look for you, I will find you and I will explain to you in no uncertain terms your mistake."_ – Williham Totland Oct 14 '16 at 10:45
  • Thanks, @JustinTime, modified the answer to note that, since the ARM ABI is the one that matters here. – paulotorrens Oct 17 '16 at 01:44
  • @paulotorrens You're welcome. I got a bit curious, took a look, and figured I'd comment with what I found. Incidentally, I didn't even notice that the question specified a platform, let alone one that uses the ARM ABI. – Justin Time - Reinstate Monica Oct 17 '16 at 06:02
  • 1
    @paulotorrens Actually used a commercial embedded MIPS compiler toolchain where, by default, the C++ and the C compilers had different sizes for bool. I will never forget the pain our team incurred hunting down the random data corruption crashes caused by this issue. Interestingly the compiler had an option to define the exact size of 'bool'... which was ultimately how we fixed the corruption issue caused by a C defined struct containing bool that was also used in our application C++ code. This was all about 18 years ago... – Matthew Eshleman Oct 18 '16 at 03:12
16

The language standards say nothing about this (I'm happy to be proven wrong about this, I couldn't find anything), so it can't be safe if we just limit ourselves to language standards. But if you're picky about which architectures you support you can find their ABI documentation to see if it will be safe.

For example, the amd64 ABI document has a footnote for the _Bool type that says:

This type is called bool in C++.

Which I can't interpret in any other way than that it will be compatible.

Also, just musing about this. Of course it will work. Compilers generate code that both follow an ABI and the behavior of the largest compiler for the platform (if that behavior is outside the ABI). A big thing about C++ is that it can link to libraries written in C and a thing about libraries is that they can be compiled by any compiler on the same platform (this is why we have ABI documents in the first place). Can there be some minor incompatibility at some point? Sure, but that's something you'd better solve by a bug report to the compiler maker rather than workaround in your code. I doubt bool would be something compiler makers would screw up.

Art
  • 19,807
  • 1
  • 34
  • 60
6

The only thing the C standard says on _Bool :

An object declared as type _Bool is large enough to store the values 0 and 1.

Which would mean that _Bool is at least sizeof(char) or greater (so true / false are guaranteed to be storable).

The exact size is all implementation defined as Michael said in the comments though. You're better off just performing some tests on their sizes on the relevant compiler and if those match and you stick with that same compiler I'd consider it's safe.

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
6

As Gill Bates says above, you do have a problem that sizeof(bool) is compiler-dependent in C. There's no guarantee that the same compiler will treat it the same in C and C++, or that they would be the same on different architectures. The compiler would even be within its rights (according to the C standard) to represent this as an individual bit in a bitfield if it wanted.

I've personally experienced this when working with the TI OMAP-L138 processor which combines a 32-bit ARM core and a 32-bit DSP core on the same device, with some shared memory accessible by both. The ARM core represented bool as an int (32-bit here), whereas the DSP represented bool as char (8-bit). To solve this, I defined my own type bool32_t for use with the shared memory interface, knowing that a 32-bit value would work for both sides. Of course I could have defined it as an 8-bit value, but I considered it less likely to affect performance if I kept it as the native integer size.

If you do the same as I did then you can 100% guarantee binary compatibility between your C and C++ code. If you don't then you can't. It's really as simple as that. With the same compiler, your odds are very good - but there is no guarantee, and changing compiler options can easily screw you over in unexpected ways.

On a related subject, your int should also be using int16_t, int32_t or another integer of defined size. (You should include stdint.h for these type definitions.) On the same platform it is highly unlikely that this will be different for C and C++, but it is a code smell for firmware to use int. The exception is in places where you genuinely don't care how long an int is, but it should be clear that interfaces and structures must have that well-defined. It is too easy for programmers to make assumptions (which are frequently incorrect!) about its size, and the results are generally catastrophic when it goes wrong - and worse, they often don't go wrong in testing where you can easily find and fix them.

Graham
  • 1,655
  • 9
  • 19
  • IIRC, the C++ standard guarantees that `sizeof(bool) == 1`. – sbi Feb 04 '20 at 12:12
  • @sbi I would be surprised at that, because byte addressing is slower than native-word addressing on some processors. It may be true for C++, I don't know. I do know that C does not guarantee that, and that using GCC for a 32-bit ARM core produces 32-bit bools by default. – Graham Feb 04 '20 at 12:19
  • @sbi My answer wasn't about the C++ behaviour, it was about the C behaviour. As I said, I have personal experience of GCC compiling a C bool to integer size (4 bytes on that processor). To figure out the problem when I ran into this, I had my program print sizeof for all the types. bool reported 4. – Graham Jul 12 '20 at 18:51
  • If you were referring to C in your first sentence, you might want to mention that. (Just like I did in my first comment.) – sbi Aug 09 '20 at 15:37
  • 1
    @sbi You **don't** recall correctly: https://en.cppreference.com/w/cpp/language/types#Boolean_type – JDługosz Aug 30 '21 at 15:57
  • 1
    @JDługosz Ugh. Thanks. – sbi Sep 14 '21 at 10:16