Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411588 Posts in 69386 Topics- by 58443 Members - Latest Member: Mansreign

May 06, 2024, 10:58:40 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Singleton interface naming conventions?
Pages: [1]
Print
Author Topic: Singleton interface naming conventions?  (Read 17333 times)
dr.crow
Level 1
*



View Profile
« on: May 29, 2010, 02:02:16 PM »

I've been happily using singletons for a while, however, I'm not quite satisfied with the syntax i'm using at the moment.

For example:
Code:
sf::Image* img=TextureManager::instance().loadImage(name, extension);
seems a bit clumsy.

I saw some tutorial using get(), instead of instance() which is a bit shorter and easier to read, but it's not much better.

I was thinking about declaring static functions which call the appropriate function on the instance:
Like this
Code:
TextureManager::loadImage(name, extension){
  instance().loadImage(name,extension)
}
And be able to call:
Code:
TextureManager::loadImage(name, extension);

Any ideas, are there any conventions out there?
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #1 on: May 29, 2010, 02:44:50 PM »

Maybe not the answer you want, but don't use a singleton, use a namespace.

Code:
namespace TextureManager
{
    sf::image *loadImage(/* not sure what your params are */);
    // other stuff...
}

Put the data that would be your singleton in the source file as module-local data (in the anonymous namespace, preferably).

Code:
namespace
{
    // local data, whatever was in your singleton class
}

