Do I need an extern “C” block to include standard

2020-01-27 08:17发布

问题:

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>
}

回答1:

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.



回答2:

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


回答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).



回答4:

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


回答5:

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" {
...


回答6:

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?



标签: c++ posix-api