42 comments

[ 6.6 ms ] story [ 93.3 ms ] thread
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).
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.
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.

My copy of the book is currently 2000 km away from me, but I think the "C++ ARM" already mentioned it, does anyone know?
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.

I agree. I'm sure many people do in the committee even. Unfortunately C++ can't introduce breaking changes even though now we know better. I wish D or a similar (sane) native language gained some real momentum, since C++ is a "lost cause" in this regard (I can't deny that the new standards make it much nicer, the problem is the baggage that is called backwards compatibility).
You should checkout Go (http://golang.org/). It is more popular than D on github and has a lot of momentum.
Why the down votes. The poster asked for "sane" native languages with momentum? I gave him a suggestion that met his needs and made no false claims...
You recommended a garbage collected language that doesn't even have generics as a replacement for C++.
When it really matters- Nothing makes you use the GC... You can use a your own pool (or in tip sync.Pool) or mmap and your own allocator...

I used to do a lot of C#/C++/Java and after two years of Go... I don't miss generics. Go has been a great C++ replacement for me...

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.

"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."

Um, citation, please? Speaking as, you know, an actual C developer working in a company full of actual C developers, I would say that "bad C developers tend to think on...", etc. There are times when we need to use assembly and micro-opts, but trust me, no one ever wants to.

Though I agree that C++ would be a better language without the backwards compatibility constraint.

Work experience from the C developers I have met on my professional life since the early 90's.

I never liked pure C, and had my endless share of performance discussions since around 1993.

Just one example, discussing how slow C++ virtual calls were and how one cannot afford to use them, while coding an application where Clipper would still be fast enough for the job.

> 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.

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.

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.

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.

C++ would be a much better language if C compatibility had been thrown out of the window.

I think that language exists. Depending on what you need from it, presumably it's Java, or one of a number of other languages.

I have used C++ since around 1993, but nowadays I only use it on hobby projects so that I keep up to date.

On my type of work we have long moved into other languages.

(comment deleted)
(comment deleted)
It's also useful for other purposes.

http://www.reddit.com/r/cpp/comments/1ygcay/rarely_known_c_c...

These days all resources should be wrapped in some kind of manager class like std::unique_ptr so there's really no need to use function try-blocks for that.

http://www.gotw.ca/gotw/066.htm

(comment deleted)
unique_ptr/shared_ptr don't make a difference with regard to whether or not you need to use function-try blocks. function-try blocks are about whether the exception leaves the constructor, not whether the exception will result in destructors being called. For cleanup of constructed members when an exception is thrown in the ctor init-list it is required that you're in a try block somewhere up the stack.

Observe: https://ideone.com/DYthrB

    #include <iostream>
    #include <memory>

    struct Foo {
        int x_;
        Foo (int const x): x_(x) {
            if (x == 1) {
            throw x;
            }
        }
        ~Foo() { std::cerr << "foo_" << x_ << " destroyed" << std::endl; }
    };

    struct Bar {
        std::shared_ptr<Foo> foo_0;
        std::shared_ptr<Foo> foo_1;
        std::shared_ptr<Foo> foo_2;
        Bar(): 
            foo_0 (std::make_shared<Foo>(0)),
            foo_1 (std::make_shared<Foo>(1)),
            foo_2 (std::make_shared<Foo>(2)) {
        }
        ~Bar() {
            std::cerr << "Bar destroyed" << std::endl;
        }
    };

    void xul() {
        Bar bar;
    }

    int main() {
        try { xul(); } catch (...) { return EXIT_FAILURE; }
    }
Outputs:

    foo_0 destroyed
foo_0 was destroyed here even though Bars destructor was never called

Change main to:

    int main() { xul(); }
Outputs nothing. foo_1 doesn't leak memory (it's freed by the shared_ptr constructor when Foos constructor throws) but foo_0 leaks because no shared_ptr destructors are ever called. This doesn't matter so much if you're just allocating memory (if you don't catch somewhere you're going to terminate anyway), but it could matter if Foo were a lock file or some other persistent system resource that can outlive the process, or another service somewhere is waiting for a message on a timeout.

The alternative to relying on there being a try{} block somewhere up the stack is to add a function-try block on Bars constructor.

tl;dr: member destructors aren't called if you throw from a constructor init list, function-try blocks fix that.

Posts like these really wind my stack. Basically, your response to a perfectly deterministic, compile-time-optimizable strategy for resource handling is regressing to the runtime. Then hoping the overhead for not only zero-initialization, but the myriad of cmp's and jmp's and re-zero'ing for cleanup get optimized away by some data flow analysis handwavy; all of this explicit, manual labor in order to avoid the "ugliness" of RAII (don't forget any of those if's ;).

Exceptions in constructors are incredibly more useful than isValid()'s due to their composability. isValid()'s have to be checked and fail in the constructor body after the initialization list. This is why you're concluding that zero-initialization is the only way out of a problem that you're creating in the first place.

Try doing everything in the initialization list, and rely on nothing in the constructor body.

How much experience have you of languages that zero-initialize before the constructor is called?

I was a compiler engineer at Borland / Embarcadero on Delphi, a non-GC language with a zero-init approach. Java zero-initializes. C# zero-initializes. In fact, I struggle to think of a modern language that doesn't zero-init or prevents access to uninitialized fields by definite assignment analysis.

Overhead in C++ is a different story. C++ is not low overhead enough, to me, to justify the claims made for it. For example, I like static data structures, especially in C. But in C++ they are unidiomatic. How do you statically initialize (in the rdata / rodata segment) an object graph (even as simple as a tree) with zero-copying at runtime?

Or how about copy constructors that may throw exceptions. That leads to inefficient APIs for things like std::stack::pop(). Guarding against exceptions in generic code has implicit overhead when the type system is as user-mutable as C++.

> For example, I like static data structures, especially in C. But in C++ they are unidiomatic. How do you statically initialize (in the rdata / rodata segment) an object graph (even as simple as a tree) with zero-copying at runtime?

How do you do that in C? Does unidiomatic mean you can't do it? Interesting challenge.

That's also an interestingly-sized data structure. Apparently big enough that the initialization time is a performance concern, but small enough to be able to lay out statically and not burden your binary size.

How do you [zero-initialize a structure] in C? Does unidiomatic mean you can't do it? Interesting challenge.

You can zero-initialize an object on the stack in C like this:

  struct my_object obj = { 0 };
Everything in the structure, no matter what it is, will be 0. Longs will be 0, shorts will be 0, pointers will be null, etc.

In C++, you can still do this, but only for POD types. Basically, in C++, it is assumed that you will manually initialize all data members in the constructor. POD objects get a free pass since they're essentially C structs.

Pre-cooking complicated data in C programs such that it can be faulted in with zero initialization time at runtime has been an idiom for a long time. Try building GNU emacs.

It's true that the language support for generating such a thing is minimal (though templates in C++ can get you pretty far), so other techniques like generating code at build time or (the emacs trick) actually executing the script interpreter in a bootstrap mode to generate the pickled data are common.

> Then hoping the overhead for not only zero-initialization, but the myriad of cmp's and jmp's and re-zero'ing for cleanup get optimized away by some data flow analysis handwavy

To be fair, with move semantics in C++11, C++ has started to depend on dataflow optimizations for zero-initialization (or, to be more precise, zero-deinitialization). After a value is moved, typical objects will zero themselves out so that the call to the destructor won't do anything, and they count on data flow analysis to strip out the zeroing out and the overhead of the destructor call.

(That said, I much prefer definite assignment analysis to zero-initialization. I tend to think zero values in general are kind of a wart in language design.)

Isn't the real problem with initializer lists? It would be much nicer if C++ would allow me to call the field's constructors inside of the class' constructor, a la:

    A::A() {
        B::B();
        try {
            foo(1);
        }
        catch (...) {}

        // you could even
        if (something)
            bar(2)
        else
            bar(3);

        // or maybe with a different syntax,
        // but without copying if this is an object
        bar = 3
    }
> C++ doesn't zero-initialize instances before calling the constructor.

This is exactly the reason why we use C++ for high performance applications: we don't pay for features we don't need. On one commercial product I worked on [1], we found out that zero-initialization of 3D vectors was consuming around 5% of the total running time on some scenes.

[1] The CPU kernel of iray: http://www.nvidia.com/object/nvidia-iray.html

And that's the reason the GP said that a pragma to not zero-init would probably be helpful for such cases.
So basically you would like to be able to write this:

    struct Bar
    {
        int * one;
        int * two;
        int * three;

        Bar()
        {
            one = new int{1};
            two = new int{2};
            three = new int{3};
        }

        ~Bar()
        {
            if (!one) return;
            delete one;
            if (!two) return;
            delete two;
            if (!three) return;
            delete three;
        }
    };
and that is somehow cleaner and more efficient than this:

    struct Bar
    {
        std::unique_ptr<int> one;
        std::unique_ptr<int> two;
        std::unique_ptr<int> three;

        Bar()
        :
            one{new int{1}},
            two{new int{2}},
            three{new int{3}}
        {}
    };
Jesus Christ. Never ever use try catch inside a constructor. It's the fastest way to a headache.
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.

I don't care for them, either, for the same reason: they are less explicit (to me) than direct error handling at the point-of-failure. I've found exceptions are also actually more verbose in many cases, for the same level of granularity in error trapping. My view is that exceptions handled in try-catch blocks are okay when handling "bulk" initialization or other computations where it doesn't matter which particular step failed. In every other case, it's better, in my view, to use explicit code.
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.

How are they more broken compared to e.g. Java and C#? (Assuming you don't have a problem with these.)

I would think having automatic cleanup via destructors would make C++ more pleasant to use in this aspect as you don't need to wrap all resource handling code in using and try-finally blocks.

You're not alone. The Google coding standard bans exceptions in C++. Firefox, V8, and a lot of other pieces of C++ software are also written without exceptions. It's particularly telling that the guys who wrote the LLVM compiler, who presumably know more about the dark corners of C++ than anyone, chose to ban exceptions in their code.
"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)