sf::image *TextureManager::loadImage(/* stuff */)
{
    return new image(/* whatever /*);
}

You can even do lazy instantiation by declaring a local struct and a pointer, and instantiating on the first call, like you do with a singleton.

I have never seen a justifiable reason for a singleton in C++ code, and singleton is considered by many to be an anti-pattern even in other languages.
Logged



What would John Carmack do?
BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #2 on: May 29, 2010, 05:17:57 PM »

You can even do lazy instantiation by declaring a local struct and a pointer, and instantiating on the first call, like you do with a singleton.
I'm baffled how you don't consider this a singleton, itself. The "considered an antipattern" part surely stems from how you have global program state, not whether you choose to represent it as a class with static members or free functions with a private implementation.
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #3 on: May 29, 2010, 06:00:49 PM »

You can even do lazy instantiation by declaring a local struct and a pointer, and instantiating on the first call, like you do with a singleton.
I'm baffled how you don't consider this a singleton, itself. The "considered an antipattern" part surely stems from how you have global program state, not whether you choose to represent it as a class with static members or free functions with a private implementation.

It's a private implementation detail that is invisible to outside code.  From the perspective of the rest of the program, there is no singleton.  This reduces the coupling between the module internals and the outside world.

The chief argument that I hold against singletons is that a class shouldn't care how many instances of itself are created.  That's a program logic detail, not the business of the data type.  There's nothing wrong with global state, inherently.  Some data is by nature global and persistent.  I think jumping through ridiculous hoops to avoid that makes things far more complex than they need to be.

Addendum:  I should perhaps point out that a singleton is a class explicitly designed to allow only one instance.  The module approach happens to use one instance of a private data-type.  Just because a program only uses one instance of something, doesn't make it a singleton.
« Last Edit: May 29, 2010, 06:18:17 PM by Average Software » Logged



What would John Carmack do?
Crimsontide
Level 5
*****


View Profile
« Reply #4 on: May 29, 2010, 06:35:43 PM »

Addendum:  I should perhaps point out that a singleton is a class explicitly designed to allow only one instance.  The module approach happens to use one instance of a private data-type.  Just because a program only uses one instance of something, doesn't make it a singleton.

Isn't that kinda splitting hairs?  If there's only one, and really only supposed to be one, isn't it pretty much a singleton?  Whether its an explicit error creating a 2nd, or simply a requirement somewhere in the documentation, or just something that isn't done, you still have 1 global at the end of the day.

The real issue I find with singletons in C++ is the static intialization order mess.  Singeltons need to be created prior to their 1st usage (often before the program has even hit main() ) and last often long after main has exited.  The only safe solution I could come up with was to use a combination of a Meyer's Singleton (to ensure creation at the proper time) combined with a Nifty Counter (Schwarz Counter I've heard it also called) to ensure it gets destroyed after everything that uses it.  Then you have to run it all through a global critical section.

In the end I made a GetSingleton function that works something like:
Code:
SomeClass& class = GetSingleton<SomeClass>();
and it handles all the mess for you, but its still rather costly in terms of peformance, so I don't use them often.

Personally I find when I need global variables, I tend to use thread locals whenever possible.  They're almost as fast as accessing a 'naked' global, and require none of the mess that true singletons require.
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #5 on: May 29, 2010, 07:01:38 PM »

Isn't that kinda splitting hairs?  If there's only one, and really only supposed to be one, isn't it pretty much a singleton?

The difference is that singleton is an attribute of a type, not a count of instances.

If my program only contains one int, would you call int a singleton?
Logged



What would John Carmack do?
David Pittman
Level 2
**


MAEK GAEM


View Profile WWW
« Reply #6 on: May 29, 2010, 09:32:32 PM »

I usually call my singleton accessors GetInstance() for maximum verbosity.

I'll use a static function to wrap the accessor sometimes. I don't really have a metric for when I do or don't use it, but generally, it's a question of whether the client code actually needs a reference to the singleton object or just needs to interface with that system.

I'm not a fan of the static instance form of singletons (for the reason CrimsonTide already mentioned, that the order of initialization is indeterminate), although you can make the static instance be a function-local static object (I guess that's Meyers's approach?) and then at least you know it won't be created until the function is called (which hopefully happens in an order you expect Tongue). I hadn't heard of this "nifty counter" trick to control order of destruction, but personally, I prefer to just give the class a static member pointer to its own type and manually destroy objects when I'm done with them. It's not the most elegant solution, but it's simple and explicit, and I like that.
Logged

dr.crow
Level 1
*



View Profile
« Reply #7 on: May 30, 2010, 12:34:29 AM »

I usually call my singleton accessors GetInstance() for maximum verbosity.

I'll use a static function to wrap the accessor sometimes. I don't really have a metric for when I do or don't use it, but generally, it's a question of whether the client code actually needs a reference to the singleton object or just needs to interface with that system.
Thanks. This is probably what I'll do with some of my singletons too.

Although, i can see some of the advantages of not creating something a class whenever it's possible. It's easier to change just the implementation without having to recompile everything or use the pimpl idiom or something.

I'm not a fan of the static instance form of singletons (for the reason CrimsonTide already mentioned, that the order of initialization is indeterminate), although you can make the static instance be a function-local static object (I guess that's Meyers's approach?) and then at least you know it won't be created until the function is called (which hopefully happens in an order you expect Tongue). I hadn't heard of this "nifty counter" trick to control order of destruction, but personally, I prefer to just give the class a static member pointer to its own type and manually destroy objects when I'm done with them. It's not the most elegant solution, but it's simple and explicit, and I like that.

I usually use the pointe approach to, as, like you say, you get to decide when to destroy the singleton. However, when i read through this, i was curious about the OP's "one-liner". And it actually works quite well (and it appeals to my inner laziness and need to have things simple).

As i don't care about when this singleton is destroyed, i simply use:
Code:
static TextureManager& instance(){ static TextureManager inst; return inst; }
to make it a singleton.
Logged
BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #8 on: May 30, 2010, 04:13:52 AM »

Isn't that kinda splitting hairs?  If there's only one, and really only supposed to be one, isn't it pretty much a singleton?

The difference is that singleton is an attribute of a type, not a count of instances.

If my program only contains one int, would you call int a singleton?
If you your .cpp file looked like this:

Code:
int count = 0;

void incrementCount()
{
  count++;
}

Then yes, I would consider that a singleton as much as if it looked like
Code:
int MySingleton::count = 0;
void MySingleton::incrementCount()
{
  count++;
}

They have different syntax, but otherwise have more or less identical effects and shortcomings. If used inappropriately, I would have equal hate for either technique. Sure, in the latter, int count must appear in the .h file as well as the .cpp, but C++ is funny about encapsulation that way, it is hardly specific to singletons. You can use the pimpl pattern with singletons, or you can use polymorphic singletons, etc. I prefer just to consider private variables to be sufficiently encapsulated, and gloss over the additional recompile cost.

"Class's should always be reusable" is a laudable goal, but I think you are following the letter of the law rather than the spirit. The point is re-usability, and functions with singleton side effects are just as non-reusable as the original class. You haven't improved the situation, you've just made it harder to see that problem.

On the other hand, the rule of thumb "classes should relate together common functions and state" is totally applicable to singletons, and forms the basis of why I'd prefer a class over a set of free functions.
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #9 on: May 30, 2010, 09:26:42 AM »

Isn't that kinda splitting hairs?  If there's only one, and really only supposed to be one, isn't it pretty much a singleton?

The difference is that singleton is an attribute of a type, not a count of instances.

If my program only contains one int, would you call int a singleton?
If you your .cpp file looked like this:

Code:
int count = 0;

void incrementCount()
{
  count++;
}

Then yes, I would consider that a singleton as much as if it looked like
Code:
int MySingleton::count = 0;
void MySingleton::incrementCount()
{
  count++;
}

The you're misunderstanding what a singleton is.

A singleton is a type, not a piece of data.  Specifically, it is a type that only allows at most a single instance of itself to exist.  It has nothing to do with how many actually exist.  A singleton is still a singleton if your program never instantiates one.  A singleton is a class, not an object.

Design patterns compensate for deficiencies in languages.  The visitor pattern is a way of faking multimethods, the observer pattern is a way of faking message passing.  Lisp has multimethods, so the visitor pattern has no place there, just use the multimethods.  Likewise, Objective-C supports message passing, so don't use the observer pattern, use the language facilities.

The singleton pattern was developed in the context of pure object-oriented languages that did not allow freestanding functions and data.  C++ does not have this deficiency, therefore the singleton has no place there.

I once read an excellent article that talked about how subroutines used to be a "design pattern" in the early assembly days.  The idea of jumping to common blocks of code was fairly novel at the time.  Eventually, this became part of basically all languages, the deficiency was removed, and people stopped doing subroutine jumps and just used the subroutines that their languages provided.

Likewise, people have been doing object-oriented programming in C for years.  The problem is that since the language doesn't directly support it, everyone came up with their own incompatible ways to do it.  The Gtk library does it one way, the Windows API does it a totally different way.  C++ added these features, so that now everyone can get along.

C++ already provides the facilities for freestanding functions and data.  When you use singletons, you're simulating something that's already there, and since there are several ways to implement a singleton, you probably aren't doing it the same way as someone else.  C++ already gives you the screwdriver, you don't need to invent your own.
Logged



What would John Carmack do?
Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #10 on: May 30, 2010, 09:57:20 AM »

At the OP, do not call the instance getter get(). "Get" is a generic and ambiguous word that lacks description of its intent or effect. "getInstance" or the "instance" you already have are better because they are self-descriptive.


If you your .cpp file looked like this:

Code:
int count = 0;

void incrementCount()
{
  count++;
}

Then yes, I would consider that a singleton as much as if it looked like
Code:
int MySingleton::count = 0;
void MySingleton::incrementCount()
{
  count++;
}

I don't think your example is a very good one. You are defining a function, the name of which implies a global variable. In your example, with your specific naming which in itself is not very good form, a singleton would be better, so yes, WidgetManager::getInstance().increaseCount() could be formally ok.

AverageSoftware rather addresses that the OP's code would be better represented as an API of functions:

Code:
// .h-file

Class Texture {...};

namespace texture
{
    Texture LoadTexture( string filename );
    Texture CreateTexture( uint width uint height );
}

Code:
// .cpp-file

#include "TextureManager.h"

namespace
{
    TextureManager manager;
    // other local data that is irrelevant to the client
}

namespace texture
{
    Texture LoadTexture( string filename )
    {
        return handler.loadTexture( filename );
    }

    ...
}

This defines a cleaner interface, does not fall victim to creeping singletonistis, promotes better decoupling of interface and implementation, and hides everything that the client does not need to know about.
« Last Edit: May 30, 2010, 10:00:32 AM by Mikademus » Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #11 on: May 30, 2010, 10:33:55 AM »

Ok, yes, that was a terrible example. I guess when discussing singletons it is necessary to use an example for which they are actually necessary. Your example is better, and closer to the OP.

And on reflection, I have to agree with Average Software, my definition of singleton is too wide. You cannot use the term every time you mean global state, or it is a meaningless term.

Nonetheless, I can still see reasonable definitions that would make mikademus' example qualify. TextureManager is a class that can/will only be instantiated once. You've hidden the entire class, which is not unlike hiding the constructor for regular singletons.
Logged
Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #12 on: May 30, 2010, 11:20:30 AM »

Yeah, the TextureManager class itself is likely something that would be well represented as a singleton. And I agree with you both in that structuring code like this is similar, perhaps identical, to class composition and private members, and in that singletons should be used when suitable.

I think that is a good point you make, because from a purely technical perspective we are doing almost the same thing since the computer and compiler cares nothing about classes or namespaces or other structures that actually only exists for the benefit of the programmer. And that is at the core of this, it think: from a pedagogical perspective it is all about producing and presenting a good interface, and functions are more elementary than classes as long as they have reasonable signatures.
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
dr.crow
Level 1
*



View Profile
« Reply #13 on: May 30, 2010, 12:06:41 PM »

Seems like I have a lot to learn about designing apis...
I found your reply very useful, Mikademus. Thank you very much!

Using that approach i can change back and forth between using singletons and not using them without the client having to worry the slightest bit.

While we're at it. Do you have any recommendations of books/tuts on the subject?
I'm taking my first shot at making an actual game with other people. Since I'm not the only programmer on the team, maintainability and clean, unpolluted interfaces are very important. And i want to get it right before i accidentily couple everything to tight.
Logged
Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #14 on: May 30, 2010, 12:47:38 PM »

You're very welcome! I'm happy I was of help to you! Smiley

As for books, well, you should probably read up a bit on what it called "design patterns", which can be of good help. The two best books are "Head First Design Patterns", which is funny and accessible, and the famous "Design Patterns" by Erich Gamma and three others (they're called the Gang of Four, so the book is often called GoF), which is the best book on the topic but also slightly academic and difficult.

You should also check out the "Effective C++" and "More Effective C++" books by Scott Meyers, which are great intro books, but also don't cover the never additions to the language.

When you've grown into the language a bit more you should also read Alexandrescu's "Modern C++ Design", it is a fantastic book on C++ techniques but also quite difficult.

Mike McShaffry's "Game Coding Complete" is also a good book that I can recommend. Then there are the classic Game Programming Gems and Graphics Programming Gems books, those would be some ten books altogether Smiley
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
Crimsontide
Level 5
*****


View Profile
« Reply #15 on: May 30, 2010, 01:52:59 PM »

Design patterns compensate for deficiencies in languages.

Ahh ok, now I understand where you're coming from.  Under that assumption it makes perfect sense. 

In university we were taught that design patterns were ways of descibing programs at a high level.  Sort of a shared/common jargon amongst programmers if you will.  When trying to talk about a program or algorithm without using design patterns it becomes quite a headache to properly convey your intention without getting muddled in the details, which is supposedly where design patterns came in.

Now whether or not my professors were correct (hehe... most of them were idiots) is another story, and TBH I haven't read that much on them apart from what was required for the few courses that covered them, and the odd wikipedia/Dr Dobb's article or what-not, so I'm sure your expertise in them are far greater than mine.  So please don't take the above as a 'your wrong I'm right' sort of thing (tone is very hard to convey via text, I really am interested in what you think as you seem to be quite knowledgeable about these things), rather that was just the way I understood it, and perhaps a few others in here as well.

Now just to stimulate a bit more dialog... 

I'm not sure if design patterns can be only used to describe deficiencies in a language.  Since many design patters are quite large implementation-wise even if only describing a simple function/feature that few (if any languages) would have many of them implemented in their entirety.  Which seems kinda silly that 95% of them would be used in normal programming talk/design docs/ect... while 5% of them are ignored because they just happen to (in this particular instance) map directly to language features.  Also it seems a bit weird that if you moved a program mid-design to another language (say from C++ to C#) that 1/2 the terminology/documentation would become erroneous because some design patterns described now map to directly language features where-as before they didn't, and vice-versa.

Also if we can't use design patterns to describe high level programming abstractions...  What else would we use?
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW
« Reply #16 on: May 30, 2010, 02:09:37 PM »

Design patterns generally don't describe deficiencies at first.  Usually they start just as you said, a common solution to a common problem.  Eventually, some language designer says, "Hey, let's just put this idea directly in the language!"  From that point forward, the pattern describes a deficiency in other languages.

Singleton is an odd case, because the earliest object-oriented languages actually regressed in capabilities by enforcing the class paradigm.  Eventually, the early OO programmers started to realize that those free functions and data that they were poo-pooing were really pretty damn useful after all, and developed singletons to cope with it.  Unfortunately, people started applying it to other OO languages that weren't missing those features in the first place.

I largely blame it all on education these days, it seems like nobody gets taught how to do modular programming anymore.  I admit that I didn't really understand it either, until I learned Ada which has a fantastic module system.  Ever since then, my work in other languages, particularly C++, has gotten much simpler and cleaner since I've been using modules when they make more sense than classes.  C++ is a hybrid language, and trying to wedge OO thinking into every corner of it is just a mistake.  The ability to mix OO, generic, and procedural code is a huge advantage that too many people squander because they don't understand anything but OO.
Logged



What would John Carmack do?
InfiniteStateMachine
Level 10
*****



View Profile
« Reply #17 on: May 30, 2010, 02:47:43 PM »

for the OP, personally I call my singletons Singleton() figure it's pretty obvious what I'm trying to do by naming it that   Grin
Logged

Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #18 on: May 31, 2010, 03:28:29 AM »

Originally design pattens were just that: an observation that certain problems tend to crop up frequently, and that sa there is a pattern to which problems that regularly occur there are also patterns to which solutions are applied to the problems. So design patterns were a description of standard problems and solutions. The inspiration for them was Christopher Alexander's 1977 book "A Pattern Language", which discusses this tendency but in architecture and the construction of buildings. Of course, by its very nature, a discussion of standard problems and solutions naturally lent itself to become a language for system architecture as well as a just pragmatic cookbook.

Note that the GoF Design Patterns book is written for C++, which is a hybrid language, and many patterns rely in procedural-imperative designs. A "pure" OO approach must necessarily be bent to accommondate some of these patterns, especially a naive OO solution, such as Java, that has misunderstood the meaning of OO (OO does not mean every function must be put inside the brackets of a class declaration). And as such I agree with Average, that in a derived sense, (some) Design Patterns are used to address shortcomings or design warts of languages, as f.i. singletons in Java are often put to work to emulate free functions in procedural languages.
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
Gnarf
Guest
« Reply #19 on: May 31, 2010, 10:13:20 AM »

I have never seen a justifiable reason for a singleton in C++ code,

What about polymorphism? And, you know, stuff...

I'm very much out of touch with C++ and that so not really saying that those surely must be magnificent reasons for using singletons. But it kind of sounds like you're saying that we don't need singletons because we can do freestanding functions and data like so. And I'd kind of guess that one approach to the singleton shenanigans is that classes let you do neat stuff so how about we use classes and shit for this piece of global state. And your way of doing things doesn't (far as I can tell) address much of that...

Eventually, the early OO programmers started to realize that those free functions and data that they were poo-pooing were really pretty damn useful after all, and developed singletons to cope with it.

Really?
Logged
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic