• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Ectara

Portable Use And Disabling Of Exceptions

31 posts in this topic

I really want to use exceptions in my code, because they are about everything that I've ever wanted in terms of handling exceptional occurrences. However, I was concerned about the overhead of exceptions, in either speed or space. However, I was reading and reading about exceptions, and I'm starting to accept that it is worthwhile to use them, and there aren't as many platforms that I support that don't have compilers that allow standard exceptions as there once was.

So, I'm looking for advice on it. For these platforms, can you tell me if standard exceptions are supported by any compilers and allow them to be disabled, and bear in mind, I don't support all of them:

Xbox
Xbox 360
PS2

PS3

PSP

Pandora
Dreamcast

Nintendo DS

Symbian OS, any versions after s60v3 (I hear the compilers finally support standard exceptions)
GCC on Windows and Unix derivatives
Visual Studio on Windows
Android (I hear the NDK does now)

iOS (mixing Objective-C with C++)

 

Additionally, for most platforms that support disabling exceptions through a compiler switch, what happens when a throw is encountered? Does it raise some sort of signal?

EDIT:
My most frequent use-case for exceptions is to indicate something that should not happen in a normal work load has happened (out of memory, data that goes into an internal structure from external code is invalid, attempting to use something that is in a valid but explicitly unusable state). In these cases, I would use a debugger to figure it out, but swallowing exceptions is against my style, so they would only occur in areas that must be fixed; a perfect run should never throw them, but still have the option of handling them just in case, like the extremely rare circumstance of expecting memory allocation to fail if you allocate based on input, and displaying an error asking for a different value, though nothrow new could just as easily be used.

 

For the sake of conversation, I use assertions for things that should never happen, and I use status codes for states that are expected to occur. It is very likely that trying to open a file will result in failing to find it, so returning a code indicating this is ideal. Ensuring that the pointer to my main structure isn't null should be left up to an assert; if it fails, then it is a logic error, that shouldn't have anything to do with what is passed from external code.

Further clarification, for how I use each:

1. Exceptions are for public interfaces that interact with external code. An exception signifies that something unexpected has happened, and it is the external code's fault.

2. Returning status codes is for both internal and external code. Status codes indicate that the expected behavior is for one of many events to occur, not all of them positive, though they are _recoverable_.
3. Using assertions is for internal code. The external code should not be able to cause an assertion.

If anyone has tips on how to improve this methodology, don't hesitate to contribute.

Edited by Ectara
0

Share this post


Link to post
Share on other sites

So, I'm looking for advice on it. For these platforms, can you tell me if standard exceptions are supported by any compilers and allow them to be disabled, and bear in mind, I don't support all of them:
Xbox
Xbox 360
PS2
PS3
PSP
Pandora
Dreamcast
Nintendo DS
Symbian OS, any versions after s60v3 (I hear the compilers finally support standard exceptions)

Don't know about these...
 

GCC on Windows and Unix derivatives

Yes.
 

Visual Studio on Windows

Yes.
 

Android (I hear the NDK does now)

Yes.
 

iOS (mixing Objective-C with C++)

Yes.
 

Additionally, for most platforms that support disabling exceptions through a compiler switch, what happens when a throw is encountered? Does it raise some sort of signal?

Depends on the compiler. For G++, it calls abort().

2

Share this post


Link to post
Share on other sites

Calling abort() is ideal behavior in the absence of exceptions, in my opinion, and I'm relieved to hear that. My three main reasons for avoiding exceptions so far are (1) lack of compiler support on some fringe cases of my supported platforms, (2) speed penalty, especially if the compiler won't let you disable them, and (3) what happens if the compiler does let you disable them?

Also, see my edit, if your post came while I was revising.

0

Share this post


Link to post
Share on other sites

GCC on Windows and Unix derivatives
Visual Studio on Windows
Android (I hear the NDK does now)
iOS (mixing Objective-C with C++)

All of the above fully support exceptions.

 

PS3

