RE: Custom error message for stack overflow in C++
August 11, 2021 at 9:38 am
(This post was last modified: August 11, 2021 at 10:06 am by HappySkeptic.)
(August 11, 2021 at 4:28 am)FlatAssembler Wrote:(August 10, 2021 at 4:11 pm)HappySkeptic Wrote: That requires making use of things like shared_ptr<> rather than copying large objects into new variables (it also helps with performance).Damned pointers, I was never good with them.
I notice that every TreeNode generates some data which looks like it is the same for all instances. That is wasteful - it should be static and initialized statically. That also causes much less data to be copied.
I have tried using static fields, but the linker keeps complaining then. I do not know enough C++ to figure out why.
Maybe you can fork me on GitHub to fix those things?
I don't know if I have the time to do a fork and fix them. I might, but, increasing the stack size is probably your solution.
The heap is literally 1000 times the size of the default stack (or more). Using bare pointers is just a bad idea in C++ 11 or higher. Always use shared_ptr<> to hold the pointer, and new the object from the heap.
The shared_ptr<> can be thought of as a reference object - don't think of it as a pointer any more (although you dereference it with a "->" post-fix symbol or "*" prefix symbol just like a pointer). When the last shared_ptr<> reference to an object disappears, the object gets deleted.
Code:
shared_ptr<TreeNode> node = new TreeNode();
Now, node is just an object that get copied into methods, returned from methods, or held in other objects - but the copies of it copy the reference, not the TreeNode data. The data is on the heap, not the stack. Only the shared_ptr<> reference object is on the stack.
Here is an example of a performance (and stack) problem.
Code:
AssemblyCode convertToInteger32(const TreeNode node, const CompilationContext context)
A correct method would be
Code:
AssemblyCode convertToInteger32(const TreeNode & node, const CompilationContext & context)
The difference is that the node and context are now passed as C++ references. Basically, a C++ reference is a hidden pointer. You still treat node and context just like before, no "->" needs to be added, but under the hood, the objects are being passed by an internal pointer.
Your way, the entire context and node are copied, and the copies reside on the stack until the method returns. That is extremely wasteful. Doing this one change everywhere may be enough to fix your blow-up, though you should increase your stack size as well.
The other stack and performance issue, is that AssemblyCode is being returned as a copy. That isn't terrible, but it is wasteful, and the variable it is being assigned to in your compile() code is on the stack.
Instead, a better method would be
Code:
shared_ptr<AssemblyCode> convertToInteger32(const TreeNode & node, const CompilationContext & context)
Of course, to use this, you would have to "new" the AssemblyCode from within the method, and use it with "->" dereference operators instead of "." . shared_ptr<> is just another std:: class (i.e. from the standard template library).
Now, if you went all the way with shared_ptr<>, you would always have context and node be shared_ptr<> as well, throughout your code. Then, you would pass those in:
Code:
shared_ptr<AssemblyCode> convertToInteger32(const shared_ptr<TreeNode> & node, const shared_ptr<CompilationContext> & context)
Here, I wouldn't get much performance penalty by changing "& node" to just "node", as copying a shared_ptr<> is relatively fast (a few bytes, plus incrementing an atomic_int in a thread-safe way). However, once you get into using shared_ptr<> everywhere, a lot of code will need changing.