What is the standard's scope for #define?

P

Pep

lol, this is kinda embarrassing.

I have been programming on nix using c++ for more years than I can
remember but have now hit a problem that challenges what I thought was
true. I have tried googling for the answer but cannot find a
definitive one, pardon the pun :)

So my problem is one of understanding the validity of #define pre-
processor defines across source files. I thought that if you #define
in a cpp implementation file, it will be honored in the #include
interface file. This has always worked like this for years. Now I come
to build code on windows and find that my knowledge is wrong :O

So a sample snippet to illustrate my question involves these 2 files

============================================== interface.h
#ifndef __IMPLEMENTATION__
#define __IMPLEMENTATION__

#ifndef DEFINE_VARS
extern const char externalString[];
#else
const char externalString[] = "an extern std::string";
#endif
============================================== other.cc
#include "interface.h"

.... some code ...
============================================== implementation.cc
#define DEFINE_VARS
#include "interface.h"

int main(int argc, char** argv)
{
return;
}

==============================================

Now based on the fact that I have #define DEFINE_VARS in
implementation.cc, I would expect interface.h to provide the extern
definitions and the declarations other.cc.

This works with g++ but not with Microsoft's compiler. With the MS
compiler I need to add /D DEFINE_VARS to the compile command line
parameters for this to work.

I stumbled upon a reference for c++ that clearly states that the MS
version is correct, which surprises me. So in order for me to continue
with a clean conscience, can anyone confirm which is the correct
method, though I now suspect the answer is the MS way.

P.S.
I have asked a couple of other C++ developers that have been
developing as long as me and none thought the MS way was correct but
they, like me, are nix developers :)

TIA
 
V

Victor Bazarov

Pep said:
lol, this is kinda embarrassing.

I have been programming on nix using c++ for more years than I can
remember but have now hit a problem that challenges what I thought was
true. I have tried googling for the answer but cannot find a
definitive one, pardon the pun :)

So my problem is one of understanding the validity of #define pre-
processor defines across source files.

No such thing. Preprocessor definitions only "live" while your
preprocessor is engaged preprocessing one translation unit.
> I thought that if you #define
in a cpp implementation file, it will be honored in the #include
interface file. This has always worked like this for years. Now I come
to build code on windows and find that my knowledge is wrong :O

So a sample snippet to illustrate my question involves these 2 files

============================================== interface.h
#ifndef __IMPLEMENTATION__
#define __IMPLEMENTATION__

It is better to avoid double underscores in your own macros. I use the
macro in the form

INCLUDED_{myheadername}_H

Here your macro would be

INCLUDED_INTERFACE_H
#ifndef DEFINE_VARS
extern const char externalString[];
#else
const char externalString[] = "an extern std::string";
#endif

Another #endif is missing here, I believe.
============================================== other.cc
#include "interface.h"

... some code ...
============================================== implementation.cc
#define DEFINE_VARS
#include "interface.h"

int main(int argc, char** argv)

Since you're not using 'argc' or 'argv', you might want to use the
shorter version of 'main':

int main()
{
return;
}

==============================================

Now based on the fact that I have #define DEFINE_VARS in
implementation.cc, I would expect interface.h to provide the extern
definitions and the declarations other.cc.

This works with g++ but not with Microsoft's compiler.

What indication do you get that it doesn't work with MS compiler?
> With the MS
compiler I need to add /D DEFINE_VARS to the compile command line
parameters for this to work.

Possibly the side effect of precompiled headers feature. Try turning it
off.
I stumbled upon a reference for c++ that clearly states that the MS
version is correct, which surprises me.

Huh? Stumbled? Where?
> So in order for me to continue
with a clean conscience, can anyone confirm which is the correct
method, though I now suspect the answer is the MS way.

It is not strictly necessary, but you could try rewriting the
conditional block in your header to read

extern const char externalString[];
#ifdef DEFINE_VARS
const char externalString[] = "an extern std::string";
#endif

IOW, declare it extern always and then define it only if the macro is
defined.
P.S.
I have asked a couple of other C++ developers that have been
developing as long as me and none thought the MS way was correct but
they, like me, are nix developers :)

Well, we've all been Unix developers at some point, so don't be
embarrassed by it [too much] <g>

V
 
A

Alf P. Steinbach

* Pep:
lol, this is kinda embarrassing.

It is.

I have been programming on nix using c++ for more years than I can
remember but have now hit a problem that challenges what I thought was
true. I have tried googling for the answer but cannot find a
definitive one, pardon the pun :)

So my problem is one of understanding the validity of #define pre-
processor defines across source files. I thought that if you #define
in a cpp implementation file, it will be honored in the #include
interface file. This has always worked like this for years. Now I come
to build code on windows and find that my knowledge is wrong :O

So a sample snippet to illustrate my question involves these 2 files

============================================== interface.h
#ifndef __IMPLEMENTATION__
#define __IMPLEMENTATION__

#ifndef DEFINE_VARS
extern const char externalString[];
#else
const char externalString[] = "an extern std::string";
#endif

As Victor remarked you lack an #endif.

Adding to Victor's remark about __IMPLEMENTATION__, it's Undefined Behavior.

Not yet said, as far as I can see, you should simply do

char const s[] = "blah blah";

and rely on the compiler's constant folding.

If you don't trust the compiler to do that you should not use the preprocessor,
but define the string in your implementation file.

If you don't trust the compiler to fold constants and you absolutely require a
single point of maintainance, then you can do

inline char const* s() { return "blah blah"; }

in the header.

There are also more fancy ways.

But don't do the conditional compilation thing.


Cheers & hth.,

- Alf
 
P

Pep

Pep said:
lol, this is kinda embarrassing.
I have been programming on nix using c++ for more years than I can
remember but have now hit a problem that challenges what I thought was
true. I have tried googling for the answer but cannot find a
definitive one, pardon the pun :)
So my problem is one of understanding the validity of #define pre-
processor defines across source files.

No such thing.  Preprocessor definitions only "live" while your
preprocessor is engaged preprocessing one translation unit.

 > I thought that if you #define
in a cpp implementation file, it will be honored in the #include
interface file. This has always worked like this for years. Now I come
to build code on windows and find that my knowledge is wrong :O
So a sample snippet to illustrate my question involves these 2 files
============================================== interface.h
#ifndef __IMPLEMENTATION__
#define __IMPLEMENTATION__

It is better to avoid double underscores in your own macros.  I use the
macro in the form

     INCLUDED_{myheadername}_H

Here your macro would be

     INCLUDED_INTERFACE_H


#ifndef DEFINE_VARS
extern const char externalString[];
#else
const char externalString[] = "an extern std::string";
#endif

Another #endif is missing here, I believe.
============================================== other..cc
#include "interface.h"
... some code ...
============================================== implementation.cc
#define DEFINE_VARS
#include "interface.h"
int main(int argc, char** argv)

Since you're not using 'argc' or 'argv', you might want to use the
shorter version of 'main':

    int main()
{
    return;
}

Now based on the fact that I have #define DEFINE_VARS in
implementation.cc, I would expect interface.h to provide the extern
definitions and the declarations other.cc.
This works with g++ but not with Microsoft's compiler.

What indication do you get that it doesn't work with MS compiler?

 > With the MS
compiler I need to add /D DEFINE_VARS to the compile command line
parameters for this to work.

Possibly the side effect of precompiled headers feature.  Try turning it
off.
I stumbled upon a reference for c++ that clearly states that the MS
version is correct, which surprises me.

Huh?  Stumbled?  Where?

 > So in order for me to continue
with a clean conscience, can anyone confirm which is the correct
method, though I now suspect the answer is the MS way.

It is not strictly necessary, but you could try rewriting the
conditional block in your header to read

extern const char externalString[];
#ifdef DEFINE_VARS
   const char externalString[] = "an extern std::string";
#endif

IOW, declare it extern always and then define it only if the macro is
defined.


P.S.
I have asked a couple of other C++ developers that have been
developing as long as me and none thought the MS way was correct but
they, like me, are nix developers :)

Well, we've all been Unix developers at some point, so don't be
embarrassed by it [too much] <g>

V

Thanks for the C++ tips, which whilst being correct, I did not feel
the need for in a simple pieceof code I rattled off to demonstrate the
problem. Apart from the missing #endif that is :D