As I understand it, the PS3's hardware isn't well suited to exceptions (i.e. the SPU programming model). Then again, it's also not very well suited to software that wasn't written with the SPU in mind - the chances that your general-purpose library will be used here are quite slim.

 

Xbox 360

Supported, but disabled by default.

 

Xbox
PS2
Dreamcast
Symbian OS

Are any of these actually realistic target platforms for a modern game development library? Might as well throw BeOS and Amiga into the mix...

1

Share this post


Link to post
Share on other sites

My most frequent use-case for exceptions is to indicate something that should not happen in a normal work load has happened (out of memory, data that goes into an internal structure from external code is invalid, attempting to use something that is in a valid but explicitly unusable state). In these cases, I would use a debugger to figure it out, but swallowing exceptions is against my style, so they would only occur in areas that must be fixed; a perfect run should never throw them, but still have the option of handling them just in case, like the extremely rare circumstance of expecting memory allocation to fail if you allocate based on input, and displaying an error asking for a different value, though nothrow new could just as easily be used.

May I ask why you can't just use assert()?

0

Share this post


Link to post
Share on other sites

the chances that your general-purpose library will be used here are quite slim.


I figured, after reading a lot about the architecture. Mostly out of curiosity; if I ever do write homebrew for the console, I'd be doing quite a bit of rewriting with the architecture in mind, or using a 3rd party library, so knowing if exceptions are usable would still be good knowledge; ideally, I'd be hitting the exceptions while testing on a machine that is set up to perform like it. The way I outline using exceptions would make it so that hardware differences would result in triggering an assertion; anything that would fail the same way on a test machine as it would on the target platform would be covered by exceptions.
 

 

May I ask why you can't just use assert()?

 

assert() can't be handled, so I would reserve it for the most extreme cases when an invariant is violated, and the execution _cannot_ proceed. For an example, a UTf-8 string class.

 

I'd return an error code if a character isn't found in the string. This is one of many expected results.
I'd throw if someone inputs invalid data. This is unexpected, and someone else's fault. They can catch it an fix it if they feel compelled to do so. I'd just validate/sanitize first.
I'd assert if I encounter invalid data in the private string processing functions. This should never happen, and there's no way to recover. If the string is now invalid when it shouldn't be, I have no idea what the string should be, or what to do with it. The behavior cannot match what it would be if the string were perfect, and a program that centers entirely around string handling will be devastated by this event.

 

0

Share this post


Link to post
Share on other sites

Ok, so in general, there are 3 sources of error:

  1. Bad input (wrong input, wrong charset, etc).
  2. Negative outcomes of normal conditions (divide by zero, null pointer, etc).
  3. Bad Shit™ (out of memory, solar flares, etc.)

You can (in theory) always sanitize/discard (1), and always anticipate (2). Unfortunately, you just have to deal with (3) as it occurs.

 

***

 

C++ is notoriously bad about (2), so you are actually forced to anticipate those (and mostly, either prevent or ignore them).

 

You should always be validating (1) at the surfaces of your system, so those can be handled with a simple error code return.

 

But I've never found a decent way to deal with (3) in the absence of exceptions...

2

Share this post


Link to post
Share on other sites

Ok, so in general, there are 3 sources of error:

  1. Bad input (wrong input, wrong charset, etc).
  2. Negative outcomes of normal conditions (divide by zero, null pointer, etc).
  3. Bad Shit™ (out of memory, solar flares, etc.)

You
can (in theory) always sanitize/discard (1), and always anticipate (2).
Unfortunately, you just have to deal with (3) as it occurs.

 

***

 

C++ is notoriously bad about (2), so you are actually forced to anticipate those (and mostly, either prevent or ignore them).

 

You should always be validating (1) at the surfaces of your system, so those can be handled with a simple error code return.

 

But I've never found a decent way to deal with (3) in the absence of exceptions...


I agree, for the most part, however (2) is a bit of a gray area.

There are many instances where I'd return an error code, throw an exception, or trigger an assertion for these things.
Some examples:

