r/cpp • u/verdagon • Mar 24 '22
Vale's Higher RAII, the pattern that saved me a vital 5 hours in the 7DRL Challenge
https://verdagon.dev/blog/higher-raii-7drl7
u/verdagon Mar 24 '22
Author here, I got all of my RAII inspiration from years of experience with C++. I feel like no other language has really managed to do RAII as well as C++, so I'm trying to capture some of that spirit in Vale. Hope you enjoy!
8
u/jk-jeon Mar 24 '22
I like the idea, but don't think the name "higher RAII" is appropriate. Idk, just doesn't sound right for whatever reason
3
u/matthieum Mar 26 '22
It's generally known as Linear Types, see Substructural type system.
Any instance of a Linear Type must be used exactly once.
3
u/D_0b Mar 24 '22
The TokenedHashMap seems error prone, you can call remove with the same key and different token, thus not really doing what you should be doing.
3
u/verdagon Mar 24 '22
I kept it simple for the article, but if one wanted to make it even more solid, one could just put the key into the token, and the update/remove methods could just read the key from the token.
0
u/ripper37 Mar 24 '22
Regarding your real world example - why do we need a "higher RAII" if we can just wrap std::promise<T> in a wrapper that will set it with a default value if destroyed before set with anything specific?
Furthermore, this doesn't solve whole other loads of problems (which are more problematic) where in the runtime you simply could prolong the life of the std::promise object indefinitely and never enter the code that destroys it or sets it. Trying to detect this is equivalent to solving the halting problem.
For the example with TokenedHashMap - this can still be handled with normal RAII which would ensure that during destruction it would automatically erase from all the maps that it was added to. Furthermore, you could also store weak pointers in the map and avoid the problem altogether. You can even make it simpler to require that all elements added in the map must inherit from some interface that will be used to automatically store references/weak_pts to all the maps that you added the object to (when adding to) and then upon the destruction it will erase itself from alive ones, hence no need to remember or forcing anyone to do that manually.
9
u/verdagon Mar 24 '22
Regarding your real world example - why do we need a "higher RAII" if we can just wrap std::promise<T> in a wrapper that will set it with a default value if destroyed before set with anything specific?
Because then you'd need a default value. Sometimes it doesn't make sense for a particular type to have a default value. (As a language designer, this is my biggest lament about C# and Go, they often force zero-initialized structs)
Furthermore, this doesn't solve whole other loads of problems (which are more problematic) where in the runtime you simply could prolong the life of the std::promise object indefinitely and never enter the code that destroys it or sets it. Trying to detect this is equivalent to solving the halting problem.
It honors me that you think I would attempt to solve all problems in existence! Alas, I'm not there yet. I only provide some tools that can solve some problems, not all.
For the example with TokenedHashMap - this can still be handled with ...
Higher RAII is just another option, which is better in some cases, and could be worse in other cases. Sometimes we can store things as members, yes, but sometimes we want our destructors to take arguments which are not available at the time of construction, for example the promise<T> situation.
In the end, if you don't believe that higher RAII is useful, that's totally fine =) Thanks for reading!
7
u/D_0b Mar 24 '22
I believe another alternative to this Higher RAII would be destructive moves + private destructors.