Intermediate C++ Game Programming Tutorial 22

From Chilipedia
Revision as of 05:47, 25 November 2019 by R vdw (Talk | contribs) (Video Timestamp Index)

Jump to: navigation, search

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 trying throwing and catching
  • 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

Tutorial 22 Part 1

[Expand]
  • Intro: exceptions are the main way of handling errors in C++ 0:10
  • Comparing error handling with and without exceptions 1:40
  • Example code to demonstrate try, throw, catch of a std::runtime_error("...") 3:53
  • Use catch(...) to catch any uncaught excepions 8:55
  • Good practice: only throw exception objects that inherit from the std::exception class 9:51
  • std::exception and its derivatives 10:49
  • Unwinding the stack: what happens to objects when throwing out of scope 13:39
  • Exceptions and destructors 17:17
  • Move semantics and containers 18:34

Tutorial 22 Part 2

  • Exceptions, applied in practice RPG Project Twin in the Chili Framework 0:10
    • A custom class ChiliException was defined (not recommended) that holds message, filename and line number information and virtual functions for information getters defined in derived classes
    • An inner class Graphics::Exception was defined that inherits from ChiliException and adds a data member of type HRESULT> (used in DirectX)
    • The Windows API doesn't use exceptions, so individual tests for every function using FAILED calls are used to throw exceptions. Macros such as CHILI_GFX_EXCEPTION() are used to generate the exceptions
    • In Main.cpp, Chili uses a try{ } inside another try{ }. The first is used to test if the creation of a MainWindow object succeeds, the second to throw exceptions using wnd.ShowMessageBox() of that MainWindow class
  • The use of macros to generate exceptions 4:22
    • A macro CHILI_GFX_EXCEPTION() is implemented that expands to a constructor for Graphics::Exception(), that also takes two pre-processor defined macros (defined by the compiler):
    - _CRT_WIDE(__FILE__), for the filename
    - __LINE__, for the line number in the code
    • You need to use a macro for this to get the line number of each function call at the different throw points (instead of the line number of the function definition)
  • The use of the HRESULT function 5:42
    • >HRESULT hr is used to perform a lookup DXGetErrosString(hr) of the error name string
  • Working with streams (std::ifstream file and exceptions in the Sound class 7:05
    • For example: file reading to load sounds
    • Steams don't work with exceptions by default, you have to check the error flags to throw based on that
    • file.exceptions(std::ifstream::failbit | std::ifstream::badbit); turns on the excceptions for the failure and bad flags
  • Recovering from an exception 9:33
    • For example: loading resources (like sound). By using a soft_fail flag, an empty sound gets added to the matrix of sounds so that the program can continue. In case of a hard fail the exception gets rethrown and stops the program.
    • Chili uses #ifndef NDEBUG throw e; #endif to always throw when in debug mode
  • Recap of main take-aways 11:32
    • DO inherit from std::exception
    • DO use exceptions for errors that happan that are out of programmer's control
    • DO NOT use exceptions for sanity checks (use assert)
    • DO throw by value
    • DO catch by const ref&
    • DO use smart RAII types like smart pointers ALL THE TIME
    • DO NOT throw exceptions from a destructor
    • DO NOT throw exceptions from a move constructor if you want the move optimization in a std::vector etc.
    • DO mark functions as noexcept if you are confident that they will NEVER want to throw an exception, either now, or later on in development
    • DO not mark functions noexcept just because they happen not to throw right now - they might in the future so think carefully before going down the noexcept path

Extra Discussion

I had an interesting exchange with a viewer, and made a bit of a writeup here: essay time.

Source Code

See also