Skip to main content
gamedev.net
🔒 Locked

Empty Namespaces (C++)

Started by Gamer Gamester 11 replies 3.8k views
Original Post
Gamer Gamester
Gamer Gamester
Let's say I have a class:
definition: MyClass.hpp
member functions (implementation): MyClass.cpp

And let's also say that as part of the implementation (MyClass.cpp) I require a std::vector<MyClass> that is shared across all MyClass instances.

Usually I would declare this as a static member of MyClass, but since std::vector<MyClass> requires the full definition of MyClass, I cannot declare this within MyClass itself (correct?). So I was thinking of instead using an empty namespace within the MyClass.cpp file. Is this good practice?

I figure it accomplishes my objectives:
- vector doesn't pollute namespace (outside of MyClass.cpp)
- vector is encapsulated (to visibility/use within MyClass)
- vector is shared among all instances of MyClass

Any considerations I'm failing to take into account? I am correct when believing ALL MyClass instances program-wide will access the SAME vector? Any situations where this isn't the case? Is this even true if MyClasses are instantiated in different threads?

Also, I find empty namespaces in general very interesting at the moment. I would enjoy any info, uses, and anecdotes about them you'd be willing to share.
Antheus
Antheus
Quote:
Original post by Gamer Gamester

I am correct when believing ALL MyClass instances program-wide will access the SAME vector?
No.
Quote:
Any situations where this isn't the case?
DLLs, obscure linking. Allocators might have different linkage.

Quote:
Also, I find empty namespaces in general very interesting at the moment.
I hear beige rocks are interesting too.


Static context in C++ depends on build process, and is under-defined by standard. Best advice - don't use it. Alternative is thoroughly studying the structure of a process in a given OS and how it relates to compilation, assembly and linking process of C++ toolchain.
Gamer Gamester
Gamer Gamester
Thanks for the info.

I had meant Unnamed Namespaces, not Empty Namespaces. But it seems you understood my intention? Unnamed Namespaces just shouldn't be used? I had been under the impression they allowed the functionality of a "file scope variable declared 'static' in C" but with "more safety". But it's looking like there's no "safe" way to have static data? Do 'static members' of classes suffer from the same under-definedness (and thus un-safeness) as the other options?

Any alternative suggestions for the functionality I'm seeking (listed in my objectives above)?
rip-off
rip-off
Using an anonymous namespace would certainly fulfill your criteria: provided you stick to the normal one-source-file-per-class organisation. Otherwise beware that you lose the benefits of access control by doing this.

However, as to the question of "is this good practise", usually it isn't. Such static data is a hint of an issue in your program design. Without knowing more it is hard to confirm, but I suspect that sharing the instances isn't as necessary as you think.
SiCrane
SiCrane
Quote:
Original post by Gamer Gamester
I had been under the impression they allowed the functionality of a "file scope variable declared 'static' in C" but with "more safety".

It has nothing to do with safety. Something defined in an unnamed namespace has external linkage (unless qualified with something that also gives it internal linkage) and can be used as template arguments, unlike something declared with the static linkage specifier.
Shinkage
Shinkage
My rule of thumb with the "static" keyword is it should only be used with entirely static linkage (i.e. uses no .dll [windows] or .so [linux]). Otherwise you're asking for trouble, especially for cross-platform software.

Assuming that's the case though, what is wrong with having something like this?
class MyClass{private:    static std::vector<MyClass> _data;};

Is that what you were talking about wanting to do? Because it works fine in the STL implementation I've got with both GCC and MSVC (and to be honest as far as I'm aware you'd have to try pretty hard to make a compliant vector implementation where declaring a member like that doesn't work).
SiCrane
SiCrane
Instantiating a standard library container with an incomplete type has undefined behavior (see section 17.4.3.6 in the standard). And undefined behavior can include working exactly how you want it to work. Discussion of why this is the case.
Shinkage
Shinkage
Quote:
Original post by SiCrane
Instantiating a standard library container with an incomplete type has undefined behavior (see section 17.4.3.6 in the standard). And undefined behavior can include working exactly how you want it to work. Discussion of why this is the case.


Well, like I said, it works on those particular implementations of the STL, and given the requirements of the vector container specifically (i.e. that data be stored contiguously), it's a safe bet (although obviously not guaranteed) that it will work as expected. If it bothers you for any of a number of good reasons to dabble in undefined behaviors, even if you know it works with your implementation, then you can always implement your own vector-clone.

