

Rarely known C++ constructs (part 1): Function try blocks - sztomi
http://szelei.me/rarely-known-cpp-constructs-part-1-function-try-blocks/

======
barrkel
This is hack on top of a broken feature.

C++ doesn't zero-initialize instances before calling the constructor. Thus you
can't tell which fields need to be cleaned up in a destructor in case of a
partial construction [1]. So destructors aren't called for partial
construction, and you need to handle the exception cleanup in the constructor.
But you can't do that for member initialization lists, so there's this thing.

RAII and wrappers is one reasonable way out of the insanity, but the costs are
arguably higher than zero initialization - certainly in ugliness of wrapper
classes, if not in absolute runtime.

Forbidding exceptions in constructors is another reasonable way out, but that
kind of sucks - constructors have no other way of communicating error, so you
need to add isValid flags to your classes, and to be belt and braces secure,
you need to check this flag for every operation.

The correct answer is zero initialization, possibly with some kind of pragma
to prevent zero init for critical performance classes. The thing with zero
init is that data flow analysis can mitigate the costs - the compiler would be
able to eliminate the earlier zero assignment much of the time.

Overall it's more of the same from C++: unsafe, broken by default, bodged up
with hacks to make it slightly more stable, and usually works mostly OK in
practice owing to limiting C++ to a reasonable subset.

[1] I know that technically 0 can be a valid physical address, but even for
(embedded) systems that do allocate there, it's worthwhile reserving it.

~~~
pjmlp
All caused by C compatibility, while trying to bring C developers on board
with the "pay only for what you use mantra".

C developers tend to think on micro-optimizations and Assembly level before
writing any line of code, even when it doesn't matter at all for the problem
being solved.

C++ would be a much better language if C compatibility had been thrown out of
the window. However, it might had not spread into the industry as it did.

~~~
tfinniga
> However, it might had not spread into the industry as it did.

Worse is better. C++ is nothing if not pragmatic, and the core of C++
programmers are similarly pragmatic.

If D or go or rust or whatever else ever become more practical to use than
C++, then people will switch. I know C++ is very ugly, but it gives me an
unmatched combination of runtime performance, good tools, API/OS
compatibility, and acceptable language design. There's a good reason why so
many VMs for other languages are written in C and C++, and it isn't elegance.

~~~
pjmlp
Sure, if you look at my history you will see I like C++, just not C.

The thing is that many of the WTF people associate with C++, are caused by C
underpinnings.

> There's a good reason why so many VMs for other languages are written in C
> and C++, and it isn't elegance.

While I do like C++, enough to have lots of C++ books, I only partially agree.

I come from the Pascal branch of languages and consider it has more to do with
existing toolchain infrastructure than anything else.

~~~
tfinniga
I agree - the C++ compilers are pretty good, and if the OS is written in C and
C++, working with the OS will be simpler if you use C++ than pascal.

One thing I've noticed is that the dominant languages for resource-constrained
programs all seem to envelop the previous generation. You can embed assembly
in C. You can embed C in C++. C++ has yet to be embedded in another language,
at least if you want to keep the same memory layout of objects.

I'm curious to see if D or some other language can buck the trend.

~~~
pjmlp
I am quite active in D forums, and also follow Rust closely, given my interest
in the ML language family.

If you look at the history of systems languages, the only ones that won market
share, had the sponsorship of an OS vendor as their official language.
Specially because many people hate to write FFI bindings for their favourite
language and it doesn't scale to map the full OS API.

So the question is which OS vendors would pick the wannabe replacements.

------
72deluxe
"They are also syntactically valid on regular functions, but of little use."

I might litter my code with some of these to make it more "fun" for others to
maintain it after I am gone / hit by a bus. At least I will go with the
knowledge that they'll have to learn an obscure feature of the language, plus
it'll remind me every time I see it (to stop the synapse degradation)

------
tenfingers
Every C++ book I even read mentions function-level try blocks. Unfortunately,
they are rarely useful due to scope semantics (it's difficult to perform
shared cleanup, even when using object guards).

~~~
mattgreenrocks
FWIW, I have never seen this, and I have quite a few C++ books on my shelf. I
either missed it repeatedly, or it is so obscure that it never came up.

~~~
nikbackm
They are included in TC++PL at least. But so is everything else in C++ :)

They are probably obscure because they are only really useful for logging or
remapping exceptions (unless a new exception is thrown the original one will
be re-thrown at the end of the catch-block) in constructors and destructors.

They can be used in normal functions as well, but offers nothing beyond what
normal try-blocks do there.

~~~
pjmlp
My copy of the book is currently 2000 km away from me, but I think the "C++
ARM" already mentioned it, does anyone know?

------
acron0
Jesus Christ. Never ever use try catch inside a constructor. It's the fastest
way to a headache.

~~~
seabrookmx
Exceptions are utterly broken in C++.

I get flamed a lot for this, but I just avoid them in general in C++. In most
languages this isn't the case, but in C++ I find explicit error handling code
to be less work to write/maintain, even if it is a bit more verbose.

~~~
byuu
I see two major use cases for exceptions in C++.

The first, is for errors during recursive functions. Very challenging to
escape from 13 levels deep into a recursive descent parser without throw.

The second, is for container access-by-reference functions that are called on
empty or out-of-bounds elements (eg array(4); array[16] = value;), since
otherwise there is no way to return from the function, as you may not know how
to construct a new container element (eg no default constructor), nor would
you necessarily want to return a dummy object in that case.

