2
votes

I have three files in my project.

a.c
b.c
test.h

test.h declares a

namespace test_namespace {
    int i;
    void f1();
};

test.h is also surrounded by

#ifndef __x
#define __x
...
#endif

now, a.c includes test.h and b.c also includes test.h . a.c has the main() function, and b.c has the implementation of test_namespace::f1()

However, on compiling this, I get a linking error -

"test_namespace::i is already defined in <b.c's object file mapping in /tmp>"

If I've taken care to include conditional compilation preprocessor directives in test.h, why is it being included in both files a.c and b.c ?

Also is noteworthy that if I compile b.c separately as a shared library and then use it as a shared library while linking a.c's object file, i don't get this error.

Can someone please explain the above error to me, specially in the face of the conditional compilation directives ?

6
Your conditionnal header file inclusion are working perfectly, but your code is broken. global variables should be only defined once.BatchyX
Conditional compilation directives (normally called include guards) don't do what you think they do. They prevent header files being included twice in the same source file, they don't prevent header files being include twice in different source files. If that was how they worked that would be magic, and we don't believe in that.john
"Things"/identifiers starting with 2 underscores or an underscore and a capital letter are reserved for the implementation. Please revise your header guard naming. Just make it HEADERNAME_H like everyone else, perhaps prefixed with PROJECTNAME_.rubenvb

6 Answers

4
votes

You cannot declare variables inside a headerfile. The symbol test_namespace::i becomes exported by both a.c and b.c. The linker find's both and doesn't know which one to use.

What you want to do in test.h is:

namespace test_namespace {
    extern int i;
    void f1();
}

and then declare test_namespace::i in eather a.c or b.c:

namespace test_namespace {
    int i;
}
3
votes

Conditional inclusion is used to prevent headers from being included twice for the same source file not for the whole project. Suppose you have headersa.h and b.h, and b.h #includes a.h. Then if c.c needs things from both headers, it #include both of them. Since the C preprocessor using literal text substitution, when it includes b.h, there will now be two #include "a.h" directives in your file, wreaking the havoc of multiple declarations. (Edit: clarify why you run into problems in this case.)

2
votes

Include guards are there to protect multiple header inclusions in the build of a compilation unit. They aren't necessary for cases when you have two separate code files and one header, like your example.

(So think more when test.c uses a.h and b.h, in those cases where b.h needs to #include a.h.)

But that's a note about what the include guard convention does and how it isn't buying you anything in this case. The specific technical issue you hit (as others have pointed out) is that you're basically defining the same variable in two different object files, and when the linker goes to pull everything together it doesn't know if you want the variable from a.o or b.o.

( Note: While compilers can generally be set to override things and build C++ code using features like namespace even if the extension is .c - you probably should be using something else, like .cpp: C++ code file extension? .cc vs .cpp )

1
votes

You're defining test_namespace::i in the header. What you probably want is extern int i; in the header, and a definition in one of the source files.

1
votes

What's happening is that when you say

int i;

this actually does two things:

1) Declares the symbol i

2) Reserves some space and a symbol for i in the object file

The trick is that (1) should only be done once per file (actually you can repeat it in this case) -- which is why you have the conditional include, which you have done correctly -- but (2) should only be done once per program

The solution is to have the header do

// Only declare -- don't create a symbol
extern int i;

And in the *.c file do

int i;
1
votes

The header guards (#ifndef .. #define .. #endif) are working as they should. Both a.c and b.c include test.h, so they both get a copy of that header. (When you compile a program, what #include does is to literally copy-paste the contents of the header inside the source file.)

Since they both have a copy of the header, they both define the variable test_namespace::i. When the linker tries to link the code generated from a.c with the code generated from b.c it finds that they both define that variable. It doesn't know what to do, so it doesn't complete and outputs an error.