A new programming language for games, Annotated: Part 3

By Shamus Posted Sunday Mar 1, 2015

Filed under: Programming 103 comments

The annotation continues…


Link (YouTube)

39:00 Exceptions Are Silly.

Exceptions are where you run a bit of code, but you sort of leave a note for the program, “If anything goes wrong, jump to this bit of code and spit out such-and-such message.” Here is the video Blow mentioned, which talks about why exceptions are such a mess.

My experience with exceptions is pretty limited. In my professional work, we used a large codebase that began life in 1994 as vanilla C. Somewhere around 2000-ish we migrated (in fits and starts) to C++. So I didn’t even have a chance to use exceptions until 2000, and we didn’t really have a lot of need for them. Our software was pretty mature and we already had all the error handling we needed.

As Blow says, in the old C days, there was just this one global variable (a variable available from anywhere in the program) that you could check. “Hey, has anything gone wrong recently?” And then it would reply “532”, and you’ve have to stop programming and find some documentation that told you what error 532 was.

I really don’t see the use of exceptions. I’m not saying they’re useless. I’m just saying I haven’t run into any cases where I thought to myself, “Man, throwing an exception would really solve this problem for me.” This might be due to the toolset I use. The best case I’ve heard for throwing exceptions is to use them during development. But in the programming environment I use, I can just set a breakpoint:

1
2
3
4
5
DoStuff ();
if (something awful happens) {
  int nothing;
}
DoMoreStuff ();

Let’s say that “something awful” is whatever circumstances would cause you to throw an exception. Instead, I stick a breakpoint on line 3. If execution ever reaches there, my program pauses and Visual Studio pops to the front. I can see the state of every variable in play, and I can even retrace the steps the program took to reach this unfortunate outcome. I can have the program advance, one line of code at a time, and watch the variable change as I go. This is a huge part of my workflow, and I honestly don’t know how I’d live without it.

With a system like this, I just can’t imagine what benefit I’d ever get from building robust blocks of code to throw and catch detailed exceptions.

55:00 “Stud Unique Putter”.

I don’t have a lot to say about this section in general, but this is a good excuse to talk about code beauty. So let’s use Blow’s example:

1
2
std::unique_ptr<Vector3>  *positions;
std::unique_ptr<int>      *index_list;

…is indeed really ugly. I realize it’s all dumb gibberish to non-coders, but once you know code you come to appreciate brevity and clarity, and this is neither. It’s messy, verbose, and unclear. Some of this is inherited from C. Here is how you want your program to look:

1
2
3
4
string        name;
int           hitpoints;
int           dexterity;
bool          is_dead;

So I’ve made four variables. Name is a chunk of text presumably holding the character name. Hitpoints is an integer. Dexterity is an integer. is_dead is a bool, which is a value that can only be true or false.

So far so good.

But you can also make pointers. If I have a pointer to a string, then what I’ve really got is an address of memory to go and get the text from. Sometimes you need to do this. So maybe you have a thing, or maybe you have a pointer to a thing. Maybe you have your dry cleaning, or maybe you just have the ticket with the address of where you can go to pick up your dry cleaning. In C, it’s done this way:

1
2
3
4
string        name;
int           hitpoints;
int           dexterity;
bool          *is_dead;

In this example, is_dead is now a pointer. (I have no idea why you would do things like this, but I’m trying to make a clear example. Just go with it.) That little asterisk tells you that is_dead is a pointer to a bool, instead of being a bool. But for the purposes of clarity, we like to keep the code neat. You can see in my example code that we have the types on the left and the variable names on the right. But now this asterisk is a little mysterious. It’s part of the type, but it goes with the variable name. If I do this:

1
bool*          is_dead, is_stunned;

Even after all my years of experience, I still look at this code and think it should mean that is_dead and is_stunned are pointers. But the asterisk only applies to is_dead. I’d need to throw another asterisk just before is_stunned if I wanted it to also be a pointer. Our desire to organize code is at odds with this one oddball symbol, and coding conventions are invented to protect against these kinds of mistakes. (The one I followed for years was “no more than one variable defined per line”. And it existed to deal with exactly this problem.)

You can get used to stuff like thisI mean, SOME people do.. But if you’ve ever tried to teach somebody the language and found yourself apologizing for strange stuff that can’t be intuited from existing rules then you know how ugly this feels. It’s a bit like this XKCD:

”Oh, you’re using their Chrome APP, not their Chrome EXTENSION. They’re very similar but one handles window creation differently.” is a thing I hope I can stop saying soon.

The code Blow is showing us is a serious problem at the other extreme, where a big long mess of syntax is used to express something that really ought to be simple.

For the record, “std::unique_ptr” is shortNot that it’s actually all that short. for “standard library, unique pointer”.

We’ll wrap this series up in the next entry.

 

Footnotes:

[1] I mean, SOME people do.

[2] Not that it’s actually all that short.



From The Archives:
 

