Difference between revisions of "Intermediate C++ Game Programming Tutorial 22"
From Chilipedia
(→Video Timestamp Index) |
(→Video Timestamp Index) |
||
Line 41: | Line 41: | ||
** Rethrowing still works | ** Rethrowing still works | ||
* Good practice: only throw exception objects that inherit from the <code>std::exception</code> class [https://youtu.be/DMdyz0lrFBI?t=9m51s 9:51] | * Good practice: only throw exception objects that inherit from the <code>std::exception</code> class [https://youtu.be/DMdyz0lrFBI?t=9m51s 9:51] | ||
+ | <div class="mw-collapsible-content"> | ||
** STL offers a range of pre-defined exception objects, with two basic types that inherit from <code>std::exception</code>: | ** STL offers a range of pre-defined exception objects, with two basic types that inherit from <code>std::exception</code>: | ||
*:- <code>std::logic_error</code>: due to faulty logic within the program | *:- <code>std::logic_error</code>: due to faulty logic within the program | ||
*:- <code>std::runtime_error</code>: due to events beyond the scope of the program (such as file access errors) | *:- <code>std::runtime_error</code>: due to events beyond the scope of the program (such as file access errors) | ||
+ | </div> | ||
* <code>std::exception</code> and its derivatives [https://youtu.be/DMdyz0lrFBI?t=10m49s 10:49] | * <code>std::exception</code> and its derivatives [https://youtu.be/DMdyz0lrFBI?t=10m49s 10:49] | ||
+ | <div class="mw-collapsible-content"> | ||
** The inherentance hierarchy of <code>std::exception</code> is very useful | ** The inherentance hierarchy of <code>std::exception</code> is very useful | ||
** The polymorphism lets you choose the level of detail in error handling, so that you can filter for specific errors | ** The polymorphism lets you choose the level of detail in error handling, so that you can filter for specific errors | ||
Line 50: | Line 53: | ||
** An exception will be caught (/dispatched) to its most derived (most specific) type, when catch functions are overloaded for different types of exceptions | ** An exception will be caught (/dispatched) to its most derived (most specific) type, when catch functions are overloaded for different types of exceptions | ||
** You can create your own error classes that inherit from the existing one for customized and finer-grained error handling | ** You can create your own error classes that inherit from the existing one for customized and finer-grained error handling | ||
+ | </div> | ||
* Unwinding the stack: what happens to objects when throwing out of scope [https://youtu.be/DMdyz0lrFBI?t=13m39s 13:39] | * Unwinding the stack: what happens to objects when throwing out of scope [https://youtu.be/DMdyz0lrFBI?t=13m39s 13:39] | ||
+ | <div class="mw-collapsible-content"> | ||
** If you throw an exception, the code is going to jump out of the current function to wherever it is being caught | ** If you throw an exception, the code is going to jump out of the current function to wherever it is being caught | ||
** Upon jumping out of scope, all objects in scope will be destroyed. This is called "unwinding the stack" | ** Upon jumping out of scope, all objects in scope will be destroyed. This is called "unwinding the stack" | ||
Line 56: | Line 61: | ||
** This is avoided by using unique (smart) pointers and allocate memory with <code>std::make_unique<>()</code> instead of using raw pointers and <code>new</code> | ** This is avoided by using unique (smart) pointers and allocate memory with <code>std::make_unique<>()</code> instead of using raw pointers and <code>new</code> | ||
** More generally, if you use RAII types (such as <code>std::vector<></code>, <code>std::string</code> etc.), you will have no issues with memory leaks | ** More generally, if you use RAII types (such as <code>std::vector<></code>, <code>std::string</code> etc.), you will have no issues with memory leaks | ||
+ | </div> | ||
* Exceptions and destructors [https://youtu.be/DMdyz0lrFBI?t=17m17s 17:17] | * Exceptions and destructors [https://youtu.be/DMdyz0lrFBI?t=17m17s 17:17] | ||
+ | <div class="mw-collapsible-content"> | ||
** Never have destructors throw exceptioins, this will mess up execution bigly | ** Never have destructors throw exceptioins, this will mess up execution bigly | ||
** If you call any functions that can throw exceptions in a destructor, make sure you always catch them | ** If you call any functions that can throw exceptions in a destructor, make sure you always catch them | ||
+ | </div> | ||
* Move semantics and containers [https://youtu.be/DMdyz0lrFBI?t=18m34s 18:34] | * Move semantics and containers [https://youtu.be/DMdyz0lrFBI?t=18m34s 18:34] | ||
+ | <div class="mw-collapsible-content"> | ||
** Move operations don't work well if an exception is thrown | ** Move operations don't work well if an exception is thrown | ||
** If a move constructor of a class can throw an exception, the STL classes will avoid it and default to the copy constructor | ** If a move constructor of a class can throw an exception, the STL classes will avoid it and default to the copy constructor | ||
** You can enable move operations in these cases by making the move constructor <code>noexcept</code>, which explicitly tells the compiler that there will be no exception thrown | ** You can enable move operations in these cases by making the move constructor <code>noexcept</code>, which explicitly tells the compiler that there will be no exception thrown | ||
** Be mindful of when you mark your functions noexcept. You basically enter into a contract with whomever is using your code that the function will never throw exceptions. If you would ever want to change this, you risk nasty errors because the codebase may rely on your function being <code>noexcept</code>. | ** Be mindful of when you mark your functions noexcept. You basically enter into a contract with whomever is using your code that the function will never throw exceptions. If you would ever want to change this, you risk nasty errors because the codebase may rely on your function being <code>noexcept</code>. | ||
+ | </code> | ||
* Best practices [https://youtu.be/DMdyz0lrFBI?t=21m56s 21:56] | * Best practices [https://youtu.be/DMdyz0lrFBI?t=21m56s 21:56] | ||
+ | <div class="mw-collapsible-content"> | ||
** Always throw your exceptions by value and catch them by a constant reference | ** Always throw your exceptions by value and catch them by a constant reference | ||
** Never use exceptions as a form of flow control (jumping through your code), this is a terrible idea. Exceptions should only be thrown when something bad happens | ** Never use exceptions as a form of flow control (jumping through your code), this is a terrible idea. Exceptions should only be thrown when something bad happens | ||
** Use exceptions for things that are out of your control as a programmer (think file / hardware access errors, network errors), otherwise use asserts | ** Use exceptions for things that are out of your control as a programmer (think file / hardware access errors, network errors), otherwise use asserts | ||
** Asserts are for checking errors that you can avoid as a programmer, but you want to test for during development. Sanity checks. They should not be necessary when you ship a product. | ** Asserts are for checking errors that you can avoid as a programmer, but you want to test for during development. Sanity checks. They should not be necessary when you ship a product. | ||
+ | </div> | ||
</div> | </div> | ||
[https://youtu.be/_6G5yDgoWiA Tutorial 22 Part 2] | [https://youtu.be/_6G5yDgoWiA Tutorial 22 Part 2] |
Revision as of 06:41, 24 November 2019
In this video we learn how to use C++ exceptions to take our error handling to the next level. There is a lot of fear and ignorance surrounding exceptions among "C++ Programmers", and Chili's goal here is to elevate you guys above the shit-tier level to coders who can actually use this feature to its full extent. The second part of this tutorial will give concrete, practical examples of using exceptions in an actual game scenario.
Topics Covered
- Basic exception
try
ingthrow
ing andcatch
ing - Exceptions 'bubbling up'
- Rethrowing with
throw
-
catch(...)
-
std::exception
and its derivatives - Polymorphism in exception handling
- Exceptions and destructors
-
noexcept
and move constuctors vs. standard containers - Throw by value, catch by
const
reference
Video Timestamp Index
- Intro: exceptions are the main way of handling errors in C++ 0:10
- Examples of its usage in the Framework, a good practice for working with any API
- Everytime a Direct 3D Function is called, the return value is checked for error codes and handled
- Comparing error handling with and without exceptions 1:40
- Conventional error handling requires checking function return errors for every function throughout the entire hierarchy of the code base
- With exceptioins the advantages are:
- - you can catch an error anywhere in the code base (they "bubble up", or propagate up to a higher level function)
- - you can seperate your normal logic (code) from your error handling, without having to pollute your return values with error codes.
- - if you don't handle the error at some point, your program will be halted (uncaught exception).
- Example code to demonstrate
try
,throw
,catch
of astd::runtime_error("...")
3:53
- The
catch
function is only executed if an exception has been thrown - Illustration of how exceptions can propagate out of functions
- You can decide the level at which you want to catch an exception 6:04
- Catch and rethrow 6:40
- You can
throw
anything you want: an object, an Integer, string, pointer, etc. - You van overload the
catch
functions based on the type of information thrown
- The
- Use
catch(...)
to catch any uncaught excepions 8:55- In this case, you don't get any exception object or value that you can output
- Rethrowing still works
- Good practice: only throw exception objects that inherit from the
std::exception
class 9:51
- STL offers a range of pre-defined exception objects, with two basic types that inherit from
std::exception
:
- -
std::logic_error
: due to faulty logic within the program - -
std::runtime_error
: due to events beyond the scope of the program (such as file access errors)
- STL offers a range of pre-defined exception objects, with two basic types that inherit from
-
std::exception
and its derivatives 10:49
- The inherentance hierarchy of
std::exception
is very useful - The polymorphism lets you choose the level of detail in error handling, so that you can filter for specific errors
- You can use parent types to catch general exceptions
- An exception will be caught (/dispatched) to its most derived (most specific) type, when catch functions are overloaded for different types of exceptions
- You can create your own error classes that inherit from the existing one for customized and finer-grained error handling
- The inherentance hierarchy of
- Unwinding the stack: what happens to objects when throwing out of scope 13:39
- If you throw an exception, the code is going to jump out of the current function to wherever it is being caught
- Upon jumping out of scope, all objects in scope will be destroyed. This is called "unwinding the stack"
- However, any objects created on the heap will not be destroyed, resulting in a memory leak
- This is avoided by using unique (smart) pointers and allocate memory with
std::make_unique<>()
instead of using raw pointers andnew
- More generally, if you use RAII types (such as
std::vector<>
,std::string
etc.), you will have no issues with memory leaks
- Exceptions and destructors 17:17
- Never have destructors throw exceptioins, this will mess up execution bigly
- If you call any functions that can throw exceptions in a destructor, make sure you always catch them
- Move semantics and containers 18:34
- Move operations don't work well if an exception is thrown
- If a move constructor of a class can throw an exception, the STL classes will avoid it and default to the copy constructor
- You can enable move operations in these cases by making the move constructor
noexcept
, which explicitly tells the compiler that there will be no exception thrown - Be mindful of when you mark your functions noexcept. You basically enter into a contract with whomever is using your code that the function will never throw exceptions. If you would ever want to change this, you risk nasty errors because the codebase may rely on your function being
noexcept
.
</code>
- Best practices 21:56
- Always throw your exceptions by value and catch them by a constant reference
- Never use exceptions as a form of flow control (jumping through your code), this is a terrible idea. Exceptions should only be thrown when something bad happens
- Use exceptions for things that are out of your control as a programmer (think file / hardware access errors, network errors), otherwise use asserts
- Asserts are for checking errors that you can avoid as a programmer, but you want to test for during development. Sanity checks. They should not be necessary when you ship a product.
Extra Discussion
I had an interesting exchange with a viewer, and made a bit of a writeup here: essay time.