4

Within extern "C" { } the macro __cplusplus is still defined. When I want to include the C version of mpi.h in the header of my library which is dynamically load this will not work as mpi.h still finds __cplusplus and will still load like it was opened by C++.

#undef __cplusplus works with gcc. But I do not want to rely on this.

So how to write a C++ program that - uses the C++ version of mpi and - is linked against a C-library that uses the C-Version of mpi (where #include <mpi.h> appears already in the header?

Example code:

library.h:

#ifdef __cplusplus
extern "C" {
#endif

#include <mpi.h>

void library_do(MPI_Comm comm);

#ifdef __cplusplus
}
#endif

program.cpp:

#include <library.h>

#include <mpi.h>

int main() {
  MPI::Init();
  // do some mpi C++ calls...  
  library_do(MPI::COMM_WORLD);
  MPI::Finalize();
}

In case somebody wants to play the example here the library.c:

#include <stdio.h>
#include "library.h"

void library_do(MPI_Comm comm)
{
    int rank;
    MPI_Comm_rank(comm, &rank);
    printf("MPI Rank: %d", rank);
}

And to compile everything I try with

mpicc -shared library.c -o lib.so
mpicxx program.cpp -l lib.so
  • 6
    `__cplusplus` is defined when you are compiling translation unit as C++ code. `extern "C" { }` does not make code inside compile as C – user7860670 Jul 10 '19 at 09:57
  • __cplusplus is a macro. It is defined and used by the preprocessor. The preprocessor has no idea about curly braces or C++ keywords. So whatever you expect of __cplusplus is fundamentally impossible. – n. m. could be an AI Jul 10 '19 at 10:02
  • 3
    The question is a prime example of an XY-Problem. You didn't include any code or error message - now you got *four correct answers* addressing your *question*, but probably not your *problem*. Please [edit] your question to make it more useful and to avoid that for any further questions. – Zulan Jul 10 '19 at 10:27
  • 1
    *this will not work as `mpi.h` still finds `__cplusplus` and will still load like it was opened by C++.* If `__cplusplus` was defined at compile time, and you didn't improperly define it yourself in C code, the library **is being opened by C++ code.** `#undef __cplusplus` is [Easter egging](http://www.catb.org/~esr/jargon/html/E/Easter-egging.html) - you are blindly changing things you don't understand trying to fix something. – Andrew Henle Jul 10 '19 at 10:47

6 Answers6

9

__cplusplus will always be defined by the compiler if the compiler is a C++ compiler. extern "C" {} only gives you C linkage so the code inside plays nice with a C compiler.

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
4

The point of using

#ifdef __cplusplus
extern "C" {
#endif

...

#ifdef __cplusplus
}
#endif

is to prevent the name mangling that C++ does. We are basically saying dont use the name mangling like a traditional C++ function call instead leave it undecorated. This link could be useful Name mangling

It is used to make C headers compatible with C++. The flag __cplusplus is automatically defined in C++ compiler.

Shrikanth N
  • 652
  • 3
  • 17
3

Because they are different things. extern "C" {} tells the compiler how to export symbols (see here), whereas __cplusplus tells you that you can use C++ code so your library can use different code paths inbetween #ifdefs.

tstenner
  • 10,080
  • 10
  • 57
  • 92
2

Of course it's defined. It's still a C++ compiler that compiled the code inside the extern "C" block. It doesn't stop treating the code as C++, only makes sure to use a C calling/naming convention.

If the header cannot be compiled by a C++ compiler, the only recourse is to create a C wrapper that exposes a C++ compatible API.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
2

The compiler outputs the following:

In file included from /usr/include/c++/4.8.2/bits/stl_algobase.h:61:0,
                 from /usr/include/c++/4.8.2/bits/stl_tree.h:61,
                 from /usr/include/c++/4.8.2/map:60,
                 from /usr/include/openmpi-x86_64/openmpi/ompi/mpi/cxx/mpicxx.h:38,
                 from /usr/include/openmpi-x86_64/mpi.h:2674,
                 from x1.cpp:6:
/usr/include/c++/4.8.2/bits/cpp_type_traits.h:72:3: error: template with C linkage
   template<typename _Iterator, typename _Container>
   ^
/usr/include/c++/4.8.2/bits/cpp_type_traits.h:85:3: error: template with C linkage
   template<bool>
   ^
...

The mpi.h header detects that it's being compiled as C++ and so includes C++ specific features. However templates (among other things) don't work with C linkage (i.e. if the header is within an extern "C" block).

Move the include above extern "C":

#include <mpi.h>

#ifdef __cplusplus
extern "C" {
#endif

void library_do(MPI_Comm comm);

#ifdef __cplusplus
}
#endif
dbush
  • 205,898
  • 23
  • 218
  • 273
  • The problem still persists in the real world program which loads a cpp library that again depends on the C library using mpi. The error is `libbla_plugin.so: undefined symbol: _ZN3MPI3Win4FreeEv` – user2722085 Jul 10 '19 at 13:45
  • Finally found the error for this too: I linked against the C version of MPI lib in the cpp library in cmake. – user2722085 Jul 10 '19 at 14:14
1

If you #include <mpi.h> from a C++ program, just don't put extern "C" around it. At least OpenMPI and Intel MPI do this for you in the header itself - if __cplusplus is defined.

You probably get into trouble because some MPI implementations still define the C++ interface in mpi.h that was deleted from the standard. This obviously breaks under extern "C". For instance with OpenMP you can skip this by setting OMPI_SKIP_MPICXX. Then the double extern "C" works, but I still wouldn't recommend it.

Update: In case you can't modify the library header, just #include <mpi.h> before #include <lib.h>.

That said, you should not use the C++ bindings for MPI. They were removed from the standard more than 6 years ago, after being for 3 years...

Zulan
  • 21,896
  • 6
  • 49
  • 109