boost::details::pool::singleton_default - use

Started by
15 comments, last by Mybowlcut 15 years, 8 months ago
I'm trying to use the boost singleton_default class... before I go any further, is this the standard singleton class to use from boost?
#include <iostream>
#include <string>
#include <map>
#include <exception>

#include "boost/shared_ptr.hpp"
#include "boost/pool/detail/singleton.hpp"

class Surface
{
public:
	Surface()
	{
		std::cout << "Loaded a surface." << std::endl;
	}
};

class Surface_Cache : public boost::details::pool::singleton_default<Surface_Cache>
{
public:
	typedef boost::shared_ptr<Surface> Surface_Ptr;
	typedef std::map<const std::string, Surface_Ptr> surface_cache;
	typedef surface_cache::iterator cache_it;

	Surface_Ptr Load_Surface(const std::string& file_name)
	{
		cache_it it = cache.find(file_name);

		if(it == cache.end())
		{ // Add surface.
			Surface_Ptr s(new Surface());
			cache.insert(std::make_pair(file_name, s));
			return s;
		}
		return it->second;
	}
private:
	surface_cache cache;
};

int main()
{
	return 0;
}

Quoting the boost documentation for the template parameter T used in the singleton_default class:
Quote:Any class with a non-throwing default constructor and non-throwing destructor
I gave Surface_Cache a default constructor because I thought it needed it and it (VC++) gave me this error:
Quote:error C2248: 'boost::details::pool::singleton_default<T>::singleton_default' : cannot access private member declared in class 'boost::details::pool::singleton_default<T>'
Why can I not provide a default constructor? I took the default constructor away and it compiled... but then I tried calling Surface_Cache::instance().Load_Surface and it gave me the error again... Cheers. :)

Advertisement
I ended up writing my own little one since I need to progress:
template<typename T>class Singleton{public:	virtual ~Singleton() {}	static T& Instance()	{		static T object;		return object;	}};
Probably not thread safe, but it's not important to me.

Why do you even want to use a singleton here? Is it an error if you ever instantiated another SurfaceCache? Probably not. If you really need global access to a SurfaceCache a global variable which gets initialized on program startup should be all you need.
Sounds like you made your default ctor public, whereas singleton_default's is private.

In general, it's a bad idea to use classes in a 'detail' namespace, as they're not considered part of the public interface and may change between versions. It's also likely that they're not very well documented and could have undocumented peculiarities.
[size="1"]
Quote:Original post by Trenki
Why do you even want to use a singleton here? Is it an error if you ever instantiated another SurfaceCache? Probably not. If you really need global access to a SurfaceCache a global variable which gets initialized on program startup should be all you need.
Because there only ever needs to be on object that controls the loading of surfaces.. why would there ever need to be more than 1?

Quote:Original post by Mybowlcut
I'm trying to use the boost singleton_default class... before I go any further, is this the standard singleton class to use from boost?


Boost singleton was rejected for now due to many issues with implementation.

There is currently no singleton pattern that is part of boost. What you're using is part of object pools, and should be used from within the interface.

That, or you're better off coding your own, or using one of existing libraries.

Quote:Original post by Mybowlcut
Quote:Original post by Trenki
Why do you even want to use a singleton here? Is it an error if you ever instantiated another SurfaceCache? Probably not. If you really need global access to a SurfaceCache a global variable which gets initialized on program startup should be all you need.
Because there only ever needs to be on object that controls the loading of surfaces.. why would there ever need to be more than 1?


Make the instance static and that's it.

static ctexture_loader texture_loader;

capplication::texture_loader.load_stuff();
[size="2"]I like the Walrus best.
Quote:Original post by Mybowlcut
Quote:Original post by Trenki
Why do you even want to use a singleton here? Is it an error if you ever instantiated another SurfaceCache? Probably not. If you really need global access to a SurfaceCache a global variable which gets initialized on program startup should be all you need.
Because there only ever needs to be on object that controls the loading of surfaces.. why would there ever need to be more than 1?


