This document discusses the project's expectations on C++ and ISPC style. In general, the items discussed are meant to be a guide and not be overly strict. However, deviations from this document should be supported with technical reasoning.
This document is relatively simple and meant as a "quick overview" of expecations for OSPRay. A more complete set of guidelines for all of C++ can be found in the ISO C++ Core Guidelines.
In general, comments in code should describe "why" not "what". Code documentation should be in terms of C++ style single-line comments or multi-line C style comments. Avoid Doxygen-style code documentation, this is deprecated.
When adding or updating public API definitions, update the corresponding
doc/api.md documentation as part of your merge request. This keeps our
documentation always current, and minimizes the need for audits at
release time. Markdown files (.md) should use a 72 column limit for
running text, exceptions are URLs or example code (i.e. preformatted
sections in backticks).
In all code, the overall goal is to make interfaces "easy to use correctly" and "difficult to use incorrectly". Some characteristics that make code better are:
- Keep reasoning as local as possible
- Descriptive names
- Short functions
- Single functions/abstractions for repetitive concepts (less copy/paste)
- Compile errors over runtime errors
We have a number of small, single-purpose abstractions in rkcommon
that aide in the above goals. It is expected for contributions to first
try and use those constructs before implementing their own.
Lastly, it is expected that code is formatted using the provided
.clang-format config found in the root of the repository, e.g. by
running git-clang-format (which also avoids changes which are only due
to formatting). Consider to pre-format very long strings using C++11 raw
string literals.
NOTE: Both C++ and ISPC files can be formatted with clang-format.
- Prefer enums over string parameters.
- Parameter and object names are camelCase (e.g.
metallicPaint) - Parameter names are singular, even if it is an array (e.g.
index,material) - Data belonging together like the members of an array of structs,
which are passed as struct of arrays, have a common prefix. For
example
vertex.colorandvertex.normals - Switches (bools) name just what they switch, e.g.
shadows(notshadowsEnablednorenableShadows, noruseShadows)
C++ is a big language, so the following are things that we prefer to see in OSPRay:
- For collections, prefer STL containers over C arrays, specifically
std::vectorandrkcommon::containers::AlignedVector. - Prefer smart pointers to manage memory over raw
newanddelete. - Prefer the usage of
std::unique_ptrto manage heap objects, until memory lifetimes must be managed by more than one observer. - Prefer constructing smart pointers with
rkcommon::make_unique<>andstd::make_shared<>over usingnew. - Avoid globals and
std::shared_ptrthat cross translation units. - Prefer global scope variables to be marked
static. - Prefer functions to access global data in another translation unit
over marking them as
extern. - Keep raw pointer usage local, either in a function or in a single cpp file.
- Prefer the usage of references over pointers for cases where
nullptris not a valid value. - Prefer using
rkcommon::utility::OnScopeExitto robustly cleanup non-trivial objects in a function. - Avoid constructing
staticdata that requires initialization ordering with otherstaticdata.
- Define empty default constructors with
= default;, not with an empty{}. - Use the
virtualkeyword only to mark that child classes are expected to override, not that the function is overriding a parent method. - Always use the
overridekeyword where appropriate. - Prefer to write
constfunctions by default. - Prefer implementing inline member functions below the definition of the class or struct. This makes classes easier to read by avoiding unnecessary implementation details when reading the class interface.
- Implement
begin()andend()iterators for types which abstract collections. - Consider whether the type should be move-only (ex:
std::unique_ptr) or copyable. - Prefer
= delete;syntax to delete constructors over marking themprivate. - Prefer giving all applicable member values inlined default values.
- Only populate values in constructor initialization if the value is different than the default or is being set by a constructor parameter.
- Only mark methods as
virtualif the class is intended to be in an inheritance hierarchy. - Always implement a
virtualdestructor, even if it's= default;.
- Prefer the usage of an algorithm call over implementing a raw loop.
- Prefer making multi-STL algorithm functions their own function with a name.
- Prefer using
rkcommon::taskingconstructs instead of raw threads. - Prefer range-based for loops over indexing for loops where possible.
- Avoid shared mutable state in parallel code (requires synchronization).
- Capture type preference for lambda functions should be
[]→[&]→[=].
- Prefer to constrain template types to only relevant types using
static_assert()and type traits. - Use
std::forwardwhen passing universal references (T&&) to other templates. - Only use SFINAE when necessary, and try to keep its usage as simple as possible.
The following are concerns specific to ISPC:
-
Prefer using
uniformvariables. The point of ISPC is to have varying data, but uniform values are more efficient than replicated varying values. -
Avoid writing unnecessary
uniformandvaryingkeywords. Examples:varying float f; // 'varying' is redundant, variables are varying by default uniform float *p; // 'uniform' is redundant, pointers point to uniform by default -
Prefer omitting
uniform/varyingqualifiers for structs. Only apply them if they need to be consistently the same for bothuniformandvaryinginstances of the struct.
- Prefer to use Embree C API bindings over ISPC where possible.
- Prefer to throw exceptions when an object is found to be invalid
during
commit(). - Prefer setting ISPC-side values in a single
set()call over many single value setting functions. - Minimize the number of alias names given to both object types and parameters.