Return an status code:
Specifying an aspect ratio with a zero denominator. It'd be business as usual to report failure in some cases, or ask the user for another input.
A function that manipulates a structure, or if the structure is null, allocates a new one instead. The return value would change from being the one passed to the newly allocated buffer.
strtok() is a murky example; passing null is valid, and will result in different behavior than passing a non-null value. However, if you pass null without first making a call with a valid pointer, there is an error. Thus, passing null may or may not be an error condition. And if it isn't an error condition, the return value can still change. You might choose to have an implementation throw on receiving null on the first call, or simply return null, as if the string were empty.

Throw an exception (not exactly null pointer or divide by zero):
Failure to allocate memory. This is capable of terminating an application if not handled, but it can be valid if you allocate a large buffer to do things faster, but if it fails, choose to slowly handle one element at a time.
Reading in an XML file. The file exists, the header is fine, but it encounters a control character in the text. It's possible to provide a lot more information about what happened if you throw an exception (what it was and where), rather than return a simple status code saying "SYNTAX_ERROR" or "INVALID_CHARACTER".
Access a vector element out of bounds. There is no way that you can return a unique error value, and an assertion should be avoided (in my opinion) because it was caused by code outside of the vector's implementation. It may be possible to recover from this, so the caller should be given the opportunity to do so.
A divide by zero is caused by code no-where near where the division occurred, so a return code can't help it. If it doesn't violate an invariant of the class, it may be a good idea to allow the caller to handle it, like a scientific calculator that is allowed to have either operand be zero, so dividing two variables wouldn't be anything noticeable until the values are evaluated. Then assert or throw.

Trigger an assertion:
An image's bytes per pixel is zero, when there's an invariant in place that it must be valid. This shouldn't happen.
A memory allocator's pool is null. That can't be good, and chances are, this can't be recovered due to being unable to allocate.
A HUD drawing function tries to render the text in a null string. This should have been handled somewhere way higher up.

Some of these, are merely differences of opinion. For instance, I'd make out of memory an exception, and solar flares an assertion.

0

Share this post


Link to post
Share on other sites

I agree, for the most part, however (2) is a bit of a gray area.

Everything in (2) is things that you can't catch in C++. 

 

