Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

 
Advanced search

879431 Posts in 32979 Topics- by 24365 Members - Latest Member: Mimiga

May 24, 2013, 05:08:24 AM
TIGSource ForumsDeveloperTechnical (Moderators: Glaiel-Gamer, ThemsAllTook)[C++] Singleton vs namespace vs rest of the world
Pages: 1 [2] 3
Print
Author Topic: [C++] Singleton vs namespace vs rest of the world  (Read 956 times)
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW Email
« Reply #15 on: April 02, 2012, 12:36:21 PM »

Quote from: EdgeOfProphecy
It is more encapsulated and safer

Citation please.

A namespace module is by far more encapsulated and safer, since the guts of the module are completely excised from the header.  The interface is much simpler (just plain function calls) and the internals can be altered without even forcing a recompile of client code.

A class is supposed to be a data type.  A class that only allows one instance is not a data type, it's global data wrapped in accessor functions.  C++ already provides a utility for this, why not use it?

Singletons are a leftover from 'pure' object-oriented languages like Smalltalk that didn't allow plain functions and data.  They were intended as a means of simulating this in these languages.  OO zealots brought the practice to C++ and people like Stroustrup have been fighting it ever since.

Now there's nothing wrong with your program only needing a single instance of something.  This is distinctly different from a singleton, which is a class that actively prevents multiple instantiations.  In these cases, by all means only use one object, but wrap it in a simple interface like a namespace.

A class should not care how many instances of itself are created.  This is part of the program logic, not the type logic.

Quote from: Netsu
What exactly would I initialise in those functions? Suppose I need some STL containers and tables in this namespace, do I just let them be initialised statically?

It depends on what you need.  The whole 'static initialization problem' only occurs when static objects depend on something in another unit for their initialization.  Something like this:

Code:
namespace
{
    std::vector<int> my_ids;
}

Is 100% safe.  The danger comes with things like this:

Code:
namespace
{
    std::vector<int> my_ids = function_in_some_other_file();
}

If function_in_some_other_file() is dependent on other static objects, the order of initialization is now a problem.  However, this sort of thing happens so infrequently that it honestly amazes me how often people cite it.  I don't think I've ever come across it.
Logged

Franchise - The restaurant wars begin!

What would John Carmack do?
EdgeOfProphecy
Level 2
**



View Profile WWW Email
« Reply #16 on: April 02, 2012, 12:39:07 PM »

Also, since people are already commenting on that, what would be considered better, making a factory for the sound sources or letting the user construct them himself? I tried both ways and direct construction seems more elegant, but with a factory I am forcing the sources to only be created when the mixer exists, which is handy.

I'm actually pretty curious about these sound sources as well.

What are they to be used for?  (Music, sfx)
Are they very performance intensive, or are they super lightweight?
What needs access to them, and when?
Do you have a limit on the number that can exist?
Are there special considerations, like in order to create one you have to load the audio file, which takes a lot of time, so you need some kind of caching solution?
Logged
EdgeOfProphecy
Level 2
**



View Profile WWW Email
« Reply #17 on: April 02, 2012, 12:40:40 PM »

Quote from: EdgeOfProphecy
It is more encapsulated and safer

Citation please.

"Than a raw global."  Not "than a namespace solution."

Come on, dude.
Logged
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW Email
« Reply #18 on: April 02, 2012, 12:42:34 PM »

Quote from: EdgeOfProphecy
It is more encapsulated and safer

Citation please.

"Than a raw global."  Not "than a namespace solution."

Come on, dude.

A thousand pardons, I misread your original post.
Logged

Franchise - The restaurant wars begin!

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



View Profile WWW
« Reply #19 on: April 02, 2012, 12:54:14 PM »

For the cause of audio, I don't see why they would make a particularly good example. You'd probably want to switch audio output methods (to support *no* audio, at least). And anywhere likely to want audio access is probably going to need access to the display and other critical components - you might as well bundle all that together in a UserOutput object, and pass a reference to that around.

