I asked you what's wrong with pool destructor function pointers. You gave a reason what's wrong and I refuted it. So no, I'm not saying that storing a function pointer is special, just that nothing's wrong with it. (And implying that since there's nothing wrong and it's probably the most straightforward thing to do, it's also probably the right thing).
HN user
jstimpfle
http://jstimpfle.de
Adding that with a principled approach, I don't even see much of an issue with doing manual creates and deletes in a object-graph type app, with many unstructured lifetimes. Sometimes that might just be required, and then the complexity is just there either way. Having to cleanups manually or not doesn't change anything about that. It's a bit more cumbersome to get everything right when doing it manually -- sure.
The problem is mostly people graduating from school thinking that somehow there is only stack and heap, and malloc/free is how you do heap. That view completely ignores that the essence of programming systems is mostly to understand the machine, and then doing conceptual and architectural work on a solution (and also on a problem). The act of writing actual code is then mostly just translating those concepts into the digital world verbatim.
Fair enough, this is an extension though and I suppose you could use it with manually constructed vtables as well?
Of course they are. The pointers to the vtable are part of the object. They aren't mutable fields as per the language, but for security concerns it doesn't matter what the language thinks. Being part of the object, the vtable pointer has to live in a writeable memory mapping (like stack / heap).
1. No, you're not writing code you don't have to. It's not different to implementing this as non-virtual methods, in fact I'd argue doing simple functions is more straightforward.
2. And the code being compiled is abstract & generic, it won't be instantiated for every type and bloat the executable or instruction cache.
3. Security concerns: With C++ virtual methods every object carries a mutable pointer too (to a vtable containing function pointers). What resource management side effects please?
To be fair, Odin _had_ a Wikipedia page and it has been removed because of... people with whatever interests. But I'm still agreeing with your general point. I use C/C++ because it seems to be the least friction option to me overall, and I don't want to deal with language but simply focus on my actual compute problem. (Given that, I spend an unreasonable amount of time in silly fights about language).
I've argued elsewhere some things that are wrong with RAII and C++ objects in general.
Here I would just like to mention that if you have to rely on "de-virtualization" passes, you're in a miserable situation architecturally. If you have code where the overhead of virtual function calls might be too much to pay, don't do virtual functions then. End of story.
To deconstruct a pool of objects, I don't see what should ever be wrong with a function pointer. The overhead of loading the function pointer will get divided by the number of objects being deconstructed. Care to explain what's the issue here?
Designing something good for me means going through at least 11 seriously bad approaches before half understanding the problem and having a half-working solution. Trying to use a notebook to aid a learning or research process at all has always meant wasting a lot of time "perfecting" those bad approaches. And ending up with lots of notes that document nothing but a painful struggle to make sense. Looking back through the notes that I did take, in notebooks that I have been gifted over the years, is almost unbearable. The approaches I tried before arriving at the right one were fundamentally wrong, there is no fixing them, and it's painfully obvious when looking back. Aside from the emotional problems I perceive there, it's obvious to me that there is no engineering value in keeping those notes at all.
The current best design always lives in my head, and by the time it exists, the solution and understanding is so clear and detailed that I can't be arsed to write it down.
I do think there is value in taking notes for others, though.
What works a little bit for me is having a lose stack of papers at work. The topmost paper serves to put a coffee mug on top, that will take the coffee stains so the table stays clean. From time to time I actually scribble something on a sheet, and since the sheets are all loose it's very easy to discard any sheet after it's served its (diminishingly small) purpose as a thinking aid.
Also one separate function being different than the operator is not a language level feature, it is how that one library data structure is made.
That's not so much the point: the point is that even when calling a regular method conveniently as ptr->methodname(), you are calling the method name not on a pointer expression but on a value expression. ptr->methodname() is effectively (*ptr).methodname().
Again you can make these bold statements but saying pointer chasing has no performance impact is what people say when they don't understand the cost of TLB misses and caches misses.
You are right to call this out but I did not mean to imply that it would not increase load on the cache at all. The most important optimization is keeping data structures simple, flat, and coherent. Making many micro optimizations can backfire because it reduces clarity and flexibility. I should not argue that vector metadata block should not be embedded as value in a struct, but move semantics is where it is getting silly, complicated, and we are losing control of program behaviour.
For example, with your cache concerns, don't forget to group data by access patterns and write patterns. Not doing so may hurt the cache more than an indirection. If you blindly embed everything by value religiously, you deserve neither flexibility nor correctness nor performance.
Yes you can, an operator is just a function, get it?
An operator is just a function but with different syntax. You can not do my example, that snippet does not do the intended job. Get it?
(unless you want to try to say that not defining your structs is a good idea).
It is a good idea when it's about architecture. It is right to embed small and in particular stateless structs directly, but otherwise adding one level of indirection is often absolutely the right call.
All I was arguing was that destructors are good
All I'm saying is that destructors, while they may seem convenient, are way overrated and the systemic issues caused by reliance on C++ classes and class features (including destructors) are still not well enough understood by the mainstream in 2026.
I was not asking, and certainly not for a toy example, are you STILL under the impression you need to explain beginner level C++ to me? You can btw remove the move() from the return statement in this example. It's normal to omit it as it is implicit and specifying it does not make a difference. Did I mention that move semantics introduce a ton of complexity and make it non obvious what is happening?
Keep an open mind, there's a reason people consider old C style archaic.
It is superficially a little bit archaic but it's not as bad or cumbersome as people make it, like baking a good bread from old grains in a modern oven. The point is if you focus on superficialities and micro optimize everything, you will make a mess and get what you deserve (i.e. modern C++).
The result from baking with simple ingredients is better tasting and healthier than a modern factory bread.
What people consider archaic or in particular insufficient is a function of their knowledge and experience. Check out the raddebugger code and the many excellent explanatory videos about the design of the codebase. You will learn a lot and you might change your mind about many of your mainstream positions.
> Move it, so simple, pointer invalidation.
It works for everyone else, I'm not sure why this is controversial.
Show the evidence. It does not. It introduces vast amounts of complexity. So much that they were thinking they had to extend the syntax. Great, from rule of 3 to 7, from 41 ways to blow your leg off to 118 ways.
I'm betting it means you can easily google LOTS of memory bugs stemming from this complexity. It "just works" for everyone else, until it does not, and then nobody understands what's wrong because they created a complexity monster.
And what do you think how many intended "moves" get silently converted into copies because some failure to properly forward rvalue references and similar?
Like bringing up PIMPL for no reason?
What disorder are you suffering from? Please check who was bringing up PIMPL. Hint: it was the person who does not know what they're talking about.
Which one is the argument against destructors?
oh. my. god. THE COMPLEXITY. The systemic issues.
Copying and moving are solutions to problems you already demonstrated
Which? I presented my solution to the move problem, it was: just don't move. And for copying: it's very context dependent what "copy" should even mean. There is no point in general in making a single function.
This whole semantics infrastructure is all just there to allow generic template code, which results mostly in: very bad, bloated, and slow code that is full of forgotten edge cases. For example, to take a very popular example, std::sort. you can sort a vector of vectors, it would then use move semantics to swap elements. Fine: I would just sort pointers instead or come up with an ad-hoc solution that is way faster anyway. (other than the fact that general purpose sorting of longer chunks is quite rare in systems programming).
This is double indirection. It is more allocations, more memory frees, more pointer dereferencing (all of which are slow) and not necessary.
My friend, you don't understand the first thing about how computers perform their work, let alone how you have to balance these concerns with software architectural concerns. You could convert all the std::vectors in your programs and access them using pointers, in most cases you probably wouldn't notice the tiniest bit of a performance difference.
But the reason why C++ likes to embed "objects" as values (even though that's almost invariably bad from an architectural standpoint) is almost entirely unrelated to performance (with the caveat that embedding might save you a couple heap allocs and frees, but for real stateful objects & data structures, that isn't a good reason to embed). Of course you shouldn't add any indirection for objects that aren't really objects but merely a scope-exit, like lock_guard for example.
The primary reason is because it painted itself into that corner. It's syntax & semantics. You wouldn't "profit" from RAII if you added pointer indirection to your objects. (To fix that, later std::unique_ptr<T> was popularized, which again is an embedded struct but contains only a pointer -- which in typical C++ manner introduced its own additional horrors).
Another reason why C++ wants to embed objects is that its operators (like operator[], operator+ etc.) are defined on "value" type syntax expressions. Well it's basically the same reason as above RAII argument, since that argument is about the destructor-"operator". You cannot call methods & operators on pointers. You cannot do `std::vector<Foo> *ptr = ...; do_foo(ptr[i]);`. Get it? The best you could do would be `do_foo(ptr->at(i))` but it's not the same. Part of the reason why C++ calls methods on value type expressions is of course C compatibility, C++ can't afford to mess with pointer syntax and arithmetic.
I'm sorry. C++ is broken.
How many did you read?
Way too many.
You are continuing to fight strawmans I have never said and continuing to not get what I said and continuing to lecture me about beginner level C++. I am bored.
Move it if you need to, so simple!
Move it, so simple, pointer invalidation.
Have you thought about... not moving it? So simple! "I need to move the outermost layer of a complicated structure, because I'm too stubborn to have just put this structure on the heap in the first place -- with nodes that link to it, and I'm not sure if other structure point to it... but anyway I NEED to move it, and I'm happy to implement all silly move and copy constructures and conform to this rigid ruleset even though there is no point in 90% of the cases, and that will lead to incredibly hard to diagnose bugs", said... no competent systems programmer ever.
Have you considered why C++ is like a pile on top of a pile of fixes, exceptions, copy semantics, move semantics, rvalue references... That is the polar opposite of simple.
Obviously you have not. But if you want to know, the reason why is that they are trying to treat things as values that are not values but stateful objects. They are trying to make a toolbox of hacks to treat everything the same. It does not work. It is a beginner level cardinal sin.
at some point you need to make real data structures that use the heap
Do you remember when you were arguing that you want to avoid putting things on the heap because performance, and I said allocating things on the stack is for toy programs, like.. in my last post from 1 hour ago? I think you must have forgotten. Now you're claiming you want to allocate on the heap and I am not doing real datastructures on the heap???
Please give up. It is embarassing.
Or better, please go read any C++ codebase with templates and classes and copy semantics and move semantics. Get more brain damage as a result. And please leave me alone.
It's long been clear that you don't understand what you're talking about when you're criticizing what I say. You don't realize that I understand everything you say and I'm only explaining where your line of reasoning breaks. The concerns you raise are beginner level C++ object thinking. You still haven't understood what I've said. It has turned out that the actual zealot is you, you're just a follower of mainstream ideology, following conventions without understanding them. Being part of the mainstream seems to mean you feel legitimized to talk down on others who are looking for solutions to genuine, serious problems. Following the mainstream will give you predictable results, but also with a low ceiling of what you can achieve this way.
Nope. You only need a class definition.
How hard is it to understand that to get a class definition, all the stuff that's used inside the class, including declarations of internals like private fields and methods, needs to be included first? How hard is it? It seems like you are terminally unable to see it. Have you never touched a seemingly unimportant header file and had to rebuild 80% of your project? Because that's the common case in most real world C++ projects (and also some badly designed C projects btw.), in my experience.
Now it's not about build times? I thought it was and you had said that all along? Now the story changes again.
Are you insane, you first critized me for making too many different points, then later you claimed I had NEVER talked about build times, which I refuted with a simple Ctrl+F argument. Then later you critized me for talking ONLY about build times. Now you critize me again for talking about a variety of things?
While I have stressed from the beginning that I'm talking about systemic problems, which manifest in lots of ways. Maybe Ctrl+F for "and here are a million examples why" for example?
You are a weasel with no purpose other than talking down on me, and you haven't made a single interesting argument. Get lost.
All 6MB of sqlite compiles in a single second
Sure, a while ago, I even compiled it in 300ms using tcc (as a test). Do you know why that works? Because sqlite is written in C, without insane C++ classes and features. They compile everything as a single file (like raddebugger does too btw., a legitimate approach but I'm not saying this is the solution for everyone), circumventing the quadratic translation unit scaling problem.
You added one already. PIMPL is the same as your indirection from heap allocating a predeclared struct so that you can return it from your constructor. The price is heap allocation and indirection.
A PIMPL object is one that is referenced as a pointer (of void-type or forward declared type) in a struct trying to protect implementation details and dependencies, because C++ doesn't exactly want you to do that.
The price of an actual C++ PIMPL object is that you'll go insane duplicating all the methods and writing all the call forwards.
This is NOTHING like a forward declared C struct, which is the exact opposite. There is NO duplication and call forwarding. You haven't got a clue what you're talking about.
A forward declared C struct does not add another level of indirection. You can't put the object itself directly on the stack unless you know the struct definition, that is obvious. It's the price to pay for true abstraction, abstracting from the internals of the object and treating it uniformly as a pointer value. But that's not a problem at all, since you would never want to put abstracted objects on the stack. Putting objects on the stack is how you write tiny scripty toy applications. In systems programming, object lifetimes don't correlate to function call lifetimes, so you cannot put objects on the stack. If you are able to put most of your objects on the stack by value, you are working on a toy app.
Contrast objects here with plain structs, for example struct Float3 { float x, y, z; }. Of course you expose those as values, these are stateless objects. And putting them on the stack is fine. That has nothing to do with PIMPL or implementation hiding AT ALL.
With your API you created an object with none of the huge advantages of being able to treat it like every other value in modern C++.
There is no "like every other value" in C++, regardless of how hard people are trying, and regardless of how much complexity these people pile on top of this language.
The only successful "every other value" is plain data, which you can simply copy as bytes. I could queue a lecture now why Unix was successful with its file concept of "raw bag of bytes", but I'm done with you. Good bye.
More insults but there is no difference here.
Rebutting your wild and easily disproved claims where it's necessary.
I looked at your godbolt link, which part of this does C avoid?
Should I spell it out again for you? With non-class programming, you can do
struct Foo;
struct Bar;
Bar *bar_create();
void bar_destroy(Bar *bar);
void foo_doThing(Foo *foo);
Foo *foo_create(int x, int y, Bar *bar);
void foo_destroy(Foo *foo);
And that's literally the API, clean and readable. Now granted, it doesn't do RAII, but here's the essence of the API, in a form that you can actually improve and develop and refactor, and which doesn't require you to rebuild the world if you changed something in a remote corner of the codebase.This is super well known common lore, and if you haven't noticed this yourself and haven't heard many people talking about this, you simply don't know what you're talking about.
Every struct and class definition you have ever written compiles in a fraction of a second on hardware from 15 years ago this makes no sense at all.
I'm not repeating once again how this requires you to include the world for the smallest thing which is extremely painful.
Is it your argument against an optional feature seriously that you can't use the PIMPL pattern from the 90s ? Holy mother of god, that's what is so important? Every struct and class definition you have ever written compiles in a fraction of a second on hardware from 15 years ago this makes no sense at all. It even took you an entire novel of "nu uh" and "this program was written in C so everything else is bad" to get there.
This is not about single-build times (which are much more affected by by template metaprogramming), but about quadratic incremental rebuild scaling. And "in a fraction of a second" is a ridiculous argument when it's well known that C++ single build times are spectacularly bad and developers have to wait way too much for clean rebuilds too.
You just don't get it. And no, you don't do PIMPL. It doesn't scale at all. It's realistic to PIMPL a plain struct once in a while, but NOT with C++ classes with methods etc on a regular basis. It's not ergonomic to do so, it requires even much more boilerplate to do the simplest thing, and it makes it incredibly hard to change and fix and evolve anything. Plus, PIMPL does add a runtime indirection btw. It's BAD.
Again, I know what I'm talking down not because I'm repeating what "my heroes" said but because I've actually tried to make it work, many times. It doesn't work. And you're not doing PIMPL either. Want to know how I know? BECAUSE IT DOESN'T WORK AND YOU ARE JUST TALKING.
Now it boils down to something that is no different but without it all the same memory bugs persist.
You can't be serious. Now you claim that's the only thing I said? No, I've said many other things. You're a loudmouth who doesn't understand the most basic thing, so we have to spend days before you accept the tiniest straightforward common sense argument that you could just google. Do you want to go down the next rabbit hole? Because I don't, I'm so done with you.
Nope, destructors have nothing to do with this and this was never something you mentioned before. If it was you could show it with godbolt.
How about you simply Ctrl+F for "build time" or "compile time" and admit you have been lying, have been extremely adversarial, and have been incredibly lazy?
Show me. They are just functions, usually one or two lines.
Isn't it strange that I'm now creating a godbolt containing absolute C++ basics as a youtuber personality fangirl, since apparently I'm only repeating stuff I don't understand, which has to be why I can't express myself and provide evidence?
It is beyond me how you don't seem to know what I'm talking about, but here you go... This should provide reasonable evidence of the lack of ergonomics when using C++, and how problematic this class stuff is from various technical standpoints.
As you mentioned, in C style programming, we could instead simply forward declare a struct, than forward declare some functions. One line for each. Good header hygiene. Clean. Done. But noo, this is C++ and we can't have straightforward code and low coupling.
Full link: https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaP...
Now we're firmly into the insults phase.
We have been from about your 2nd comment on.
"I haven't considered that"
Considered WHAT?
I repeated what was in the comments. This is another example of claims without evidence, you realize that right? You didn't explain it and how I was wrong, you just said "no, nu uh".
I was merely giving you a second chance to notice by yourself that the Sleep-Code in question is code that is being debugged, not part of this project. But instead of double checking, again you've been relentlessly continuing on your trajectory.
You've ended up furthering your wild accusations without entertaining the possibility that I might be not completely wrong on all fronts, and you might be only half right (or less).
Are you talking about build times now? What happened to all the stuff about being terrible for the APIs and so toxic for the whole system that you can't write anything in godbolt?
It's one of the topics, one of the manifestations of the systemic issues I was talking about, if you had paid any sort of attention.
Finally an explanation of some sort. Too bad this isn't true. Do you know that the implementation can be in a compilation unit just like C?
You are still not understanding? I gathered you seem to be a game developer of some sorts but you don't understand the simplest basic facts about C++?
Please show me how you can call a destructor in C++ without seeing the declaration of the destructor. (I mean calling it directly of course, since calling it indirectly would refute your claim that a destructor is needed -- since you can't have an arbitrary intermediate function called automatically by RAII).
Or show me how you can see the declaration of the destructor without seeing the definition of the class.
JUST DO IT. SHOW THE EVIDENCE. DONT TALK.
Your own project
First, tone it down a little please, would you? It's not "my project", what is wrong with you?
... has plenty of the bugs that destructors and contained memory allocation avoid. Raw heap allocations, raw mem copies etc. https://github.com/EpicGames/raddebugger/issues/735 . Here's a complex memory cleanup race condition that's 'solved' with a sleep function. https://github.com/EpicGames/raddebugger/issues/855
Now you've provided two links and they contain little evidence to back up your claims. Linking issue 735 suggests that you've misunderstood the part about a "Sleep", which evidences either your lack of capacity to grok the context of the issue, or evidences your unhealthy adversarial stance (I strongly suspect the latter).
You don't give any explanations, they are just claims, do you understand that? Do you know what evidence is? It's the thing you say you can't provide.
You haven't been able to take the most obvious uncontentious claim from me. But if I'm not explaining as you say, what are YOU doing?
Are you seriously too stubborn to accept that exposing a destructor (or any other method) in a header file requires exposing the class's definition (private method declarations including signatures, include internally used headers for internally used data structures, which again have methods, which have dependencies...) in C++? And when any of those not-real-dependencies changes, you have to rebuild everything needlessly? Are you somehow mentally unable to see how long compile times can subtly degrade a project over time?
Because I didn't need any youtuber personality to find that out for myself. And for some humility, the fact that I was able to see that suggests it's not particularly hard to find out.
Why don't you link something besides a name of a big C project and explain it. The idea that someone making a project in C somehow means other features and tools are terrible isn't even logic. That would mean that one project in a language from the 70s would negate every garbage collected and dynamically typed scripting language.
And also I didn't claim it, you fantasized it and I can't help but ridicule you for having such wild ideas. To make such claims you would actually have a closer look at a project.
By your own logic the fact that https://github.com/chromium/chromium has 1.7 million commits to your project's 4,385 means that modern C++ is more than 380 times as effective.
How is that by my own logic? But please accept that this is the work of mainly 1-2 persons, and compare _hours_ of build time (35 MLOC) to 3 seconds of build time (300K LOC).
You think that somehow something simple like automatically running a function you have to run anyway is a problem, it's nonsense.
You are misrepresenting what I've said so badly it's not funny anymore. I've said multiple times that maybe this function shouldn't even exist. And even if the function has to exist, maybe it shouldn't be coupled so hard, at a language level, with the actual state & logic definitions. I did provide reasons why I've perceived the coupling to be an architectural problem.
I would be very open to a discussion when this can or can not be a serious problem and what are possible tradeoffs and points in the design space. It wouldn't make me feel bad and wouldn't take away from my claim that there is actual evidence for the issues being discussed. But with a person like you, that's not possible.
That's a matter of dependencies and doesn't have anything to do with destructors.
I've explained multiple times why I think it does. If you want a destructor declared in C++, you have to give the full class definition.
Now we're to the "I don't like how you're saying it" part of the no evidence playbook. This happens when someone can't admit that they don't have any information and they can't believe that people won't accept them repeating claims as evidence.
No, it happens when I can't believe how you haven't managed to stop talking down on me instead of making an effort to be anything other than an annoying adversary. That's all.
I think it's time to admit to yourself that you have accepted some internet youtuber personalities as a religious belief and that you haven't questioned these beliefs with thoughts that go beyond the claims of the cult.
I think it's time for you to shut up. You are providing zero evidence for your baseless (even counter-factual) accusations towards myself. What you say makes ZERO sense.
If you can't demonstrate it with actual source code then you just have claims and no evidence.
I can NOT demonstrate it in a tiny godbolt because the effects I am talking about don't apply on a micro-scale.
I CAN demonstrate it with actual source code, but you are unwilling to look it up. You are disagreeing without making the tiniest effort. Not being willing to make an effort by itself is fine. But you're being contrarian in the worst and laziest way.
Do you not understand the concept that I can't explain the world to you from the ground up? You say no to everything without trying to understand it. Your "rebuttals" indicate you're not willing to follow me in the slightest and you reject even the most common lore (for example the thing about C++ class features (destructors, methods...) leading to exposure of internals in public headers, leading to every TU including the world). Or you are intentionally trolling me. You are being uncooperative and you're the single worst interaction I've had on this platform. Now please get off my lawn.
You bringing up something unrelated doesn't mean anything just because you keep bringing it up.
It's NOT unrelated. It is a real world demonstration of the simplicity possible by custom modeling of allocation and lifetime concerns instead of buying into a fixed scheme set by a language.
There is one singular question and you seem to think it's absurd to answer it and not veer off into a hundred different directions about unrelated projects and your favorite influencers instead of being able to focus on it.
You made me do it by repeatedly talking down on me in a condescending tone, when I tried to keep it simple and referred to external sources. What the heck. You want a simple reply. I cannot give you one, except for, "RAII is this simplistic solution that makes the language complicated while still being by far not enough to handle interesting use cases. It has turned out to be wrong and misguided every time I've tried to use it, and here are a million examples why".
If it automates things why would it have a typing cost? That makes zero sense.
It automates generating boring boilerplate code (which is good on one hand but it doesn't reduce the semantic complexity AT ALL, so actually it just promotes doing the wrong thing by making the wrong thing easier). To generate the boilerplate code it requires type machinery. What doesn't make sense about this?
Why don't YOU start telling ME?
> it will "infect" your codebase,
How? It can be used or not.
To use it e.g. in containers, you then have to also use it for the things contained -- e.g. unique_ptr<T> or vector<T> or whatever, you will have a bad time if you don't model T's destruction with a C++ destructor too. Also you can't just insert random manually managed objects within a group of RAII managed objects, because that will break the "reverse destructor calls" assumption.
It's a viral thing.
> It will break modularization of your codebase, leading to subtle but substantive degradation.
These are strange claims, they don't have any evidence
I explained why: internals have to get moved to public API in practice. This is almost the definition of breaking modularization.
> This is part of what leads to long compile times
No it isn't, it's running the same function at the end of a scope. Where are you getting this idea? Templates you could say this about, but a simple concept has nothing to do with it.
Why are you being so stubborn? Destructors (like other methods) practically require classes to be fully exposed (that you can protect them with silly public/private/protected specifiers doesn't change the fact they're exposed), requiring includers to pick up transitive dependencies. That results in huge amount of included code per TU, and results in far too many TUs that have to be rebuilt on incremental changes.
This is a very well known thing.
"Temporal coupling" doesn't make any sense, you have to deallocate memory anyway, why would it be any different to not have to write the line explicitly?
Why doesn't it make sense? In practice, allocation is coupled with initialization and destruction is coupled with deallocation. It's literally in the name, Resource Allocation Is Initialization. You can work around it, sure, but it's painful and literally just an escape hook to escape the very thing you're arguing for.
If you want to have a real explanation, especially to yourself, you should probably show a scenario on godbolt or something like that. It really seems like people have led you down a path that doesn't make sense and you haven't gone back and tested it yourself.
ON GODBOLT? Are you kidding me? I've been arguing all day how RAII is a simplistic rigid mechanism builtin to a language that will lead to slow creeping systemic issues, in practice, in my experience, in larger and more complicated systems, especially with performance and abstraction requirements. I CAN NOT explain to you what sucks about it in a godbolt. You can use RAII fine for simpler programs. You can use it fine in a godbolt. You can use it fine for leetcode. etc.
Asking for a Godbolt example of architectural coupling is like asking for an assembly listing proving that a database schema is hard to migrate.
If this is so simple
I _never_ said it was simple. It is actually very subtle, which is also why I made many attempts at integrating RAII style programming for many projects. RAII works for applications with simple lexical and local lifetimes and ownership. But it's not a good fit for more complex systems with more global and coordinated allocation and ownership patterns in my experience. The concept of ownership and lifetimes encoded by it are too simplistic (when in reality, these terms are not even well defined), as is evidenced also by the various extensions to the machinery over the years (such as exceptions, move/copy asignment/constructors, rvalue references, unique_ptr, shared_ptr, trivially_copyable, trivially_destructible...) that contributed to form the probably most complex and hard to read language out there.
A big part of the problem is that there is no easy migration path from a smaller program with simpler lifetime and ownership patterns (relying on RAII) to a larger program with much more complex patterns (gradually discarding RAII/reworking allocation and ownership strategies). It is hard to refactor stuff that happens "between the lines" as part of the language rules, besides being hard to read also (unless the patterns are trivial).
> raddebugger
In what world does this have anything to do with anything being talked about? You need to focus.
Like, in the world where I've referenced this in 3 of my 4 previous comments, as an example of a serious application built without RAII, which leans heavily into arenas, resulting in extremely compact and fast code?
Why don't you say what it is they actually have to say about destructors?
Is this an exam? I referenced these guys instead of regurgitating because they can do it better, the content is out there, and I hardly could do justice to the matter in a few lines. If you need a couple more catchy but badly written phrases, it's that RAII comes at a large cost in terms of typing, and even though it automates some painful code, it will "infect" your codebase, requiring an all or nothing approach or you're actually back to manually managing memory, in fact you can not insert manual frees between arbitrary RAII destructor calls.
It will break modularization of your codebase, leading to subtle creeping degradation. It breaks separation/abstraction of memory management from data representation across module boundaries. This is part of what leads to long compile times that are very common with C++ code bases, as typically type definitions (including any internal matters and their dependencies) will be fully exposed in the headers instead of hidden in implementation files.
RAII adds temporal coupling between memory allocation and data lifetimes, which is a disadvantage as you scale your codebase up. And under the hood, RAII is still fundamentally built on a model of individual object thinking, allowing for the typical classes of memory bugs, like use-after-free if you do tree type or even spider web type object graphs (which sometimes is just what you have to do).
Separate allocation approaches on the other hand allow for better modularization and abstraction, and allow to handle allocation more centrally instead of cluttering allocation conerns across all types of a codebase.
That's it finally for me, this is a rushed summary and as said other people have explained this much better. I've also had enough of your condescending replies. Have a good day.
EDIT: if you are looking for text content instead of video rants, checkout https://www.dgtlgrove.com/p/untangling-lifetimes-the-arena-a... as an example of a well written treatment in this area. But really, these 3 guys (besides others) are all very good at explaining that stuff in videos too, just use google please and make up your own mind.
Iterator invalidation is absolutely a huge deal for many datastructures in practice. From an ergonomic standpoint alone it makes a huge difference. 10 years ago I argued the opposite, but juggling indices simply is not fun. Replacing pointers with indices can be useful but it's not a good default.
You are also ignoring bookkeeping overhead and fragmentation that comes with individual allocation.
The important difference between arenas and RAII as far as I'm concerned is that the former allows exploitation of allocation patterns, like stack shaped allocations, which can reduce complexity in a meaningful way, requiring no cleanup code at all. RAII on the other hand does not give you that -- it is just a disciplined system to automate some code, but the complexity is still fully there, and a lot of constraints are put on the types and your internal organization in order for RAII to work.
Arenas are quite a good fit for frame memory in graphics programming, and they do not waste memory there if done right. On the contrary they probably save some, but that's not the point here. We're typically talking about a couple megs per frame at most, which is spare change.
Some people, like Ryan, have managed to apply arenas more widely, but personally I am more in favour of a varied mix, and don't mind a bunch of manual code either, but I definitely like to control allocation more closely compared to what e.g. malloc/free allow. I like to keep objects in their specific subsystems. I have written my own block allocator to do this. Now allocation does not show up anymore on my profile almost at all, it's <<< 1÷ of my CPU usage even though I recreate tens of thousands of objects per second. Also syscalls are ~zero as long as memory usage is roughly the same from frame to frame.
As I said elsewhere, performance and correctness/clean modelling always go hand in hand. The correct model must allow for the right level of performance, or else it's not the correct model.
I told you what to check out. I'm not regurgitating this, I've done it often enough.
Have you spent 1 minute cloning the raddebugger, 3 seconds to build it, and 0.5 seconds to launch it, to be convinced that it is a solid product?
It's you and the three guys you are fans of against the entire world of systems programming
What is this "entire world of systems programming"? From what I've seen, there are very few accomplished experts who would disagree a lot with what e.g. Jon/Casey/Ryan have to say.
What are you working on?
So lets hear them.
There are few people delivering more in-depth explanations of their claims, like literally thousands of hours of content. You can just look it up. If you disagree, please disagree. Personally I find what they say articulates almost 100% of my independent experience. And I do systems programming in C++ full-time (file systems / desktop app infrastructure). Personally I've made at least 9 serious attempts of building robust and maintainable systems on top of C++ features like RAII and templates and have always returned to simple straightforward C-style code. And I've had to work many times with existing C++ codebases that had bought into these features, and it always seemed like a giant waste of time, a way to add more spaghettis to a hot mess. And also, any technically convincing C++ codebase I've seen always seemed to make only very light use of C++ features. Not going to argue more here.
Who cares? They have nothing to do with anything
Quite the opposite, it's a third person from the same circle making the same kind of claims (there are more persons), who have, contrary to what you say, actual evidence to back up their claims. You could just go ahead and check these works out yourself and see if it convinces you that the methods they claim to be better are actually better.
This is the statement that no one seems to be able to back up.
Actually there are many explanations of systemic issues arising from reliance on RAII. Both Jon and Casey are very good explaining the matters.
Instead you start talking about a third person making a debugger for some reason as if the solution to appealing to authority is more appealing to authority.
A third person (already mentioned in a parent comment), who is creating an extremely snappy and powerful debugger using those exact approaches under discussion.
the main reason to choose arena allocators is for correctness, not speed. They make similar time/space tradeoffs to garbage collectors in that they grant higher allocation throughput in exchange for more memory usage.
I'm not quite sure, do you mean arenas are slower, and use more memory, compared to standard vector-style allocation (which does a lot of copying, iterator invalidation, and wastes like 0.25x memory on the average), or compared to individual malloc/free (which does a lot of book-keeping work and wastes book-keeping memory)?
You must mean confusing achievements in the area of creating large maintainable and performant systems on the one hand, with achievements in the area of creating large maintainable and performant systems on the other hand?
And let me incude Ryan Fleury here who graduated from Casey-school with honors, he is maybe the most impressive programmer I know. His work on the raddebugger is outstanding.
Correctness (the kind that comes from simplicity and maintainability) and performance pretty much always go hand in hand. The only additional ingredient to "bridge the gap" between the two is detail.
Apparently you're the school of dismissive person who can't even be bothered to look up their technical achievements.