Testable debug run-time lifetime tracking#3097
Conversation
45c1d6f to
96e88d4
Compare
|
I find this an interesting idea, my only concern is that powerusers aren't the ones with lifetime woes, and noobs may not reap the benefits because lifetimes are such a new concept that they won't think to reach for a debugging tool oriented around it. And also even with nice friendly documentation it will still be underutilised by the audience most likely to need it. I also worry the verbose error message may be failing to get across the kind of issue the user is facing. Terms like dependants and dependencies are alien to newbies. I myself find the example error message a bit tricky to follow and I'm an experienced developer and one who has used SFML for many years and understands the pitfalls with lifetimes between sprites & textures. Feels like maybe missing the mark by being more complex than is necessary for the audience of people most likely to require this. I can tell you all the powerusers I speak to don't struggle with texture-sprite lifetime woes, but I regularly see newbies that do. I'd start, at a minimum, with reconsidering the language in the error messages. Maybe even defining what audience you intend to utilise such a feature. |
|
@Bambo-Borris: Thanks for the feedback! You raised three main points:
Here are my thoughts:
|
c669ae3 to
71087cf
Compare
Pull Request Test Coverage Report for Build 9517208803Details
💛 - Coveralls |
|
New error message example:
Note that the error message generation dynamically changes depending on the object types, it's not hardcoded for sprites and textures specifically. @Bambo-Borris: What do you think? |
badbc4e to
630cc3f
Compare
Pull Request Test Coverage Report for Build 9529431118Details
💛 - Coveralls |
| return sf::Text(localFont); | ||
| }; | ||
|
|
||
| const sf::priv::LifetimeDependee::TestingModeGuard guard; |
There was a problem hiding this comment.
The tests currently make no mention of the priv:: namespace and I'd like to keep it that way. There are definitely some sf::priv:: things I wish we could test but I'm rather dogmatic about the unit tests only using the public API and am not comfortable testing implementation details like this which users are not intended to use or even know about.
How much code could be removed from this PR if TestingModeGuard was removed?
This code will still be tested although instead of explicitly testing for failure we will be implicitly testing for the lack of failure. I know that's not the same but by simply using types like sf::Sprite we will still be testing that your lifetime tracking has no false positives. We cannot test for true negatives quite as well though but I think I'm okay with that. It's the same as all of our asserts. We can't test for assertion failure but we can test that assertions don't have false positives. In practice this seems to work quite well.
| //////////////////////////////////////////////////////////// | ||
| // Lifetime tracking | ||
| //////////////////////////////////////////////////////////// | ||
| SFML_DEFINE_LIFETIME_DEPENDANT(SoundBuffer); |
There was a problem hiding this comment.
sf::Sound and sf::SoundBuffer already perform their own internal lifetime checks where sound buffers are de-registered with the Sounds that use them when the SoundBuffer is destroyed. Do these new lifetime checks means we can remove all that machinery from Sound and SoundBuffer?
|
@ChrisThrasher @eXpl0it3r @Bambo-Borris example of lifetime issue that this PR would have caught: https://discord.com/channels/175298431294636032/175298431294636032/1262381178523222086 |
Add a new
SFML/System/LifetimeTracking.hppheader that provides testable run-time lifetime tracking.The tracking is enabled only when
SFML_ENABLE_LIFETIME_TRACKINGis defined. That macro is controlled by a CMake option of the same name, enabled by default when building in debug mode.The tracking is also enabled for our CI test suite, regardless of the build mode.
The tracking catches common lifetime mistakes between dependee types (e.g.
Texture) and dependant types (e.g.Sprite) at run-time, providing the user with a readable error message:Note that the error message generation dynamically changes depending on the object types, it's not hardcoded for sprites and textures specifically.
Adding lifetime tracking to SFML types is trivial and requires minimum changes, as shown by the diff. It consists in a few simple steps:
In the dependee type (e.g.
Texture):Sprite);privatesection:SFML_DEFINE_LIFETIME_DEPENDEE(Texture, Sprite);In the dependant type (e.g.
Sprite):privatesection:SFML_DEFINE_LIFETIME_DEPENDANT(Texture);SFML_UPDATE_LIFETIME_DEPENDANT(Texture, Sprite, m_texture);That's it!
For testing, use the provided
sf::priv::LifetimeDependee::TestingModeGuard:Drawbacks:
Some valid constructs might need to be slightly rearranged to please the lifetime tracker. E.g.:
However, this is more of a design problem with
sf::Spriteitself than with the lifetime tracker, assf::Spriteonly needs a reference to the texture during its draw call, but SFML artificially extends the relationship betweensf::Spriteandsf::Textureway beyond that. Seesf::Spritedoes not storesf::Texture*#3072 andsf::Spriteas temporary view over geometry + texture #3080 for an ample discussion on that.FAQ: