The C++ Subset
I like to keep an eye on new developments of the C++ spec and the direction the language is moving, but in the code for Rival Fortress I don’t use most of the features that C++ has to offer.
The features I don’t like
I don’t use object-oriented programming. Most of my code is written in a functional programming style, as I’m a big fan of data-oriented design.
I rarely use inheritance, and if I use it, I stay away from virtual functions or deep hierarchies. I find the code to be cleaner and easier to understand when one doesn’t need to study deep class hierarchies to figure out what a piece of code is doing.
I’m also not a big fan of the Standard Library as I find that most of the functionality it provides is either less performant than ad-hoc implementations or tends to hide complexity that I’d rather have out in the open.
I rarely use templates as most of the problems they address can be solved with code generation as I talked about in my reflection preprocessor post. Templates also bloat compile times considerably and produce god-awful compiler error messages.
I never use exceptions as I find them to be a useless performance penalty that can be easily avoided with careful coding. They also increase code complexity because of all the redundant try
/catch
blocks that tend to pollute codebases that use them.
I don’t use RTTI as a lot of the code that would require introspection is generated by Rival Fortress’s engine preprocessor.
I don’t use RAII and very rarely use object constructor/destructor pairs. I find that RAII is unnecessary when coding in a functional style.
The features I like
Most of my code is C99-like, with the exception of the following features that I found useful:
- Operator overloading: when it makes sense, like for simple vector math (i.e. addition, subtraction, scalar multiplication).
- Function overloading: I try to use self-documenting function names, but I find that in C the lack of function overloading can force you to either use verbose or cryptic names when the same logical function needs to operate on different data types.
- const_expr: is sometimes useful when I don’t want to go the macro route and need something to be evaluated at compile time. For example, string length and string hashing have both a runtime and a
const_expr
version. - static_assert: I find it very useful especially for sanity checks and paired with
const_expr
functions or for validation of meta programmed code generated by the engine’s preprocessor. - raw string literals: are very useful when dealing with long string. I use them very often while working on the engine preprocessor.
- lambdas: are sometimes useful as a convenience feature, but most of the times I just use function pointers.