From d1d979d2f193d58dd50d03065d669cac539162ca Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Fri, 24 Jan 2020 18:55:29 -0600 Subject: [PATCH] [nostalgia] Add section to Developer Handbook about mixing C and C++ --- developer-handbook.md | 71 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/developer-handbook.md b/developer-handbook.md index 9314f8da..7c142c0c 100644 --- a/developer-handbook.md +++ b/developer-handbook.md @@ -61,12 +61,6 @@ optimizing the passing in of parameters and for returning from accessor operators (e.g. ```T &Vector::operator[](size_t)```). As parameters, references should always be const. -### Casting - -Do not use C-style casts. C++ casts are more readable, and more explicit about -the type of cast being used. Do not use ```dynamic_cast``` in code building for the -GBA, as RTTI is disabled in GBA builds. - ### Error Handling Exceptions are clean and nice and gleefully encouraged in userland code running @@ -163,3 +157,68 @@ void studioCode() { } ``` +### Write C++, Not C + +On the surface, it seems like C++ changes the way we do things from C for no +reason, but there are reasons for many of these duplications of functionality. +The C++ language designers aren't stupid, trust them or question them, but +don't ignore them. + +#### Casting + +Do not use C-style casts. C++ casts are more readable, and more explicit about +the type of cast being used. Do not use ```dynamic_cast``` in code building for the +GBA, as RTTI is disabled in GBA builds. + +#### Library Usage + +C++ libraries should generally be preferred to C libraries. C libraries are +allowed, but pay extra attention. + +This example from nostalgia::core demonstrates the type of problems that can +arise from idiomatically mixed code. + +```cpp +uint8_t *loadRom(const char *path) { + auto file = fopen(path, "r"); + if (file) { + fseek(file, 0, SEEK_END); + const auto size = ftell(file); + rewind(file); + // new can technically throw, though this project considers out-of-memory to be unrecoverable + auto buff = new uint8_t[size]; + fread(buff, size, 1, file); + fclose(file); + return buff; + } else { + return nullptr; + } +} +``` + +In practice, that particular example is not something we really care about +here, but it does demonstrate that problems can arise when mixing what might be +perceived as cool old-school C-style code with lame seemingly over-complicated +C++-style code. + +Here is another more concrete example observed in another project: +```cpp +int main() { + // using malloc does not call the constructor + std::vector *list = (std::vector*) malloc(sizeof(std::vector)); + doStuff(list); + // free does not call the destructor, which causes memory leak for array inside list + free(list); + return 0; +} +``` + +The code base where this was observed actually got away with this for the most +part, as the std::vector implementation used evidentally waited until the +internal array was needed before initializing and the memory was zeroed out +because the allocation occurred early in the program's execution. While the +std::vector implementation in queston worked with this code and the memory leak +is not noticable because the std::vector was meant to exist for the entire life +of the process, other classes likely will not get away with it due to more +substantial constructors and more frequent instatiations of the classes in +question.