I guess the fact that you are saying it's possibly a side effect of
the MS pre-comppiled headers feature, means it should work in MS C as
it does in g++, so I'll check this further. I won;t be changing the
way the definitions/declarations are done because this is a very large
legacy system that would simply fall apart if I tried it. Evidence
this problem, that has arisen merely because I moved the definitions/
declarations from one source/header pair of files to another source/
header pair.

The indication that is not working with the MS compiler is the very
long stream of undefined variables at the linking stage.

As for embarrased??? I'm more than happy to work on nix, even windows
when the need arises :p
 
P

Pep

I think, the word "scope" is not proper in this context.
#define, #ifdef, etc. are  not elements of the language (so they hove no scope),
they processed during the pre-processing step, which is basically nothing more
than pure text inclusion and -sometimes- substitution.

Every source (*.cc, headers are NOT sources) file is processed independently
during compilation, so there is no cross-source file effect regarding #defines.

The snippet you've posted is correct. It seems to me that the MS compiler
ignores your #define in implementation.cc, and accepts only the (equivalent)
command line option. This is some nasty optimization from MS or a bug ...

Tamás

Thanks, your reply kinda backs up Victor's suggestion that the pre-
compiled headers mechanism ins probably gettign int the way. So first
thing is turn that off and start the build from clean.
 
R

red floyd

Thanks, your reply kinda backs up Victor's suggestion that the pre-
compiled headers mechanism ins probably gettign int the way. So first
thing is turn that off and start the build from clean.

Skip them completely. Precompiled headers (at least MS's
implementation of them)
are pure evil. Partly because of issues such as the one you are
facing.
 
J

James Kanze

So my problem is one of understanding the validity of #define
pre- processor defines across source files. I thought that if
you #define in a cpp implementation file, it will be honored
in the #include interface file. This has always worked like
this for years. Now I come to build code on windows and find
that my knowledge is wrong :O

No it's not. Macro expansion and defines are handled by the
preprocessor, and ignore scope.
So a sample snippet to illustrate my question involves these 2 files
============================================== interface.h
#ifndef __IMPLEMENTATION__
#define __IMPLEMENTATION__

Names with two adjacent underscores are undefined behavior.
Names starting with an underscore are in the implementation
namespace. Don't use either.

For an include guard, you'll also want to munge in some sort of
a random sequence, to avoid conflicts. (Your editor should do
this for you, when you open a new header file.)
#ifndef DEFINE_VARS
extern const char externalString[];
#else
const char externalString[] = "an extern std::string";
#endif

Note that if DEFINE_VARS is defined, externalString will have
internal linkage, and will not be visible in any other
translation unit. What you probably want is:

extern char const externalString[] = "and external string" ;

(Not that I think this technique is a good idea. If the actual
string is to be visible in a header, you might as well just use:
char const externalString[] = "..." ;
everywhere.)
============================================== other.cc
#include "interface.h"
... some code ...
============================================== implementation.cc
#define DEFINE_VARS
#include "interface.h"

int main(int argc, char** argv)
{
return;

Not relevant to your problem, but the above line shouldn't
compile. Since main returns an int, any return statement in
main must have a return code.
}
==============================================
Now based on the fact that I have #define DEFINE_VARS in
implementation.cc, I would expect interface.h to provide the
extern definitions and the declarations other.cc.

Except that you don't have an extern definitions in interface.h.
This works with g++ but not with Microsoft's compiler. With
the MS compiler I need to add /D DEFINE_VARS to the compile
command line parameters for this to work.

I get the same behavior with both. If I don't use
externalString in other.cc, no problem. If I do, the code
compiles, but fails to link. Which is the expected behavior.

BTW: when you do get an error, it would help if you cite it in
your posting. Similarly, you should cut and paste that actual
code you tried to compile -- the missing #endif in interface.hh
could be a cut and paste error (one line too few), but the
return statement in main won't compile either.
I stumbled upon a reference for c++ that clearly states that
the MS version is correct, which surprises me.

MS is correct, at least Visual Studios 8, but since it was also
correct in this regard in version 1.0 of its C compiler, I don't
think it's recent bug fix. On the other hand, MS behaves
exactly like g++, not like you describe it.
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top