c++cgmp

Compiling Mixed C/C++ Code With C Code using GMP Library


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?


Solution

  • 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.