4 ms·
Normally I don't comment on typos, but RIAA instead of RAII is a pretty funny one.
by hn_go_brrrrr 4y ago
Normally I don't comment on typos, but RIAA instead of RAII is a pretty funny one.
- cyber_kinetist 4y agoWhich illuminates the real problem of RAII (that instead all Resource Initialization is Acquisition.) You often need to explicitly control the order and timing of the initialization of various resources. But RAII often gives away control of that and either becomes a source of bugs or makes your code more complicated when working around it. Separate init() methods are not the devil, there’s a reason for using them… (if you’ve ever tried writing and failing to create an object-oriented wrapper of a graphics API like OpenGL you’ll know what I mean.) RAII is good enough for automatically managing some simple parts of your data structures (some auto-freed vectors and strings are convenient), but it becomes haywire when you’re using it to manage more complex data structures, or something other than conventional memory (Like GPU buffers, handles from external systems like the OS, etc…) Single ownership of data can be too much of an ideal in reality, and shared_ptr semantics can be undesirable for expressing shared ownership in many scenarios.
- hn_go_brrrrr 4y agoI don't like Init methods because they permit the creation of invalid objects. Instead, either throw an exception from the ctor or use a factory function that can either return a T or an error. I'm curious what weaknesses of shared_ptr you've found. With the addition of weak_ptr and enable_shared_from_this, they're a pretty versatile tool.
- cyber_kinetist 4y agoThrowing exceptions might not be acceptable in your use-case, with many codebases disabling it for concerns of performance or executable bloat. And factory functions cannot really help the initialization problem anyway, since your field is still in an uninitialized state before calling the factory function. And also, returning std::optional or std::expected can sometimes lead to slower code (copy constructor being called) because of NRVO not working when the compiler “feels like it”. So an init() method/function that returns an error code is the best you can get. I think the best way to really solve the invalid object problem in C++, is to always put sensible default values inside your struct/class definitions, so your code will at least fail without UB (in addition to using asserts liberally to check for index overflow problems, especially in your own containers even in. Release mode.) Zero-initialization is good for most cases, but sometimes you might need to default-initialize your indices to -1. weak_ptr is really slow, because of the lock instruction generated for each lock(), as well as the added indirection cost. Once you use them all over the place to fix reference cycles, the overhead of using these becomes hard to ignore. Generational indices/references remove all the ref-counting and gives programmers the semantics of weak_ptr without the performance cost (see https://verdagon.dev/blog/generational-references https://verdagon.dev/blog/generational-references, the details are mostly language-agnostic).