multiple definition error with namespace in .h file

Started by
13 comments, last by c0uchm0nster 14 years, 11 months ago
Yeah that works great.

I used to have about 50 global variables with "extern" all over the place. As I learned more I got rid of all that. I'll be getting rid of most of those #defines, too.

I don't think I'll be getting rid of this, though:
#define IGIZ 3

I figure whenever I make up a new kind of monster I can just increase IGIZ to 4, then 5, etc. I have to program a new AI and define how many sprites it gets and how it's drawn. Then I increase IGIZ and the number is correct throughout the program.

Is that a bad idea? I don't see anything wrong with it. I don't see any other way to do it. If someone using the editor wants to use some sprites to create a new monster he has to give it one of those types and valid types are 0-IGIZ througout the program.
Advertisement
Quote:Original post by c0uchm0nster
...


Err, but you see that you basically copied my post with some added salt?

The only difference being that you explicitly don't recommend to use an instance of some class that is passed through (whereas I implicitly didn't recommend that inside my code-sample).


Quote:Passing an instance of a class along to every function in your project is sloppy and not much better than globals (it could even be worse).


No, it is better than global variable, because you can limit visiblity before nodes higher in the call-tree and before nodes deeper in the call-tree. E.g., say your call-tree is [<program-scope>, <filescope>, x_0, x_1, x_2, x_3, x_4, x_5, x_6, x_7]. Then you can make it so that only [x_2..x_4] can see and use a particular instance of some class, but [<program-scope>..x_1] and [x_5..x7] can't neither. You can even make it so that only x_2 has random-access to your instance, but [x_3..x_4] only get read-access. You can even control when your instance is constructed, and when it is destructed, with global variables, you can not.


Anyways, that is not what I recommended. I recommended (I know it wasn't your idea to pass them through, just clarifying):
class Environment {public:    std::string aniboxPath () const { return "Data/Anibox/"; }    [...]};int main () {    Environment env;    std::cout << env.aniboxPath() << std::endl;}


It's not a global variable, it's not passed through, it's not a singleton, it does not contain any static methods, and with an increasing number of paths, adding new values is even less typing work than writing redundant pairs of declarations and definitions (*). And the function main() will work fine in the future when you rewrite the exemplary class Environment to something like:

class Environment {public:    Environment () : aniboxPath (loadConfig("anibox-path")) {    }    std::string aniboxPath () const { return aniboxPath; }private:    std::string aniboxPath;    [...]};


Also, you can anyhow declare a global instance of that class, if you must, and still be future safe to some degree (if you ever intend to make such paths configurable some day, you could run into static initialization order fiasco).

Btw, using a surrounding class, your code became (unclean):
#include "switchmaster.h"void someFunction(){  // do some stuff  loadMap(fileLocations.Map_Path() + "mapname.map");}

Not less clear, and just one additional char. Plus, sorry, you are future safe to some degree.

Anyhow, I have nothing against globals in that case actually, but my intention was a bit blurred. Though a class doesn't cost more than a namespace, in any respect. But you get some benefits (no, YAGNI arguments are not applicable, as wee see at (*)).


Quote:Notice that the extern keyword only appears in 1 place, the .h file declaration.

It is still perfectly allowed to put the extern keyword upon definitions, which will contribute to self-documenting code (you clarify that you didn't intend to make that variable file-global, but program-global).

Quote:Notice that the variables can be const.

Actually, they should be const.


(*)
(p-code with a handful of paths)
class Environment {public:    std::string aniboxPath () const { return "Data/Anibox/"; }    std::string aniboxPath () const { return "Data/Anibox/"; }    std::string aniboxPath () const { return "Data/Anibox/"; }    std::string aniboxPath () const { return "Data/Anibox/"; }};


vs.

namespace Environment {    std::string const aniboxPath = "Data/Anibox/";    std::string const aniboxPath = "Data/Anibox/";    std::string const aniboxPath = "Data/Anibox/";    std::string const aniboxPath = "Data/Anibox/";}namespace Environment {    extern std::string const aniboxPath;    extern std::string const aniboxPath;    extern std::string const aniboxPath;    extern std::string const aniboxPath;}




edit
sidenote: You could also make the member functions static, or non-member inline functions.

[Edited by - phresnel on April 30, 2009 5:04:58 AM]
Hey Phresnel, you define the Environment class above main(). You declare an instance of it in main() called env. Don't you have to pass env to everything that needs those paths?

And if there's a class defined in another file, it's going to look like this:
class Whatever{  void FunctionThatNeedsPathInfo(Environment &env);};


And now that file won't compile because Environment isn't defined. Were you just typing a general solution and I was supposed to figure that out or am I misunderstanding something?
Quote:Original post by icecubeflower
Hey Phresnel, you define the Environment class above main(). You declare an instance of it in main() called env. Don't you have to pass env to everything that needs those paths?

You can do one of those:
0) You pass it through (maybe as const-reference, i.e. as read-only)
1) Everyone who needs the information declares an own, local instance
2) You create a global instance


Quote:
And if there's a class defined in another file, it's going to look like this:

class Whatever{  void FunctionThatNeedsPathInfo(Environment &env);};



There are two solutions:

class Environment;class Whatever{  void FunctionThatNeedsPathInfo(Environment &env);};

Which works because you pass by reference. But you will have to #include the whole definition of Environment before the actual definition "void FunctionThatNeedsPathInfo(Environment &env) { .... }".

Or, better, just #include the proper file:
#include "environment.h"class Whatever{  void FunctionThatNeedsPathInfo(Environment &env);};


Of course, environment.h was only before function main() for the sake of example. So put it into a header-file (like your extern declaration previously), but don't forget include guards (#ifdef XXX ... #define XXX ... #endif // XXX).


Quote:Were you just typing a general solution and I was supposed to figure that out or am I misunderstanding something?

Yes.
;)

Quote:Original post by phresnel
Err, but you see that you basically copied my post with some added salt?


Yeah, that's why I said that that's what I was doing at the bottom of the message. He didn't understand previous explanations so I thought I'd elaborate, and lo and behold he then understood.

As for a class vs globals I guess we'll just have to agree to disagree on this implementation. Nevertheless hardcoding these values and only allowing one value for each isn't "best" either IMHO, but I didn't want to muddy a simple compile problem topic with design pattern arguments. =p

No reason to bite my head off about it, just trying to help.

This topic is closed to new replies.

Advertisement