103 thoughts on “A new programming language for games, Annotated: Part 3

  1. Bropocalypse says:

    Pointers are scary. I’ve read about them a bunch of times, but I never saw anything that made me think “Ah, so THAT’S why this exists.” Same with bitwise operations.

    Also yeah, I never really got the point of exceptions. I mean, if you can forsee a problem arising in your code, why not actually fix your code to prevent that problem from arising? Maybe if you want to add new code incrementally?

    1. David says:

      There are always problems you can’t write around maybe happening. For instance… every bit of network access you try to do could fail for reasons you cannot control, despite every pre-access test saying they’re fine. (A tree falling on your local Charter routing station after the test and before the real access.) You could wrap every single thing that tries to use the network in error handling checks… or you could use exceptions to keep that check in one place.

      That’s what exceptions are: a flow control tool. You can say “when a problem happens in this section of code, go there to deal with it”, with standard syntax for describing what problems you actually know how to handle and which should be kicked up the chain until someone else knows what to do. (With the final handler being an uncaught exception crashing the program.)

      It’s a matter of taste, but I also tend to prefer “catch (FileNotFoundException, UnicodeDecodeException, TreeFellOnRouterException) {” to “if (err == 322 || err == 6233 || err == 733) {“. You could define constants for the error codes, but that gets so fun when dealing with whatever codes a third party library decided to return.

      1. ET says:

        You also implicitly show another useful benefit of exceptions that error codes don’t get: inheritance. If you don’t care that there’s a NetworkUnavailableError instead of a NetworkLatencyThresholdExceededError, you can just catch the parent NetworkError, saving yourself time and effort. With error codes, such simplifications are much more difficult to do.

      2. Evilmrhenry says:

        In more general terms, exceptions are for exceptional cases.

        Example: you have a parse_file() function, which calls a parse_section() function, which calls a parse_line() function. If one of the lines is broken, you want to stop parsing the file, and output an error. You could have parse_line() return an error code, returning the parsed data and potential error message as input pointers, then if an error was found, returning it to the parse_file() function, which will handle it. This requires 2 variables for error handling in parse_section() and another 2 in parse_line(), and code in parse_section that just checks for an error and passes it upstream.

        Alternately, you could throw a ParseFileException in parse_line(), include the error message, and catch it in parse_file(). This means that parse_line() just needs to return the parsed data, and parse_section() doesn’t need to care about line-level parse errors at all. The logic is the same, but less code is needed to specify the logic, which results in fewer places for bugs to hide, and less thought needed to understand the functions when you come back in 3 years.

        Another example: You now have a cache of data formed from reading the parsed data. You then write this data to disk because it takes much less time to read. Except, oops, sometimes you don’t have write permissions on that directory/file, or the disk is full, or it was on a flash drive that just got removed or some other weirdness comes up. None of your development or testing machines run into this, so you never put code in to handle it, but a small number of players have this bug, which causes the game to crash on startup. However, if exceptions were in use, you could wrap the file writing code in an exception handler, catch the generic IOError or something, and do something more useful.

      3. Alexander The 1st says:

        Pretty much that last bit about library errors – it allows you to get a general understanding of where the error will be triggered in your code without having to look up the error code and confirming that a.) It’s an error on your end that you can fix (i.e. “Oh hey! I miscalculated the edge of this list and end up accessing the i+1 item in the array when i == the last index in the array!”), b.) It’s an error that had to do with user input, and thus is something you need to tell the user to fix (i.e. “Please choose a filename that does/doesn’t [as applicable to reading/writing respectively] exist”), or c.) It’s an error based on the library not taking a string formatted float, double, or long instead of an integer, especially in cases where you need to communicate with another service (i.e. “This value for the user’s age in the sql table you’re trying to add to does not support floating point numbers as valid age values.”).

        As a bonus, someone else can be added to the team who doesn’t know all the error checking a particular chunk of code could be doing, take a look at the fixes involved, and understand both why the error could happen, why the checking solution does what it does, and what to look for if the error is thrown, without needing to look up all the documentation of all the code used for that project, at the appropriate version level.

    2. Kian says:

      About pointers: There are a couple reasons you need pointers. The first, is to hold data of variable length.

      Say you want to hold some kind of text. If you know exactly how long the text is, you can tell the computer “give me room for this many characters”. But if you don’t? Trying to guess at the maximum length would be inefficient, you’d be wasting memory if you guess wrong. Instead, you store a pointer. When you get the text you need to hold on to, you tell the computer to write it down wherever it’s good for it, and you store the position of where it wrote it.

      The other big use of pointers is to avoid copying needlessly. Lets say I want to give the text to some other function. I could copy the text into the function, or I can give the function a pointer to the text and let the function access that text directly.

      They can also be used to create data structures. Let’s say I want to make a list. One easy implementation would be to create nodes that each store the data I want to make a list of, and a pointer to the next node of the list. Or you could make a tree, with each node holding a pointer to each child node.

      1. David says:

        Even if you’re not using C or some other pointer language, you often have to deal with the general concept of pointer indirection, since there will almost always be the idea of stuff passed by value or by reference.

        E.g. If I pass an array to some function and modify it inside the function… does that modification persist outside of the function? Depending on the language this changes. (Actually, for funsies, in PHP in particular it changes depending on how the function is defined…)

        1. nm says:

          ZOMG PHP is such a pile of WTF.

          The concept of “pass by reference” is abstracted in a lot of languages. In C (not C++; they’re different), it’s done with pointers. Direct access to blocks of memory mapped I/O needs pointers to work. If you’re working on a driver that needs to twiddle bits in a memory-mapped register on some peripheral, you must use a language with raw pointers.

          1. Volfram says:

            In the world of programming languages, PHP is really more of a pidgin.

    3. Peter H. Coffin says:

      Pointers themselves aren’t exceedingly useful. Pointers to pointers, though…. Lemme see if I can show why.

      Pretend you’ve got a big thing you want to load. Texture pattern, or audio snippet or something. If you load it, it’s got to go somewhere, so you ask for some memory to load this thing, get back an address for your block of memory, and load thing thing there. Where you store that address of your block of memory is the pointer, and we’re all clear on what that is from Shamus’s description.

      HOWEVER, that thing is stuck in memory at that very spot, forever. (Or as long as “forever” is until you tell the operating system that you’re done with it.) If it would be convenient for it to move, as (for example) part of garbage collection, so that it can be snuggled up to other things and not leave little blocks of unused memory between things still being used, it can’t. Because your program is the only thing that can do anything with that chunk of memory.

      Where the pointer to the pointer comes in is that if, when you ask for a chunk of memory (and your OS/resource allocator if you wrote your own, supports it), you can mark the chunk as movable. Instead of getting back the address of a block of memory, you get back the address to a pointer that the OS/allocator maintains. When you want to access that chunk of memory, you dereference twice instead of once, to get from your pointer, to the OS pointer, to the actual thing.

      Needless complication? Nope. Because between when you loaded your pattern and when you next refer to it, the OS/allocator has cleaned up. It’s seen that you disposed of another pattern below and a pattern above, and just prior, you asked for a block of memory that was bigger than either section alone. So it made a copy of your pattern, updated the OS pointer to the new copy, and allowed the memory where the old pattern was to be added to the free memory right next to it, and fulfilled the request for the bigger block. And your program didn’t have to pay any attention to that the pattern moved, because it could still get to it from its own pointer to the OS pointer.

    4. guy says:

      You are most likely to need to do it if users are allowed to input things, because they are jerks and will input wrong things. It’s also particularly advised for code reuse; if you have a method that is supposed to insert into a list, you may want to react differently to failure in different places that call the same thing.

    5. The Snide Sniper says:

      Here are examples of problem arising in code that you will not be able to fix:
      – Suppose you need to store N bytes of data in memory. If N is greater than the amount of memory that can be allocated, you have a problem.
      – Suppose you create a library which lets a user create and destroy resources. What happens when a user destroys a resource, then tries to use it?

      Even if your code is 100% error-free, it might be asked to do the impossible.

      You could also have situations where the problem is (at least at the moment) not worth fixing. If you know a corner case exists and is easily detectable, but you think it won’t interfere with what you’re planning to do, and you realize it would take 70 hrs to fix that one case, it becomes a far-better use of your time to simply test for the case and throw an exception. If you have time, you can come back and fix it later; if not, at least your code won’t silently give wrong answers.

      Pointers are useful for anything that you can’t allocate when the program is compiled.
      Yes, you can do things like assume that you will never have more than 16 space-marines, and allocate their memory beforehand. Yes, you can enforce a rigid format and a hard size-cap on your video game’s levels.
      Or you could do the smart thing, and use pointers.

      Once you make the jump to using pointers, you gain tons of freedom as far as memory goes.
      Need another enemy? Allocate some memory.
      Need to add and remove elements from an ordered list in constant time? Use a linked list.
      Do you have 3 chunks of data that are used by 300 otherwise-identical objects? Pointers to the rescue!

      Now, I know what you’re thinking… “But Snide, you can do all that (except the last one) using the standard library’s containers!” And yes, you can use someone else’s code. If you think this means pointers are useless, however, you’re deluding yourself; the code you’re using just hides the pointers it’s using behind a layer of abstraction.

    6. bloodsquirrel says:

      The main advantage to exceptions over error codes is that they prevent your program from plodding on in an error state for who knows how long because you forgot to put in an error check in one place. Your program stops where it is and the exception goes up the stack until it’s caught. And it can be packed with whatever data you want so that (if they’re implemented well, which they often aren’t) you can tell what’s going wrong without having to trace through the code.

    7. bloodsquirrel says:

      Every programming language uses pointers. Any object type in Java is a pointer. C++ just gives you more control over them.

      And not just to do a specific thing- pointers exist in C++ because that’s how memory works, and C/C++ are primarily useful for having less layers of abstraction between you and the hardware as other languages.

      But one good example of a practical application: In C++, you can choose between allocating memory on the heap for an object or just putting it on the stack.

      They’re also handy for passing to functions so that you can get multiple return values from a function without creating a new class just for that one function’s return value.

      1. guy says:

        The advantage of Java’s way of doing things is that you can’t add an integer to a character unless you really mean to. Which is actually the first thing I ever did with C++ pointers; I was writing a character-shift cipher.

      2. Downie says:

        “pointers exist in C++ because that's how memory works”
        And this means that it’s a lot easier to write efficient code. Suppose I’m writing a paint program that is doing something like this to update a couple of pixels in an image: (Not to be used as actual code.)

        pixels[x][y] = rgb;
        x++;
        pixels[x][y] = rgb;

        This looks clearer than:

        pixelPtr = rgb;
        pixelPtr += SCREEN_HEIGHT;
        pixelPtr = rgb;

        But: at the assembler level, depending on your optimizer, the first one might come out as:

        Take the address of ‘pixels’ array and then add x multiplied by the height of the array and then add y and then copy the RGB value into it and then add one to x and then take the address of ‘pixels’ array and then add x multiplied by the height of the array and then add y and then copy the RGB value into it.

        While the latter bit of code compiles as:

        Copy the RGB value into the address, then add a number to the address, then copy the RGB value into the address.

        …which should be many times faster.

  2. heroofhyla says:

    Exceptions are weird. If you’re writing a function that only takes a subset of whatever data type it accepts as a parameter (only even numbers for example) then it would make sense to have it throw some sort of exception if the parameter is unacceptable. However, exception calls (at least in java) are very slow, and shouldn’t be used for general program flow control. That means you need to do your own validity checking before you pass your variable to the function, and then the function does it’s own checking before processing. It all seems inefficient. And it’s even worse if it’s a checked exception, now you need to stick a try-catch block around it to handle the exception, even if you know you won’t ever trigger it.

    1. Richard says:

      Exceptions are a way to handle things going hideously wrong.

      If the exception was thrown because I, as the programmer, did something stupid then I just let the program crash.
      – eg if I tried to access a value beyond the end of a vector, std::vector throws an exception.

      It bubbles up to the top and kaboom, my program crashes and the either OS displays an error message, or the top-level exception handler catches the exception, logs whatever info (stack trace etc) is available, shows a “I’m really sorry about this” message to the end user, tries to save as much of their data as it can and dies.

      I prefer this to “carry on regardless” (as may happen in C arrays), because I think it’s better to fail fast and have the user’s data still be “mostly” there, than to propagate weirdness for some time, corrupting their data until it’s finally totally impossible to do or recover anything.

      If there’s any predictable error conditions that I can deal with within my actual program, then I don’t throw. Instead I check (and fix as appropriate).

      If an external thing (library or communication with the outside world) can cause an unpredictable error, then I’ll use exceptions to handle them.

      A lot of this is simply code style – in C++, exceptions are pretty fast.

      1. The Snide Sniper says:

        Exceptions aren’t just for when things go horribly wrong; they’re also useful for when something goes wrong, but can be fixed.

        There are four different levels of exception safety:
        No-throw guarantee – Exactly what it says on the tin; almost always impractical.
        Strong exception safety – If something goes wrong, restore to the state from before we did anything. Sometimes practical.
        Basic exception safety – If something goes wrong, leave everything valid (if not correct), and don’t create leaks. Often practical.
        No exception safety – Exactly what it says on the tin; always practical.

        Why are exceptions useful for this? Because it becomes easy to catch an exception, undo any damage that you’ve done on this level (if possible), and pass it up the chain. With inheritance (and with different standardized exceptions for the strong, basic, and “no” safety levels), you can even have self-reporting safety levels.

        Can it be done using an “error-code” variable? Yes, but testing for exceptions by checking variables is probably less efficient than using the exception-handling mechanism (in most C++ compilers, exception handling occurs ONLY when an exception was thrown), and it doesn’t play nice with template or general-purpose code because you’d need to know in advance which variable to check. Depending on the programmer, it might also be interpreted as less elegant.

        Also note that with catch (…) {}, C++ lets you see an error and handle it however you wish (even ignoring it). It does change the default behavior (default C behavior is continue unless caught, default C++ behavior is exit unless caught), but is that really a bad thing?

        If nothing else, exceptions are useful because they provide a standard way of indicating an error. Without exceptions, it’s quite likely that every library would have its own “did I fail?” function, which should be checked after every operation. With exceptions, error checking are simply a matter of try {} catch () {}.

      2. Blake says:

        My problem with exceptions (outside of the ugly C++ syntax, which, to be fair, is pretty common in C++), is that they’re basically a ‘goto SomeOtherFunctionOrMaybeCrash’.
        And if I don’t know the code I’m calling will throw an exception I might not catch it.
        With simple error codes I know to check for them, deal with them, and do whatever printing/asserting I want with unhandled cases. Exceptions aren’t obvious in the function declaration.
        This sort of thing could also mess you up if somebody adds an extra exception to a function you’re already calling, suddenly the code crashes on what might be an acceptable use case.

        Without exceptions I know my code
        A();
        B();
        C();
        Will call A, B and C in order. With exceptions if A throws something then B and C might not happen (even if all 3 were caught in a try/catch somewhere).

        And lets not get into exceptions for acceptable behaviour, I know some Xbox One functions that throw an exception instead of returning nullptr (which is pretty poor), but also some that throw exceptions internally as part of their normal routine which means turning on all exceptions in the debugger is bad, but not turning them on means you don’t break when something goes wrong. The flexibility of exceptions can lead to some really poor use cases.

        TL;DR; I prefer asserts and error codes to exceptions, they’re faster, more robust, and you can’t really do them wrong.

        1. guy says:

          Java actually requires you to put “throws X Exception” right next to the parameters in the declaration if there’s any possibility it’ll throw most types of more esoteric exceptions. It’s not required for stuff like IndexOutOfBounds, but if you define anything custom you’ll have to do it, and that propagates up the chain. Also, the Javadoc syntax is designed to let you specify every Exception a method can possibly throw and the conditions under which it occurs.

          1. Neko says:

            As tedious as it was to write methods that could throw all number of exceptions, and as tempting as it often was to catch (Exception e) { }, I really think Java did the right thing there – it forced you to consider all those points of failure that typically surprise you because “that’ll never happen”, like a previously existing file suddenly becoming read-only.

          2. Zukhramm says:

            Actually (sorry) whether your custom exceptions need to be checked in Java depends on which exception class you inherit from.

        2. The Snide Sniper says:

          Asserts are faster than exceptions (because they are completely removed except in debug code), but have only one option – crash.
          Error codes are slower than exceptions on average, and have exactly as many options.
          Exceptions act like error codes, but faster and with crashing as the default behavior.

          You mention that some libraries use exceptions as part of their normal routine… Use try {} catch () {} instead of turning off exceptions.
          Yes, the library is probably poorly made, but you’re being faced with a problem similar to someone poking you in the face, and your solution is to chop off your head so they can’t do it anymore.

          Let’s compare error codes and exceptions.
          First (and most importantly), catching them: Exceptions aren’t obvious in the function declaration unless the library-creator used the deprecated throw(type) syntax. Error codes aren’t either. If you’d realize you need to catch something with an error code, you’d realize you need to catch it with an exception. The difference is that an uncaught error code silently fails (which, depending on the error, can be either desired behavior or a horrible code-breaking problem), while an uncaught exception kills your program (letting you know there’s an error).
          Second, performance: Regardless of error-handling method (except not handling it), the error needs to be detected. This is equally fast for all approaches. Next, the error needs to be recorded; error-codes set a variable (fast) and later check a variable (also fast). Exceptions unwind the stack (would happen anyway when the function ends) and pass handling on to the exception handler of the function that called this one. When an error occurs, exceptions may be slower. When an exception does not occur (which should be the usual case), an exception does nothing where error-codes still need to be checked (fast, but not as fast as literally doing nothing).

          Yes, changes to a code that’s already in use will cause trouble; however, the trouble caused will be no more for exceptions than for error codes. If you’re already checking error codes (handling exceptions), the new error code (or exception) may be unrecognized, and your program quits (or your default error scheme). If you’re not already checking error codes, then the error-code version silently fails where the exception version will let you know there’s a problem by crashing.

          For those of us who prefer errors to be reported, silent errors are worse than crashes.

        3. Kian says:

          “With simple error codes I know to check for them, deal with them, and do whatever printing/asserting I want with unhandled cases. Exceptions aren't obvious in the function declaration.
          This sort of thing could also mess you up if somebody adds an extra exception to a function you're already calling, suddenly the code crashes on what might be an acceptable use case.”

          Error codes are no more clear in the function declaration than exceptions. At most, you know they return a special error type. But some functions return valid values if they succeed and special values if they fail. The signature doesn’t clarify. With exceptions, you should proceed as if everything throws unless they specifically say they don’t.

          And if someone adds a new error code you didn’t check for, you might crash or worse, continue in an invalid state.

          Also, when things are going right exceptions are faster than error codes. Error codes require constant “if” checks. They’re faster when errors happen at the cost of showing down regular execution.

          1. Richard says:

            No, when things are going well exceptions are roughly the same speed as error codes.

            There is still an “if(error) {doStuff();}” there, it’s just hidden by the syntax.

            1. Kian says:

              Not with modern (zero-cost) exceptions. I’m not an expert, but essentially my understanding is that all the tables for what to do in case of an exception are built at compile time, and so there is no runtime cost. Here’s a write up that explains it in more detail than I can: http://mortoray.com/2013/09/12/the-true-cost-of-zero-cost-exceptions/

              If you disagree, please provide sources.

            2. The Snide Sniper says:

              Suppose things are going well (no errors, no reasons to throw exceptions).

              Let’s look at what something using an error-code method does:
              -function begins-
              Check for errors. None found, so do nothing.
              -function ends-
              Check error code to see whether anything happened.

              Now let’s look at the same situation, handled using exceptions:
              -function begins-
              Check for errors. None found, so do nothing.
              -function ends-

              Notice that there is less work done for exceptions. This is why exceptions have less overhead than error codes when things are going well.

              What about when something goes wrong (when there is an error)?

              Let’s look at what something using an error-code method does:
              -function begins-
              Check for errors. Error found, so set a variable.
              -function ends-
              Check error code to see whether anything happened.

              Now let’s look at the same situation, handled using exceptions (There are several ways that “throw” can be handled by a compiler, so we’ll look at one of them):
              -function begins-
              Check for errors. Error found, so “throw”.
              “throw” first destroys all variables the function was using, then checks the stack to figure out where this function was called. If that spot has an exception handler (provided by an “except” block), jump there and perform that code. If not, move up the stack and repeat until an exception handler is found.

              This is why, when exceptions actually occur, they are not as efficient as error codes. Keep in mind that an inline function can also streamline exception handling.

              Between the two cases, it becomes clear when each one should be used:
              Use error codes for warnings and other common, non-fatal errors. (why you’d leave a common error though…)
              Use exceptions for rare or fatal errors.

              You can also make a choice based on default handling behavior.
              Default error-code handling is “ignore”.
              Default exception handling is “crash”.

              When writing a program, crashing is usually preferable, though providing an appropriate error message, using std::exception’s “what()”, is wise.

        4. wumpus says:

          Yep, that’s the dirty secret of exceptions. When your CS profs told you to never, ever use goto, they meant never, ever _explicitly_ use goto. Implicitly, well, that’s another story. On a related front, I’ve seen people use do {} while(false) ‘loops’, just so that they could use ‘break’ instead of ‘goto’.

        5. Hankelhankel says:

          Without exceptions I know my code
          A();
          B();
          C();
          Will call A, B and C in order. With exceptions if A throws something then B and C might not happen (even if all 3 were caught in a try/catch somewhere).

          That’s basically the point though? If A() does something that you didn’t think it could do (because if you had thought of it, you would have checked for that possibility and prevented the exception), then most of the time you don’t want B() and C() to happen, because you cannot know how they’ll interact with A()’s hitherto unknown behaviour. You want to go straight to the “restore last good state and tell the user I couldn’t do whatever he asked me to do” piece of code.

          In the much less common instance where you’re positive you want B() and C() to still happen no matter how A() spazzed out, you just wrap them in separate try-catch blocks.

          (Or, if you’re coding VB and you think Russian Roulette is a fun game but a bit too safe and predictable, you put a nice On Error Resume Next at the top of your function and enjoy the tears of future maintainers.)

          1. Blake says:

            “You want to go straight to the “restore last good state and tell the user I couldn't do whatever he asked me to do” piece of code.”

            That is the exact opposite of what we want to do when making games for consoles.
            If it can crash, we want it to crash immediately while we’re still making the game.
            We don’t want to write anything that might occasionally fail under some circumstances.

            The only time we want to ‘restore the last good state’ is in development when our scripts hit an assert, we fix the issue, then reload the scripts and continue execution.
            We never want to write anything that can occasionally fail, because if some code has a 1/10,000 chance of failure, then we can be sure people will be hitting it day 1.

            1. Hankelhankel says:

              “Turning off” exception handling while you’re developing or testing is the easiest thing in the world.

              The standard exception handling function, which you will call in (almost) every catch{} block, typically starts with some debug-mode-only commands that will display useful information about the exception. But if being notified of the exception isn’t enough for you, you can simply add a debug-mode-only command that re-throws the exception, or Asserts 1=0, or does whatever you need to happen while testing. Then the production release won’t have anything of that and will skip to the real exception handling code.

              We never want to write anything that can occasionally fail

              Nobody wants to! And yet, mysteriously, everybody does.

              Assume that you will have bugs in production. Do you always want your game to crash out if it encounters a bug while updating the UI textures, even if that bug will be meaningless in the next frame? When Skyrim couldn’t load an animation, a model or texture, should it have CTD’d every time instead of showing a static purple cube?

  3. Bruno M. Torres says:

    Since we are talking about game programming, you guys should check HandMade Hero. It’s a very interesting series about coding an entire game from scratch, without 3rd party libraries.

  4. Chris Davies says:

    The C++ standard library is indeed something of a horror show when it comes to code that at least looks elegant. unique_ptr is nothing when compared to types that look like std::const_iterator<std::pair<int, myclass>>. Lets face it, the standard template library is the whole reason C++ now has auto typing. Of course it doesn’t have to be like this, there are any number of std library replacements for C++ that don’t have that kind of pain associated with them.

    As for exceptions, there’s a deal of freedom associated with knowing that if flow of control ever reaches this line, the things that happened on the last line definitely succeeded. There’s a good way you can tell the novice C coder from his experienced professional brethren, and it’s the their attitude towards “goto.” The novice programmer has had it drilled in to them that goto is considered harmful, and they dogmatically avoid its use, which practically speaking means they start abusing “return” rather than correctly using “goto,” ironically making their code much less maintainable. On the other hand, I don’t think I have ever seen professional quality C that doesn’t half-heartedly reimplement exceptions and stack unwinding using goto. Some sort of structured error handling is a must for a programming language in my opinion.

  5. I’m not a fan of exceptions either, particularly in C++, but they were invented to solve a really hard problem in programming, It’s perhaps the hardest problem in practical computer programming because it’s the #1 source of complexity: the combinatorial explosion of error paths. Suppose you’re writing a function that needs to perform a number of actions that can independently fail (invalid file access, out of memory, overflow/underflow, etc.), and each of these actions requires a cleanup step. For example, imagine a function that opens two files (which each must be closed later) and dynamically allocates some memory (which must be freed later). Normally, A happens, then B, then C. Finally in reverse order at the end C is cleaned up, then B, then A.

    If something goes wrong in A, you clean up A (if needed) and return an error code. Simple. If something goes wrong in B, you clean up A and B, then return an error code. If something goes wrong in C, you clean up A, B, and C, then return an error code. In C++, exceptions combined with RAII (read: cleanup is automatic and implicit) is intended to make this complexity easier to manage. When something goes wrong, you throw an exception (or let one fall through), which cleanly unwinds the stack (you hope) and allows someone somewhere else to handle it all in one place higher up the stack.

    To complicate matters, sometimes cleanup itself can fail. C++, and exceptions in general, are really awful at this part. For example, closing a file can fail (disk full), which means the file is probably truncated, and that data corruption must be handled. So in that rather simple function that does three things, there are up to 12 error paths that need to be considered and appropriately handled in order to be bug-free. Now scale that to even a moderately sized application and you’ve got a recipe for lots of subtle bugs. Most applications are riddled with these bugs, and exceptions only help a little to reduce their complexity. All the error paths are still there and you now how to consider how the stack will unwind when an exception is thrown, and, yo dawg, don’t even think of throwing an exception while an exception is already in motion! Those file close failures will be silently squelched!

    There have been other approaches to this. Go has a defer, panic, and recover system that’s sort of like exceptions but gives the programmer more control with less overhead (you don’t need to declare a new class, like in RAII). Unfortunately I don’t have enough experience with it to say if it’s any better than exceptions, though. In C you can use setjmp() and longjmp() to simulate exceptions in some limited circumstances, but without RAII it’s really difficult to ensure that you’re cleaning up properly. This is a problem still lacking good solutions. The best we can do right now is make designs that minimize error paths.

    1. Blake says:

      FYI, Blow’s language goes for defer, and having looked at the concept I wish it was a part of c++ (I know I could do magic making scoped structs or something that call functions on destruction, but ick basically).

      As for exceptions I know in the games I’ve worked on nobody has ever felt like exceptions would be the right way to handle things (we tend to use error codes for system things that could fail for any reason, and asserts in our own code to verify things like inputs).

      When I was starting out error handling seemed like it would be a big deal. But now, years on, I’ve written tons of network and input code on a lot of different platforms and haven’t run into a situation where they were difficult.
      But now working on the the Xbox One where any system call can throw an exception has been a pain because they’re the only places in the code base where we have try/catch blocks and when you don’t use them all the time they’re far too easy to forget.

      1. AdmiralJonB says:

        It’s not that error codes are difficult to write, it’s really just that they’re very time-consuming to implement and understand. Having to write different error codes for different functions and handling that all throughout your program has a very high cost in terms of time. Having to read the documentation every time you look at a new function to know if the return value is an error code or the result of the function you want to use. Or do you use the pointer parameter for the result, or is that just there for speed of an input. Is a success a 0 value or not? Even in the C standard library this changes, and if you don’t know off the top of your head your going to google to search for documentation (if it exists). Have you ever tried to use code written by foreigners with comments in other languages? Try understanding their documentation with google translate and you’ll start to realise that exceptions are easier for a coder.

        I agree however that exceptions should be used for exceptional cases. In your networking code, there’s probably errors far beyond your control that happen all over the place, and happen quite regularly. Unwinding the stack does take time, and you don’t want to be taking up precious processor cycles all the time.

        But if you’re writing code that uses other peoples’ code that has yet to be written, and they aren’t perfect programmers that know how to handle every error case, I’d rather be able to handle it with a try catch block. It’s much simpler and saves a lot of time. I don’t need to know what error is thrown, I can just print out the error message due to polymorphism, and close down the program cleanly. Plus, it means if a new error comes up from someone else’s coding, my code can handle it and still print a completely relevant error message.

        1. Blake says:

          “I can just print out the error message due to polymorphism, and close down the program cleanly.”
          Not so much an option in console games.

          And sure you need to know what you’re calling and what it decides is an error case.
          Outside of a few weird Win32 calls on our PC build though, pretty much every external library I’ve used has a negative number as an error (or occasionally a non-zero number).

          The code I write myself tends to assert on the inputs if it’s stuff we can control (we know every bit of data that’s shipping with our game so we can guarantee it’s all correct). But for all cases where error codes are necessary (system libraries mostly) I have a simple struct that contains an enumerated error code (which we can define) and the system error code.
          Then all the game side code that deals with asynchronous tasks (the main ones that return errors) just check whether the enumerated error code is ‘success’, if it is not it can try handling special known cases (using our code as it is cross-platform), or default to a generic error message.

          TL;DR;
          I don’t need to write error codes throughout most of the code as most of the code is not built to fail. The places we know things can go wrong (those that call external libraries) need to know what they’re dealing with, but that’ll be the case regardless of whether they’re using errors or exceptions (spoiler: most platform APIs don’t use exceptions).

  6. Kian says:

    I’m in the camp that likes exceptions. I’ve worked with code that does error checking correctly, and exceptions are, I feel, a much cleaner solution. Shamus’ approach, putting a breakpoint, can work in development to catch programming errors, but it wouldn’t work for a shipped program.

    For example, if you ask the system for memory and it reports “out of memory” in a user’s computer, you can’t debug his pc. And it wouldn’t make much sense, being out of memory is not a programming error. It doesn’t matter the state of the program at the time, because the issue is that the user has too many programs open and not enough memory. You don’t want to immediately crash either, just because you couldn’t do one thing. Maybe he has been working on his thesis for three hours without saving, and you want to preserve that work. So you throw a pop-up, “System is out of memory. Please close some programs and save your work.”

    You have two ways of doing that. One is to report errors. The other is to throw an exception. Reporting errors means that every function that could fail, has to return an error variable. And a function can fail if either it, or any of the functions it calls, can fail. So when making robust code, you have to check if your calls failed, and if they failed you have to either manage the error or, more likely, report that error to your caller, which will report that error to their caller and so on until you reach a level that can do something about it.

    Which means that Shamus’ snippet would have to look like:


    ErrorType SomeFunction() {
    ErrorType e = DoStuff ();
    if (e != SUCCESS) {
    return e;
    }
    e = DoMoreStuff ();
    if (e != SUCCESS) {
    return e;
    }
    return SUCCESS;
    }

    The whole codebase is absolutely covered with the pattern “e = Call(); if (e != SUCCESS) { return e;}”. And if someone ever forgets to write that around a call that could fail, your entire project is compromised. Because things will fail in every possible way they can fail. So when writing you have one more thing to take care of, that is tedious and repetitive. And nothing is better for introducing errors than tedious and repetitive tasks. Maybe someone makes a typing mistake and checks if (e = SUCCESS), which assigns SUCCESS to e instead of comparing them, and now your code will quietly ignore any errors reported there.

    With exceptions, the same code looks like this:


    void SomeFunction() {
    DoStuff ();
    DoMoreStuff ();
    }

    The error propagation is taken care of for you automatically. You can focus on what the code does without having to check error codes constantly.

    1. ben says:

      Exceptions also allow you to have a standardized error handling way in third party library. There nothing more frustrating to me than finding a library that perfect for me except that it decide to quit on error when you want to continue the execution.

      It also really cleaner when you want to check different types of errors at different levels of your program.

      all be said, exceptions are really required for very big software projects. They are a bit of overkill for small program.

      1. Atle says:

        That’s one of the problems with exceptions, it “quits on errors”. If I want to do A() then B(), and don’t care if A() succeeds or not, exceptions forces me to implement error handling code between A() and B() that does nothing.

        I want:
        A()
        B()

        Exceptions forces me to:
        try{
        A()
        }
        catch (Error){
        /* do nothing because A() failing is of no concern */
        }
        B()

        Exceptions can be useful to separate normal behaviour from error handling, creating cleaner and more readable code. But it can also force extra mess, boiler plate and unwanted behaviour.

        1. Kian says:

          I find it odd that you would want to call a function and not care if it fails at all. It may be your code is abusing exceptions and using them for things that aren’t really “exceptional”. Otherwise, if you don’t care if it fails, why call the function at all? You could assume it always fails and remove it entirely.

          1. guy says:

            An example of that sort of failure is an external sensor that might intermittently give a garbage value. So you might send the value to a function that fails on a garbage value and then do something based on the last n successful inputs. There’s probably a more graceful way to handle that if you’re writing it entirely yourself, of course.

            It’s also possible that the function matters to the user but has no impact on overall program flow, though again if you’re writing it yourself you probably just won’t throw one out of that function. And I’m not sure about C++, but in Java the compiler makes you catch/pass on certain exceptions, which can be inconvenient. Specifically, if you have a function that throws an exception that needs to be dealt with on a negative input, and it’s literally impossible to get a negative input there due to program flow because you’re passing the absolute value of something, it’ll still make you deal with it. You tend to get these issues with code reuse, where throwing an exception makes sense in only some of the uses. You could write multiple versions, but that way lies madness.

          2. Atle says:

            Assume you want something like:

            data = addDataFromResource(data)
            doStuffWithData(data)

            The resource may or may not be there. If it’s there, data is updated with new data. If not, the data is not updated.

            If addDataFromResource() is trying to read a resource which is not there, all it means for my program is that data is not updated. This isn’t an error situation, and I don’t want to be forced to handle it as such.

            Exceptions are sub functions trying to decide on behalf of parent control flow the importance of a situation. “Hey, you need to handle this important situation!” I should not have to write extra code just to say “No, I don’t, now let me continue!”.

            Sub functions should simply raise a flag, and leave it to the parent control flow to decide what to do about it.

            1. Kian says:

              If addData is throwing an exception when you try to read the resource, but you know the resource is not there, then that’s an abuse of exceptions. You don’t throw exceptions for things that are acceptable.

              Now, maybe inside addData you have a readFile function, which simply returns the file in some format. It makes sense for readFile to throw, since it has a simple job that it can or can’t perform. The solution then is to check if the file exists before trying to read it, instead of trying to read it and failing and checking the error later.

              addDataFromResource(data)
              {
              if (!fileExists())
              return;
              fileData = readFile();
              data.update(fileData);
              return data;
              }

              This is completely orthogonal to the existence of errors, because the file not existing is not an error. Trying to read a file that doesn’t exist is an error, and you should not do it.

        2. ben says:

          ok it be a while that I used exceptions seriously (or coded seriously for that matter) but that really depend of the language you use. In python I’m quite certain you don’t have to, I’m not sure in Java and I never worked with c++ exceptions.

          But as far as I’m concerned, that kind of prove my point: exceptions allow you to deal with quit on error problem. I’m assuming the case you cite can only happen with a third party library that doesn’t exactly fit your need (because otherwise you wouldn’t throw a useless exception), so it allow you to work with it as you wanted it to work. Is it pretty? no, but working with third party lib is rarely pretty.

          (By the way, I am the only one that think that Shamus should finally bite the bullet and start learning python?)

    2. Richard says:

      That’s a bad example, because if you’re out of memory then you’re (almost always) simply screwed.
      As the MSDN says, “OutOfMemoryException exception represents a catastrophic failure.”
      It takes memory to create a dialog – and you’re out of memory, so you can’t.

      That said – .NET does throw that out of memory exception when some types of container are full (stringbuilder). They don’t actually mean out of memory, just “I’m full”, and are thus possible to handle.

      1. Kian says:

        Sort of. The unwinding will release some resources, which could help, and you can have preallocated memory for them that only comes into use in these emergency situations.

        Of course, with virtual memory and the like, it’s rare that you’ll run into that issue.

      2. Mephane says:

        Many an out-of-memory error does not originate from your memory being actually full to the brim, but simply not being able to provide that big contiguous piece of memory you requested, while having plenty of space for some exception code, creating and printing error messages etc.

    3. Mephane says:

      While I absolutely agree with your point, the unintentional assignment as in “(e = SUCCESS)” results in a warning anyway (at least in all C++ compilers I have ever used).

      1. Kian says:

        True, but I’ve worked in places where compilation could throw thousands of warnings and no one cared (I went on a crusade to clean up warnings in the files I touched, but the rest of the team didn’t much care and the count kept going up). So a warning is no guarantee that a mistake will be caught and fixed.

        1. Ian says:

          The shear joy of working with a code base that has warnings as errors turned on is a pleasure that is hard to explain to non programmers.

  7. EmmEnnEff says:

    For being a standardized error-logging mechanism, exceptions are pretty good (Especially in languages where you aren’t dealing with raw pointers, and returning -1 for an error is very un-idiomatic).

    C++ exceptions, unfortunately, due to RAII, are a bit broken by design. [http://yosefk.com/c++fqa/exceptions.html#fqa-17.1] – however, this is a problem that mainly affects unmanaged languages. This distinction is a big reason for why the Google C++ style guide [http://google-styleguide.googlecode.com/svn/trunk/cppguide.html#Exceptions] bans them, but exceptions are encouraged in other languages.

    1. Alan says:

      The C++ FQA should be taken with a large grain of salt. The author has an axe to grind and is uninterested in actual evidence that people manage to be productive in it. He trots out theoretical problems as though they are real. He rehashes the same complaint repeatedly pretending he’s actually made multiple points. I can only assume Stroustrup kicked his puppy.

      1. Alan says:

        A more full discussion of the FQA.
        https://gist.github.com/klmr/5423873 Ultimately, if the FQA says you can’t do something in C++, it’s almost certainly wrong.

        1. Mephane says:

          I always read the FQA as satire about critisizm against C++ and never took it seriously anyway. :)

        2. Neko says:

          I think the FQA is useful if only to keep you aware of the uglier parts of C++ that have all the gotchas and landmines. While it might be possible to code around a lot of those things, it’s important to be wary of all the pitfalls.

    2. Kian says:

      I’ve read the FQA several times (it has a lot of very useful info), but the writer has a clear bias against C++, and it’s a bit out of date currently.

      I would not say they are broken by design due to RAII. On the contrary, RAII is intended to permit exception safe code to be written. Yes, the problem of exceptions in destructors is a real one. However, it’s important to understand that the issue is not throwing an exception in a destructor while an exception has already been thrown. What you don’t have to allow to happen is letting an exception escape from your destructor. You can have an exception in your destructor, so long as you catch it in your destructor and deal with it before you return.

      Which is a bit of subtlety that the FQA doesn’t cover. It says instead that you can’t throw or you’ll call terminate, which is not right.

      And in any case, the problem is not with exceptions. Exceptions just force you to deal with it. In error returning code, if you detect an error, jump to the error handler, and your error handler calls something that causes an error, you’re just as screwed. What do you do, an error handler for the error handler? Then what if that error handler throws? Error handlers all the way down?

      1. Mephane says:

        and your error handler calls something that causes an error, you're just as screwed. What do you do, an error handler for the error handler? Then what if that error handler throws? Error handlers all the way down?

        You can totally throw another exception in a catch-block, it is a useful way to translate one exception type into another*, or even to simply rethrow the original exception.

        *For example, I prefer to get rid of the MFC CException and all its derived ones ASAP and replace them with something that

        a) inherits from std::exception and
        b) uses proper RAII for the exception classes themselves (seriously, Microsoft…).

        1. Kian says:

          You can catch and rethrow exceptions, sure. The problem I was replying to is throwing and throwing again before catching the first one. Which can happen if any of your destructors throw. The solution is to not let your destructors throw, by keeping any throwing operations in your destructor in a try-catch block.

          This is often brought up as an “unsolvable” problem for exceptions, but it disregards the parallel issue in error returning code. In the part you quoted I misspoke. “Then what if that error handler throws?” should read “Then what if that error handler reports an error?” In error reporting code, you’d have a goto to a cleanup section or some similar mechanism when you detect an error (you can’t use the destructor to clean up because it doesn’t return an error code if it fails).

          If your cleanup section reports an error, you have the same issue as if your destructor throws. You have to fix whatever failed in that spot.

  8. Alan says:

    If the concern is debugging and development, exceptions aren’t useful enough to justify their existence. One of the strengths are that they can simplify error handling. If you’ve got a big blog of code, dozens of lines, say, writing a save game to disk, really you should check after every write call to see if something went wrong (disk full, removed, corrupted, etc) and cope. It’s a nuisance even if all you say is “return with an error code.” Tedious. But I can wrap the entire block with a single “if anything related to writing goes wrong, run this code to deal with it,” then not have to do lots of individual checks.

    1. guy says:

      Well, I dunno about C++ exceptions, but in Java they’ve got a stacktrace. It’ll tell you exactly which line in which class threw the exception, exactly which line in which class called the method that exception was thrown in, exactly which method called that one, etc. all the way up the chain. That’s really, really nice and it lets you go in and add breakpoints in the specific place you need them easily.

      1. bloodsquirrel says:

        C++ doesn’t give you a stack trace.

        1. guy says:

          Well that’s substantially less helpful. I can see why C++ people are less enamored with them; the stacktrace is the best part.

          1. Kian says:

            Well, not by default. It’s C++, there’s a library for that :P Or you can do it yourself, by asking the system for the back trace and putting that in the exception.

        2. Duffy says:

          As a predominantly C# and SQL person that simple statement suddenly made this entire series of comments 1000x times less bizzare. I thought you all had lost your minds with the rampant indifference to Exceptions.

  9. DGM says:

    >> “is_dead is a bool, which is a value that can only be true or false.”

    Schrodinger’s cat would like a word with you.

    1. Blake says:

      While the memory is uninitialised it is in a superposition.
      The moment you check the variable the entity is in fact, either alive or dead.

      1. WJS says:

        If your memory is uninitialised, won’t it almost certainly evaluate to true?

        1. Daemian Lucifer says:

          Unless thoroughly scrubbed the last time it was used,it will be whatever was there before.Usually some random junk.

    2. Phill says:

      Schroedinger’s Cat *might* want a word with you…

      Anyway, everyone knows that a bool can famously have three values: http://thedailywtf.com/articles/What_Is_Truth_0x3f_

    3. Mephane says:

      Enter the dreaded custom tristate boolean “true, false, unknown”…

      1. Richard says:

        FILE_NOT_FOUND

      2. Blake says:

        That’s Lua for you, true, false or nil.

  10. fscan says:

    Regarding unique_ptr, i cannot think of an scenario where you would need a ptr to a unique_ptr. I would go so far and say it’s most likely a programming mistake. Either you have ownership (unique_ptr) or you just need the underlying pointer. If you need extended lifetime (shared ownership) of the resource, use a shared_ptr.

    Exceptions are very important for readability and correctness but only if they are really just used for runtime errors (eg, out of memory, io error) and not for program logic (eg. file exists, .. ). You can write a piece of code assuming everything is ok and don’t need ugly if/then nesting you often see in c programs. It’s probably even a performance increase in 99.9% of the time because you take the performance impact only in failure cases.
    When you want to debug something that should not happen (eg. in new code) like in the sample you provided, use “assert”. It automatically gets removed in production builds.

    If you want to write simple, clean and save code in C++ look at RAII for a start. Understanding this basic concept of C++ has helped me a lot.

    1. Kian says:

      “When you want to debug something that should not happen (eg. in new code) like in the sample you provided, use “assert”. It automatically gets removed in production builds.”

      Asserts in C++, assuming you mean the macro defined in , is just a macro that basically does “if (!condition) abort();”. And it’s only removed in release versions if you define NDEBUG in the release version. It’s not automatically removed.

      That said, you can make your own assert that does “if (!condition) throw exception()” or some other thing like call the debugger, etc. The nice thing about exceptions though is that you can stick as much information in them as you want, which abort doesn’t really let you do. You could even have several different macros, used to set different levels of checking for different targets.

      So in a debug build you add the most exhaustive checking for programming errors.
      Then you could have a “safe” build that is optimized but still has some error checking, which is useful because a lot of undefined behavior doesn’t misbehave until the optimizer takes a crack at your program. Debugging issues that only crop up in release versions is a pain.

      And finally the release version which would have most error checking disabled. But you can absolutely have exceptions that only trigger in some builds and not in others, to help with debugging. Which is something that you can’t really do with errors, because all your code has to be aware of every error that could flow through them. With exceptions, you only need to look at where exceptions are thrown and where they are caught.

    2. paercebal says:

      Regarding unique_ptr, i cannot think of an scenario where you would need a ptr to a unique_ptr. I would go so far and say it's most likely a programming mistake. Either you have ownership (unique_ptr) or you just need the underlying pointer. If you need extended lifetime (shared ownership) of the resource, use a shared_ptr.

      My thought, exactly.

      I’ve yet to see the video (It’s 01:33am here, so I’ve better things to do), but writing a pointer to a unique_ptr (or any other smart pointer) is like turning the lock key of an open door to make sure your house is safe. IT’S MINDNUMBINGLY DUMB.

      This alone shows a deep ignorance of C++.

  11. Canthros says:

    Exceptions are useful and make for clean code, but are actually quite a bit harder to reason about than error codes.

    Error codes are good enough, but make for ugly code. However, they’re relatively easier to reason about.

    Some years ago, I read an article which argued that good error-handling code was easier to write with error codes than with exceptions. I think this is more or less the case. (My professional experience suggests poor error-handling in one paradigm tends to correlate closely with the programmer writing poor error-handling code in the other model, too. Maybe I’ve just worked with too many not-very-good programmers, though.)

    I think each approach has its value, though, and each should be used when appropriate. (Especially in languages which let you expose an error code as a named enumeration:

    if (myoperation(arg) == MyErrors.FrobberUnderflow) { /* … */ }

    seems more maintainable than the equivalent C idioms of an all-caps’d #defined value and more readable than a magic number, like

    if (myoperation(arg) == FROBBER_UNDERFLOW) { /* … */ }

    or

    if (myoperation(arg) == 42) { /* … */ }

    YMMV, of course.)

  12. Scerro says:

    I’ve always liked exceptions because they’re a really easy way to do error handling during debugging. Sure, of course you were used to the way you handled errors before… but as a new programmer they’re quite nice. Mostly because you tell it to try something that may fail, and if it does fail, it automatically breaks out and you don’t have to worry about break commands (they don’t really mention those much anymore)

    The ability to use pointers and optimize at that level is partially why C++ is so standard for AAA game development. Sure, it’s pain, but it’s more reliable than your compiler doing the work.

    Then again, .NET lets you combine with other languages… but I’ve never personally done it.

  13. Volfram says:

    Exceptions, I think, are a good tool for when you’re writing code which someone else is going to mess with, because they can’t necessarily put a breakpoint in, and they may not be catching everything.

    For pointers:
    “Even after all my years of experience, I still look at this code and think it should mean that is_dead and is_stunned are pointers.”

    D actually does this. Part of this is because the “pointer” attribute is tied to the data type, not the variable, and it was done for exactly the reason you described: because it makes more sense that way. I still prefer to put each variable on its own line, though.

    Another interesting thing D does that I think no other C-type language does is that “struct” and “class” types have well-defined differences which make one objectively preferable over another depending on the situation. In short: “struct” are data-type and have a default-public type. You can initialize a struct, but you don’t have to, and the memory for it is allocated when it’s created. “class” is a reference-type, and defaults to private visibility. Instantiating a class only allocates the pointer, the class itself has to be created using the “new” operator.

    So if you just want to throw down a variable, make it a struct. If you want it automatically treated as a pointer, make it a class. My game engine actually uses several pointers, but I haven’t had to mess with pointers directly in years unless I was interfacing with C code, because I create the object as a class or struct depending on whether I want it to be a pointer or not.

    1. Richard says:

      So what is the difference between C++ and D for the class/struct divide?

      In C++, a struct is default-public, while a class is default-private – and that’s it.
      The rules for when the members get initialised (or if they do at all) are the same either way.

      You can have a POD class or a POD struct, and you can have non-POD of either as well.

      While I tend to use structs for PODs (and I never use class for a packed POD), that’s a conscious style choice, much like bracing style.

      The “new” keyword is for getting the memory from the heap as opposed to the stack, and you can use it with either. I believe that is the same in D.

      1. Volfram says:

        I don’t understand the point of your post. Was the first line a hypothetical question which you answered in-post, or was it an honest question which you were hoping I would address?

  14. Mephane says:

    Shamus, I think you are victim of a severe misconception about exceptions:

    They are not a debugging tool.

    Exceptions are for errors that can and will occur when the program is run on customer machines. Anything from a missing or broken file to a suddenly interrupted network connection, incorrect user input, these kinds of things. As the word says, things that can indeed go wrong and be handled in some way.

    (And whenever they cannot be handled, you can still throw an exception and let it fall through – causing an instant crash instead of the program running along in an unknown state.)

    They are also especially useful when you have complex operations that consist of many smaller subroutines, for example file parsing. Having learned C++ from the start, and not C, I find code like this absolutely atrocious:

    int DoStuff()
    {
      int error;

      int mynumber = 0;
      error = ReadNumber(&mynumber);
      if (error != SUCCESS)
        return error;

      int output;
      error = CalculateOutput(mynumber, &output);
      if (error != SUCCESS)
        return error;

      mystruct* mydata = 0;
      error = FormatData(mynumber, output, &mydata);
      if (error !=  SUCCESS)
      {
        free(mydata);//especially fun when the author forgets this and only returns the error…
        return error;
      }

      error = PrintResult(mydata);
      if (error != SUCCESS)
      {
        free(mydata);//gotcha – need to free the object, here, too
        return error;
      }

      free(mydata);//and here
      return SUCCESS;
    }

    While in C++ this might look like this:

    int DoStuff()
    {
      int mynumber = ReadNumber();
      int output = CalculateOutput(mynumber);
      std::unique_ptr<mystruct> mydata = FormatData(mynumber, output);
      PrintResult(mydata);
    }

    And then you catch any exceptions only at the level where you can actually do anything useful with it other than cancelling the operation. But as I said, this approach makes only sense when you see exceptions as something to handle exceptional scenarios that your program should be ready to handle in some way (even if that simply means telling the user what went wrong), and not to debug your code.

    1. Robyrt says:

      This is exactly what I was going to say. I work mostly with programs that will run on a closed environment with no debugger access, and having exceptions and voluminous logs really helps catch some of the bizarre issues that never really happen on a development machine. (Printer drivers not installed properly! Local database setup scripts time out! Network access cuts out temporarily! Et cetera.)

  15. John says:

    I am a strictly amateur programmer, mostly self-taught. I had never so much as heard of exception handling until about a year ago when I started working with file IO in Java. I quickly discovered that I hate it. It makes my code ugly to look at and longer than I feel it really needs to be.

    I don’t doubt that there are good reasons for the existence of exception handling–there are probably several given in the comments above mine. Nor do I doubt that there are reasons for it to take the form it does in Java. I guess I’m just bitter that reading from or writing to a text file is so complicated in Java when it’s so simple in the older languages I know.

    Fortran, I miss you! It’s not that I don’t love you any more, it’s just that I have other needs now. I’m sorry.

    1. Kian says:

      Is it really simpler in FORTRAN, or are you just used to ignoring any possible failures there?

      Still, I don’t like the way Java does exceptions (what I understand of it, anyway, I don’t program in Java). C++ also has issues with how to specify exceptions. In fact, they recently changed the format.

      The current way to do it is to declare that it’s “nothrow” or not, not every way it can throw. And when it’s ambiguous, you can make it conditional on if a given statement can throw. So you can do “nothrow(statement)” which means that your function has the same behavior as the statement. Which is useful for tempaltes, since whether a templated function throws might depend on whether the type you instantiate it with throws for a given operation.

      Some throwing specification are horrible to look at. It’s a difficult problem that many smart people continue to work on.

      1. guy says:

        Java has you put “throws [Exception], [Exception]…” right after the method declaration. Some of them you don’t have to do that for (IndexOutOfBoundsException and IllegalStateException being two I remember) because they’re common and would go everywhere. The Javadoc setup then lets you list them and the conditions under which they occur. You can always do a blind try-catch if you’re feeling lazy and want to make your software engineering TA disappointed in you.

        1. Kian says:

          That’s what I understand it does. I don’t like it because most functions shouldn’t care about exceptions, except for what safety they themselves offer. Which is not related to what exceptions can be thrown in your function. A list of exceptions doesn’t tell you if you offer strong, basic, or no safety at all.

          I believe it’s a consequence of exceptions being still somewhat new when Java was created, so there were still some misconceptions about how to use them. Essentially, the belief that you need to deal with exceptions as soon as possible, as if exceptions were evil and needed to be squashed quickly.

          The current C++ approach is slightly better, because knowing if a function doesn’t throw at all lets you know what safety you yourself can offer. And in fact, it’s used by the standard library to make more efficient code.

      2. Volfram says:

        I think it’s less that FORTRAN is better and more just that Java is really good at making your code long and hideous. Maybe that’s why they like the stupid “same-line” bracing style. “Well my code’s already ugly and unreadable, this won’t make it any worse.” ;p

      3. John says:

        Fortran is both simpler and more dangerous. It’s simpler in that in Fortran, I can open a text file with a single line of code. Reading a line from the file is likewise a single line of code, and closing the file takes one more. It’s more dangerous in that the file’s failure to exist or be readable will cause a crash. Similarly, the contents of the file being other than what the program expects will (hopefully) cause a crash or (in the worst case scenario, when your program takes hours or days to run to completion) utter garbage for output. The Java file IO classes anticipate those kinds of problems and force you to write code blocks for those cases whether you want to or not. I wouldn’t be quite as irritated if it were optional.

        My regard for Fortran is largely nostalgia based. My Fortran projects were mostly academic in nature: open some files, read some data, do a bunch of math, write some output to different files. I was both the developer and the only user. When the programs crashed, as they often, often did, it was usually pretty obvious why and not too difficult to fix. Java assumes, probably rightly, that you are going to develop software for other people and that you should be more careful. Still, I would be a happier man if the file IO exception handling were optional.

  16. Rick says:

    PHP and JS (scripting, not “programming”, I know) automatically pass certain types by reference instead of comring them. Being self-taught, this caught me it a few times initially.

    Also, I agree, the syntax he suggested is far from graceful.

  17. Piflik says:

    Strictly speaking, the asterisk to declare a pointer is not really part of the type.

    bool *isdead;

    doesn’t declare a pointer to bool… *isdead is bool. If I dereference isdead (by adding a asterisk), I get a bool.

    But yeah…pointers in C++ are a mess (and don’t get me started on call-by-value and call-by-reference)

    1. Richard says:

      I dunno, if you pay attention to const-correctness then most of the pointer and reference stuff just handles itself.

      Although the syntax for const-pointer-to-variable and variable-pointer-to-const and const-pointer-to-const is a bit of a mess.

    2. Blake says:

      Except that it sort of is.
      bool* is a type, one that can be implicitly cast to a void* for example.
      A function can return a bool* or be declared with a bool* as an argument.
      All this means that bool* is absolutely a type, but when declaring a variable the asterisk changes to be a modifier on the variable instead of the actual type.
      And then there is the stuff Blow mentions with std::unique_ptr which looks like std::unique_ptr is the important information not the bool pointer which is what most people reading the code cares about.
      So yes I agree, with you, c++ pointers are a mess.

      Also as you mentioned, call by value or call by reference is just the worst.
      The ONLY thing I can see it helping with is for smart pointers to have operator-> overloads, but I’m not convinced that is a great paradigm either.
      But most of all, why the hell use 2 characters (one needing shift) for ->, like that is something you do quite often, could they really not find some way of making it a single character?

      1. Kian says:

        Hey, that’s C’s fault! C++ inherited it :P In fact, C++ added reference parameters to avoid that pain. A reference is essentially a constant pointer. Since the pointer is constant, you don’t need to support syntax for differentiating between the pointer and the value pointed to. So with a reference, you can avoid the -> syntax.

        void SomeFun(MyClass var) : pass by copy, you refer to var’s members with var.member, you don’t affect the original.

        void SomeFun(MyClass * var) : pass by pointer, you refer to var’s members with either var->member or (*var).member (I can never remember the operator precedence between *, . and ->)

        void SomeFun(MyClass & var) : pass by reference, you refer to var’s members with var.member, same as with pass by copy, but you affect the original.

        For extra added super fun, you can also pass a pointer to a pointer, or a pointer by reference! That way, you actually affect the pointer you pass in, not the variable the pointer pointed to (or being referenced) is pointing at. Except you deal with a pointer to pointer using pointer syntax and a reference to a pointer with value syntax. I don’t see what people are so confused by :P

  18. Tse says:

    I love pointers. If you’re looping through a table in memory you can just make changes directly without copying to a structure and then replacing table records.
    Too bad you can’t use pointers to pass data to or store data in objects in ABAP, but have to use references and then dereference and use pointers inside the method to do what you need.
    I also imagine the lack of a Garbage collector in C++ would favor using more pointers, since a memory leak caused by a (huge number of instances of a) rogue pointer is much less serious than one caused by a (huge number of instances of a) rogue variable.

    1. Kian says:

      In C++ you can’t have a leak caused by a huge number of instances of a variable. Automatic variables (generally referred to as “on the stack”) are cleaned up automatically. You can get pointers to them, of course, but that doesn’t create leaks. It just avoids the overhead of copying when passing them to a function.

      That is to say, if you write:

      int Foo()
      {
      int x = 0;
      int y = 1;
      return x+y;
      }

      x and y are never leaked. They have a well defined lifetime. If, on the other hand, you do this:

      int Foo()
      {
      int *x = new int(0);
      int *y = new int(1);
      return *x+*y;
      }

      x and y are both leaked each time you call Foo. You’d need to either explicitly clean them up:

      int Foo()
      {
      int *x = new int(0);
      int *y = new int(1);
      int z = *x+*y;

      delete x;
      delete y;

      return z;
      }

      like a C savage, or use a smart pointer like a civilized C++ citizen:

      int Foo()
      {
      std::unique_ptr x (new int(0));
      std::unique_ptr y (new int(1));
      return *x+*y;
      }

      1. Neil Roy says:

        Go ahead, try int *x = new int(0); in C… let me know how that works out. “new” is a C++ command, not C. Now do you have a real world example? Something that makes sense that might actually happen where a unique pointer would be better?? I only see foolish examples that nobody in their right mind would do.

        In C, if I wanted to change the value of a pointer in a function, I would have…

        foo(int *i)
        {
           int x = 0;
           int y = 1;
           *i = x + y;
        }

        Not a problem. When I create the pointer and allocate the memory for it, I immediately write the code to deallocate the memory. Never had a problem with it, my code looks nice and clean, no lengthy lines of fugly C++ hand holding garbage. Been doing it since the 90’s, I have more problems with forgetting semicolons (I’m certain C++ will come up with a convoluted template for that soon).

  19. bool* is_dead, is_stunned;

    I’m surprised that the statement above doesn’t initialize both variables into pointers. I’m pretty sure if you removed the asterisk character that statement would initialize both declared variables as boolean base data type. Yet you need n number of asterisk for n number of declared pointers.

    1. Blake says:

      Yeah it’s weird, when declaring multiple variables suddenly the asterisk is a modifier on the variable rather than part of the type like it is in every other circumstance.

      I think even most of the people disagreeing with Blows conclusions (at least in their use case) would still agree C++ syntax is a mess, and even if you just ended up with a language with the exact same features as C++ you could do it in a much cleaner way.

      1. paercebal says:

        C++ syntax is a mess, and even if you just ended up with a language with the exact same features as C++ you could do it in a much cleaner way

        That ugly type declaration is inherited from C.

        Quoting Bjarne Stroustrup: “Within C++, there is a much smaller and cleaner language struggling to get out. […] And no, that smaller and cleaner language is not Java or C#.” (Source).

        One of C++’s constraints was it had to remain reasonably compatible with C. It is both a boon (e.g. you want to migrate a multi-million LOC from C to a more evolved language) and a curse (you have to deal with C weaknesses and sometimes ugly syntax).

        1. Neil Roy says:

          Don’t blame C. I always have the * next to my variable, because that is where it belongs. I think it is perfectly obvious why

          bool* is_dead, is_stunned;

          fails… the * is not next to the variable where it belongs, that would make more sense and help people see the problem. This line is saying I am creating two bools. One is a pointer, the other is not. But because of this STUPID convention of putting the asterisk next to bool, it causes confusion. bool isn’t a pointer, the variable is! If you want two pointers than you need to tell the compiler that BOTH are pointers! All bool says is that these variables are of type bool. You then tell it that this particular variable is a pointer by putting an asterisk next to it.

          bool *is_dead, is_stunned;

          …see the difference? Now it’s plain why is_stunned is not a pointer, because we didn’t make it one!

          People need to stop listening to youtube tutorials that teach bad habbits.

  20. Neil Roy says:

    Been programming in C since the 90s and I just do not have the problems with pointers I see all the scary warnings alluding to. What I see are a bunch of lazy programmers who are not paying attention to what they’re doing then blaming the language for their own stupidity.

    When I create a pointer and allocate memory, I IMMEDIATELY write code to deallocate that memory. It’s not rocket science. All unique pointers do is hold the hands of fragile, precious little C++ programmers who are scared of those evil pointers.

    Oh, and I seen people in online tutorials talking about the proper place to put * like you showed… bool* my_variable; always annoyed me. BOOL isn’t a damn pointer, the variable is!!! Gah… it’s like newer versions of C++ cater to the “I’m too stupid and lazy to learn, please make a template/class/function to hold my hand and to it for me and teach people that it is the right way to do things”.

    Oh, I also never had a problem with global variables either, though I do understand some of the problems with them, it’s probably one of those things I hear a lot about the dangers of, but never actually ran into any problems with them. I do try and limit their use, but I won’t go out of my way to avoid them. I have more problems with forgetting to put a ; at the end of my lines of code than anything else… please C++, make me a lengthy template to fix that… maybe we can have

    lineOfCode::addLineHere::start() a = 1 lineOfCode::endLineHere::end()

    lineOfCode::addLineHere::start() a++ lineOfCode::endLineHere::end()

    lineOfCode::addLineHere::start() myLibrary::display::print(“A equals %i”, a) lineOfCode::endLineHere::end()

    anyhow… I’m done ranting now. ;)

Thanks for joining the discussion. Be nice, don't post angry, and enjoy yourself. This is supposed to be fun. Your email address will not be published. Required fields are marked*

You can enclose spoilers in <strike> tags like so:
<strike>Darth Vader is Luke's father!</strike>

You can make things italics like this:
Can you imagine having Darth Vader as your <i>father</i>?

You can make things bold like this:
I'm <b>very</b> glad Darth Vader isn't my father.

You can make links like this:
I'm reading about <a href="http://en.wikipedia.org/wiki/Darth_Vader">Darth Vader</a> on Wikipedia!

You can quote someone like this:
Darth Vader said <blockquote>Luke, I am your father.</blockquote>

Leave a Reply

Your email address will not be published.