Retiring the Singleton Pattern - Peter Muldoon - Meeting C++ 2019 Slides: meetingcpp.com... Survey: survey.meeting...
Пікірлер: 19
@mikaelwallin34683 жыл бұрын
One of the best talks I have seen. I have contemplated large refactoring of Singleton riddled code. To make it testable. The singletons might be there because it makes sense. There only needs to be one. There should be a global cache of something etc. This talk gives way forward. It gives you hope that it is possible and within a reasonable effort, as Peter also describes.
@babgab4 жыл бұрын
At about 19:00, the solution given to not wanting vtables is using std::function. That's actually *worse* if you care about vtables, because if you care about vtables you probably care about the performance implications and debuggability of virtual dispatch/function pointers in general, and now to make matters worse you have a potential heap allocation if you give the function a capturing callable that is larger than the small block optimization of std::function for your particular compiler! function_ref is better, but doesn't actually address the concerns that some people have regarding virtual.
@petermuldoon81114 жыл бұрын
The use of the std::function is shown using reference semantics - via std::ref - so the original callable is not copied so no heap allocation. True, there is a pointer indirection for the function call
@babgab4 жыл бұрын
@@petermuldoon8111 I suspect most C++ devs don't even know that std::ref exists. :/
@Alexander-yk4 жыл бұрын
I believe it's mostly about keep classes as simple as possible. All this derivations+virtuals mechanics is a bit hairy and complicates reasoning about code. std::function gives you polymorphic behavior only there where you actually need it, without intervention into class interfaces and behavior.
@TekmanRO4 жыл бұрын
All this for slightly easier testability? What if you have thousands of callers of sendData? Are all of them (and/or their stack parents) expected to create instances of the wrappers? What if the DB singleton has a pool of connections (for perf / throttling reasons)? Do people actually use this pattern in real-life? I highly doubt it, at most they'll end up just exporting getDefaultComm and pass that in to get rid of the deprecation warning.
@petermuldoon81114 жыл бұрын
The techniques actually moves the code from being completely untestable to fully testable - nothing slightly As I stated in the talk, it is being used in multiple areas by various groups
@patlefofort4 жыл бұрын
There isn't much of "this", it's not harder to write. I have no idea what do you mean by creating instance of the wrappers, you are not creating instances of them, they are statically initialized once and you use a reference to them. It makes no difference in your program, it only makes them easier to test and maybe a bit cleaner. The wrappers are only for old code that can't be replaced, you don't need it in new code.
@pavelperina76294 жыл бұрын
Problem of singletons is that they are impossible to replace. If you have logger singleton, you can't change it. All code is referring to the same implementation. If you want to redirect logging to TCP connection instead of file, you can't. Well ... you have to change existing class (if you have source code) to do both things. So it's better to depend on some interface to be able to switch between implementation. Other example might be deserializer. You may want to pass either XmlNode or something with same interface that is created in memory. So you pass IXmlNode instead and implement class that provides "fake" hierarchy of subnodes and attributes to test for example previous versions of XML. Or you may want to replace licensing service by something that makes all licences valid for test code. It's not only about singletons, it's a good practice how to implement service classes inside bigger code in general.
@martinhrcak27964 жыл бұрын
I agree with your implementation: template static T &getInstance() { static T singleton; return singleton; } But, how unique is a static variable in your project (eg. in a web server)? One compiler switch could cause loading a library twice for two "Singleton" instances. You do not need to recognize it in large projects.
@petermuldoon81113 жыл бұрын
From Scott Meyers "This approach is founded on C++'s guarantee that local static objects are initialized when the object's definition is first encountered during a call to that function." ... "As a bonus, if you never call a function emulating a non-local static object, you never incur the cost of constructing and destructing the object." What C++ compliant compiler switch would cause it to produce 2 different instances when calling getInstance() given T is non - trivial ?
@mapron13 жыл бұрын
@@petermuldoon8111 Include and call this code in two different DLLs. boom. Standard talks only about TU. It's does not care about code existense across different shared libraries or something, there is no such thing. So Martin is totally right here, you should not consider template singletons.
@qqi239z1234 жыл бұрын
It seems that it would not work particularly well. Say I have 1000s functions in 10 layers of 25 years old code, the lowest 3 layers calls original singleton. If we want to pass wrapper around we have to add it to each and every function and if we have three wrappers we have to pass three wrappers around to each and every function too. Any attempt to do initialization on the first use will have problems with threads and there is not much difference with the original singleton. It seems that correct way to do it: (a) define an abstract singleton and derive the real singleton clients will use abstract interface, (b) auto link real singletons into a static initialization list, (c) initialize singletons on the list early in the program lifetime before starting the second thread, (d) for testing derive another singleton from the same base, initialize it in setup function and delete it in tear-down function and use it while in test case -- this is it.
@qsvui4 жыл бұрын
this doesn't get rid of singletons at all nor does it move towards any attempt at removing them
@petermuldoon57514 жыл бұрын
The requirements for a solution are that the callers of the function do not have to change their code. This is accomplished by the dispatch function with the original signature that calls the new testable function. So code 10 layers down is not affected and does not have to pass the wrapper around, its unaffected. The talk also shows thread safe solutions so not sure why you believe thats an issue
@qqi239z1234 жыл бұрын
qsvui - singletons are part of life that has to be expressed one way or another the quetsion is how to do it
@qqi239z1234 жыл бұрын
@@petermuldoon5751 . Sorry a function in lower 3 layers does 'Singleton::bind().singleonFunc(x), now we have to modify each and every one of them to pass reference to Singleton. It is also true for newly written code. If we are trying to come with better bind() that will be still initialized on the first use, it will be not that different from the original one.
@qqi239z123Ай бұрын
(1)We have to initialize before the first use, (2) we are talking about cases where all functions already call bind.