• Content count

  • Joined

  • Last visited

Community Reputation

163 Neutral

About TheFlabbergastedMonkey

  • Rank

Personal Information

  1. Singleton pattern abuse

    [quote name='Washu' timestamp='1339693388' post='4949199'] [quote name='Promit' timestamp='1339690904' post='4949187'] Actually I would suggest writing your logger in C. It's the craziest thing -- for example, you can write to stdout using printf or fprintf or fputs from [i]anywhere[/i] in your code, at all! And they didn't use any singletons at all either. Who knows how they managed to pull off that wild trick? Oh right, it's because we had the ability to access global values safely before the GoF book burst in like Seinfeld's Kramer looking bewildered at its own discoveries. [/quote] Best of all, you don't even have to expose the "globals" those use, just the functions that use them. Thus you've contained your globalitus into a single compilation unit. Or you could do it the C++ way, like std::cerr and std::clog, if you need a slightly more useful interface. No singletons there either. [/quote] I'm curious, how are these different from having a singleton Logger class? Something like printf() has singleton-like behavior, you can't have multiple versions of printf() that have different output locations (fprintf supports this, by explicitly providing the stream, but I'm talking printf since it was also mentioned). Similarly, std::c[out|err|log] always write out to the same location. I believe in both cases they can be redirected to any other stream, but it still globally affects it; a singleton Logger could support redirection as well. Building a class around printf, std::c[err|log|out], etc would allow you to change your underlying logging system in a single place, instead of anywhere you log a message. Or, if you don't like classes, wrap with a bunch of global functions, it'll be pretty much equivalent if the class is a singleton. Okay, so Logger doesn't technically need to be a singleton, just providing a global instance would be sufficient for most needs, with the possibility of creating additional special-purpose Loggers. This would make it more powerful than just using printf() or std::cout, since those behave similar to singletons.
  2. Singleton pattern abuse

    I've been working on a Content Manager (Cache, if you don't like the term "manager") class, for loading in and caching various content files. ContentLoader subclasses can be registered via a template function, to load certain types of file. The manager itself is not a singleton, and not global (may make it globally available some time in the future, but not at the moment), however the way I'm dealing with the loader registrations requires that they be kept in a singleton-esque structure. They aren't really kept in a single structure, but through static fields in a couple of templated structs. So I guess it's not technically following the "singleton pattern", but it does follow the concept of global access to a singular collection of data. The registration records are only used internally by the CM, and exist until they are no longer needed (I use reference counting along with a static, again singleton-esque, collection of records to ensure they stay alive until all CM objects are deleted and the program explicitly clears the static collection). The overall benefit is that the code to register loaders and to load resources is quite nice, without any extra setup on content types, content loaders, etc: [source lang="cpp"]// ImageLoader is the default loader for Textures. ContentManager::RegisterLoader<Texture, ImageLoader>(); // DDSLoader will be used to load "*.dds" files. ContentManager::RegisterLoader<Texture, DDSLoader>("dds"); // This will use ImageLoader Texture *tex1 = myContentManager->Load<Texture>("an_image.png"); // This will use DDSLoader Texture *tex2 = myContentManager->Load<Texture>("");[/source] Although not shown, each CM keeps a cache of all loaded resources, so that loading the same resource multiple times only does the work once (if a file is loaded once as BinaryData and once as Texture, then it's loaded twice, but that's because it requires different work). To get the appropriate loader for a resource, there is no runtime lookup into a std::map or anything, I just create a templated struct on the stack based on the resource type (Texture), and that struct gives me access to an existing loader for that type (creates a new one if it doesn't exist yet). As I mentioned, the records are reference-counted, so the loader will be properly freed when it's no longer needed. A single instance of each loader is used, instead of instantiating a new one for every load operation. If I want to make it multi-threaded, I may add support for creating multiple loaders of a single type, or just make sure the loaders don't maintain internal state. I imagine I could have written the loader registrations in such a way that each CM could keep its own separate list of them, but it probably would require a bit more work, and really... why? It's not likely that two different CMs are going to need totally different loaders for each resource type, and would require setting the loaders individually for each CM. I think that having these loader records in a singleton-esque set-up is a small price to pay for ease of use.
  3. Singleton pattern abuse

    [quote name='Aardvajk' timestamp='1339418350' post='4948139'] I don't understand your point here. [/quote] Cornstalks and rip-off above are correct, the point was to show that [i]anything[/i] is subject to abuse (and quite easily, too), but you don't often hear people say "don't ever use if statements, because it's possible to put more than just comparisons and logic operations in the conditional." [quote name='rip-off' timestamp='1339423750' post='4948160'] I believe that we can avert harm by saying that [url=""]singletons are evil[/url]. [/quote] Okay, I like that FAQ's definition of "evil". It's much better than the people above who say flat out that such-and-such a thing should [i]never[/i] be used and there are [i]absolutely no exceptions[/i]. There are always going to be exceptions to anything. In any case, I still stand by my opinion that the OP should use whatever makes the most sense. I'll revise it to emphasize that you should consider alternatives (really, you should probably do this for most design decisions), but if it still looks like the simplest approach to you, do it and see what happens. That's what learning is all about!
  4. Singleton pattern abuse

    [quote name='rip-off' timestamp='1339352601' post='4947984'] [quote] Just a thought... whether you're using a pass-by-argument sort of thing, or a singleton, you're still using that object in the same places. [/quote] In my experience, once an object becomes globally accessible, the probability of "unnecessary" accesses increases enormously. So, no, I think in practise one finds that your average global will be accessed by considerably more areas of the code than the same state would be accessed in a less coupled design. Also, once there is at least one globally accessible object, the probability of other (arguably far less worthy - it is easy to argue for a global texture cache) candidates being "promoted" to globally accessible increases too. [/quote] True enough, it is important to practice some self control to avoid overusing them. But just because there's a risk of over-/misuse is no reason to completely discard something. I refer you to [url=""]this lovely piece I wrote some time ago[/url]. I'm actually quite proud of it (especially the second version, near the bottom of the page). In it, just about everything I've used has been misused. But it's no reason to condemn any of them (can't say for loops are bad just because it's possible to put the entire loop body inside the conditional and/or increment sections); only those particular cases. But, that's getting a bit off-topic I think.
  5. Singleton pattern abuse

    [quote name='Narf the Mouse' timestamp='1339342895' post='4947936'] 2) The thing is, if you want to change what a pass-by-value system uses, you only need to change a few upstream values. If you want to change what a Singleton system uses, you have to re-write it to a pass-by-value system. [/quote] Just a thought... whether you're using a pass-by-argument sort of thing, or a singleton, you're still using that object in the same places. In the example given, the TextureManager is being used in function used for loading stuff. Whether the Mgr is available as a global/singleton or it's passed as an argument (or some other object containing some context data that could be used in lieu of the Mgr), if you make a change to it, you need to account for the change in that function. It really doesn't matter. Putting it in a singleton just makes it easier to call the loading function. If anything, the singleton requires an equal or lesser number of changes, since you don't have to change upstream references, just the actual usage. There's been mention of not being able to see at a glance what data/objects a function uses, and forgetting about updating references when changes are made... true, you don't get the same immediate indicator that a load function is going to use some sort of content system as you would if you passed it as an argument. Instead you've got, well, the fact that it's a loading function, plus perhaps some documentation for the function that says something like "gets the appropriate texture from the TextureManager". As far as forgetting to update references, I'm sure the IDE can help you with that; just do a search for the class name, it's a singleton, so you probably don't have to worry about it being referenced using some subclass (there may be a subclass, but you're likely using the base class' name). [quote name='Narf the Mouse' timestamp='1339222796' post='4947579'] 2) If you have an easily-accessible hammer... [/quote] ... then when you need a hammer, it's easy. If that hammer is complicated, you're going to start using the butt of a screwdriver or a shoe to pound nails. Personally, I won't shy away from singletons (or really, anything) when I feel they are appropriate. An example for me, something I've used in pretty much any project: I generally have some sort of Event Dispatcher, which allows listeners to register for certain event types, and then anyone can fire out events as appropriate. Fairly similar in concept to Win32's message stuff. Pretty useful, I'm sure there are other solutions, but this is the one I like to use, and it has evolved over several projects. It means, for example, that an explosion class doesn't need to get a list of all nearby actors and manually call a dealDamage() function or anything. Instead, damageable actors register for the EXPLOSION event, and the explosion fires the event (either the actors check if they're in range, or I could implement some sort of filter to decide which listeners will actually get the event). Sure, a lot of things end up being connected to the EventDispatcher singleton, but that's never been a problem for me. You might say "oh, but that can't be thread safe!" Well, it's no different than getting nearby actors and calling dealDamage() on each of them. And there's no way I want to pass an EventDispatcher around to every single object I create (not every object uses it, but we don't want to lock that sort of assumption into the code now, do we) My general opinion is that you shouldn't dismiss something just because everyone says it's bad. I feel pretty confident in saying that [i]any[/i] construct has some case where it is appropriate. Sure, there are places where a singleton is far from the right tool, but sometimes it's going to be much cleaner than the alternative. I see several people here saying that singletons are pure evil and should never be used under any circumstance. I say use whatever makes sense. If using a global object or a singleton is going to make your code cleaner and simpler than passing extra context objects around, use it! If down the road you find that you're running into problems with it, then you can change it up. [b]Especially[/b] since the OP stated outright s/he's doing this as a hobby, and wants to learn. What could be better for learning what works and what doesn't, than trying it out? If you try something and it works through to project completion, great! If you find that at some point it's no longer sufficient, then you'll understand [i]why[/i] it doesn't work and can move to a better solution. Sure, it may take a bit of time to refactor, but the experience is, I think, a lot more valuable that someone just telling you "it's bad, never do it." Of course, I'm not saying that everything can/should be a singleton either, just do what makes sense to you. And back to the OP's example of the TextureManager... my opinion would be that you could instead have a single ContentManager, which defers requests to the TextureManager, ModelManager, SoundFileManager, WhateverElseManager as appropriate based on resource type; so it's one singleton instead of many. Or instead of separate *Managers, provide separate *Loaders for each resource type, and ContentManager maintains a list (or whatever your favorite container is) of generic Resources (which is subclassed or something for each resource type) provided by those *Loaders.
  6. Guess What, You're Dead!

    Hi folks! I recently finished up working on a demo for a game I've been working on for 8 months or so, titled "Guess What, You're Dead!" (GWYD). I'm hoping it can help me break into the industry. GWYD is a first person shooter style of game. Kill all the enemies. Simple... right? As a demo, it only has a single level to play, but I would like to make more in the future. It's built with XNA 4.0 using the HiDef profile, so it requires a DirectX 10 video card to run. Right now I'm a bit busy with some other work, but I hope to lower that requirement and have it running on older hardware as well. Here's a lame screenshot: [img][/img] And here's a link to the project page, where you can download it and stuff: [url=""]Guess What, You're Dead! project page[/url] There's no installer yet, but if you don't have (gasp) DX9 or XNA4 installed, the redist installers are included. Gotta have .NET 4.0 installed as well, but I didn't include that. Can't remember why not. I'd love to hear comments on it, if it's fun, if it runs decently, what it runs on, all that good stuff.