Do I need an extern "C" block to include standard POSIX C headers?
Asked Answered
I

7

18

Do I need an extern "C" {} block to include standard C headers in a C++ program. Only consider standard C headers which do not have counterparts in C++.

For example:

extern "C" {
 #include <fcntl.h>
 #include <unistd.h>
}
Intendance answered 10/11, 2011 at 22:46 Comment(1)
Just found this question that's similar to yours: Why do we need extern “C”{ #include <foo.h> } in C++?Chalkboard
L
11

The behavior of <fcntl.h> and <unistd.h> in C++ is not specified by the standard (because they are also not part of the C89 standard). That said, I have never seen a platform where they (a) exist and (b) actually need to be wrapped in an extern "C" block.

The behavior of <stdio.h>, <math.h>, and the other standard C headers is specified by section D.5 of the C++03 standard. They do not require an extern "C" wrapper block, and they dump their symbols into the global namespace. However, everything in Annex D is "deprecated".

The canonical C++ form of those headers is <cstdio>, <cmath>, etc., and they are specified by section 17.4.1.2 (3) of the C++ standard, which says:

<cassert> <ciso646> <csetjmp> <cstdio> <ctime> <cctype> <climits>
<csignal> <cstdlib> <cwchar> <cerrno> <clocale> <cstdarg> <cstring>
<cwctype>

Except as noted in clauses 18 through 27, the contents of each header cname shall be the same as that of the corresponding header name.h, as specified in ISO/IEC 9899:1990 Programming Languages C (Clause 7), or ISO/IEC:1990 Programming Languages—C AMENDMENT 1: C Integrity, (Clause 7), as appropriate, as if by inclusion. In the C++ Standard Library, however, the declarations and definitions (except for names which are defined as macros in C) are within namespace scope (3.3.5) of the namespace std.

So the standard, non-deprecated, canonical way to use (e.g.) printf in C++ is to #include <cstdio> and then invoke std::printf.

Licketysplit answered 10/11, 2011 at 23:0 Comment(2)
The behavior of <fcntl.h> and <unistd.h> is specified by the POSIX standard -- POSIX requires that these header files be usable directly by the supported C or C++ compiler without anything additional -- so essentially, they're required to have a embedded, guarded extern "C" or something else equivalent for all supported compilers.Brieta
@ChrisDodd - That is true for C, of course. Can you cite any POSIX document that says these headers must "be usable directly" in C++?Licketysplit
H
24

The system C headers usually already include a extern "C" block, guarded by #ifdef __cplusplus. This way the functions automatically get declared as extern "C" when compiled as C++ and you don't need to do that manually.

For example on my system unistd.h and fcntl.h start with __BEGIN_DECLS and end with __END_DECLS, which are macros defined in sys/cdefs.h:

/* C++ needs to know that types and declarations are C, not C++.  */
#ifdef   __cplusplus
# define __BEGIN_DECLS  extern "C" {                                            
# define __END_DECLS }
#else
# define __BEGIN_DECLS
# define __END_DECLS
#endif
Hendershot answered 10/11, 2011 at 22:56 Comment(0)
L
11

The behavior of <fcntl.h> and <unistd.h> in C++ is not specified by the standard (because they are also not part of the C89 standard). That said, I have never seen a platform where they (a) exist and (b) actually need to be wrapped in an extern "C" block.

The behavior of <stdio.h>, <math.h>, and the other standard C headers is specified by section D.5 of the C++03 standard. They do not require an extern "C" wrapper block, and they dump their symbols into the global namespace. However, everything in Annex D is "deprecated".

The canonical C++ form of those headers is <cstdio>, <cmath>, etc., and they are specified by section 17.4.1.2 (3) of the C++ standard, which says:

<cassert> <ciso646> <csetjmp> <cstdio> <ctime> <cctype> <climits>
<csignal> <cstdlib> <cwchar> <cerrno> <clocale> <cstdarg> <cstring>
<cwctype>

