Question

As part of a game engine I am writing in cython, one of the challenges I am facing is creating a consistent interface for error handling that works at the C and python levels of my code. I am interested in maintaining two interfaces to the engine; one that operates at the C-level on structs with no involvement of the python interpreter, and one that operates on python "cdef classes" that indirectly wrap these structs.

Given that raising python Error objects would invoke the GIL, I am interested in using error codes within my C-level functions. These error codes would then be mapped to a corresponding error message string for display on the python wrapper function, which can freely use the GIL.

At the moment, I am planning to store the error codes in a single massive enum alongside a corresponding dict mapping error codes to error strings. This raises the following questions for me:

  1. Should error codes be named per-struct? For example, I have multiple structs that represent common data structures such as vector and slot map. As these are dynamically sized containers, allocations could fail on growing these structures. Would it be better to use generic error codes like ERROR_OUT_OF_MEMORY to represent such failures, or use struct-specific codes like ERROR_ITEM_VECTOR_OUT_OF_MEMORY, ERROR_ITEM_SLOT_MAP_OUT_OF_MEMORY, etc? Another example would be individual graphical shader versus shader program compilation, where I could either use a single ERROR_COMPILE code or an ERROR_SHADER_COMPILE and ERROR_PROGRAM_COMPILE codes.

  2. How should the error code <-> error message mapping be stored? On one hand, I could have a global dict in a header file that maps a unique code to a unique corresponding error message in a centralized location. Alternatively, I could write the strings directly in the python code, which might allow some added flexibility (e.g. using format strings to provide context-specific error messages).

Any help would be appreciated.

Was it helpful?

Solution

Should error codes be named per-struct

The granularity of error codes should fit to the existing requirements of the system. Make them as granular as needed to display meaningful error messages, or to detect certain types of failures required for debugging, but not more.

If you don't know how granular you need them yet, start with the simpler approach first, which requires the least effort to implement. I guess in your example this is ERROR_OUT_OF_MEMORY with no separation between structs. When you later get a requirement where it turns out this is not sufficient and you need a finer categorization for "out of memory" errors, refactor immediately. I would not be astonished when you find out you need a completely different categorization, not by "structs" - so any upfront categorization with wrong guessing about the real requirements would have been counterproductive.

As a general principle: try to defer design decisions up to the point in time when you have actual requirements and can foresee their consequences.

How should the error code <-> error message mapping be stored?

Same principle applies here: I guess the global dictionary is the most-simple-to-implement approach and sufficient for now? Then start with that. If it later turns out you need something more flexible, refactor. Note in this case, you still may use the dictionary for 90% of all cases, and hardcoded strings with special parameter formatting for the remaining cases, these approaches are not mutual exclusive.

You may have the problem that you are currently working at a stage where your game engine exists only in "thin air", without a real application using it. Therefore you face the problem of missing requirements. I think the only sensible way of dealing with this problem is to design "test cases" in form of one or more games using your game engine. That will give you the perspective of a user of the engine and help you to find out what is really needed.

Licensed under: CC-BY-SA with attribution
scroll top