[nostalgia] Reorganize developer handbook and add section on file I/O
This commit is contained in:
parent
e91e9ad58f
commit
95e752bf3b
@ -54,6 +54,72 @@ All components have a platform indicator next to them:
|
||||
* Pointer and reference designators should be bound to the identifier name and
|
||||
not the type, unless there is not identifier name.
|
||||
|
||||
### 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<int> *list = (std::vector<int>*) malloc(sizeof(std::vector<int>));
|
||||
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.
|
||||
|
||||
### Pointers vs References
|
||||
|
||||
Pointers are generally preferred to references. References should be used for
|
||||
@ -157,68 +223,11 @@ void studioCode() {
|
||||
}
|
||||
```
|
||||
|
||||
### Write C++, Not C
|
||||
### File I/O
|
||||
|
||||
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.
|
||||
All engine file I/O should go through nostalgia::core::Context, which should go
|
||||
through ox::FileSystem. Similarly, all studio file I/O should go throuh
|
||||
nostalgia::studio::Project, which should go through ox::FileSystem.
|
||||
|
||||
#### 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<int> *list = (std::vector<int>*) malloc(sizeof(std::vector<int>));
|
||||
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.
|
||||
ox::FileSystem abstracts away differences between conventional storage devices
|
||||
and ROM.
|
||||
|
Loading…
Reference in New Issue
Block a user