What’s your opinion on this? Leave more comments for me to make videos about 👇 Also don’t forget you can try everything Brilliant has to offer-free-for a full 30 days, visit brilliant.org/TheCherno . You’ll also get 20% off an annual premium subscription.
@michaplucinski1423 ай бұрын
Hello! This video definietly made what you meant more clear, however, I am not sure how other way of implementing system like this could look like. For me that's because I can't imagine resource manager class having many instances to kind of use well what class are made for (creating objectS (or at least that's what I think ) ), but at the same time I feel like it makes sense for it to be a class, so that it has it's own data storage, it's own methods and all of that fancy thing which classes provide us with. But then it is singleton again, right? So if you could make a video explaining that or pointing me (and anyone reading this who has similar problem) to some reliable information source or maybe even just answering here how it could actually look like, I would be really grateful =).
@gweltazlemartret67603 ай бұрын
Hi! At 24:16 you say a static global is not available in C#, but I swear it is. You can define your singleton as another’s class static variable, likely in the Program body `static SingletonClass mySingletonStatic = new ()` should work to handle a(nother) magical var usable everywhere in this class' scope, at the condition the SingletonClass itself isn’t static. This static ref of a pure class is likely something Java could handle aswell. It’s not the exact same top-level file defined static variable, but it does sound close enough. (`static readonly` in C# prevents the reference change case you mention afterwards.)
@gweltazlemartret67603 ай бұрын
For the last point, you should go for data-class, this could alleviate the need for a "one class does all everywhere" need: place only the data you need in the datatype you need, and let manager classes manage the workload on this data.
@rinket77793 ай бұрын
There is an advantage to the static inside the method, it's guaranteed to be thread safe when it's initialized.
@guywithknife2 ай бұрын
@@michaplucinski142 just because you can’t imagine a specific class having more than one instance, doesn’t mean you should enforce that it can never have more than one instance. A singleton is a bad choice when you only “want” one instance, because it ENFORCES it. Instead it should be used only when it’s important that you only have one instance (and global access) - if you don’t require both of those, then it’s probably not the right tool. For example, I’ve often heard people give a logger as an example saying they would never want more than one, so make it a singleton! Until you do want more than one: maybe you want different types of logs to go to different places (screen vs file vs to the gui vs network), maybe you want an audit log, maybe user facing log and internal log, whatever. In a game it’s less likely that you need multiple loggers than in business software, but again, why constrain needlessly? You cannot know the future need. So for a resource manager, sure, you probably only need one, but there’s no reason to force that limitation. What if you have an editor build of your engine (editor and engine in one) and you want to have a separate resource manager for each? Why might you want this? Perhaps because you want to keep the game runtime as similar to a real scenario as possible (eg to track memory and access) so don’t want editor-only resources in the same one. Is it a likely scenario? Maybe not, but it’s just to illustrate that just because you can’t imagine a case for it doesn’t mean none exists. Instead, create a normal class, then instantiate it only however many times you need. Done. As for global access: if you need it global, then make a global. If you don’t, then pass it in eg by reference to the dependents that need it.
@derivepi69303 ай бұрын
Get married and become a Coupleton. Even better, a Factory.
@Kokurorokuko3 ай бұрын
My girlfriend is abstract
@felipeliradev3 ай бұрын
@@Kokurorokuko💀
@soniablanche56723 ай бұрын
wife becomes a BabyFactory
@spiderpig203 ай бұрын
My girlfriend is virtual
@JM-is1vf3 ай бұрын
Delete the copy constructor of the Wife class to avoid UB.
@codemetas12843 ай бұрын
Wow! I did not expect my tiny comments on the other video to blow up and trigger such an extensive response. Let me begin this reply by reiterating the most important part of those comments: I am sorry if I came across as rude or condescending, that was not my intention at all! I merely saw claims I deemed factually wrong or at least misleading without further explanation, which I considered worth pointing out. Judging by the upvotes and this video, it seems like you and others agreed :) I also agree that discussion is a good way to grow as a community and as individuals! Just to make this abundantly clear once more: I really am not a fan of singletons and don't want to be upheld as their champion xD I'd much prefer something like the "tree" you describe any day of the week. I have no major objections to anything stated in this video, just one minor nitpick: The moment you move the static pointer from the function into your class and add the "if(!s_Instance)" check to initialize on first use you lose the guaranteed thread safe initialization of the "standard" Meyers Singleton and would have to manually add locking or similar to preserve it. This does - of course - become irrelevant once you change your code to use explicit initialization, which you presumably will only ever call once from one defined place and before any potentially accessing other threads are started.
@kolosso3053 ай бұрын
Congrats on being featured lol
@aloluk3 ай бұрын
Yeah, i'm waiting for any mention of thread safety, part way through video so far.
@patrickgono60433 ай бұрын
Regarding lazy loading: I remember playing Albion Online. The game was available on mobile and PC. Every time a spell or ability was used for the first time, PC users would get a lag spike. Especially on devices with HDDs. An SSD improved the situation somewhat, but the problem was still there. The lazy loading meant that all the textures, shaders, sounds etc. for the abilities had to be read from file, taking much, much longer than ~16 ms (i.e. running at 60 fps). Though I understand why they went this way, as mobile devices have fast storage but usually are quite limited in memory. Still, would have been nice to have the flexibility to preload everything on PC while lazy-loading on mobile.
@JDoucetteАй бұрын
A game I released on the Xbox 360 using XNA and the XACT sound system would have a frame lag on the first projectile fired. I couldn't get into the sound system to control the loading and caching as I wanted, so I coerced it to stop lazy loading by playing the sounds at volume 0 to force them to load. (The game was Score Rush, a bullet hell, small hit box, tight controls, one hit death... so the gameplay couldn't survive any frame lags.)
@GuilhermeTeres3 ай бұрын
You had a good point in the video. I'm a senior game engine dev and also the guy behind Cave Engine (that Ive been written for almost ten years and currently have around 130k loc) and I would like to throw my two cents here on how I ended up figuring out the same solutions you suggested in the video. For that, let me tell a small story that happened to me. When I started writing engines years ago, it was after all the software engineer stuff we learn at university so I obviously tried to over engineer it a bunch. I found singletons very interesting at first and started using them all over the place. The first problem I had, which is probably the most obvious is the dependency thing that you mentioned in the video. it gets really hard to know and handle when a specific Singleton gets in each realized or destroyed if they rely on other singletons. And what is interesting about this is that this problem is very easily spotted by anyone writing your system like that if they use/develop the software for even a small amount of time. so when I see someone with this singleton madness, the first thing that comes to my mind is that the programmer does not have a lot of experience. So, moving on, as I evolved as a game engine programmer, I found that it's best to have up to one Singleton max, which is the app or application Singleton. The first iterations I did on that, more than 10 years ago, was exactly like the Dev from the previous video did: statically allocating it on the heap. But funny enough, just like the previous case, if you use the system or develop it for long enough, you very soon start to realize that this approach is also another rabbit hole. The main problem as you demonstrated in the video is exactly that you do not have control over when this get initialized or destroyed.. and that's a problem! So I'm sharing all this because I found very interesting while watching the video and remembering all that, because it is impressive how a lot of times the best way to learn and evolve as a programmer is the simply spend enough time, writing and dealing with your own code. Cave Engine nowadays, have exactly one singleton, which is the app, and it's implementation is very similar to the one you suggested with a couple more functionalities. A good one that I would love to point out here is a static method to check if the instance is a really initialized or not. Because some other systems in your code Could be made to also work regardless of the singleton being valid or not. And the thing is that when you have such a large codebase, specially, if you're not working alone, something that I ran into a lot in the past is deep down the cold, having systems being initialized in the singleton's constructor that also relies on the singleton. Causing an initialization loop that may not be trivial to deal with. Anyways , nice video!
@linnealager61463 ай бұрын
I am in the singleton craze phase right now. They are so cool and can make the code look really clean. I've used it for listener pattern, input manager, state stack and more. I've heard singletons are considered an antipattern but was never convinced why. Your comment was very insightful for me.
@ferinzz3 ай бұрын
As someone who started with programming robots for school I find it fascinating that people would skip initialization phases as that's the first thing you do. determine what needs to be done for everything to be in place and how to return the robot back to its station once it's all done. Then also make sure that it stops if/when it looses track of where it's at so that it doesn't fly into stuff and break it.
@shadichy3 ай бұрын
As a fresher, I personally do not even bother to use more than one singleton in my code. I just feel like 1 is enough to "be a global variable manager" that can actually be initialized whenever the app starts. But now after I read your comment I feel like it makes sense. Very helpful!
@TheEVEInspiration2 ай бұрын
*All Applications really need is a good bootstrap, e.g. controlled initialization.* This requires so have a clear, well thought out architecture as to what becomes online, "when". Then we have a well established, predictable runtime-environment for the application code work with. And programmers are saved from always changing networks of context objects needing to be passed around, that at scale become meaningless, slow and tedious. It's setting up the run-time environment that matters. Never leave "main" or process input, before this most important job is done IMO!
@TheEVEInspiration2 ай бұрын
@@ferinzz I fully agree!
@JaceMorley3 ай бұрын
My stance is that traditional global singletons and global variables are invisible parameters on every single function call in your application, and so greatly prefer explicit dependencies via dependency injection patterns. They do not make code cleaner, they just make the mess harder to see and deal with. EDIT: to clarify, DI does *not* mean using a DI framework. It's only the idea of passing in dependencies instead of instantiating them or pulling them from global state. Like 'polymorphic', it's a fancy name for a simple idea, and the frameworks/libraries are tools that can help reduce boilderplate but not needed for following the principle.
@Shadow-xi2sv3 ай бұрын
++
@Kokurorokuko3 ай бұрын
@@calvinsomething5348Singletons are problematic because they use static methods. Static methods, in general, are a problem for testing because you have to be more careful not to create tests that affect other tests
@KingKarEl1003 ай бұрын
Also makes testing (unit,integration, take your pick) harder since every change to the singleton affects the outcome of the next test
@danielsharp24023 ай бұрын
At the end of the day you either provide it as a parameter or it's going to be global that is the two options. Providing all dependencies as parameters is cumbersome as all hell so we cheat with globals. Fancy DI containers are also globals unless you pass them as parameter to everything again.
@JaceMorley3 ай бұрын
@@danielsharp2402 Classes can contain references to their dependencies that are passed in via constructors, functions can take parameters too if needed. That way dependencies are transparent and documented by the code itself, and lifetimes are clear to the developer. With globals all that information is hidden and obscured, but that's just hiding important information for code maintainence. The DI containers really are just for automatically hooking up implementations to interfaces and managing lifetimes, but shouldn't be appearing in the actual 'business logic' code, and you can do all that just by initializing stuff before you pass it in if you like, they're not needed for following DI practices.
@alexkhazov72643 ай бұрын
On top of that, the System class doesnt need to be a class. It shoild be a namespace with free functions. All the state can be an internal struct in the translation unit
@CDBelfer43 ай бұрын
You could make the argument that with static class functions which would behave essentially the same as free functions, you have the added benefit of having private/protected functions instead of the weird detail/impl inner namespace that is usually done
@garrafote183 ай бұрын
@@CDBelfer4 from my experience singletons, static classes and system namespaces all achieve the same functionality. However I tend to prefer system namespaces because they don't polute header files with unnecessary private/implementation detail data leading to faster overall compile times for the pplication and most importantly faster compile times when iterating on the system - changing header files can sometimes be a real pain when working on big projects. So as long as you have control over the system's lifetime and don't polute headers with unnecessary data I wouldn't mind the chosen implementation pattern.
@theo-dr2dz3 ай бұрын
First off: I am not a fancypants architect. I program in two contexts: at work, where I typically have no influence on the architecture, and at home where I do everything myself and have absolute power over everything and no team to worry about and all that stuff. So, you quite often have classes of which it doesn't make sense to have more than one of it. So, my solution would be to simply not create more than one of it instead of going out of your way to create clever ways to make it absolutely impossible to create a second one (even with multithreading, doing silly classloading or reflection tricks and all). Defend your singleton with the Nancy Reagan defence: just say no. Just don't do it. Making a resource manager dependent on game states (or layers as he calls it)? I typically would implement the paused state as a game state. If pausing the game would unload all resources, and unpausing would cause all resources to be reloaded, that would be not very logical. So you would have to create layers that do that and a different kind of layer that doesn't do that. Already makes stuff more complicated. Another one: if your main gameplay uses ECS, you would like your ECS manager classes alive from the moment actual gameplay starts up to the moment that it stops. So, you initialise all that when he exits the main menu to start the game proper. You don't want your ECS to disappear every time the player goes into the options menu screen and be recreated from scratch when he returns to the game. When he is in the options screen, the ECS stuff is not active, but it should remain alive. Only when he quits, you unload all that. What I would typically do is to have a kind of context class that holds unique pointers to all these central things that should be widely accessible and alive almost all the lifetime of the program, and pass that context around to every function that needs access. It is not completely global, just largely global. Simply let the context run out of scope whenever it is no longer needed, this will call the destructor and free everything. Also, I really don't like init() and shutdown() functions. It should be RAII if at all possible. It's way too easy to forget to call init(), call it twice, call shutdown() at the wrong point or twice and all that. I've seen the best programmers cause segmentation faults. It's too easy. Maybe at the point where the resource manager is created it is not yet possible to load textures. Add an add_texture() function to do that and a way to indicate that there are zero textures loaded. Seems much easier to me.
@oliver_twistor2 ай бұрын
I don't like init() either, because I like to know that when I have constructed an object, it is fully ready to use. In general, if one has methods they _must_ call every time, that functionality should probably be contained within the constructor.
@lavatheifАй бұрын
omg i learned programming 9 years ago from you, im so glad you still make videos
@jamesburgess9101Ай бұрын
great post, thank you! Agree with most of your reasoning but Ooph, 21:33 look out! You just introduced a data race in your code. One of the super important things about a Meyer's singleton is the compiler inserts a lock before the initialization of a function local static assignment. Your new "if" needs an explicit lock if there is the chance of multiple threads calling this. Since the whole point is "this can be called from anywhere" that is also likely going to include "can be called from any thread". Anyway, I think worth mentioning, as EEVBlog Dave would say, definitely a trap for the younger players since the lock is not obvious.
@zombi10343 ай бұрын
Because Java is mentioned many times in this video, I want to mention that the most idiomatic and error proof way to create a singleton in Java is by defining an Enum with a single constant (that being the singleton instance). It is thread safe, it guarantees that only a single instance can ever be created (including through tricks like serialization or reflection) and it provides lazy loading automatically. And you can define methods, implement interfaces just like you would on your normal singleton class.
@kostiapereguda3 ай бұрын
I don’t think it provides lazy loading in the same sense as here. Enum instance initialization happens during class loading (as does any static initialization), and while class loading itself is lazy, as it happens when you refer to the class for the first time, it is not the same as referring to the class instance for the first time. For example, you can trigger class loading (and thus creation of your instance) of a MySingleton enum by typing the statement “Class.forName(“MySingleton”)” anywhere
@kostiapereguda3 ай бұрын
But yeah, singletons though enums are cool
@emilyy-dev3 ай бұрын
@@kostiapereguda loading a class doesn't trigger initialization, for example, doing Foo.class will load Foo but won't initialize it (this is easily testable by having a println in a static init block), however calling/accessing any static method/field or constructor will trigger initialization
@kostiapereguda3 ай бұрын
@@emilyy-dev you are right actually
@mr.m85393 ай бұрын
@@kostiaperegudaJust wanted to you for being adult enough to admit being wrong. The world needs more people like you.
@suryanshusharma32273 ай бұрын
Good take. Need more videos talking about design decisions.
@Spongman3 ай бұрын
Ditch Init() & Shutdown(), just do that stuff in the constructor/destructor. Stack-allocate your singleton object ("System system;") & just use raii to control its lifetime. that ensures you can never get your lifetime boundaries overlapped, it's also exception-safe.
@madwax5803Ай бұрын
I was thinking the same thing while watching the video. There are two 'rules' I would add 1) The singleton object should be an interface or public API to a (sub?) system (think logging system with logError(...), logWarning(...)), NEVER use the global directly/indirectly internally in the "system" and do the hole pass ref thing, good for injection/test etc etc. 2) In the constructor Assert/throw/Jump up and down if the global is NOT nullptr, that stops a BIG problem.
@sebbbi22 ай бұрын
The classic Design Patterns book didn’t explain singleton destruction problem at all. In trivial cases these lazy static singleton just works, but once you start having dependencies the system will crash at shutdown, and crash is system and compiler specific as destruction order is not well defined. Simple example: You have two singletons: memory allocator and logger. System is shut down and language destroys all static objects. It decides to destroy the memory allocator first. Logger is destroyed next and in logger destructor it frees memory. But the memory allocator singleton static is already destroyed. Thus we get a crash at shut down. The only way to make singletons work in C/C++ is to have explicit shutdown calls and do them in right order. Once you have done this, you might as well do the init calls also explicitly in the right order to get nice symmetric and deterministic init and shutdown. This makes debugging and stepping easier when something fails during system init.
@sebbbi22 ай бұрын
If you dislike calling MyClass::getInstance().myFunc(), you can also have a public static pointer in the class called instance. Then calls look like this: MyClass::instance->myFunc(). This way programmer knows there’s no function call overhead. Compiler can of course inline these GetInstance() calls, but it’s not guaranteed to do that. And the instance pointer is simpler to reason about. Init and shutdown are of course the same in both ways.
@Slashx92Ай бұрын
That last part actually sounds quite nice if one must use singletons
@simspawn10 күн бұрын
Absolutely love your videos. I dont even understand a fraction of what you're talking about, but its still enjoyable. There was even a moment when you talk about a first init and closing down and I was like "yes, thats exactly the kind of thing I want to avoid in the future..." but this is all still way over my head. Man I cant wait to actually be able to understand all this.
@dhickey59192 ай бұрын
Thank you, Cherno. Really enjoyed you diving into details on Singletons and how it's applied in architecture. The asserts video you mentioned would be great to see as well.
@Chukozy3 ай бұрын
While I can't speak for everyone, we need more of these videos sharing your insights and expertise!
@dct48903 ай бұрын
I really enjoyed this one. There's nothing better than multiple perspectives. I watch a lot, and they're all good, and I always learn something. Stay on the mend.
@Strazz8013 ай бұрын
What a lovely and in depth video! Thanks Cherno , super insightful for someone like me that’s finally starting to come to grips with all the complexity of the language.
@retakenroots3 ай бұрын
After 25+ years development experience I completely stopped using Singletons altogether but also developed a strong dislike for globals. Singletons are in a way just nicely wrapped globals. I only work (in my own projects) with dependency injection. It makes the code more clearer (shows intent) and testing a lot easier. Yes you get more boilerplate code and sometimes classes and objects that contain references that are not used by the instance itself but by one of its 'children' but for me that is acceptable
@prostmahlzeit3 ай бұрын
That's true, but please add that dependency injection also provides the ability to export as singleton or export with scope. That way you still can have some classes created once or created once per subtree
@almightysapling3 ай бұрын
In my opinion Singletons are just *poorly* wrapped globals. Extra OOP fluff for no gain in safety/utility/readability.
@dagoberttrump92902 ай бұрын
same. and DI ftw!
@justanaveragebalkanАй бұрын
If you have classes that are injected because of interfaces in the dependency provider, just lazy load them, i see no point in registering them as accessible dependencies if the only reason they are being registered for is to be consumed for other dependencies.
@retakenrootsАй бұрын
@@justanaveragebalkan fair point but as injection typically happens at construction, the constructor signature will already show me all the required dependencies at that point. That said in really big projects this might not be what you want but for me it is an indicator that when I have a lot of injectables I might need to rethink the design.
@jlr37393 ай бұрын
Using singletons for the property that they’re accessible anywhere is problematic for sure. But you can also get way too granular with systems/classes to the point where all work is done by dispatching calls to functions all over memory. Having a ‘Singleton’ or a manager that is in charge of a system and runs said system on all entities is not only simple, it’s also generally more performant and makes it clearer when something is happening. It’s an alternative to overly OO-ifying your code architecture. But yes, lazy initialization for an important system like this is ill advised.
@almightysapling3 ай бұрын
Regarding the lazy init being bad in this specific case: does it really matter? *Because* it's so critical it's certainly going to be one of the first things created. The "rocket launcher" example in the video makes sense but just doesn't apply to the memory manager.
@pharoah3272 ай бұрын
Am I missing something here? Lazy Initialization is independent of the Singleton design pattern. You can create the singleton when the application starts or create it when it is first grabbed. Either way works and it is easy to switch between the two. Having a singleton instance doesn't mandate either lazy initialization or greedy initialization. You get to choose which one suits your application better.
@JavedAlam-ce4muАй бұрын
@@pharoah327 They are referring to the specific example from the code review.
@funkdiscgolf3 ай бұрын
Please make the logging/tracing/asserts you mentioned around 26:30 !!!! specifically how you go about the code changes between configurations. (preprocessor, oop , just branching, ...) , what different configs you setup/recommend/use? (like debug, release, final, internal, external, testing, ...). What build configs are the most and least useful in development? my drag and drop setup for any new project is a spdlog setup with some simple preprocessor flips between debug, release, and final configs. It works, but i often find it could be extended in a bunch of ways and would love to hear your thoughts and methods!
@duncangibson62773 ай бұрын
Fantastic explanations of pros and cons of [variations of] the Singleton pattern, and alternate ways of achieving more control over timing and access 👍 More discussion of architecture issues like this please 👍
@ashleigh.3 ай бұрын
tl;dw sorry, but something I noticed the C# peeps posting about but I'm not sure if it was addressed in the video, `AddSingleton` in C# DI is not a singleton that this video is talking about, rather that is a "singleton lifetime", a very important distinction
@kolosso3053 ай бұрын
I was literally just researching this topic for the past few days as I was refactoring my engine. I can tell this is going to be juicy!
@DynamicalisBlueАй бұрын
Asynchronous lazy loading is pretty much essential in modern games. Preloading a rocket asset on level load and hanging onto it for the entire level is not ideal when you can have thousands of high quality assets but only a few hundred ever present in your scene. It’s an insane amount of memory.
@3draven23 күн бұрын
If it's a game object that is constantly used like a projectile then you would preload rockets / bullets in an object pool and keep a set amount in memory and pull them into existence enabling their physics / needed raycasts / sound as needed and recycle them. Constant memory allocation / de-allocation is just bad practice.
@DynamicalisBlue23 күн бұрын
@ Async loading is not the same as constantly streaming stuff in and out. For something like a projectile, once it’s loaded in, you’d want to keep it in for quite some time as well as set pools up for them, like you mentioned, but likely not forever. There’s no reason why you can’t use async ‘on-demand’ loading but give the asset and pools an expiry time of let’s say, 10 minutes. This way, you don’t need to preload potentially hundreds or thousands of different assets related to weapons until someone actually uses said weapon. Once no one uses the weapon for 10 minutes, then it’s assets and pools.
@Splntxx3 ай бұрын
Really nice video quality - lighting and angle is perfect!
@flyinginthedark61883 ай бұрын
The code suggested in the video is not exactly the same as what Meyers version gives you. The Meyers Singleton is thread safe, meaning it implicitly provides atomic check and a mutex during initialization, which allows you to do lazy init and access the instance from more than one thread. The version with global variable is good only if it doesn't do lazy init and has explicit Init() call as shown in the last example, or else you are risking data races in your program.
@Serendipity44773 ай бұрын
On a modern compiler the static variable actually takes care of this automatically. But this also hides it from the developer, which is a really bad thing. I had several cases where callling this kind of singleton access function completely tanked performance highly multithreaded code and figuring that out is sometimes not that easy.
@rinket77793 ай бұрын
What? No it doesn't, can you point to a source backing up your claim that any static is thread safe initialized? It only applies to statics defined inside functions
@flyinginthedark61883 ай бұрын
@@rinket7779 I assume the person above meant statics in a function, as it was said in the context of Meyers Singleton which is relying on this property to work.
@rinket77793 ай бұрын
@@flyinginthedark6188 ah ok, makes more sense. Curious how initializating a function static could "tank performance" though, sounds very odd.
@rbaron7352Ай бұрын
After working on a code base that was developed for years by the same group of software engineers, I can confidently say that group would definitely just set the global pointer to null and not care if any memory were leaked. One of the persistent bugs that I fixed in that code base was one such issue. Another was one in which a global value which should have been the length an an array was set to an positive error code that was in the same range as would be expected in the array. Both of these bugs were written and tested by profession software engineers with years of experience.
@etherweb6796Ай бұрын
So a lot of these things are addressable using the techniques discussed in "untangling lifetimes" by Ryan Fleury. Resources that are opened by a program are released by the OS after the program ends execution, but additionally this can be handled by simply using arenas to allocate things. (In C++ this means using a custom allocator with either placement new and/or overloads of new and delete for your classes)
@minastarosАй бұрын
25:00 yes, always _static_ for module-only objects. It's not only a functional thing, but also a _visual_ aid for the reader: ah, this is module-local. Great video, good to hone my skills from time to time!
@Leonhart_932 ай бұрын
You tie things together only when they belong together. There are always reasons to have things that are accessible from everywhere for clear reason, that's why the "static" keyword exists.
@steel0tsunamiАй бұрын
I really liked this style of video, I don’t do much OO programming, but one place I like singletons is for the Null objects when using the Null object pattern.
@dimanarinull9122Ай бұрын
person: doesn't understand the use cases for a design pattern. person: this is bad because what if you made using design pattern? therefore it's bad and you shouldn't use it. person: hashtag I'm very smart.
@bebe8090Ай бұрын
At my work, we follow something similar to the MISRA standard. MISRA says that you shall not allocate dynamic memory in your programs. Ours is a little more lenient, and we can allocate dynamic memory only when the program starts up but not during runtime. So we initialize all of our singletons right away, and there is no risk of one of our classes being deconstructed and remade later. If you're doing something like this, all singletons really do is make it so you don't have to pass a pointer to whatever class needs it, and it can just call on the singleton instead. You don't really have all of the downsides like lazy loading, potentially deconstructing something and worrying if it is available, or whatever else. Really the only negative would be that someone could use it where it shouldn't be used, but I'll accept being a little more vigilant on code reviews watching out for that since it makes things a lot easier. Thank you friend.
@azemu3 ай бұрын
a video on how you do assert would be awesome! Especially the dialogue boxes and such.
@juanmacias59223 ай бұрын
Code review series has been solid gold! :D
@paherbst5243 ай бұрын
I've been trying to push this w my team. That if you think you need a Singleton, consider just using a namespace.
@jkrigelman3 ай бұрын
We are not software guys but we do a lot of Python for my previous job. I argued with someone on multiple occasions why I removed the Singleton classes and how it was breaking our testing and how it affected real work flows.
@uliwitnessАй бұрын
Great explanation! IMO the most obvious argument against singletons is that it hurts testability. If you have "the tree", you can create a new instance of the singleton for each test, and destroy it at the end, making sure one test can't screw up the next test. Also, it makes mocking the singleton class much easier if it's just an object you pass in, and you don't have to add a setter for the singleton only intended for use in tests. While I am also generally for predictable construction/destruction, in many applications, you end up doing a surprising amount of work just destructing objects when a process terminates, even though a modern OS will clean up your app's heap anyway. So to improve termination times, you actually might want to only very selectively clean up. (E.g. on macOS, there is an "applicationWillTerminate" notification that you can use to e.g. persist data to disk when the app quits, but e.g. NSApplication's destructor will never be called).
@kanecassidy91263 ай бұрын
yes, please make a video about asserts and error checking/logging
@charliecooper98342 ай бұрын
I would love a video on setting up a robust assert system
@d.g.m.0410 күн бұрын
Hi, there is one thing you forgot to mention that is really bad about the singleton pattern when using the static "stack" initialization inside a function and returning a reference to it. We start from the fact that we want to prevent Static Initialization Order Fiasco, and yeah this code does it - but what it also does is it allows for Static DEInitialization Order Fiasco to happen! This bug is as hideus (if not more) as the SIOF case, and the worst part is that it's usually hard to test for. I struggled for like an hour recently to make a simple test that shows you can crash your program with it, and yes you can! Another thing is that there is a nifty counter approach that I think might be the best of them all but it has some non-trivial costs. Usually you can get away with "leaking" the memory using heap allocation when the destructor call is side-effect free and you don't want to free the thing until the program is done.
@ProGaming-kb9io3 ай бұрын
Feel better Cherno!
@mr.anderson50773 ай бұрын
Also please create a stand alone series on all kinds of design patterns and their real world use cases
@marcsh_dev2 ай бұрын
I was ready to come in and argue, but yeah, Id agree with not using a Meyers Singleton for game style resource managers. Resource managers are a nice system to explicitely instantiate early. They often run threads for various processes, look at varoius directories to get a feel for what resources are around, etc, and having that happen at a very well defined time is solid.
@twigsagan385713 күн бұрын
Singleton are definitely an antipattern, however. I work in Apex in Salesforce, and the only elegant solution I could come up with to actually test well and mock a database were singletons. For instance. A controller has to be static, so how can you then inject a database mock instead of the real thing. Well I now interchange them if in your test you call getInstance on the subclass mock first, you get the mock, if you call it on the real class you'll get that instead. Works perfectly. But yeah it also means hard coupling a little bit.
@Cruxics3 ай бұрын
Another one to throw my interns. Very nice. As far as why you would do something hazardous. It happens. I've had less experienced engineers re-init the entire damn thing because they didn't understand what they were looking at or the naming was just not what they expected which happens in 20+ year old engine code, whether its madden or call of duty as the coding by contract makes a dumb move and decides naming conventions should change. Worse still introducing new language features when moving from a C only engine to a C++ one. Which has happened a few times in my career. Or the stacking of lazily converted code from Action Script, to C#/XNA, to its final form of C++, using a converter instead of rewriting the code properly. So writing defensively and using the tools the language provides to communicate not just intent, but have the compiler guide against unwanted actions with access modifiers and scope limiters can go a long way in preventing someone from squashing your football texture every other frame from a thread not controlled by the rendering thread causing the damn thing to sporadically flicker in game play, because someone decided to re-initialize in the wrong memory pool. Leaving two different jira's for "why is the load time longer" and "why is the ball flickering" at opposite ends of the studio.
@denysmaistruk46793 ай бұрын
The solution to any problem one is trying to solve ALWAYS depends on CONTEXT. No pattern is universally good for every use case. There are cases where a singleton is not applicable, just as there are cases where a singleton is a good match, perhaps even the best. As a programmer, you should understand the design and requirements of your application and make decisions based on this context. Design patterns are a very controversial topic in general. If someone calls a singleton an antipattern, it doesn’t mean you should never use it. It’s similar to the advice against using the friend keyword or dynamic_cast in your code. It’s unwise to think about such design challenges in a black-and-white manner; there is no silver bullet.
@K3rhos3 ай бұрын
Yep, I agree, and also, there is is huge difference when you have the entire program source code and you can actually just follow a clean structure to access things and when you mod the program (game modding for example), you don't know about the whole program but just took some instances you needed here and here, so in game modding it's pretty common to have a LOT of globals variables (and eventually wrap that as a singleton structure) using a custom injected dll. (By getting stuff from here and here in the program without having the entire "application tree", reverse engineering is already pretty hard, so it's common to not know about the entire program structure !)
@pharoah3272 ай бұрын
Agree 100%! I hate blanket advice such as "never use this...". In my mind it almost always points to a novice developer who hasn't been in enough situations to realize that everything has its place. Programming is not black and white and design decisions are not universal for all projects.
@MichaelPohoreski2 ай бұрын
Agreed. An expert is one who knows *when* and *why* to break the rules of thumb. EVERY solution is a trade off; being pragmatic means you understand the pros and cons of (most) potential solutions for the problem at hand.
@dbattleaxeАй бұрын
Agreed. I've used singletons to hold information about the program's layout in memory to find function pointer offsets in order to communicate function pointers across address spaces with ASLR enabled. That's truly a global state. This was to build an RPC system for distributed memory programming. I could have had a global init() call but that's also a global state. Information like the program's layout in memory is an inherently global state so it makes sense. You just tend not to run into inherently global information like that unless you're doing something quite advanced.
@user-wo5dm8ci1g2 ай бұрын
It seems there is an entire dimension related to threading missing here. Lazy loading using that basic singleton pattern without memory barriers can be dangerous. Adding a barrier would make it safe at a fairly high cost to access, but might be worth it if that cost is paid for all the locking needed to synchronize the data structure. Loading early (like in your static example) allows you to do it before threads start accessing it, so its generally way easier to do it that way.
@sacredgeometry3 ай бұрын
Singletons are fine ... the problem with singletons I have found especially whilst trying to help people in the Unity space is its often a crutch for people who have no idea how to architect their code. Often if you go to unity answers you will see people using it because they have literally no idea how to pass references around or call code on one object from another and their solution is a boat load of singletons. Its painful. There is a definite set of cases where you want to insure there is only ever a single instance of a class at runtime ... half of peoples uses are not it.
@teamdoodz3 ай бұрын
100% agree. singletons are not the devil, theyre just abused
@CodeStructureTalk3 ай бұрын
My guess is that this crutch is still going to be much easier to fix though. If the developers do create this singleton and it keeps all the data, it's much easier to later efficiently map it to GPU buffers to arrive at a very high throughput application. If the developers used shared_ptrs instead to pass small arrays 5 layers down via dependency injection... Well... That's it. The architecture is done, no coming back from that one.
@wb39043 ай бұрын
@@sacredgeometry exactly most times people don't know who should own the instance or where it should live, so grabbing it through a singleton looks like a perfectly valid solution. It's an architectural issue for sure. Singletons souls be avoided as they are mostly a sign of architectural problems.
@sacredgeometry3 ай бұрын
@@CodeStructureTalk Oh absolutely not its a recipe for total spaghetti. Like most "I am doing it this way because I don't know how to do it properly" solutions.
@devluz2 ай бұрын
The average unity app is probably the best example for the downsides of singletons. I work on a very code heavy unity asset which my customers can import into their project and use via a C# API. A lot of my bug reports come from them hacking their own singleton into my C# module and once they update the asset to the latest version it gets overwritten and the house of cards collapses ... Instead of registering a C# side event handler they edit my class that emits the event and forward it directly to their singleton... Absolute madness.
@miikavihersaari3104Ай бұрын
Saying that global variables are bad misses what's relevant a bit. What's relevant is control over time and place of both init and term, and scope of access. Our basic building blocks are types (primitives and structs, i.e. descriptions of data), variables (data stored in memory that is of a type) and functions (a sequence of instructions that operates on data). All of these have a scope, the highest of which is global. Whatever the scope of a single variable is, you need to have explicit control over what happens to it and when.
@TryboBike2 ай бұрын
Is 2 million LOC project 'large'? Singletons are my bread and butter, however of a very specific kind and there are a few caveats to their use. First of all - singleton _is a_ global variable with extra steps. Singletons which rely on static storage are asking for trouble, because at some point one singleton will ask for features provided by another and that is going head-first into static initialization fiasco. I could tell you horror stories about this. At one point I refactored the application I work on to have an explicit singleton initializer that is run at the start of the application, which is also explicitly destroyed at the application exit. This works really well. In my experience - singletons make sense for avoiding link-time dependancies and for systems which ought to be accessible throughout the application and in its whole lifecycle. Essentially - a singleton becomes a 'namespace' that can hold some state ( like a cache or somesuch ).
@kiyasuihitoАй бұрын
I found singletons to be a life saver when working with old legacy spaghetti code lol i prefer not to use them either usually, but when the old code your boss hands down to you has messy inlcude dependencies, they can help keep the code you add to the codebase encapsulated in one testable class. Great video. 😂💐
@LuRybz23 күн бұрын
I am not from C++ (yet) but in Unity we also have this discussion and Singletons are specially super used. In resume the main point is that Singleton usually breaks any kind of Single Entry Point or Tree Structured architecture and just gives goes for a Global things floating around the code. At first Singleton feels like a clean code thing because your code gets really short and super simple to understand but one or two steps beyond and you will notice one of the problems: having multiple objects with its own decoupled "universe" don´t caring about anything else and just floating in the scene is really problematic if you have to deal with the order of the execution. Usually, you need something ready at some point in the code but it is not in the state that you desired at that point in the code and you have no control over it so you end up creating some kind of event to listen and then you do your logic, and it create eve more global events spread in the code and you and up super decoupled and clean instances but a real spaghetti of events connecting those things. But there are some cases that I am not sure how to not use a Singleton. For Example, an AudioPlayer in the game seems like a good case for a Singleton for me (but I am open to other ideas) and also Login that can be used in different scenes of one App, specially a mobile Game, it feels like a Singleton for the Login could work but I am not sure. Single Entry Point and Dependency Injection is helping me a lot to avoid Singleton and Global things in the code. All that said, I use Singletons with no resctrictions in small projects that I absolutely know it is not going to scale.
@Johan-rm6ec24 күн бұрын
We learn every day🎉
@Mystixor3 ай бұрын
Here to let you know I'd love to hear some thoughts on asserts
@istvanszennai5209Ай бұрын
of course this would be the desired pattern in a newly developed application. However when you have to work with an already existing 10+ million lines of code mess then singletons bcome pretty handy. To tell an actual example: imagine that you would like to pass data between "far away" modules and it also has to be accessible from the "spine code". One option would be going the long way and modify all those 20+ classes that the data goes through... Or I can just create/use an existing singleton class to bypass all that otherwise unrelated stuff. I chose the latter in a way that it's clear from the code that it is bypassing things (added a BRIDGE_ prefix to these methods).
@berzurkfury3 ай бұрын
The 2 place I use this type of singleton is the application composition root/dependency injection and globalized message resources. Otherwise everything else if needing a singleton behavior, my DI knows which ones will be served as a singleton under an interface
@Omnifarious03 ай бұрын
27:28 - It's only sort of thread safe. Only in that if you can make sure you only call Init from one thread, then it's thread safe. And even then, not really. Because getInstance() from any other thread might still return nullptr even if You know for sure that some other thread has called Init and Init has finished running. And that can be the source of some extremely maddening bugs. In order to make it thread safe, the instance pointer as to be atomic or it has to be protected by a mutex. Also, the init/shutdown thing is error prone. You should make RAII do that. I don't like your global variable, and I would do it rather differently. I can understand your concern about lifetime control, and it is an important concern. And so your objection to the Meyers Singleton makes a lot of sense. But I would replace it with something a lot different than what you replaced it with.
@dbattleaxeАй бұрын
The initialization of static local variables is thread safe as of C++11. The standard states: "If multiple threads attempt to initialize the same static local variable concurrently, the initialization occurs exactly once (similar behavior can be obtained for arbitrary functions with std::call_once). Note: usual implementations of this feature use variants of the double-checked locking pattern, which reduces runtime overhead for already-initialized local statics to a single non-atomic boolean comparison."
@Omnifarious0Ай бұрын
@@dbattleaxe - Exactly. That's one reason why the Meyer's Singleton is such a good idea so much of the time. He replaces it with something that doesn't have that guarantee.
@vedqiibyol2 ай бұрын
I usually use singleton as structs, I like to use singleton when I have a rather complex bunch of unique data, like for example when you're making an algorith, your input can be a singleton that you pass around just as a struct, or use singletons as an added namespace, structs make everything public by default, this way it really acts as a namespace
@fejimushАй бұрын
Another reason not to use Meyer’s double checked locking implementation is that it is not guaranteed to be thread safe prior to C++11. There are plenty of critical applications that depend on older tool chains. Also, (prior to C++11) Arm based cpus that support instruction reordering optimizations at compile, link, and runtime make the Meyer singleton problematic without a memory barrier.
@ilyashcherbakov41153 ай бұрын
DISCLAMER! I am not promoting Singletons. Now, that this is out of the way: It’s fine that you don’t like Singletons, but the alternative you suggested are not very good IMO. You didn’t suggest the tree architecture for the resource manager - you suggested making it global and to provide Init and Destroy functions. So you are providing an API for resource manager that is supposed to be easy to use. Or rather is should be hard to use wrong. But I find that as I client of such API I would start asking questions on how to use it properly. For instance, what happens if I call Init twice? Will it invalidate all the resources that I’ve loaded thus far or do nothing? (I’m not even sure what is the correct thing to do when I call Init twice) Do I need additional API for IsInitialized? Would I need to call IsInitialized every time I need to use the Resources Manager? What happens if I call Destroy twice? I think you see the point. As a “client” of that API I really shouldn’t worry about those things. This adds a lot of manual management to the client, that needs to control when to call Init and Destroy. And since it’s global, technically any one can use this API. P.S. I think the part about lazy loading is irrelevant, since it’s an implementation detail, and can be also done regardless of class lifetime, be it global or singleton. I know you just gave an example of Mayers Singleton being lazy initialized ( hence another name is Lazy Singleton ), but it doesn’t mean that resource need to be lazy loaded as well. Sorry for the long rant. Here’s a potato(. ‘)
@Internetzspacezshipz3 ай бұрын
I feel like a lot of times in programming we end up with issues like these, where by attempting to avoid a certain “easy but lazy” style of code, we end up with a “complex and confusing” style of code instead. All I’m gonna say is that everything is a lot easier when you have all the source code for everything you’re using, so if you ever have any deep implementation questions like “what if I call Init() twice?”, you can go answer them yourself by looking at the code. Maybe I am a bit biased, since this is how Unreal Engine is set up… but when I go to work inside Unity or anywhere else the source isn’t always immediately available, I find it to be frustrating to try and use an API that inexplicably doesn’t work how you want it to… then you go and google online and find “oh yeah this doesn’t work”, or “oh you need to do it this way” etc etc, when I could have just stepped into the functions I’m calling to figure out the problem. But really that’s just my preference and experience; seeing all the source of the API you’re using makes life way easier, whether the API is complex and confusing or easy but lazy.
@ilyashcherbakov41153 ай бұрын
@@Internetzspacezshipz you would think that having all the source code is good enough, but when you have a 10M loc codebase - you don’t really want to start debugging it. But the main point is that the API should have a well documented and expected behaviour, that is hard to misuse. If you want to tinker with it an debug it - feel free to do so, but it shouldn’t be a requirement. I don’t think that Init() and Destroy() are a very C++ y way of designing an API. It’s more of a C style when you know you need to release the memory you’ve allocated or close a file handle that you’ve opened. We have RAII for this ( speaking of APIs - horrible name ) Ever seen a std::vector call to empty() that ignores the return value because the developer clearly intended to make it empty, instead of calling clear()?
@Internetzspacezshipz3 ай бұрын
@@ilyashcherbakov4115 Oh yeah, std::vector is definitely bottom tier as far as clear naming of functions goes. I’ve 100% made that exact mistake myself. This is where variable and function naming is critical… IsEmpty(), or how about just not having this function exist? Lol. .size()
@almightysapling3 ай бұрын
@@ilyashcherbakov4115oh God vector.empty()... I try to follow (but often fail) that all "checking" functions take the form of a question, in this case it would be isEmpty(). Not groundbreaking or original, but verbs should verb.
@abcabc-ur3bf3 ай бұрын
Lean Inversion of Control (IoC) and Dependency Injection. This type of problem has been solved many many years ago...
@blarghblargh2 ай бұрын
It was solved before that, too, by structured programming. Objects and highly flexible composition (especially at runtime) are both massively oversold.
@RedSilencer3 ай бұрын
new phrase unlocked 15:52 more c plus plussy
@nativeme2143Ай бұрын
Hmm, i like to use these in embedded environment ie. where i have "Servo motor" in my device, and i am sure there will be no more other servo motors, usually i am maing key elements like that singleton. Other situation is like mentioned some kind of "Controller", "Manager", "Provider" classes that usually group other entities in system and they are at the top of the hierarchy.
@TOAOGG3 ай бұрын
Singletons are often used instead of dependency inversion. Real pain to test that
@brokensytheАй бұрын
The singleton is definitely not an antipattern. An obvious example of when you would want to use this pattern is creating a connection pool. Generally speaking you probably don't want multiple connection pools but also don't want to initialize the pool until you really need it.
@yrucoding3 ай бұрын
alot of time when your process is terminating, you might not really want to call destructor on everything…. Consider your process use lots of memory, and your OS has to do some paging to disk. Now when ur process is terminating, do you really want to call every destructor, to just say free up my memory. (at the cost of paging memory back from disk). The OS already does book keeping about the memory used for a process. So you dont have to individually cleanup every object, let the OS do it for you. Unless you have some external resource cleanup that OS processor terminating wont cleanup for you.
@madpuppet666Ай бұрын
I use a module singleton system with each module having a priority and the game engine guarantees all modules init in priority order, and then shutdown in the reverse order. The initlialization order is just too critical, plus a module system gives a formal framework that all singletons will obey. It also means you have a list of all modules that can be accessed and queried which can be useful for various debugging systems. I would never use the meyers pattern for the same reasons discussed in the video. I am a big fan of singletons . There's nothing that complicates your code more than having to pass around accessors to system managers and find at some leaf in your 'tree' you simply can't do some things without refactoring the entire tree to give you access to a module. You never have to 'shutdown' a singleton. Thats the whole point of them. You never have to shutdown your inputmanager. Its viable for large programs. Unreal Engine is built on a bunch of singletons. Not saying its perfectly designed, but saying singletons only work on small programs is absurd. You still need to design your singleton acess intelligently.
@SownJevan3 ай бұрын
The lighting on this video is very good.
@WoodySTP3 ай бұрын
One really big reason why i hate singletons from a non game dev is that it's a pain to test. You hold state that is hard to manipulate once you created an instance.
@oliver_twistor2 ай бұрын
Good discussion! As an unwashed Java developer, I can't really comment on the C++ specific stuff, but I agree that singletons should generally be avoided (or at least used with caution) regardless of language used.
@kamilkopryk85723 ай бұрын
Another problem with Scott Meyers singletons can happen when one singleton is being destroyed and tries to use another singleton that was created in a different translation unit. If that other singleton has already been destroyed (the order of destruction for static objects across different translation units is undefined), it can cause the application to crash
@codemetas12843 ай бұрын
That is true for static variables in some namespace. It is *not* true for function scope statics(as used in Meyers singletons), where the order is well defined. Those are destroyed in reverse order of their construction. See basic.start.term in the current c++ standard: "If the completion of the constructor or dynamic initialization of an object with static storage duration strongly happens before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first." (I seem to recall that is true since C++11, but am slightly unsure about that) Even with a defined order so, I agree that it might still cause problems and it is - in most cases - likely best to avoid such slightly hidden dependencies.
@tolkienfan19723 ай бұрын
@@codemetas1284op is correct. There is no guarantee an object that refers to the singleton was constructed before or after it, or even during. You have to ensure the constirction order explicitly.
@nicholaskomsa17777 күн бұрын
The first time you call that singleton, it is constructed right there. Therefore, to use them with a specific setup order, you should simply call the singleton collection in your setup routine. I make use of singletons though not that kind which I also do not like a lot. If you have to use singletons, the use-case is almost always with a collection of interdependent stuff, so you would always need a specific static setup routine.
@Adam_Lyskawa3 ай бұрын
I like how you explained the "costs" and down sides of singletons. I don't dislike them, I'm neutral towards them ;) Maybe because I work alone and I don't have to deal with someone else's messy code too often. In some contexts a singleton is just a tool like everything else. I use them mostly in one case: when I want zero or one instance, and I want to be initialized on first use, if it is used at all. So - obviously if an external factor (like a user input) decides whether my object would be used at all. So, a ResourceManager class doesn't fit that scenario, it's something that is a dependency for basically everything else. Of course the singleton can be replaced with a global variable, but when we sorted out our initialization / cleanup issues - the difference is purely cosmetic so it's a matter of what is more pleasing to your eyes ;)
@etherweb6796Ай бұрын
I dunno - pretty sure that no matter what "mixins", and multiple inheritance are anti patterns - the downsides outweigh any benefit
@Hersatz2 ай бұрын
Like for any type of global-ish access pattern, singleton is good to use in specific contexts. Same applies for Services and Signals and Dependency injection, and whatever else architectures. The way I see it, the issue is that they look like an easy solution to a lot of complex problems. Hence why, I suppose, we see so many "anti-pattern" usage stemming from it.
@Omnifarious03 ай бұрын
13:55 - There is another reason Singleton is bad. Singleton is, effectively, a global variable. And the problem with a global variable is that you have no idea who touches it or when. This can make the behavior of your program much harder to reason about because it depends on non-local effects. It also makes testing really hard. If you want to mock out part of your system so you can test other parts with a 'fake' system, a Singleton makes that nearly impossible.
@mike2000173 ай бұрын
I'm definitely on the side of avoiding global mutable state at all costs. One crucial aspect that was not mentioned in the "tree" explanation is testability and scalability. This may be less common in computer games, but in general, if any of your components depend on singletons (or any other form of global mutable state) it becomes really frustrating and hard to write good test suites for the components (because you have to always setup and correctly reset the required global state) and it becomes hard to scale it in the sense that you can't instantiate multiple standalone "trees". I don't like this init/shutdown solution. In my experience, if you have some quasi-global state that you need to create and destroy at specific points, you should not make it a singleton, just pass it down to whatever objects need it and properly tie together the life-times. And in the rare cases where it might be appropriate to have a singleton, it's very important that all uses of that singleton are idempotent, that is, it is irrelevant (as far as what matters to you), at the point of use, what the state of the singleton is. In the case of resource loading, if it matters whether or not a resource has already been loaded (because of dropping frames, as explained), then a "lazy" singleton is not idempotent. The only kind of singleton that still allows for testable and scalable code is an idempotent singleton. I have generally reached the conclusion, after decades of experience, that the design space that exists between the idempotent/lazy singleton and the "fly-weight" object (i.e., the quasi-global object passed down to the rest of the "tree") is vanishingly small.
@FJhunman3 ай бұрын
I am not particularly a fan of singletons, but they do have their advantages. Sadly some of my colleagues (who mostly work with Unity and C#) seem VERY fond of them and use them everywhere they can, making testing somewhat of a nightmare in our project in the past. In your (final) example I don't like how you have to explicitly call a shutdown function, I would probably write a RAII-style initter and deinitter (with [nodiscard], since I am elevating that warning into an error with compiler flags), but that's just me.
@DLCSpiderАй бұрын
You can tell him that Eric Lippert, a former member of the C# commitee, once said that singletons are only okay when their removal doesn't change the output of your program (e.g. loggers, grabage collectors).
@eduameli3 ай бұрын
do a video about the asserts pls
@Southpaw173 ай бұрын
Interesting. My company is currently in the process of completely moving _away_ from the "Two-Phase Init" paradigm that you're advocating for here. While my takeaway from this video is still "Avoid Singletons as a rule, but if you need to use one, here is the least bad way," I would have liked to see you go into more detail about the issues that arise from adopting this pattern. Specifically, the initialization hell that occurs from manually init''ing several Singletons at the top of your application stack or what happens when one Singleton depends upon another (and thus imposes an invisible ordering on the series of Init calls)
@almightysapling3 ай бұрын
I don't think "avoided" is the right word, I just think the use case for them is extremely niche and the typical things people think would make a good reason to have a Singleton are the wrong reasons. I mean, "My program should only have one of these" seems like a damn good reason, but it almost always isn't.
@Southpaw173 ай бұрын
@@almightysapling "Prefer" and "Avoid" are pretty common terms in coding standards docs, which is why I chose that particular word here. Typically "Avoid" doesn't mean something is banned, just discouraged, and its use may require additional review or approval by your leads
@daydreamer06063 ай бұрын
Thanks
@mr.anderson50773 ай бұрын
Get well soon Sensei
@Navhkrin3 ай бұрын
I very much like Unreal's subsystem system for this; It works very much like singleton but its lifetime is well maintained based on what kind of subsystem it is. For example, a world subsystem will be created and will die with the world. And you can access this subsystem from a pointer to the world. This pattern isn't too difficult to implement on a custom engine either, a little bit more work than singletons but you get benefits of well maintained lifetimes. My only caveat with it is that we can't do replication from these, because replication is strongly tied to actors in Unreal which imo is not the best idea.
@kidmosey3 ай бұрын
Nearly every singleton I've designed eventually needed multiple instances.
@pharoah3272 ай бұрын
Can't say I agree. I've never had this issue and I've worked on several projects that took years to create (with changing requirements). But to each his own.
@kidmosey2 ай бұрын
@@pharoah327 For starters, it forces you to do your unit tests synchronously.
@pharoah3272 ай бұрын
@@kidmosey every framework I've used for unit testing has been synchronous by default. So I don't have experience with that but that is interesting. I'll have to look into how to do unit tests in an async manner. Even still, I imagine not all unit tests will need to touch the Singleton, therefore many can still be done in parallel. So that alone definitely doesn't make singletons bad. Every design decision will have pros and cons. Every single one. So just mentioning a con (or even several cons) doesn't immediately invalidate a technique for all purposes.
@megadodd3 ай бұрын
One practical reason to avoid singletons, especially in more complex systems, is related to how static memory is handled in real-world applications. When different executables or DLLs use the same static library, each creates its own copy of the static data. This means that even though they use the same library, they do not share the same memory instances for global or static variables. As a result, if an executable loads a DLL, any singletons defined in the static library will be unique to each module. This can lead to situations where the executable and the DLL, which are supposed to work on shared data, are actually working with separate copies, causing inconsistencies and unexpected behavior.
@death-sign3 ай бұрын
Correct me if I'm wrong. But I believe using an anonymous namespace or "unnamed" namespace would provide the same functionality but keeping an instance shared.
@alphenex89743 ай бұрын
I am pretty sure you can easily prevent it using specific keywords so it should not be a problem
@piootrk3 ай бұрын
This was my first thought when I saw 'static' version of singleton code. It is a nightmare in Windows program.
@anon_y_mousse2 ай бұрын
One minor problem with your weapon example is that the game should load it when it loads the map. Every game I've seen does it like this and everything contained in a given map is listed in the map file. Animations might be different, depending on the game, but ideally, they should all be preloaded too. This isn't intended as a criticism, because I agree with you regarding singletons, but the point could use some strengthening there with a better example. Not that you or anyone else will see this.
@matthewread90012 ай бұрын
I used singleton for my Random class. That’s it.
@Wo1fieАй бұрын
I'm mostly self taught with C and C++ but why would we not call Init during constructor and Shutdown during destructor, or even just place the code within those methods inside of the constructor and destructors?
@lambdacalculus33853 ай бұрын
yeah, global instances are sometimes useful but it's not thread safe; i'd just stick singleton pattern actually with using smart pointers; it guarantees thread safety*. * seems access to resource is not thread safe but accessing to object is safe. solution is either using `boost::atomic_shared_ptr` or new template for `std::atomic`, or experimental `std::atomic_shared_ptr`, same thing. if every operation done using `std::atomic`, then there is no problem. comes with c++20! thanks for correction by the way.
@zanagi3 ай бұрын
So this is just thread specific issue? Any way to learn more about this?
@Firestar-rm8df3 ай бұрын
It's important to note that only the control block is thread safe due to the atomic ref count. The actual access to the managed object is in no way thread safe by default, so make sure you still account for that in your thread safety design. This is a common mistake I still see made by professionals on the daily.
@_lod3 ай бұрын
global smart pointer 🔥🔥🔥
@AlexandreA-w5c3 ай бұрын
Construction of the singleton is thread safe, not the usage though!
@lambdacalculus33853 ай бұрын
@@calvinsomething5348it mostly about TheCherno's implementation is directly creating a static instance. with use of a function, at least you can check for any manual deallocation.
@porky11182 ай бұрын
1:00 I already agree. Either just pass things around or use a global variable.
@skale77383 ай бұрын
you are 100% right
@mrpocock16 күн бұрын
Singletons tend to be a code workaround for not having resource and configuration baked in.
@fredhair3 ай бұрын
I'm just imagining this on the fly and haven't worked with C++ really for years now but perhaps you could have a Meyer's singleton that holds some weak pointers (e.g. to registered services with service locator pattern); the static destructor checks that services have shutdown correctly i.e. pointers == nullptr? In theory since the static memory is destructed last could it be useful for checking that other systems are shutting down and freeing memory correctly? Could you use it in some sort of new / delete overload to do basic logging where something has failed to free up? Fairly sure I've seen the Meyer's singleton used to create a runtime resolver for DI in C++. Regarding DI; dependency injection can't always just be used to replace singletons, occasionally you may not be in charge of specifying object dependencies / constructors, perhaps call backs or situations where there is requirement that the client implements a certain forward declared function - you have to work with limited scope of dependencies. Sometimes you may want a lazy loaded Meyer's singleton for this situation? Probably pretty niche uses but always try use the right tool for the job; which at times will be Meyer's singleton.
@electricspeedkiller89503 ай бұрын
I use them often for a class that holds data for resources and such.
@thebatchicle34293 ай бұрын
Just use a global struct
@wb39043 ай бұрын
You've been indoctrinated to think that everything just be a class. A singleton is basically a single global structure.
@PopescuAlexandruCristian3 ай бұрын
I am 100% with you on this, but if you think like this why do you use shared pointers, how can you accept does
@TruthAndLoyaltyАй бұрын
Just here to say, there is no such thing as an antipattern. All named patterns have pros and cons and are more appropriate in different scenarios. It's pretty rare for a pattern to be universally bad or always reign supreme in every scenario to an extent that alternatives should be called antipatterns. If anything, we should treat them like code smells. As in "this pattern often has problematic usage, there may be a better way to accomplish this" and investigate if that's the case.
@Beatsbasteln3 ай бұрын
I'm writing VST plugins in C++ and this whole talk about singletons makes me think of the Utils class in my code base. It is created in the top most component of the graphical interface and then passed on to all child components as a reference. that's like having a pseudo singleton. i couldn't just make it static because then different instances of the same plugin would share the state of its values and often that is not a very good idea. I'm kinda annoyed by the references tbh, because you keep having to repeat yourself in your code about sharing the Utils reference with another class and another class and another class, you know? i wish there was like a soft singleton that I can use in such cases. the initialisation problem might be a little annoying, but ultimatively it would speed up the workflow of adding new code a lot I think. But maybe I'm wrong and I should be happy that I never had to deal with such initialisation bugs yet. maybe they are even worse than having to write the same stuff over and over again.
@godotc3 ай бұрын
The second mthod could not works in a dynamical shared library.