Linking to C libraries from C++: why isn't extern always needed? - c++

Usually to get a C library working from C++ you have to include it with extern "C" { #include <clibrary.h> }. Many libraries will include in their header files code like #ifdef __cplusplus extern "C" { ... to make them more user friendly to C++ code (e.g. pthread.h). Sometimes this is not the case. For instance, stdio.h has no such #ifdef, yet I can still compile & link the usual #include <stdio.h> int main() {printf("Hello");} using a C++ compiler without wrapping it in an extern "C" statement. Why is this?

Usually to get a C library working from C++ you have to include it with extern "C" { #include <clibrary.h> }.
Only when the library was not designed with C++ compatibility in mind. But this is a hack.
Many libraries will include in their header files code like #ifdef __cplusplus extern "C" { ... to make them more user friendly to C++ code (e.g. pthread.h)
Yes, a good library will do this.
As a result, you do not need to and should not add another extern "C" around the #include.
stdio.h is an example of a header that will be doing this properly (see below).
For instance, stdio.h has no such #ifdef
Sure it does! Follow the money trail…
why isn't extern always needed?
So, in conclusion, you only need to do this yourself when the author of the header file didn't do it for you. When the author of the header file did it, you do not need to do it.

For instance, stdio.h has no such #ifdef
It probably does. Regardless, <stdio.h> is a header provided by the C++ standard library (inherited from the C standard library). It is guaranteed to work without extern "C" as are all standard headers.
Note that the usage of <name.h> name of the inherited standard headers in C++ instead of <cname> is deprecated in the current edition of the standard and has been identified as a candidate for removal in future revisions.
why isn't extern always needed?
Simply because some headers have been written to support C++ directly, and so do it by themselves.

Related

clang-tidy: How to suppress C++ warnings in C header file?

I've got a .h file that is included by both C and C++ source files. Its contents is wrapped in
#ifdef __cplusplus
extern "C" {
#endif
...
#ifdef __cplusplus
}
#endif
Yet, when I include it in a .cpp file, clang-tidy issues C++-specific messages, like
warning: including 'stdbool.h' has no effect in C++; consider removing it [hicpp-deprecated-headers,modernize-deprecated-headers]
warning: inclusion of deprecated C++ header 'stdlib.h'; consider using 'cstdlib' instead [hicpp-deprecated-headers,modernize-deprecated-headers]
warning: use 'using' instead of 'typedef' [modernize-use-using]
I like these checks and I want to keep them active in my clang-tidy configuration, but of course for C++ code only. I can't change the header file to use using instead of typedef or <cstdlib> instead of <stdlib.h> because it's also included by C sources, hence the extern "C".
Is there any way to tell clang-tidy to treat code in extern "C" as C instead of C++, even if included from a .cpp file?
The clang-tidy version is 12.0.0.
Clang-Tidy can make use of the special NOLINT or NOLINTNEXTLINE comments to suppress warning of specific lines. It is intended exactly for your use case:
some lines contains legacy or not stricly nice C++ code
there is a good reason to do so - here the code has to be parseable by a C compiler.
The higher risk when using that is to abuse it and silence warnings where it would have be possible to improve the coding. But when you need a header to be used form both C and C++ sources, and you have carefully twice read the NOLINTed line, it is perfectly fine, at least IMHO. Furthermore, it is even possible to indicate the warnings to silence:
#ifdef __cplusplus
extern "C" {
#endif
// NOLINTNEXTLINE(hicpp-deprecated-headers,modernize-deprecated-headers) C compatible code
#include <stdbool.h>
#include <stdlib.h> // NOLINT C code requires that header
...
#ifdef __cplusplus
}
#endif
Those C and C++ headers strictly aren't equivalent for C++. Those warninggs are legal and indication of code non-compliant to C++ standard.
extern "C" only declares linkage type of declared functions and variables as global and without mangling, names are used in C format. It cannot affect object and functions that are declared with C++ features use. It have nothing to do with language's ecosystem. More of, if a C++ header appear inside those brackets, there may appear linking errors if a function declared there was originally in C++ linkage.
For C++ you have to use #ifdef __cplusplus to include C++ alternatives, including C ones otherwise.
Instead of:
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdlib.h>
// your declarations
#ifdef __cplusplus
}
#endif
You would have a header with following structure
// headers that are universally compatible (3rd party)
#ifdef __cplusplus
# include <cstdlib>
// other C++ headers
// Your C++ - only declarations (incompatible with C rules)
extern "C" {
#else
# include <stdbool.h>
# include <stdlib.h>
// other C headers
// C-only declarations (incompatible with C++ rules)
#endif
// your compatible declarations available for both
#ifdef __cplusplus
} // extern "C"
#endif
In some cases this structure would need to be adjusted because of required order of header inclusion or declarations's dependency. Blindly changing linkage of declarations in library headers you didn't write and you cannot guarantee what naming conventions and linkage type those use is a faux pas that can result in ODR breach or linking failures.
The typedef issue is annoying and you can use NOLINT or command-line option to suppress modernize-use-using as an interim measure. In general for portable header this would be done by macro-definitions at least for struct types. There is a problem where typedef and alias declaration aren't convertible in some case by standard means, e.g. consider a declaration of function pointer type.

In ref : Linking C compiled static library to C++ Program

Stackoverflow won't let me comment because I am a newbie.
Borrowing the title from the question posted here, the accepted solution says that you have to modify the C header file using #ifdef __cplusplus and extern "C".
I have the exact same question but I have been provided a C library containing over 100 .h C header files and .c C program files and now I have to use a C++ library so my main() will be in C++.
So do I now have to add those modifications to each one of those C header files or is there some other approach? My main() is in a .cpp file which will use functions from this C library.
EDIT
This question has been resolved. I wish for this question to be closed or removed.
You don't have to modify any headers. What is necessary is to wrap all C declarations with extern C {}. You can do this just as well in you cpp file:
extern "C" {
#include "some_c_header.h"
}
int main() {
std::cout << "This is C++ code\n";
}
It might be useful to create a header file which simply includes C headers with C linkage:
// c_header.hpp
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "c_header_1.h"
#include "c_header_2.h"
#ifdef __cplusplus
}
#endif
// main.cpp
#include "c_header.hpp" // extern "C" {} already handled for you
Only those declarations of C functions and objects that are accessed by C++ components need to declared with C linkage, and only to the C++ components. Presumably, your new C++ library accesses very little, if anything, from the existing C code, so the primary concern would be whatever is accessed (directly) by main(). Those functions must be declared (to main) with C linkage. If that turns out to be a significant number of things then you could consider refactoring to make it fewer.
Moreover, no, you do not need to modify existing header files. The C++ files that reference your C library, and that therefore include some of its headers, can wrap the relevant #include statements an an extern "C" block to take care of the issue.

Why could c++ include <stdio.h> and invoke printf method which is c(platform:ubuntu 14.04 compiler:gcc 4.8.4)

c++ compiler could compile code like this, and it executed correctly
#include <stdio.h>
int main() {
printf("test...\n");
return 0;
}
I think printf.c will be compiled to printf.o with c compiler, I just checked the stdio.h, there is no extern "C" keyword, then how could c++ linker link printf in printf.o which is compiled with c compiler?(By the way, my platform is ubuntu 14.04, compiler is gcc 4.8.4)
printf is part of the C++ standard library.
The <stdio.h> header that you include in the C++ source, belongs to the C++ standard library, and is not necessarily the same contents as a C compiler will include.
How the C++ implementation leverages the corresponding C implementation (if at all) is an implementation detail.
When C++ was originally made it was effectively a superset of C. That is to say, you can code perfectly good C in the C++ environment, just by ignoring all of the features that C++ adds. That, and because nowadays most C compilers are C++ compilers in which you can code C, is why you can use printf.
Secondly, no object code is generated for stdio because it is already a library, and so you are linking your own .o code against the already compiled stdio library code, which will be located somewhere in your compilers directory.
Nobody can give you a definitive answer without knowing what implementation you're using.
Cheers and hth. - Alf gave one possibility, which is that the stdio.h that is included by a C++ program may not be the same as the stdio.h that is included by a C program. Another possibility is that it is the same header, and there is an extern "C" block, and you just can't see it. For example, I use gcc, and my /usr/include/stdio.h contains a __BEGIN_DECLS macro, which expands to extern "C" { when compiled as C++. See Do I need an extern "C" block to include standard C headers?
You may not see an explicit extern "C" in stdio.h, but it is there. It's just hiding.
For example, on Linux, in stdio.h we see this:
#include <features.h>
__BEGIN_DECLS
In <features.h> you will find the following:
# ifndef _SYS_CDEFS_H
# include <sys/cdefs.h>
# endif
And in <sys/cdefs.h> you finally see:
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS
# define __END_DECLS
#endif
So, via a fairly roundabout path, Linux header files have a __BEGIN_DECLS/__END_DECLS wrapper which, when compiled by a C++ compiler, end up wrapping the whole thing inside extern "C".

