2

I know I must be doing something wrong here.

rank.h

#ifndef RANK_H
#define RANK_H
namespace mmi {
int chunk;
void rank(int my_rank);
}
#endif

rank.cpp

#include "rank.h"
namespace mmi {
//do something with chunk
}

main.cpp

#include "rank.h"
int main() {
    mmi::chunk = 1;
}

And the output of compilation;

g++ -g -Wall -std=gnu++11   -c -o main.o main.cpp
g++ -g -Wall -std=gnu++11   -c -o rank.o rank.cpp
mpic++ main.o rank.o  -o main
rank.o:(.bss+0x0): multiple definition of `mmi::chunk'
main.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status
Makefile:12: recipe for target 'main' failed
make: *** [main] Error 1

My understanding is that header file is being included multiple times. But I was expecting to correct this issue by using the #ifndef.

So, may I ask what's going on here?

KcFnMi
  • 5,516
  • 10
  • 62
  • 136
  • 1
    "even using ifndef...". Include guards have absolutely no relation to program-wide multiple definition problems.There's no "even" here. `ifndef` has nothing to do with it and can't help you in any way, shape of form. Just don't define non-inline variables in header files. – AnT stands with Russia Jul 20 '18 at 05:08
  • 1
    the `ifndef`, an [Include Guard](https://en.wikipedia.org/wiki/Include_guard), prevents multiple inclusions within one [Translation Unit](https://en.wikipedia.org/wiki/Translation_unit_(programming)). You have two translation units, one for each compiled file, each including rank.h exactly once. The problem comes when the linker assembles the two into a single program and finds `chunk` in each compiled object. The linker doesn't try to figure out which is the real `chunk`, because they both are, and gives up. – user4581301 Jul 20 '18 at 05:11

2 Answers2

4

The line

int chunk;

is not only a declaration, it is also a definition. Every .cpp file that #includes the .hpp file ends up defining it.

Change it to

extern int chunk;

Then, make sure to define it in a .cpp file.

rank.cpp

#include "rank.h"
namespace mmi {
   int chunk;
  //do something with chunk
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Another option, since C++14, is `inline int chunk;` in the header – M.M Jul 20 '18 at 05:13
  • 1
    W00t! There goes a bunch of boiler plate. Now all I have to do is talk the boss into letting me use a modern compiler. – user4581301 Jul 20 '18 at 05:18
  • @M.M, I have not been able to catch up with C++14. How's that different from using `inline` for global variables? – R Sahu Jul 20 '18 at 05:21
  • @RSahu this is a global variable – M.M Jul 20 '18 at 05:30
  • Why would your boss force you into using an old compiler in the first place @user4581301? Knowing that the updated version probably has thousands of bug fixes – HackerDaGreat57 Jul 20 '22 at 19:04
  • 2
    @HackerDaGreat57 but all those bugs are known and will have been taken into account in the existing software or were never encountered at all. Migrating to the new compiler is an unnecessary risk, could be new bugs or break code counting on a now-fixed bug, and the project manager will likely be gone before migration to new tools is forced by changing circumstances. The smart move is to spend a bit of money to keep the products and and tool chains up to date, but humans, being what they are, will almost always kick the problem down the road for more money or easier living now. – user4581301 Jul 24 '22 at 19:17
2

In C++ each file (aka translation unit) is compiled separately. So the compilation of main.cpp is completely independent from the compilation of rank.cpp. There is no way that a #define in one compilation can affect the other compilation. And by the time you link your two object files together the defines have disappeared.

The purpose of include guards is to prevent the inclusion of one header file twice during a single compilation, not across multiple compilations.

john
  • 85,011
  • 4
  • 57
  • 81