I'm writing just the audio library, not a game Smiley

It depends on what you need.  The whole 'static initialization problem' only occurs when static objects depend on something in another unit for their initialization.  Something like this:

Code:
namespace
{
    std::vector<int> my_ids;
}

Is 100% safe.  The danger comes with things like this:

Code:
namespace
{
    std::vector<int> my_ids = function_in_some_other_file();
}

If function_in_some_other_file() is dependent on other static objects, the order of initialization is now a problem.  However, this sort of thing happens so infrequently that it honestly amazes me how often people cite it.  I don't think I've ever come across it.

I had problems with the 'static initialisation order fiasco' before, that's where my concerns come from. What I'm worried about is what if the programmer decides to initialise an object of my class (the sound source class) statically, and the object (in it's constructor) references parts of my library that are also initialised statically.

I'm actually pretty curious about these sound sources as well.

What are they to be used for?  (Music, sfx)
Are they very performance intensive, or are they super lightweight?
What needs access to them, and when?
Do you have a limit on the number that can exist?
Are there special considerations, like in order to create one you have to load the audio file, which takes a lot of time, so you need some kind of caching solution?

The sound source class is for creating objects that are supposed to play sounds that can move around and change. For example if I'm making a racing game then each car could have a source object (or even more than one) to play its sounds. The source objects have properties like the name of the sound file they are playing, position, pitch, loudness, velocity etc.
They are pretty lightweight because they are just a level of abstraction on top of the mixer.
There is no limit to the number of them (this is actually the main reason I am writing this library).
Who needs access to them really depends on the application, but I would imagine that if you are writing a game you want to make those sources a part of your classes, so that things like player, bullet, car etc have their own sound sources packed inside.
There are no special considerations, the sources are completely abstracted from the actual underlying sound module, they only store and manipulate information needed by the mixer to play sounds BUT I need to register every source object upon creation and unregister it upon destruction so the mixer always keeps an up-to-date list of all sources.
Logged

eigenbom
Level 10
*****



View Profile WWW
« Reply #20 on: April 02, 2012, 01:13:58 PM »

I think choice number 1 (what rivon said), will work. It works for box2d just fine...


b2World* world = new b2World;
b2Body* body = world->CreateBody(...);
.. later ...
delete world;
Logged

Netsu
Level 10
*****



View Profile WWW
« Reply #21 on: April 02, 2012, 01:27:44 PM »

I think choice number 1 (what rivon said), will work. It works for box2d just fine...


b2World* world = new b2World;
b2Body* body = world->CreateBody(...);
.. later ...
delete world;


I really liked how Bjarne Stroustrup advocates using pointers as little as possible, especially 'naked' news and deletes. I suspected that making the library mange its own memory as opposed to the programmer using new and delete would work better.
Logged

rivon
Level 10
*****



View Profile
« Reply #22 on: April 02, 2012, 02:01:01 PM »

You're "new-ing" and deleting only the biggest/topmost object... I see no problem there.
Logged
Netsu
Level 10
*****



View Profile WWW
« Reply #23 on: April 02, 2012, 02:11:12 PM »

Hm, okay I've got one question about this approach:

Code:
CMixer* mixer = new CMixer();

CSource* src = mixer->create_source();

delete mixer;

What happens to the source object? Is it destroyed together with the mixer? Does it still exist but is useless since the mixer managing it no longer exists?

EDIT: Also, how do I get rid of the source object? Don't I need to delete it manually too?
« Last Edit: April 02, 2012, 03:19:23 PM by Netsu » Logged

eigenbom
Level 10
*****



View Profile WWW
« Reply #24 on: April 02, 2012, 02:30:32 PM »

I would let the mixer own it and be responsible for deleting it. So after delete mixer, src would be dangling.

I think this thread shows that there are a number of ways to do what you're after, it just depends on what API you want to expose to your users. I've worked on an audio library beads (for Java) where it was required that you create a single instance of the mixer object, then whenever you create a sound source you just pass the mixer as the first param. This works, but the parameter lists get a bit cluttered. I'd just look at other successful systems and see what they've done.

Differences aside, I'd personally prefer to do the object-based way:

Source* s = mixer->createSource();

than the namespace-based way:

Source* s = mixer::createSource();

as the former implies (to me) that the mixer has a state and is an 'object' in memory. But it's just a issue of style.



Logged

eigenbom
Level 10
*****



View Profile WWW
« Reply #25 on: April 02, 2012, 02:37:39 PM »

I really liked how Bjarne Stroustrup advocates using pointers as little as possible, especially 'naked' news and deletes. I suspected that making the library mange its own memory as opposed to the programmer using new and delete would work better.

Oh and in regards to this, as a future feature you could give the user the option to supply their own allocators. The initial new Mixer() doesn't matter because the user can allocate it however they want. (The bitsquid blog is pretty good for all this applied c/c++ tech stuff.)
Logged

Netsu
Level 10
*****



View Profile WWW
« Reply #26 on: April 02, 2012, 02:38:24 PM »

I would let the mixer own it and be responsible for deleting it. So after delete mixer, src would be dangling.

I think this thread shows that there are a number of ways to do what you're after, it just depends on what API you want to expose to your users. I've worked on an audio library beads (for Java) where it was required that you create a single instance of the mixer object, then whenever you create a sound source you just pass the mixer as the first param. This works, but the parameter lists get a bit cluttered. I'd just look at other successful systems and see what they've done.

Differences aside, I'd personally prefer to do the object-based way:

Source* s = mixer->createSource();

than the namespace-based way:

Source* s = mixer::createSource();

as the former implies (to me) that the mixer has a state and is an 'object' in memory. But it's just a issue of style.

Style is all I'm after Wink
But what if I hide the communication between mixer and source from the programmer altogether:

Code:
CSource::CSource()
{
  mixer::register_source(this);
}

CSource::~CSource()
{
  mixer::unregister_source(this);
}

Code:
mixer::init();
// make a local object
CSource src;
// or use new
CSource* src = new CSource();

I can also replace the namespace in the above code with a singleton, and it is actually what I am using now.
Logged

eigenbom
Level 10
*****



View Profile WWW
« Reply #27 on: April 02, 2012, 03:06:47 PM »

Quote
But what if I hide the communication between mixer and source from the programmer altogether..

From a user-perspective, this is fine too. Though i don't think the stack allocated CSource is a benefit -- if anything it complicates your tutorials etc., as now there are two ways to make CSources.

From a dev perspective it means moving your registration of objects into the objects constructors/destructors instead of the factory functions in the mixer.

But ... I don't understand the system enough to see which way will work better. As long as its simple and consistent for the user. Maybe write up sample user-code for different use-cases and see if any problems pop-up.
Logged

Netsu
Level 10
*****



View Profile WWW
« Reply #28 on: April 02, 2012, 03:16:52 PM »

I have some simple examples for the library, they work fine both ways. From my perspective it's not much of difference if I register in the factory or in the constructor.

But from the user perspective... people are probably used to having factories, but I have a feeling like it's redundant. The language already has those methods for object creation (new and local object), every programmer knows them well and they give you more flexibility, you choose how you want to create them.

I was probably wrong in the first place to ask about C++ style when there is no one correct style Wink
Logged

eigenbom
Level 10
*****



View Profile WWW
« Reply #29 on: April 02, 2012, 03:34:38 PM »

I don't think it's redundant, as (assuming the user understands what a factory is) it has an implication that the mixer is creating the object and doing whatever it needs to create that object. You also have the flexibility of offering an abstract CSource interface, then having different CSource implementations.

But I don't want to convince you one way or the other. The popularity of your software will likely just depend on the awesomeness of your logo. Wink
Logged

Pages: 1 [2] 3
Print
Jump to:  

Theme orange-lt created by panic