How to use C code in C++

Just a small question:
Can C++ use C header files in a program?
This might be a weird question, basically I need to use the source code from other program (made in C language) in a C++ one. Is there any difference between both header files in general? Maybe if I change some libraries...
I hope you can help me.
Yes, you can include C headers in C++ code. It's normal to add this:
#ifdef __cplusplus
extern "C"
{
#endif
// C header here
#ifdef __cplusplus
}
#endif
so that the C++ compiler knows that function declarations etc. should be treated as C and not C++.
If you are compiling the C code together, as part of your project, with your C++ code, you should just need to include the header files as per usual, and use the C++ compiler mode to compile the code - however, some C code won't compile "cleanly" with a C++ compiler (e.g. use of malloc will need casting).
If on, the other hand, you have a library or some other code that isn't part of your project, then you do need to make sure the headers are marked as extern "C", otherwise C++ naming convention for the compiled names of functions will apply, which won't match the naming convention used by the C compiler.
There are two options here, either you edit the header file itself, adding
#ifdef __cplusplus
extern "C" {
#endif
... original content of headerfile goes here.
#ifdef __cplusplus
}
#endif
Or, if you haven't got the possibility to edit those headers, you can use this form:
#ifdef __cplusplus
extern "C" {
#endif
#include <c_header.h>
#ifdef __cplusplus
}
#endif
Yes, but you need to tell the C++ compiler that the declarations from the header are C:
extern "C" {
#include "c-header.h"
}
Many C headers have these included already, wrapped in #if defined __cplusplus. That is arguably a bit weird (C++ syntax in a C header) but it's often done for convenience.

How can we use any C library inside our C++ code?

How can we use any C library inside our C++ code? (Can we? Any tuts on that?) (I use VS10 and now talking about libs such as x264 and OpenCV)
Yes, the only thing you need to do is to wrap the #include statement with extern "C" to tell the C++ compiler to use the C-semantics for function names and such:
extern "C" {
#include <library.h>
}
During linking, just add the library like any normal C++ lib.
Well you can use any C library from your C++ code. That's one the cool thing with C++ :-)
You just have to include the libraries headers in your C++ code and link with the libraries you use.
Any good library handles its header inclusion from C++. If it is not the case you have to do it yourself with things like :
#ifdef __cplusplus
extern "C" {
#endif
#include "c_header.h"
#ifdef __cplusplus
}
#endif
Edit: As Mike said, the ifdef parts are only needed if you do not know if your file will be used with C or C++. You can keep them if the file is a header of an API header for example.
By the way, opencv handles the inclusion by C or C++ (thus you already have the #ifdef part in opencv headers). I do not know for x264 ...
my2cents
As far as I know, if you have the library you want to use, you just stick an include in your header file and you can use it.
from there on.