I have a project consisting of C
and C++
files. The C
file code uses the GMP
library. The C++
file contains the main
function and calls a function from the C
file.
point.h
#ifndef POINT_H
#define POINT_H
#ifdef __cplusplus
extern "C" {
#endif
#include <gmp.h>
typedef struct {
mpz_t x;
mpz_t y;
} Point;
void init_point(Point* p);
void clear_point(Point* p);
void print_point(const Point* p);
#ifdef __cplusplus
}
#endif
#endif // POINT_H
point.c
#include <stdio.h>
#include "point.h"
void init_point(Point* p) {
mpz_init(p->x);
mpz_init(p->y);
mpz_set_ui(p->x, 42);
mpz_set_ui(p->y, 17);
}
void clear_point(Point* p) {
mpz_clear(p->x);
mpz_clear(p->y);
}
void print_point(const Point* p) {
gmp_printf("Point(x = %Zd, y = %Zd)\n", p->x, p->y);
}
main.cpp
extern "C" {
#include "point.h"
}
int main() {
Point p;
init_point(&p);
print_point(&p);
clear_point(&p);
return 0;
}
The C
file object gets built but the C++
one doesn't:
[ 33%] Building C object CMakeFiles/gmp_example.dir/point.c.o
[ 66%] Building CXX object CMakeFiles/gmp_example.dir/main.cpp.o
In file included from /usr/include/c++/13/bits/stringfwd.h:40,
from /usr/include/c++/13/iosfwd:41,
from /usr/include/x86_64-linux-gnu/gmp.h:35,
from /mnt/HDD_Small/Bitcoin_Jumper/src/gmp/point.h:8,
from /mnt/HDD_Small/Bitcoin_Jumper/src/gmp/main.cpp:2:
/usr/include/c++/13/bits/memoryfwd.h:64:3: error: template with C linkage
64 | template<typename>
| ^~~~~~~~
/mnt/HDD_Small/Bitcoin_Jumper/src/gmp/point.h:5:1: note: ‘extern "C"’ linkage started here
5 | extern "C" {
| ^~~~~~~~~~
/usr/include/c++/13/bits/memoryfwd.h:67:3: error: template specialization with C linkage
67 | template<>
| ^~~~~~~~
/mnt/HDD_Small/Bitcoin_Jumper/src/gmp/point.h:5:1: note: ‘extern "C"’ linkage started here
5 | extern "C" {
| ^~~~~~~~~~
/usr/include/c++/13/bits/memoryfwd.h:73:3: error: template with C linkage
73 | template<typename, typename>
| ^~~~~~~~
/mnt/HDD_Small/Bitcoin_Jumper/src/gmp/point.h:5:1: note: ‘extern "C"’ linkage started here
5 | extern "C" {
| ^~~~~~~~~~
...
/usr/include/c++/13/iosfwd:217:3: error: template with C linkage
217 | template<typename _CharT, typename _Traits = char_traits<_CharT>,
| ^~~~~~~~
/mnt/HDD_Small/Bitcoin_Jumper/src/gmp/point.h:5:1: note: ‘extern "C"’ linkage started here
5 | extern "C" {
| ^~~~~~~~~~
/usr/include/x86_64-linux-gnu/gmp.h:2293:33: error: conflicting declaration of C function ‘std::ostream& operator<<(std::ostream&, mpq_srcptr)’
2293 | __GMP_DECLSPEC_XX std::ostream& operator<< (std::ostream &, mpq_srcptr);
| ^~~~~~~~
/usr/include/x86_64-linux-gnu/gmp.h:2292:33: note: previous declaration ‘std::ostream& operator<<(std::ostream&, mpz_srcptr)’
2292 | __GMP_DECLSPEC_XX std::ostream& operator<< (std::ostream &, mpz_srcptr);
| ^~~~~~~~
/usr/include/x86_64-linux-gnu/gmp.h:2294:33: error: conflicting declaration of C function ‘std::ostream& operator<<(std::ostream&, mpf_srcptr)’
2294 | __GMP_DECLSPEC_XX std::ostream& operator<< (std::ostream &, mpf_srcptr);
| ^~~~~~~~
/usr/include/x86_64-linux-gnu/gmp.h:2292:33: note: previous declaration ‘std::ostream& operator<<(std::ostream&, mpz_srcptr)’
2292 | __GMP_DECLSPEC_XX std::ostream& operator<< (std::ostream &, mpz_srcptr);
| ^~~~~~~~
/usr/include/x86_64-linux-gnu/gmp.h:2296:33: error: conflicting declaration of C function ‘std::istream& operator>>(std::istream&, mpq_ptr)’
2296 | __GMP_DECLSPEC_XX std::istream& operator>> (std::istream &, mpq_ptr);
| ^~~~~~~~
/usr/include/x86_64-linux-gnu/gmp.h:2295:33: note: previous declaration ‘std::istream& operator>>(std::istream&, mpz_ptr)’
2295 | __GMP_DECLSPEC_XX std::istream& operator>> (std::istream &, mpz_ptr);
| ^~~~~~~~
/usr/include/x86_64-linux-gnu/gmp.h:2297:33: error: conflicting declaration of C function ‘std::istream& operator>>(std::istream&, mpf_ptr)’
2297 | __GMP_DECLSPEC_XX std::istream& operator>> (std::istream &, mpf_ptr);
| ^~~~~~~~
/usr/include/x86_64-linux-gnu/gmp.h:2295:33: note: previous declaration ‘std::istream& operator>>(std::istream&, mpz_ptr)’
2295 | __GMP_DECLSPEC_XX std::istream& operator>> (std::istream &, mpz_ptr);
| ^~~~~~~~
make[2]: *** [CMakeFiles/gmp_example.dir/build.make:93: CMakeFiles/gmp_example.dir/main.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:87: CMakeFiles/gmp_example.dir/all] Error 2
make: *** [Makefile:91: all] Error 2
I tried moving the extern "C"
in the C
file to different places, tried removing it, but nothing worked. Have spent half a day but without success (though, some tests without GMP
were successful).
Question: What is the root of the problem?
The problem here is that have taken a 3rd party header <gmp.h>
and wrapped it with extern "C"
. This happens in two places. In point.h
#ifdef __cplusplus
extern "C" {
#endif
#include <gmp.h>
...
#ifdef __cplusplus
}
#endif
and in main.cpp when you include point.h
extern "C" {
#include "point.h"
}
In general this is a bad idea, and in this specific case what seems to be happening is that gmp.h, when compiled as C++, is including a C++ header file <iosfwd>
. Since this has been wrapped in extern "C"
it is not compiling.
You should trust 3rd party libraries to do the right thing with regard to C/C++ interoperability. Only if you are sure that some 3rd party header is not correct should you attempt to fix it by adding extern "C"
. Of course <gmp.h>
is a well established and widely distributed header file. I'm sure they are doing the right thing already.