[nostalgia] Cleanup and update developer handbook

This commit is contained in:
Gary Talent 2021-10-23 14:10:16 -05:00
parent 6074cf7cd6
commit 6160335af3

View File

@ -23,7 +23,7 @@ All components have a platform indicator next to them:
* Nostalgia * Nostalgia
* core - platform abstraction and user I/O (PG) * core - platform abstraction and user I/O (PG)
* gba - GBA implementation (-G) * gba - GBA implementation (-G)
* sdl - SDL2 implementation (P-) * glfw - GLFW implementation (P-)
* qt - Qt implementation, mostly for studio support (P-) * qt - Qt implementation, mostly for studio support (P-)
* userland - common things needed by all non-bare-metal implementations (P-) * userland - common things needed by all non-bare-metal implementations (P-)
* studio - studio plugin for core (P-) * studio - studio plugin for core (P-)
@ -134,20 +134,25 @@ As parameters, references should always be const.
A non-const reference is generally used because the parameter value is changed A non-const reference is generally used because the parameter value is changed
in the function, but it will look like it was passed in by value where it is in the function, but it will look like it was passed in by value where it is
called and thus not subject to change. called and thus not subject to change.
The reference operator makes it clear that the value can and likely will change. The reference operator makes it clear to the caller that the value can and
likely will change.
## Project Systems
### Error Handling ### Error Handling
Exceptions are clean and nice and gleefully encouraged in userland code running Exceptions are clean and nice in userland code running in environments with
in environments with expansive system resources, but absolutely unacceptable in expansive system resources, but absolutely unacceptable in code running in
code running in restrictive bare metal environments. restrictive bare metal environments.
The GBA build has them disabled. The GBA build has them disabled.
Exceptions cause the compiler to generate a great deal of extra code that Exceptions cause the compiler to generate a great deal of extra code that
inflates the size of the binary. inflates the size of the binary.
The binary size bloat is often cited as one of the main reasons why many The binary size bloat is often cited as one of the main reasons why many
embedded developers prefer C to C++. embedded developers prefer C to C++.
Instead of throwing exceptions, all engine code must return error codes. Instead of throwing exceptions, all engine code must return Ox error codes.
For the sake of consistency, try to stick to Ox error codes in non-engine code
as well.
Nostalgia and Ox both use ```ox::Error``` to report errors. ```ox::Error``` is Nostalgia and Ox both use ```ox::Error``` to report errors. ```ox::Error``` is
a struct that has overloaded operators to behave like an integer error code, a struct that has overloaded operators to behave like an integer error code,
plus some extra fields to enhance debuggability. plus some extra fields to enhance debuggability.
@ -168,7 +173,7 @@ declared as ```noexcept``` and all exceptions should be translated to an
```ox::Result``` can be used as follows: ```ox::Result``` can be used as follows:
```cpp ```cpp
ox::Result<int> foo(int i) { ox::Result<int> foo(int i) noexcept {
if (i < 10) { if (i < 10) {
return i + 1; // implicitly calls ox::Result<T>::Result(T) return i + 1; // implicitly calls ox::Result<T>::Result(T)
} }
@ -222,7 +227,7 @@ void studioCode() {
doStuff(val); doStuff(val);
} }
ox::Error engineCode() { ox::Error engineCode() noexcept {
auto [val, err] = foo(1); auto [val, err] = foo(1);
oxReturnError(err); oxReturnError(err);
doStuff(val); doStuff(val);
@ -245,7 +250,7 @@ void studioCode() {
doStuff(valerr.value); doStuff(valerr.value);
} }
ox::Error engineCode() { ox::Error engineCode() noexcept {
auto valerr = foo(1); auto valerr = foo(1);
oxReturnError(valerr); oxReturnError(valerr);
doStuff(valerr.value); doStuff(valerr.value);
@ -269,11 +274,11 @@ fn f2() -> Result<i32, i32> {
```oxRequire```: ```oxRequire```:
```cpp ```cpp
ox::Result<int> f() { ox::Result<int> f() noexcept {
// do stuff // do stuff
} }
ox::Result<int> f2() { ox::Result<int> f2() noexcept {
oxRequire(i, f()); // const auto [out, oxConcat(oxRequire_err_, __LINE__)] = x; oxReturnError(oxConcat(oxRequire_err_, __LINE__)) oxRequire(i, f()); // const auto [out, oxConcat(oxRequire_err_, __LINE__)] = x; oxReturnError(oxConcat(oxRequire_err_, __LINE__))
return i + 4; return i + 4;
} }
@ -302,8 +307,8 @@ Where trace statements are intended to be written with thoughtfulness,
debug statements are intended to be quick and temporary insertions. debug statements are intended to be quick and temporary insertions.
Debug statements trigger compilation failures if OX_NODEBUG is enabled when CMake is run, Debug statements trigger compilation failures if OX_NODEBUG is enabled when CMake is run,
as it is on Jenkins builds, so ```oxDebug``` statements should never be checked in. as it is on Jenkins builds, so ```oxDebug``` statements should never be checked in.
This makes oxDebug preferable to other forms of logging, as temporary prints should This makes ```oxDebug``` preferable to other forms of logging, as temporary prints should
never be checked in anyway. never be checked in.
```oxError``` always prints. ```oxError``` always prints.
It includes file and line, and is prefixed with a red "ERROR:". It includes file and line, and is prefixed with a red "ERROR:".
@ -381,23 +386,23 @@ struct NostalgiaGraphic {
}; };
template<typename T> template<typename T>
constexpr ox::Error model(T *io, NostalgiaPalette *pal) { constexpr ox::Error model(T *h, NostalgiaPalette *pal) noexcept {
io->template setTypeInfo<NostalgiaPalette>(); h->template setTypeInfo<NostalgiaPalette>();
// it is also possible to provide the type name and number of fields as function arguments // it is also possible to provide the type name and number of fields as function arguments
//io->setTypeInfo("net.drinkingtea.nostalgia.core.NostalgiaPalette", 1); //h->setTypeInfo("net.drinkingtea.nostalgia.core.NostalgiaPalette", 1);
oxReturnError(io->field("colors", &pal->colors)); oxReturnError(h->field("colors", &pal->colors));
return OxError(0); return OxError(0);
} }
template<typename T> template<typename T>
constexpr ox::Error model(T *io, NostalgiaGraphic *ng) { constexpr ox::Error model(T *h, NostalgiaGraphic *ng) noexcept {
io->template setTypeInfo<NostalgiaGraphic>(); h->template setTypeInfo<NostalgiaGraphic>();
oxReturnError(io->field("bpp", &ng->bpp)); oxReturnError(h->field("bpp", &ng->bpp));
oxReturnError(io->field("rows", &ng->rows)); oxReturnError(h->field("rows", &ng->rows));
oxReturnError(io->field("columns", &ng->columns)); oxReturnError(h->field("columns", &ng->columns));
oxReturnError(io->field("defaultPalette", &ng->defaultPalette)); oxReturnError(h->field("defaultPalette", &ng->defaultPalette));
oxReturnError(io->field("pal", &ng->pal)); oxReturnError(h->field("pal", &ng->pal));
oxReturnError(io->field("pixels", &ng->pixels)); oxReturnError(h->field("pixels", &ng->pixels));
return OxError(0); return OxError(0);
} }
``` ```
@ -411,7 +416,7 @@ The model system also provides for unions:
class FileAddress { class FileAddress {
template<typename T> template<typename T>
friend Error model(T*, FileAddress*); friend constexpr Error model(T*, FileAddress*) noexcept;
public: public:
static constexpr auto TypeName = "net.drinkingtea.ox.FileAddress"; static constexpr auto TypeName = "net.drinkingtea.ox.FileAddress";
@ -425,25 +430,26 @@ class FileAddress {
uint64_t inode; uint64_t inode;
}; };
protected:
FileAddressType m_type = FileAddressType::None; FileAddressType m_type = FileAddressType::None;
Data m_data; Data m_data;
}; };
template<typename T> template<typename T>
constexpr Error model(T *io, FileAddress::Data *obj) { constexpr Error model(T *h, FileAddress::Data *obj) noexcept {
io->template setTypeInfo<FileAddress::Data>(); h->template setTypeInfo<FileAddress::Data>();
oxReturnError(io->field("path", SerStr(&obj->path))); oxReturnError(h->field("path", SerStr(&obj->path)));
oxReturnError(io->field("constPath", SerStr(&obj->path))); oxReturnError(h->field("constPath", SerStr(&obj->path)));
oxReturnError(io->field("inode", &obj->inode)); oxReturnError(h->field("inode", &obj->inode));
return OxError(0); return OxError(0);
} }
template<typename T> template<typename T>
constexpr Error model(T *io, FileAddress *fa) { constexpr Error model(T *h, FileAddress *fa) noexcept {
io->template setTypeInfo<FileAddress>(); h->template setTypeInfo<FileAddress>();
oxReturnError(io->field("type", bit_cast<int8_t*>(&fa->m_type))); oxReturnError(h->field("type", bit_cast<int8_t*>(&fa->m_type)));
oxReturnError(io->field("data", UnionView(&fa->m_data, static_cast<int>(fa->m_type)))); oxReturnError(h->field("data", UnionView(&fa->m_data, static_cast<int>(fa->m_type))));
return OxError(0); return OxError(0);
} }
``` ```