News for hackers. Stuff that matters.
Re:Practical Guide to Bare Metal C++ by jandrewrogers on Thursday July 21, 2016 @03:42PM (#12139473)
Maybe my experience is atypical but RTTI and exceptions are features of C++ that I never see used in real-world C++ almost regardless of the application. When I have seen them used, it is by people new to C++ that haven't fully figured out the common development idioms of the language yet.
Re:Practical Guide to Bare Metal C++ by jlarocco on Thursday July 21, 2016 @04:03PM (#12139619)
I've never seen RTTI use in production code. Even with modern C++, RTTI almost always adds too much runtime overhead and negative performance impacts. Most importantly, it's usually a sign of bad design.

My experience with exceptions is different, though, and I see them used quite a bit. It depends a lot on the age of the code base and the group working on it. Old code is less likely to use them, and developers with a heavy C background, or a lot of pre-C++98 experience are less like to use them. Except for maybe some tiny embedded systems (like microcontrollers), the overhead of exceptions isn't that bad any more.

Re:Practical Guide to Bare Metal C++ by fauigerzigerk on Thursday July 21, 2016 @04:30PM (#12139814)
The strange thing about exceptions in C++ is that they make ubiquitous use of RAII a necessity, but at the same time they must not be thrown in destructors.

And then C++ exceptions don't come with a stack trace that makes quick and dirty exceptions based code so convenient in Java or C#.

I was never a principled opponent of exceptions, but I find myself removing them from more and more code the more thoroughly I think about all the possible error states of code that should really be robust.

Re:Practical Guide to Bare Metal C++ by maxlybbert on Friday July 22, 2016 @03:10AM (#12142178)
I don't find it weird that exceptions shouldn't be thrown from destructors. I remember being surprised when I learned that rule, but the reasoning seems sound to me: what can you do when the cleanup failed? Clean up harder?
Re:Practical Guide to Bare Metal C++ by fauigerzigerk on Friday July 22, 2016 @04:47AM (#12142427)
The short answer is that action B may depend on whether or not action A was able to finish successfully, including any cleanup.
Re:Practical Guide to Bare Metal C++ by maxlybbert on Friday July 22, 2016 @07:27AM (#12142913)
The official reason destructors are discouraged from throwing exceptions is that one reason destructors get called is that something already threw an exception, and there is currently no way to handle two exceptions in flight at once, so in that case the process gets killed.

It's also true that with more experience, we've figured out that cleanup code is special. When I throw an exception during normal processing, I want to abandon what I'm doing. But if I discover that one step of a destructor cannot be completed, I do not want to abandon the rest of the cleanup. If I throw an exception in a destructor, I can't come back later to finish any remaining steps.

And, as I asked earlier, if I throw an exception because I can't close a file, what do I expect the exception handler to do? I guess it could try closing the file again, but I'm not going to hold my breath on that working. Perhaps the file is on a network share and the network's gone down. Perhaps closing the file will involve writing some cached data to disk, but the disk is full. In my experience, in this case your only real options are "ignore it, either because there's nothing you can do or because you're already dealing with an exception and you've simply discovered another symptom of the original problem" or "kill the program because there is no hope." You can log the fact that a step failed, but remember that it's possible for logging to fail, if the disk is full.

Re:Practical Guide to Bare Metal C++ by fauigerzigerk on Friday July 22, 2016 @09:33AM (#12143371)
>It's also true that with more experience, we've figured out that cleanup code is special.

In my experience the exact opposite is true. Error handling or "cleanup" code is in no way special. Errors are not exceptional and there is nothing we can say in general about what error handling code will or will not need to do.

Error handling code needs to be able to use the entire language and reuse all the regular code that is used elsewhere. A language should not have two modes, an error mode and a normal mode with completely different non-local semantics.

>And, as I asked earlier, if I throw an exception because I can't close a file, what do I expect the exception handler to do?

That depends entirely on the context of the program you're writing. It might want to do things like writing a record to a database that marks the file as invalid/corrupt. It may need to notify some other system about the failed action. It might want to roll back a transaction or initiate a compensating transaction. It may want to abort some session and schedule it to restart at a later time.

There is simply nothing we can know in general about what a caller of an action that failed to complete cleanly may want to do as a result of that failure.

Using exceptions for error states that may require a complex response is just not a good idea and in C++ it's an even worse idea.

[Edit] But to be clear, my initial thought wasn't about exception handling but rather about ubiquitous use of RAII for all sorts of stuff that needs to get done at the end of a scope. Much of that has nothing to do with errors at all. Maybe if you think of these situations it will become clearer why I think that arbitrary limitations of code that runs in a destructor is problematic. C++ has no finally block. Destructors is all we have.

Re:Practical Guide to Bare Metal C++ by quicknir on Friday July 22, 2016 @02:41PM (#12145721)
I don't agree with much in this post, but in particular "Destructors is all we have": destructors + lambdas + templates allow you to write ScopeGuard, which is a pure superset of finally blocks. Modern C++ has zero need for finally.
Re:Practical Guide to Bare Metal C++ by fauigerzigerk on Saturday July 23, 2016 @09:03AM (#12149352)
ScopeGuard relies on destructors, so it inherits the no exceptions in destructors issue.
Re:Practical Guide to Bare Metal C++ by quicknir on Monday July 25, 2016 @06:53PM (#12162181)
Throwing from a destructor in ScopeGuard is equivalent to calling a function that can throw in a catch or finally block, which you can do in most (any?) languages with exceptions. This no exceptions in destructors "issue" is not a C++ issue. It's a fundamental issue in error propagation. What do you do when propagating error correctly, causes a new error? You can't propagate the first error, because you can't do it correctly. You can't propagate the second error, because that would mean dropping the first.

Classic example is logging an error on failure. This means calling a logging function in the catch block, and then letting the exception propagate. But what if the call to the logging function fails? In Java, coded naively you'd simply drop the original exception. Usually that's not what you want.

You can examine the issue with error codes, it's not any better.

Re:Practical Guide to Bare Metal C++ by maxlybbert on Friday July 22, 2016 @10:05AM (#12143549)
> But to be clear, my initial thought wasn't about exception handling but rather about ubiquitous use of RAII for all sorts of stuff that needs to get done at the end of a scope.

I agree with you on that. It turns out that, say, having a lock guard automatically release a lock when it falls out of scope can cause a ton of trouble when it falls out of scope in a place the programmer overlooked.

Re:Practical Guide to Bare Metal C++ by tibbetts on Thursday July 21, 2016 @08:55PM (#12141074)
Getting a stack trace on Linux or Windows is pretty straightforward. It is too bad it is not standardized but the large projects I work on have stack traces
Re:Practical Guide to Bare Metal C++ by vvanders on Thursday July 21, 2016 @04:25PM (#12139774)
My experience has been no rtti and no exceptions. Quite a few compilers generate rtti if you use exceptions.

Generally I haven't found any error handling solution where exceptions provided superior semantics. The lack of checking means that you can catch more bugs with enum error codes + switch and Wall.

Re:Practical Guide to Bare Metal C++ by MaulingMonkey on Friday July 22, 2016 @12:11AM (#12141655)
> I've never seen RTTI use in production code.

I have, but it's almost never the extremely limited built-in RTTI.

> Except for maybe some tiny embedded systems (like microcontrollers), the overhead of exceptions isn't that bad any more.

Big caveat here: Unthrown / rare exceptions aren't that bad.

I ran into a bug with the windows 8 touch APIs, where the pointer ID for a given finger would become invalid just before we recieved the "pointer up" notification if you pawed at the screen in just the right way. This manifested as an exception being thrown when querying the pointer position - which, of course, wasn't caught, meaning pawing at the screen just right crashed the program.

Catching the exception stopped it from crashing, at least. But the framerate would tank if you pawed at the screen, from exception handling overhead alone.

I've come to frown on exceptions in C++, for production gamedev. Everything else is using error codes anyways, why add a second error handling style into the mix? Your callbacks may be invoked from third party C, which you can't safely throw across, or C++ built with exceptions disabled, which you also can't safely throw across. Heck, just building with exceptions enabled can be difficult - I could not link against some of the C++ PS4 libraries without matching many of their build settings - including having exception handling disabled.

...I resorted to alternative options for handling failed unit tests.

Re:Practical Guide to Bare Metal C++ by maxlybbert on Friday July 22, 2016 @03:17AM (#12142198)
I've seen very little RTTI, but I've seen it.

There are a large number of programmers who use C++ "because it's fast," and they've heard RTTI and exceptions (and virtual function calls, ...) are slow, so they avoid those features.

But the common implementations for RTTI and exceptions have improved. They're much faster than they used to be. The performance problem isn't that they're slow, but that their performance is unpredictable. Not terribly unpredictable, but unpredictable enough to rule them out in real time code.

But for most everyone else, they're plenty fast. Or, put another way, if RTTI and exceptions make your project noticeably slower, you're doing too many dynamic_casts and/or throwing too many exceptions.

At this point the question should be "does this lead to a good design?" and not "does my gut think it's fast enough?"

Re:Practical Guide to Bare Metal C++ by pjmlp on Friday July 22, 2016 @02:11AM (#12141986)
Game engine middleware like Unreal, Qt, MFC, VCL.

The difference being that they created their own version.

Re:Practical Guide to Bare Metal C++ by vvanders on Friday July 22, 2016 @10:21AM (#12143654)
Large difference there is that in those systems you opt-in and only pay for it in the classes you want(which is more in line with C++).

In the case of Unreal we used it to extend the RTTI system to to more things than just isA(). Stuff like member properties(editor hints in window), serialization(network & disk) and stuff you'd use C# attributes for.

Re:Practical Guide to Bare Metal C++ by pjmlp on Friday July 22, 2016 @10:57AM (#12143914)
I guess C++ would have been more well served by having static RTTI, but if it comes will be only on C++20 most likely, although type traits and template meta-programming already get us half way there.

However when I look to such examples, I am quite happy to spend most of my time on JVM/CLR lands.

It is not the meta-programming as such, rather the decltype, trailing return types and SFINAE tricks that put me off.

Re:Practical Guide to Bare Metal C++ by erichocean on Thursday July 21, 2016 @09:36PM (#12141192)
> exceptions are features of C++ that I never see used in real-world C++

Well, I use exceptions in exactly one situation: my request handlers on the server are all one-shot, transactional, and throwing anywhere unwinds to the top of the stack, aborts the transaction, and results in an error message being returned to the caller.

For that specific use case, I find exceptions cleaner than any other approach I've tried. Otherwise, I avoid them as well.

Re:Practical Guide to Bare Metal C++ by pmelendez on Friday July 22, 2016 @12:17PM (#12144478)
Do you work for a Foreign Exchange company? We used it exactly for the same scenario at a previous employer.
Re:Practical Guide to Bare Metal C++ by erichocean on Friday July 22, 2016 @02:47PM (#12145772)
I do not, but thanks for the info I'm not the only one!
Re:Practical Guide to Bare Metal C++ by pif on Friday July 22, 2016 @04:10AM (#12142326)
> RTTI and exceptions are features of C++ that I never see used in real-world C++

Regarding exceptions, in my experience it depends a lot of the cultural background of the developers: Unix developers tend to use them, Windows developers don't.

Re:Practical Guide to Bare Metal C++ by p0nce on Friday July 22, 2016 @07:17AM (#12142884)
In my experience not using exceptions in C++ is more a generational thing than anything. Older foxes don't use them because they don't use RAII systematically either.
Re:Practical Guide to Bare Metal C++ by 72deluxe on Friday July 22, 2016 @05:04AM (#12142475)
Hang on, from all these comments we are saying that nobody ever does dynamic_cast ???
Re:Practical Guide to Bare Metal C++ by Asooka on Friday July 22, 2016 @05:23AM (#12142540)
dynamic_cast doesn't work across .sofiles loaded with dlopen:

https://stackoverflow.com/questions/23383102/dynamic-cast-tr...

I've seen it first hand crap itself on Mac OS X. The gist of it is that if you have a plugin architecture, you can't pass an object allocated in one plugin to another and have the latter do a dynamic_cast. For the same reason exceptions thrown in a plugin can't be caught in another unless the type is defined in the host binary. I.e. suppose the main application has:

    class HostException {
        virtual ~HostException() {}
    };

    class FooInterface {
        virtual void doBar()=0;
    };
Then inside plugin A you do

    try {
        foo->doBar();
    } catch (HostException& ex) {
        // stuff
    }
where foo was allocated in plugin B. Then,

    // in plugin B
    class FooImpl : public FooInterface {
        virtual void doBar() override;
    };
    class MyException : public HostException {
        virtual ~MyException() {}
    };
    void FooImpl::doBar() {
        throw HostException(); // should be just fine
        throw MyException(); // here be dragons
    }
For that reason in places where we call foreign code that we know could throw we just do a blanket catch(...) and bail out on any exception. Similarly we don't rely on dynamic casts at all anywhere, we use our own type system.

The OOP features of C++ are fine for small toy programs but ironically they break when you try to use them for actual large ones that would benefit the most from them. Stick to virtual functions, static and reinterpret casts and nothing else.

Re:Practical Guide to Bare Metal C++ by pjmlp on Friday July 22, 2016 @08:42AM (#12143172)
It works on Windows since the 16 bit days, actually.

You just need to declspec(dllexport) the classes, with the caveat that thanks to the lack of standard ABI you need the same compiler on both sides.

Re:Practical Guide to Bare Metal C++ by Asooka on Saturday July 23, 2016 @05:02AM (#12148880)
Yes, it does work on Windows. Not anywhere else.
Re:Practical Guide to Bare Metal C++ by pjmlp on Saturday July 23, 2016 @10:35AM (#12149560)
Which makes it a compiler issue, not a language one.

Then again, most other platforms never were too C++ friendly and rather lean on C.

BeOS, Symbian, Genode and Windows are probably the only OSes that give C++ some love.

Re:Practical Guide to Bare Metal C++ by _pmf_ on Friday July 22, 2016 @05:49AM (#12142628)
Microsoft's COM probably exists because of this.