Unless the universe blows up by creating a second Foo, Foo should not be implemented as a Singleton.

Seriously. There are no real benefits to this pattern. Well, maybe when dealing with the C++ static order initialisation fiasco - but then again you should really try limit the amount of code that runs before main() anyway.

If you want to ensure there is only one instance of a particular class, there is a really easy way without corrupting the public interface:
class NonCopyable // or use boost::noncopyable{private:    // deliberately unimplemented    NonCopyable(const NonCopyable &);    NonCopyable&operator=(const NonCopyable &);};class Foo : NonCopyable {public:    Foo(/* ctor args */)};Foo::Foo(/* ctor args */){    static bool thereCanOnlyBeOne = true;    assert(thereCanOnlyBeOne);    thereCanOnlyBeOne = false;    // ...}


Ignoring threading issues, this will give you peace of mind by ensuring any accidental creations are detected. Then, when your design changes and you need to have more than one of these objects, you will not have to change any more that the few lines in the constructor (and possibly no longer derive from the noncopyable class).
Quote:Original post by rip-off
Quote:Original post by Mybowlcut
Quote:Original post by Trenki
Why do you even want to use a singleton here? Is it an error if you ever instantiated another SurfaceCache? Probably not. If you really need global access to a SurfaceCache a global variable which gets initialized on program startup should be all you need.
Because there only ever needs to be on object that controls the loading of surfaces.. why would there ever need to be more than 1?


Unless the universe blows up by creating a second Foo, Foo should not be implemented as a Singleton.

Seriously. There are no real benefits to this pattern. Well, maybe when dealing with the C++ static order initialisation fiasco - but then again you should really try limit the amount of code that runs before main() anyway.

If you want to ensure there is only one instance of a particular class, there is a really easy way without corrupting the public interface:
*** Source Snippet Removed ***

Ignoring threading issues, this will give you peace of mind by ensuring any accidental creations are detected. Then, when your design changes and you need to have more than one of these objects, you will not have to change any more that the few lines in the constructor (and possibly no longer derive from the noncopyable class).
Should an assert or exception be used? I've only used exceptions. Could you please explain why you'd prefer an assert?

You say when my design changes, but I'm pretty sure it won't. There should only ever need to be one of this object.

Also, since this is a global now, how would I go about declaring it in other files? I've never had to use a global across files before haha.. :s

Cheers.

Quote:Original post by Mybowlcut
Should an assert or exception be used? I've only used exceptions. Could you please explain why you'd prefer an assert?


Exceptions are a tool for error handling. Having two "Singletons" is not really a recoverable error - it is a programming logic error.

Logic errors should never happen in release code, so assert() is usually used to detect them.

Quote:You say when my design changes, but I'm pretty sure it won't. There should only ever need to be one of this object.


I deliberately chose the word "when" to make a point. Design changes. Maybe not this small bit, but in general. My game had only one "World" object in its inception. I could have made it a Singleton and avoided a little parameter passing. Now, I have a situation where two world objects are in memory at the same time. With hindisght, making this a Singleton would have been painting myself into a corner and would have resulted in either a *lot* of refactoring or a much more complicated design to get myself out of it.

I would agree that most of the time you will never need two surface caches. I implement a similar class myself as a global. More precisely, a global pointer to an instance in main(), returned by reference from a function. This gives me the advantage that no extra code runs before main().

IMO, a few well chosen globals can often strike a nice balance between idealistic and pragmatic design. Any more than that and programs tend to suffer as the design can become very inflexible.

Another option might be to try completely hide this behind the surface interface. Caching is an optimisation really, you might be able to come up with a nice interface that doesn't expose the fact that surface pointers are being shared under the hood. Some ideas in his direction would be to either make surfaces immutable or copy on write.

Quote:Also, since this is a global now, how would I go about declaring it in other files? I've never had to use a global across files before haha.. :s


Relevant reading.

This topic is closed to new replies.

Advertisement