Of course there is. Do not make the call-tree big enough to do that,
and if there's no recursion involved, it's rather easy to derive a max
stack size and put it in your hardware requirements.
Imposing a hardware requirement is what makes that solution
non-portable. It's not a bad solution; but it cannot be ported to a
machine that doesn't meet those requirements. Some mechanism for
determining, within your program, whether a given function call would
require more stack than is currently available would make it possible to
write code which at least fails gracefully on such hardware, instead of
simply aborting (or worse).
So, basically, you're saying, since your cannot avoid it absolutely
anyway, all advice is useless, and it's OK to use tail-recursion to
skip through a 2 Mb file. Ok.
No, I'm saying that any precautions you need to take to make recursion
safe necessarily limit the portability of your code; the same is true of
making non-recursive function calls safe; it's just a little easier to
take the appropriate precautions with non-recursive code. As long as you
take those precautions, and consider the associated limitations on the
portability of your code acceptable, there's no other reason to avoid
recursion. And, where it's appropriate, there can be strong reasons to
favor a recursive approach.
....
You're like a mechanic telling me how brakes are useless since they
cannot guarantee that you won't run into anything, anyway. Even when
applying the brakes, you can still run over a pedestrian or a 20-ton
truck, so basically brakes are useless.
No, you should not confuse non-portable with useless.
....
Nope, it's mandated by the POSIX standard instead. But i'm getting
curious, pray describe the superior solution you have in mind.
I haven't come up with an alternative that is easy to use.
The simplest approach I can think of, from the point of view of the
implementor, would be two constructs. I would call them standard library
functions, and using function-call syntax might be the appropriate way
to specify them in the standard, but they must be implemented in such a
way that they can always be called safely, regardless of how little
stack there is left. One function determines how much memory is
currently available for objects with automatic storage duration (if the
exact amount is hard to determine, it may instead return a guaranteed
minimum amount).
The second function takes a function pointer as an argument, and reports
the amount of memory required for objects with automatic storage
duration needed to call that function. It would not include the memory
needed by function calls within the pointed-at function; avoiding stack
overflow due to those calls would be the responsibility of the function
that directly performs them.
There's a number of reasons why this simple approach would be inadequate
for C: VLAs and objects with automatic storage duration defined inside
conditionally executed blocks are the two biggest problems I can see;
I'm sure there are others. However, a C-like language without those
features could implement such constructs. It might be possible to
implement some variant of this idea in C itself.
The key advantage of this idea, over SIGSEGV, is that it allows a
program to avoid the problem, rather than merely react to the problem
after it has already happened.
Understand: I am not advocating the creation of such a mechanism; my
main point is that the problem that such a mechanism would solve, exists
whether or not the program in question uses recursion. You shouldn't
avoid recursion just because that problem is a little harder to deal
with in recursive code than in non-recursive code.
....
Ok. So you have different ways of dealing with a stack-overflow. Again
i'm curious how you do that. Portably, since you've stressed the
importance of that quality in your software.
I can't - because neither C, nor any other language that I'm familiar
with, has portable methods to avoid stack overflow. I use non-portable
methods that are probably not too different from yours. They basically
amount to making sure that it works on every machine I have available to
test it on, and hoping that it works elsewhere.
While my software is required to be available to the public, the only
machines it absolutely must run on are machines operated by my own
company, and I have development machines I can test my code on that are
(supposed to be) configured identically to the production machines.
But if a user is having trouble porting my code to their machine, and is
willing to give me access to that machine for testing, I would certainly
be glad to fix the problem. I have anecdotal evidence suggesting that
most users don't bother; many of them simply make the fix themselves,
without even bothering to complain about it. I found a bug last year
when we first ported our code to a 64-bit CentOS machine, and when I
announced my bug fix, three users wrote to tell me they'd already
noticed the problem and fixed it in their own copies of the code,
without bothering to tell me about the bug.
....
So your point applies, and i can't guarantee that a single function-
call won't crash the system. Mission impossible? Mission
irresponsable?
What alternatives do you have?
None - which is the substance of my complaint.
I'm getting curious again, what sort of killer-functioncall did you
have in mind? Something which allocates a 2Gb array on stack and
passes it around by value? If that's the case, your design stinks.
No, just a function that requires more total stack space than is
available; it doesn't all have to be allocated in a single object.
Virtual memory makes such a limit harder to exceed, but not all systems
have virtual memory. On some small machines, it could take a whole lot
less than 2GB to exceed that limit.