There is no portable way to recover from dereferencing a null pointer, or a divide by zero exception (and on a PowerPC chip, 1/0 = 0, so you won't even know it has occurred).

0

Share this post


Link to post
Share on other sites

dereferencing a null pointer


Ah, _dereferencing_ a null pointer. Yeah, that gives a better idea of what you mean. Yeah, for those events, I can only think of avoiding them.

However, as far as (3) goes, I draw a division of what to do based on whose fault it was. If it was their fault, the problem can possibly be remedied, and thus a catchable exception should be thrown. If it was my fault, due to invariants being violated, or things that shouldn't be possible occurring, I use an assertion.

Though, we may be agreeing perfectly on this, because solar flares aren't something the language is capable of detecting. :)
0

Share this post


Link to post
Share on other sites

Also, what is your opinion on defining new exception classes? Do you always extend a standard exception class, or do you define your own base class? Additionally, do you define all of your own exceptions, or do you only define new exceptions that don't cover what the standard ones don't?

0

Share this post


Link to post
Share on other sites

Here's an alternative to throwing exceptions for game development purposes:

 

1. Any error that's caused by bad code, or is unrecoverable, should call a custom assert() macro so the program halts immediately (when IsDebuggerPresent() returns true) so the author of the bad code can see what's wrong and fix it. Any build that's not a final one should report the issue to the tester somehow so they can submit a bug report. Ideally you'd save out a crash dump to go in the bug report.

 

2. Any error that is caused by bad data should (in non-final builds) inform the user at runtime e.g. with MessageBox() for at least the first instance of that error so that someone who adds bad data and then tries to run the game can identify what's bad with their data and fix it. The game should then work round the error, e.g. by returning some default data that visually represents an error. There's not much data in a game that can't be replaced reliably like that. You could also have a button on the message box to allow a tester to treat it as if it was a fatal error.

 

3. For error conditions where it's unclear, go for #2 if possible.

 

 

Generally this will all be done via game engine code, so a portable library could expose error / warning callbacks, and provide a default implementation that does something simple and portable (e.g. call printf() and assert(0)).

 

 

The D3D11 implementation has a similar approach, but it's overkill for a small library. It lets you disable specific warnings, and ask it to break in the debugger on the error types you're interested in.

1

Share this post


Link to post
Share on other sites

I used to be a huge fan of assertions, though exceptions speak to me in a higher-level sense.

Another problem that I encounter, is that if I am going with the rationale that I'd disable exceptions in a release build, then no behavior that relies on exceptions being caught can be used, because attempting to swallow an exception will instead never get called, and the application would terminate, or whatever the compiler does decide to do. Thus, exceptions can only be used for fatal circumstances, resulting in behavior that is like glorified assertions.

Can anyone provide a reason to use exceptions over assertions, given that exceptions may be disabled at any point? I feel that I should use exceptions, to have code that will provide added functionality if the application is compiled with exceptions enabled in all builds, like an application that isn't relying on the fastest execution time.

0

Share this post


Link to post
Share on other sites
Exceptions and assertions are for different things.


Use exceptions if you need your program's flow to reactively change to unexpected circumstances. Use assertions if you realize that some invariant or expectation has been violated and terminating the program is preferable to continuing blindly into whatever chaos may result. These are orthogonal concerns.


Exception support generally sucks on the current console platforms, btw, and is almost universally disabled for performance-intensive projects. My general rule is: assertions should kill the program and dump core. Anything that would normally trip an exception should kill the program and dump core. Recoverable errors should be handled in that the design of the system should elegantly allow for the program to continue and, well, recover. If all of your target platforms have exceptions and you are very good at exception safe design in C++ and you like them, go ahead and use them. If any of those things fails to be true, don't mess with them (in C++ specifically) because you're liable to blow something up if and when a real error does occur.

I'm on the side of forbidding the use of exceptions entirely in C++, FWIW.
1

Share this post


Link to post
Share on other sites

I'm still wavering heavily on the performance concerns, but there's still one way that I can think of exceptions as negating the speed penalty: propagating return codes. I used to spend so much time checking if a return code indicates success, and if not, clean up and return the error to the caller. Then the caller checks if the return code indicates success, and if not, clean up and return the error to the caller... ad infinitum.

Propagating error codes made code much less readable, and it causes you to perform a ton of conditional checks repeatedly to ensure that no error occurs.

It is not hard to imagine that checking error codes results in more time being spent checking for errors during successful execution than when an error occurs; handling an error is very fast to propagate the error, and then have someone take responsibility and do something about the error at the top of the call chain. However, there is an extremely larger percentage of time where an error does not occur. As a result, all of these conditionals to check each and every return code, not even to handle the error but to propagate it up the call stack to the code that will finally handle the error code.

This tells me that even while there is a penalty for using exceptions, it might not be any worse than checking error codes for every little thing.

The only way I can think of eliminating all error checking overhead is to make almost every error a fatal one, where it doesn't get propagated at all, at the expense of being unable to recover from a possibly recoverable error.

0

Share this post


Link to post
Share on other sites

I'm still wavering heavily on the performance concern

The performance issues are very different, depending on whether we are discussing consoles, or general purpose PCs.

 

Consoles tend to have abysmal performance when it comes to exceptions. In part this is because the compilers and runtimes have not been extensively optimised for the use of exceptions, and in part because consoles operate within strict hardware limitations.

 

If you are only targeting full-blown PCs, then the discussion is largely moot, because you'd have to be using exceptions to simulate control flow statements before they became a noticeable performance hotspot...

 

---

 

That said, I'm not a fan of exceptions. They can't be thrown across the boundary between threads, processes, hosts or programming languages. And anywhere in your program, you can can never tell if a function call might result in an arbitrary exception being thrown.

 

They have been termed "invisible gotos", and I don't think that is an unfair characterisation.

0

Share this post


Link to post
Share on other sites

I do like the concept, when used properly. I agree that it isn't easy to know when a function will throw an exception, unless you check its documentation. However, I'd argue that it's just as hard to tell if it will trigger an assertion, when used in place of exceptions.

0

Share this post


Link to post
Share on other sites

However, I'd argue that it's just as hard to tell if it will trigger an assertion, when used in place of exceptions.

Every function should be a candidate for triggering an assertion, but that doesn't matter because the caller doesn't need to be aware of this. The caller just needs to be aware of any pre-conditions -- e.g. "index must be smaller than size" -- and know that if these mustn't be violated. I find it cleaner to say that these things can't be violated "or else!" (so that in the retail version you can remove all the error checking code) rather than saying that if they are violated then an error will be returned (because then the error handling is part of the interface, and can't be removed in case a caller relies on it).
 
With exceptions, the caller needs to know what kinds of assertions can be thrown under which circumstances, so that they can either handle them, or add them to their own documentation and pass the buck to their own caller. Failure to do so will result in an unhandled exception, with your program unwinding to main, so callers need to know.
 
With assertions, if something is broken, then you get a crash dump / debug breakpoint so that you can fix your code, which is alerting the developer, not the caller.
 

The only way I can think of eliminating all error checking overhead is to make almost every error a fatal one, where it doesn't get propagated at all, at the expense of being unable to recover from a possibly recoverable error.

Do you have any examples of a recoverable error that you want to support? e.g. in my experience, on consoles if you get a std::bad_alloc, then you've already lost -- if the program needs 600MiB of RAM to run, then it can't run, and needs the attention of the developers, there's nothing a catch block can do to fix the game.
 


My most frequent use-case for exceptions is to indicate something that should not happen in a normal work load has happened (out of memory, data that goes into an internal structure from external code is invalid, attempting to use something that is in a valid but explicitly unusable state). In these cases, I would use a debugger to figure it out, but swallowing exceptions is against my style, so they would only occur in areas that must be fixed; a perfect run should never throw them, but still have the option of handling them just in case, like the extremely rare circumstance of expecting memory allocation to fail if you allocate based on input, and displaying an error asking for a different value, though nothrow new could just as easily be used.

May I ask why you can't just use assert()?


QFE -- for any kind of error that requires the attention of a developer in order to fix it, then exceptions are harmful. When you throw an exception, you're unwinding the call-stack, which is like ordering a clean-up crew to a crime-scene before forensics have arrived. In order to debug the issue, you need to freeze the crime scene as soon after the illegal operation as you can. Edited by Hodgman
2

Share this post


Link to post
Share on other sites

I still maintain that exceptions and assertions are orthogonal concepts.

 

An assertion says "something happened [b]which emphatically should not be possible[/b]" -  a contract violation, or something fundamentally broken in the code of some sort. Pinpointing the cause of this failure and preserving the complete context of the assertion failure is paramount. You can and should dump core - any reasonable user-facing system should automatically submit the complete callstack and (ideally) a minidump to the developer. Any middleware should gracefully fail with one of two kinds of information: a complete description of the nature of the assertion and what, if anything, the caller did to trip it; or a comprehensive location identification and relevant state so that the middleware author can locate, ideally reproduce, and then fix the internal cause of failure.

 

Exceptions are totally different. An exception says "something happened which I cannot handle [b]in this current control flow context[/b]." The implicit assumption is that someone higher up the call stack [i]can[/i] deal with the situation. If you do not comprehensively catch exceptions when they can possibly be thrown (accidental oversight aside), you are using exceptions [b]wrong[/b].

 

Earlier in this thread, someone made the remark that having a "disabled" exception call abort() is a reasonable tradeoff. I think this reflects a completely misguided interpretation of how exceptions are meant to be used. Exceptions should be thought of as recoverable in almost all circumstances. If you want a recoverable error, the right thing to do is to catch the exception at the appropriate contextual site and recover. If you want to kill the program, you should dump core and immediately exit, period. Unwinding the stack in this scenario is a [b]sin[/b] and you should be dragged behind the metaphorical woodshed and beaten to within an inch of your life if you ship software that unwinds the stack and then abnormally terminates. Put another way, having a try/catch in main() is a crime against humanity.

 

If we're talking about a middleware product, for instance, I should also get consistency across platforms; if I compile your code with exceptions off, I shouldn't get abnormal exits left and right when recoverable errors occur. By the same token, if I'm used to your code dumping core when I do something wrong, I shouldn't port to a new platform and suddenly get your exceptions getting thrown instead.

 

In fact, if this is for a C++ middleware product, exceptions shouldn't even be considered an option. The boundaries and limitations on exception propagation have been hinted at already, but it cannot be overstated that these [b]will[/b] cause your users to have painful problems. Even header-only libraries should avoid C++ exceptions because you can't guarantee that the client code is exception safe (or even has them turned on).

 

Genuine "oh shit" moments should assert and dump core, end of story. Recoverable errors should be propagated by return or out-parameter or some other easily retrievable status field, depending on your preferred style.

 

If this is not middleware, it's really up to your preferences, but please don't abuse exceptions, especially not in C++.

 

If this is not C++, then we need to re-frame the discussion, because other languages have occasionally managed to not suck at exceptions as badly as C++ does :-)

2

Share this post


Link to post
Share on other sites


Failure to do so will result in an unhandled exception, with your program unwinding to main, so callers need to know.


I definitely agree with this.

 

 


Do you have any examples of a recoverable error that you want to support?

My guess would be benign ones like out_of_range. I haven't designed any exceptions yet, because I'm still on the fence about what they would be used for, if anything, so I have a hard time answering that one.

To be honest, I have a hard time deciding which path I should go, because I'm hearing conflicting viewpoints in this thread, and around various other blogs, fora, and reference sites. Some people say to never throw, and to die immediately in every bad circumstance. Others say that most talks of side-effects of exceptions are ill-informed, and there's nothing wrong with using them for performance. I would personally subscribe to a three-pronged approach, but then there is a problem that code that interacts with this library might not have exceptions enabled, and thus I can't use exceptions for recoverable things, because this will result in abortion. Then there's the problem that unwinding the stack for fatal problems is nonsensical, so using them in place of assertions makes no sense. So, if I can't guarantee that exceptions can be used, they seem to get underfoot. However, having the execution terminate at the first sign of unexpected trouble seems like an unfriendly policy for other code that uses this library.

So, I'll entertain this thought pattern, too. If assertions are used for everything that isn't useful and expected behavior, how does this affect the usage of constructors and RAII principles? Does this not affect one's methodologies if they swap from using exceptions if a fatal error occurs in a constructor? I also feel like this would create the strange requirement that a custom allocator class should trigger an assertion if it fails to allocate memory, like new would if exceptions are turned off.

Edited by Ectara
0

Share this post


Link to post
Share on other sites

Constructors are a weird situation in C++, but personally, unless failing to construct an object is somehow recoverable, I'd rather assert than throw. A fatal construction error is still going to fall into one of two camps: I can keep chugging along, or I need to terminate. In the latter case, asserts let you dump core on-site instead of unwinding a level and possibly losing context as to what happened in the constructor.

 

If you have genuinely recoverable construction errors, exceptions are pretty much your only option in C++, aside from redesigning your API to some extent.

0

Share this post


Link to post
Share on other sites

I see. There aren't many instances where a constructor's failing is still recoverable, in my mind; a file stream failing to open a file is an example I can think of. One would need to check if the file was opened after the constructor returns; I find this acceptable. Opening a file has pretty much always been a two-stage affair: try to open it, then check if it is open.

I guess there's nothing to it but to try to design with one or the other, and see if I run into any problems where I'd need a more elegant solution. I'll start with trying to implement everything unexpected as assertions, and see where that gets me.

As a quick question, for my UTF-8 string example, if I assign invalid text to it, and it fails validation, is it overkill for that to be a cause for terminating the program?

0

Share this post


Link to post
Share on other sites
I largely agree with ApochPiQ. Here's my (simplified) take on assert vs. exceptions:
 
Assert is for when the programmer has screwed up.
 
Exceptions are for when the user (or the user's computer, but not the programmer) has screwed up.
 
Of course, you can replace exceptions with return codes. And, for the most part, I honestly don't find a lot of situations in which exceptions are truly necessary. They're minor details in a much larger picture, overall, because they're... exceptions (that is, they're not the rule).
 
Part of this question though is about when you do your sanity checking. Should you do sanity checking before accessing a vector element to make sure you're in bounds, or should you just go ahead and attempt the access and require the vector to do the sanity checking for you? If you design your library such that you require sane inputs, you can use mostly asserts instead of exceptions (or return codes). However, if you want your library to handle insane/invalid inputs without crashing, then you need to use exceptions (or return codes). In one case, the programmer violated your contract and your assertion catches this. In the second case, the programmer did not violate your contract, but there was still some type of program that requires either an exception or a return code. You need to decide where to draw the line. You can't put all the responsibility on the user of the library (after all, you need to at least handle failed allocations, for example), but you can put a good amount on the user of the library. For the erroneous situations you have to prepare for, like failed allocations, exactly how you handle it is up to you...
 
One option is to provide a custom error handler and a callback system. For example, you can do:
 
[source]void die(int errorCode, const char* errorMessage)
{
    // log if you want
    abort();
}
 
// If the user doesn't want to abort() on an error, but would rather handle it themselves, you can let them
// set this userCallback to their own function. It's then their responsibility to make sure that if control returns
// to your code, that the program isn't in an invalid state.
void (*userCallback)(int, const char*) = die;
 
void throwOrCallback(int errorCode, const char* errorMessage)
{
#if USE_EXCEPTIONS
    throw std::exception(); // or whatever, you can throw the right kind of exception based on the error code
#else
    userCallback(errorCode, errorMessage);
#endif
}
 
// Then, in your code, use throwOrCallback() instead of an actual throw statement
[/source]
 
Another option:
[source]
// I'm not a fan of macros, and this is semi-evil, but then again you're already doing some semi-evil stuff...
#if USE_EXCEPTIONS
#define ON_ERROR(errorCode, errorMessage) throw std::exception() // ... or whatever, you can have a fancy function to determine the right exception type
#else
#define ON_ERROR(errorCode, errorMessage) return errorCode
#endif
 
// Then, all of your library functions return an error code and use ON_ERROR for an error. For example:
int doStuff()
{
    if (someCondition)
        ON_ERROR(42, "You gave me the wrong answer. The right answer is 42.");
 
    return SUCCESS_CODE_OR_WHATEVER; // functions that can't fail don't have to return an error code
}
[/source]
 
Basically, you have to just choose either exceptions or error codes. Or, if you want to use both (and let the developer decide between the two), you have to do something along the lines of the above.
 
Edit: WTF is up with GameDev.net and mangling my code? Edited by Cornstalks
0

Share this post


Link to post
Share on other sites

I'm giving up on trying to fix the above mangled post. Gosh I hate the code mangling that always happens on this site.

 

As a quick question, for my UTF-8 string example, if I assign invalid text to it, and it fails validation, is it overkill for that to be a cause for terminating the program?

I depends on what your contract is. It's perfectly fine for you to have a strict contract that says "I expect only valid UTF-8, and if you give me invalid UTF-8 I will assert() your sorry butt" or you can say "I expect only valid UTF-8 but I'm going to let you be a little sloppy and will throw an exception for you to catch and decide how you want to handle your invalid text." Both options are fine. Just be clear and consistent.

 

Personally, I'd prefer a library to use assert(). And provide a "isValidUtf8(const char* str)" or some kind of function so I can validate my text if I want. You can also use this function in your assert(). But that's just my personal preference.

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0