Except as noted in clauses 18 through 27, the contents of each header cname shall be the same as that of the corresponding header name.h, as specified in ISO/IEC 9899:1990 Programming Languages C (Clause 7), or ISO/IEC:1990 Programming Languages—C AMENDMENT 1: C Integrity, (Clause 7), as appropriate, as if by inclusion. In the C++ Standard Library, however, the declarations and definitions (except for names which are defined as macros in C) are within namespace scope (3.3.5) of the namespace std.

So the standard, non-deprecated, canonical way to use (e.g.) printf in C++ is to #include <cstdio> and then invoke std::printf.

Licketysplit answered 10/11, 2011 at 23:0 Comment(2)
The behavior of <fcntl.h> and <unistd.h> is specified by the POSIX standard -- POSIX requires that these header files be usable directly by the supported C or C++ compiler without anything additional -- so essentially, they're required to have a embedded, guarded extern "C" or something else equivalent for all supported compilers.Brieta
@ChrisDodd - That is true for C, of course. Can you cite any POSIX document that says these headers must "be usable directly" in C++?Licketysplit
I
3

Yes, you do. However, many systems (notably Linux) are already adding an extern "C" bracketing like you do. See (on Linux) files /usr/include/unistd.h /usr/include/features.h and the macro __BEGIN_DECLS defined in /usr/include/sys/cdefs.h and used in many Linux system include files.

So on Linux, you usually can avoid your extern "C" but it does not harm (and, IMHO, improve readability in that case).

Inseverable answered 10/11, 2011 at 22:54 Comment(1)
"does not harm"? But I was told "You shouldn't even need to do that (the extra extern "C" {}) in most cases as they are system headers. You can actually break things doing that as some have C++ only exports". Is that true? I just asked a question here Are most linux system headers C++ compatiable?Murmuration
T
2

No, you should use the C++ wrapper headers (for instance like <cstdio>). Those take care of all that for you.

If it's a header that doesn't have those, then yes, you'll want to wrap them in extern "C" {}.

ETA: It's worth noting that many implementations will include the wrapper inside the .h file like below, so that you can get away with not doing it yourself.

#ifdef  __cplusplus
extern "C" {
#endif

#ifdef  __cplusplus
}
#endif
Thitherto answered 10/11, 2011 at 22:51 Comment(1)
Worth noting that the <cstdio> etc. headers technically put their definitions in the std namespace. (Many implementations also put them in the top-level namespace, but that is not what the standard says.)Licketysplit
C
0

the macro __BEGIN_DECLS defined in /usr/include/sys/cdefs.h and used in many Linux system include files.

enter image description here

Cogon answered 6/7, 2022 at 17:37 Comment(0)
C
-1

It is a good idea to let the compiler know so that it can expect C code when compiling as C++. You might also find that the header files themselves contain extern "C" { as guards.

For example, curses.h on my system contains:

#ifdef __cplusplus
extern "C" {
...
Chalkboard answered 10/11, 2011 at 22:53 Comment(3)
"let the compiler know so that it can expect C code when compiling as C++" - That's not what extern "C" does. It doesn't change the interpretation of code. It doesn't even apply to code. It's a language linkage directive that instructs the compiler to generate symbols compatible with those the linker expects for C. It makes C++ code callable from C, but doesn't change code generation.Woodward
@Woodward Doesn't "instructs the compiler to generate symbols compatible with those the linker expects for C" imply that it applies to code, or at least the calling convention of code? It is definitely letting the compiler (as well as the linker) know something.Traipse
@Tanz87: It doesn't change code generation one bit. The generated object code is identical with or without extern "C". The directive applies to symbol naming only. It has no bearing on calling conventions.Woodward
L
-1

I just double checked the stdlib.h for the GNU compiler and the declarations do not use extern "C" as declarations.

edit:

if defined __cplusplus && defined _GLIBCPP_USE_NAMESPACES
define __BEGIN_NAMESPACE_STD    namespace std {

So including the old headers will place declarations on std provided _GLIBCPP_USE_NAMESPACES is defined?

Ludeman answered 10/11, 2011 at 22:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.