Multiple Inclusion Guards Work in MSVC++ .NET...right?

F

Fritz Foetzl

I'm flummoxed. I'm a veteran C++ programmer from the Unix/Linux camp,
trying to learn Visual C++. I'm trying to build a project in which I
need to include one header in a couple of different files, but the
classic multiple inclusion problem is biting me hard. The
#ifndef..#define..#endif method doesn't seem to be working, although
all the documentation I've read indicates that it should.

As a small example, I have an empty console project with three files:
globals.h, functions.cpp and driver.cpp. They look like this:

// ----------------------------------------------------
// globals.h
#ifndef _GLOBALS_
#define _GLOBALS_

const char *msg = "Wiggety wack";

#endif
// EOF

// ----------------------------------------------------
// functions.cpp
#include <iostream>
using namespace std;

#include "globals.h"

void getWiggety ()
{
cout << msg << endl;
}
// EOF

// ----------------------------------------------------
// driver.cpp
#include <iostream>
using namespace std;

#include "globals.h"

extern void getWiggety (void);

int main (void)
{
cout << msg << endl;
getWiggety ();
return 0;
}
// EOF

This won't link, because msg is declared twice, in spite my
#ifndef..#define..#endif in globals.h. I've gone and looked at
<iostream>, and it's protected against multiple inclusion the same way
as I'm doing it. I'm also including it in two places but...the linker
doesn't complain about std::cout et. al.

WTFO?

ff
 
J

Jonathan Turkanis

Fritz Foetzl said:
I'm flummoxed. I'm a veteran C++ programmer from the Unix/Linux camp,
trying to learn Visual C++. I'm trying to build a project in which I
need to include one header in a couple of different files, but the
classic multiple inclusion problem is biting me hard. The
#ifndef..#define..#endif method doesn't seem to be working, although
all the documentation I've read indicates that it should.

As a small example, I have an empty console project with three files:
globals.h, functions.cpp and driver.cpp. They look like this:

// ----------------------------------------------------
// globals.h
#ifndef _GLOBALS_
#define _GLOBALS_

const char *msg = "Wiggety wack";

#endif
// EOF

// ----------------------------------------------------
// functions.cpp
#include <iostream>
using namespace std;

#include "globals.h"

void getWiggety ()
{
cout << msg << endl;
}
// EOF

// ----------------------------------------------------
// driver.cpp
#include <iostream>
using namespace std;

#include "globals.h"

extern void getWiggety (void);

int main (void)
{
cout << msg << endl;
getWiggety ();
return 0;
}
// EOF

I think your problem has nothing to do with include guards. The header
"globals.h" is properly included twice, once when each of the files
"driver.cpp" and "functions.cpp" is compiled. The problem is that msg
is being defined twice in the same program, violating the ODR.

You should probably decalre it extern and define it in "global.cpp",
or use an inline function instead

inline msg()
{
static const char* s = "Wiggety wack";
return s;
}

Jonathan
 
K

Kevin Goodsell

Fritz said:
I'm flummoxed. I'm a veteran C++ programmer from the Unix/Linux camp,
trying to learn Visual C++. I'm trying to build a project in which I
need to include one header in a couple of different files, but the
classic multiple inclusion problem is biting me hard. The
#ifndef..#define..#endif method doesn't seem to be working, although
all the documentation I've read indicates that it should.

As a small example, I have an empty console project with three files:
globals.h, functions.cpp and driver.cpp. They look like this:

// ----------------------------------------------------
// globals.h
#ifndef _GLOBALS_
#define _GLOBALS_

You need a new idiom for include guard names. Identifiers beginning with
an underscore followed by an upper case letter or another underscore are
reserved for the implementation's use for any purpose. Unless you are
sure you know better, avoid identifiers that begin with an underscore.

This is not your problem, however. Nor is it multiple inclusion. Include
guards protect against the same thing appearing multiple times in a
single translation unit, but not against the same thing appearing in two
different translation units. For example, if I have a main() function in
a.cpp and I also have a main() function in b.cpp, my program won't link.
This is basically what you are doing - you have 'msg' appearing in both
..cpp files, because each has it's own #included copy of globals.h.

The proper way to do this is to only declare 'msg' in the header (with
'extern', and no initialization) and then have its definition (along
with the initialization) in a .cpp file somewhere.

-Kevin
 
J

John Carson

Kevin Goodsell said:
This is not your problem, however. Nor is it multiple inclusion.
Include guards protect against the same thing appearing multiple
times in a single translation unit, but not against the same thing
appearing in two different translation units. For example, if I have
a main() function in a.cpp and I also have a main() function in
b.cpp, my program won't link. This is basically what you are doing -
you have 'msg' appearing in both .cpp files, because each has it's
own #included copy of globals.h.

The proper way to do this is to only declare 'msg' in the header (with
'extern', and no initialization) and then have its definition (along
with the initialization) in a .cpp file somewhere.

I agree that that is the best way to do it, but I think that the strategy of
the OP should still work. This is because const variables should have
internal linkage by default, i.e.,

const char *msg = "Wiggety wack";

should be equivalent to

static const char *msg = "Wiggety wack";

The latter will certainly build without a problem and so should the former.
 
J

Jonathan Turkanis

John Carson said:
I agree that that is the best way to do it, but I think that the strategy of
the OP should still work. This is because const variables should have
internal linkage by default, i.e.,

const char *msg = "Wiggety wack";

should be equivalent to

static const char *msg = "Wiggety wack";

The latter will certainly build without a problem and so should the former.

The former does not build on VC7.1 or GCC 3.2, and I don't see why it
should. 3.5/3 says names explicitly declared const have internal
linkage if they are objects or references; msg is a pointer. Could you
explain why you think msg should have internal linkage?