I guess what I'm saying, in other words, is that there's no fundamental reason a design like that CAN'T work, and if you want to be totally proper about implementing it, you can always just make a vector-like class that you know for a fact will always function properly in that situation.
dmatter
dmatter
Quote:
Original post by SiCrane
Instantiating a standard library container with an incomplete type has undefined behavior (see section 17.4.3.6 in the standard).
Something I'm not sure about is whether there are exceptions to this rule? Such as maybe std::auto_ptr for example or, similarly, std::shared_ptr where Boost's version allows the parameter to be an incomplete type.
Gamer Gamester
Gamer Gamester
Yes Shinkage, that's precisely what I was looking to do. I was under the impression that doing so was illegal C++ because:

1) You need the full definition of a class to use it as a template parameter
2) The full definition is not available within the definition itself

For example, I wouldn't expect this to work:
class MyClass{private:    MyClass myClass_instance;};


It's neat to know that your vector example (what I originally wanted to do) works. I'm curious, is this "C++ Standard" behavior, or just the practical behavior of compilers and stl implementations?

Currently I'm rethinking my design to see if I can eliminate static data altogether. I wanted it for reference counting -- wasn't actually a vector but a map with filename strings as the keys and Texture--reference_count pairs as the items. I was going to put this in an anonymous namespace in my Texture class definition. Then to use my Texture class I just call Load(filename) and Close(), with the class only actually loading the file if it isn't already listed in the map (and only actually destroying/closing the texture if it's the last reference).

Since the Texture class contains very little data (texture_id, width, height, filename) I wanted to have an instance of it directly in the map itself, so the Load function can just set it's instance equal to the one in the map.

This type of resource management is of course common, so I'm in the midst of doing more research to try and find the best approach for my game. Any suggestions would certainly be appreciated.
Gamer Gamester
Gamer Gamester
Ah, didn't see the posts below Shinkage's first one. Disregard my question on whether or not it's standard behavior.
SiCrane
SiCrane
Quote:
Original post by dmatter
Something I'm not sure about is whether there are exceptions to this rule? Such as maybe std::auto_ptr for example or, similarly, std::shared_ptr where Boost's version allows the parameter to be an incomplete type.

The relevant section applies to all the standard library templates defined in the C++ standard. However, shared_ptr is defined in TR1 and not the standard. I don't have a copy of TR1 so I don't know if they made any exceptions for the TR1 components. If C++0x ever gets finalized it likely will include exceptions to the incomplete type rule. However, std::auto_ptr<> is one of those cases where instantiating it with an incomplete type is likely to fail. The reason is that ~auto_ptr calls delete on the held pointer and you can't delete an incomplete type.
Shinkage
Shinkage
(edited several times to make sure things look right)

Gamester, if I understand your design goal correctly, you should probably use smart pointers for a design like that, boost being the obvious implementation. For example, something like this:

class Texture{   typedef std::map<std::string, boost::weak_ptr<Texture> > file_map;   static file_map _fileMap;   boost::shared_ptr<Texture> Load(const string &fileName)   {      file_map::iterator itFound = _fileMap.find(fileName);      if((itFound != _fileMap.end) && (!itFound->second.expired()))         return itFound->second.lock();      // If you get to here, you need to load the texture from disk   }};

And shared_ptr will automatically handle the reference counting for you. As for why it generally works for most vector implementations, it's explained in that Dr. Dobbs article SiCrane linked, but the long and the short of it is this: the vector class is storing its data as a pointer that is allocated on the heap inside a function, and template functions are instantiated at the point of first use, and first use in this case is going to be at a point in the code where the class is no longer incomplete. To illustrate:

// Like you say, this will not workclass MyClass{private:    MyClass myClass_instance;};// But this willclass MyClass{private:    MyClass *myClass_instance;public:    void foo();};void MyClass::foo() { myClass_instance = new MyClass; }


And it's the second of these that's essentially what's happening with a vector, and will be pretty reliable across vector implementations (although not guaranteed, so don't count on it unless you've investigated the implementation you're using) due to the fact that the specification for vector requires its elements be contiguous in memory (correct me if I'm wrong on that one).

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.