Newer
Older
Import / applications / HighwayDash / ports / Design / CodingGuidelines.txt

Coding Guidelines
-----------------

Introduction:
-------------------------------

All guidelines are general rules which typically are best followed, but can be broken
when needed. There are exceptions to every rule, but think carefully about what you are
doing if not following these rules, and add comments/documentation where and why that is.

Also rules can change, so if the rule is a bad one, revise it.


Pointers:
-------------------------------

Use of pointers is one of the most difficult aspects of C and C++ programming as there
are many dangers if not used carefully.

One is memory leaks. Another is dereferencing a dangling reference. Another is semantics
of ownership, which is what can lead to the other mentioned problems.

There are some solutions:
  - C++11 adds std::shared_ptr (and unique_ptr) as a solution.
  - Rust has a borrow checker (but we are using C++, so not an option)

What std::shared_ptr manages to provide is a solution to all of the mentioned problems.
It avoids memory leaks because it keeps a count of references and on the last reference,
deallocates the memory. It avoids dangling references because it won't deallocate until
the last reference. Because there are no explicit new/delete, all copies of the shared_ptr
that reference the memory are in a way owning pointers. So one way to express a non-owning
pointer is to use a raw pointer.

The rules to follow here are to never use new/delete anywhere. Creation is done using
std::make_shared which will do the allocation. The created std::shared_ptr is an owning
pointer. Using get() and passing the raw pointer to a function for a non-owning pointer.
There is no need to call delete on the non-owning pointer. Keeping a copy of a
raw pointer in a data structure needs to be done with care that there is still a
shared_pointer somewhere referencing it.

Problems with using std::shared_ptr: there is overhead in both processing and storage.
The place new occurs is explicit, however where delete happens is not. Therefore there
are performance implications about the timing of destructors etc. A bit like the micro
stalls of a garbage collector, but not quite as bad. Keeping copies of raw-pointers that
are non-owning is error prone, no concept of a borrow checker.


Concept: borrowed_pointer (non-owning pointer).
  - in release builds the idea is it should optimize away to just being a raw pointer.
  - in debug builds it can do some runtime checking that it isn't being used in a
    dangerous manner. However this is not as good as something which is checked at
    compile-time, it is better than nothing
  - debug implmentation - has a copy of the shared_pointer, an extra reference for
    this borrowed pointer is added, and before the pointer is used it checks there
    are still other references besides its own.