Regards,
Jonathan
 
J

John Carson

Jonathan Turkanis said:
The former does not build on VC7.1 or GCC 3.2, and I don't see why it
should. 3.5/3 says names explicitly declared const have internal
linkage if they are objects or references; msg is a pointer. Could you
explain why you think msg should have internal linkage?

Regards,
Jonathan


Actually, we are both wrong. A const pointer does have internal linkage, as
I suggested, but

const char *msg = "Wiggety wack";

does not define a const pointer. Rather, it defines a non-const pointer to
const char. To create a const pointer, we need:

const char * const msg = "Wiggety wack";

This compiles on VC++ 7.0.

As for the standard, it says:

"A name having namespace scope (3.3.5) has internal linkage if it is the
name of
— an object, reference, function or function template that is explicitly
declared static or,
— an object or reference that is explicitly declared const and neither
explicitly declared extern nor previously declared to have external linkage;
or ..."

Observe that there is no explicit mention of pointers under the first dashed
point dealing with the use of the static keyword, yet we know that declaring
a pointer static will give it internal linkage. Accordingly, I infer that
pointers are included as "objects" under both dashed points.
 
J

Jonathan Turkanis

John Carson said:
Actually, we are both wrong. A const pointer does have internal linkage, as
I suggested, but

const char *msg = "Wiggety wack";

Your right. Duh!
As for the standard, it says:

"A name having namespace scope (3.3.5) has internal linkage if it is the
name of
— an object, reference, function or function template that is explicitly
declared static or,
— an object or reference that is explicitly declared const and neither
explicitly declared extern nor previously declared to have external linkage;
or ..."

Observe that there is no explicit mention of pointers under the first dashed
point dealing with the use of the static keyword, yet we know that declaring
a pointer static will give it internal linkage. Accordingly, I infer that
pointers are included as "objects" under both dashed points.

I don't follow your reasoning here. The passage is talking about
const, not static. I believe objects and pointers are usually treated
separately by the standard.

Jonathan
 
J

Jonathan Turkanis

John Carson said:
"Jonathan Turkanis" <[email protected]> wrote in message
"A name having namespace scope (3.3.5) has internal linkage if it is the
name of
— an object, reference, function or function template that is explicitly
declared static or,
— an object or reference that is explicitly declared const and neither
explicitly declared extern nor previously declared to have external linkage;
or ..."

I guess the answer comes from 1.8/1.

Jonathan
 
J

John Carson

Jonathan Turkanis said:
Your right. Duh!


I don't follow your reasoning here. The passage is talking about
const, not static. I believe objects and pointers are usually treated
separately by the standard.

Look again. The first dashed point says:

"an object, reference, function or function template that is explicitly
declared static"
 
J

Jonathan Turkanis

John Carson said:
Look again. The first dashed point says:

"an object, reference, function or function template that is explicitly
declared static"

I see what your argument was now:

1. We've all heard that delaring a pointer static has gives it
internal linkage.
2. This must be the passage which states that rule.
3. Therefore, this passage must be refering to pointers (among
other things)

Right? :)

This get's the interpretive porcess exactly backwards. I'd like to
think we should start with the text, read it carefully, and figure out
what it means. Of course, in this case, I did a horrible job.

Jonathan
 
J

John Carson

Jonathan Turkanis said:
I see what your argument was now:

1. We've all heard that delaring a pointer static has gives it
internal linkage.
2. This must be the passage which states that rule.
3. Therefore, this passage must be refering to pointers (among
other things)

Right? :)
Exactly.

This get's the interpretive porcess exactly backwards. I'd like to
think we should start with the text, read it carefully, and figure out
what it means.

Yes, that would be ideal. But the standard often isn't that easy to
interpret. To be a really good interpreter of the standard, I think you
would have to read the whole thing from the beginning --- which I am
disinclined to do.
 
E

E. Robert Tisdale

Fritz Foetzl wrote:

[snip]
This won't link.

Try this:
cat globals.h
#ifndef GUARD_GLOBALS_H
#define GUARD_GLOBALS_H 1

const
char *const msg = "Wiggety wack";
extern void getWiggety(void);

#endif//GUARD_GLOBALS_H
cat functions.cpp
#include <iostream>
#include "globals.h"

void getWiggety(void) {
std::cout << msg << std::endl;
}
cat driver.cpp
#include <iostream>
#include "globals.h"

int main(int argc, char* argv[]) {
std::cout << msg << std::endl;
getWiggety();
return 0;
}
g++ -Wall -ansi -pedantic -o driver driver.cpp functions.cpp
./driver
Wiggety wack
Wiggety wack
 
F

Fritz Foetzl

Jonathan Turkanis said:
I think your problem has nothing to do with include guards. The header
"globals.h" is properly included twice, once when each of the files
"driver.cpp" and "functions.cpp" is compiled. The problem is that msg
is being defined twice in the same program, violating the ODR.

You should probably decalre it extern and define it in "global.cpp",
or use an inline function instead

Maybe it works differently with gcc, or maybe I've just never
adequately understood the problem. Declaring my globals extern in the
header and defining them in a seperate .cpp file works beautifully.

I'm over that hurdle. Thanks for your help!

ff
 
J

Jonathan Turkanis

Fritz Foetzl said:
"Jonathan Turkanis" <[email protected]> wrote in message

Maybe it works differently with gcc, or maybe I've just never
adequately understood the problem. Declaring my globals extern in the
header and defining them in a seperate .cpp file works beautifully.

I'm over that hurdle. Thanks for your help!

ff

Glad to help. Now if I could just erase my others posts in this
thread, in which I misread the standard half a dozen times, I'd be
happy forever!

Jonathan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top