You can learn everything you need to know about git here: the very basics, git branching, and how to write git commit messages. New code should include unit-test and pass all test in debug and release as well as conform to the style guide (still WIP).
///
doxygen-style comments. To generate the
documentation: make docs
.Indent like this (google
style-guide): to check
indentation just make style
.
inline Ind size() const noexcept { return size_; }
inline quantity::Dimensionless pInfty() const noexcept
{ return unit::pow(Tinfty(), gamma() / gammaM1()) / gamma(); }
template <class T, class P> T find_if(T first, const T last, P&& p) { // don't use extraneous spaces
while(first != last && !p(first)) { // minimize vertical empty space, prefer if(!p) to if(p == false)
++first;
} // never omit blocks { ... } !
return first;
}
add_hom3_test(name)
to a CMakeLists.txt
and write your test in
name_test.cpp
.add_hom3_mpi_test(name #np)
lets you specify the number of MPI processes
(#np
) that your test uses.All utilities are included with src/global.hpp
and are located in src/misc
.
DBG("this is printed if ENABLE_DBG_ == 1 for the current file");
DBG_ON("this is always printed");
DBGV_ON((var)(f())(3)); // prints: "var : value | function() : value | 3 : 3"
Deprecated: For "long" functions (i.e. not one liners), we also have a trace
back system that is used with the TRACE_IN
/TRACE_OUT
macros:
int fun(int a) {
TRACE_IN((a)); // pass the parameters to trace in
/*
do stuff
int b = ...;
*/
TRACE_OUT((b)); // you have to call a TRACE_OUT; before every return statement.
return b;
}
By enabling debug for a module with ENABLE_DBG_ == 1
you'll get the entry and
exit points of every function in that module as well as the input parameters and
return values of every function call.
Note: the above functionality is deprecated but hasn't been completely
removed yet. To generate traces the preferred method is to use the
instrumentation utilities with -finstrument-functions
(GCC only, clang has a
bug here).
Check pre-conditions, post-conditions, invariants:
ASSERT(condition, message); // perform checks in debug mode
ASSERT([&]() -> bool { /* executes in debug mode */ }(), message);
TERMINATE(message); // exits the program
There is also basic support for exceptions:
error::exception(message); // throws run-time error
For the moment there just hasn't been any exceptional behavior to handle. Most
of the time if an error occurs there is absolutely nothing that we can do about
it so hard errors (using TERMINATE
) have been the preferred way of dealing
with these situations.
std::make_unique
/std::make_shared
.Stack allocation:
std::array/std::tuple
are your friends,memory::stack::arena<double,10> stackMemory; // memory pool on the stack
auto myVector = memory::stack::make<std::vector>(stackMemory); // stack allocated std::vector<double>
auto myList = memory::stack::make<std::list>(stackMemory); // std::list<double> shares memory pool with vector
TERMINATE
is called if you run out of memory.memory::stack::vector
which is a drop in replacement
for std::vector
and contains its own arena.Heap vs stack?
math::ct
,math::rt
,math::absdiff
takes the difference of two unsigned integers,math::approx(T a, T b)
computes a == b using Google Test
FloatingPoint,io
namespace,Properties (simulation, grid generation, ...):
io::Properties ps; // creates property container
io::insert_property<Num>(ps, "dx", 3.0); // inserts property of type Num and name "dx"
auto dx = io::read<Num>ps,"dx"); // reads property of type Num and name "dx"
io::read(ps, "dx", dx); // reads property "dx" into dx
algorithm
namespace,Range<T>(.,.)
of integers or iterators,RangeFilter<T>
s and filtered ranges FRange<T>
,RangeTransformer<T>
s ,AnyRange<T>
.boost::mpi::root_do(comm, F&& f)
executes computation f
at the root of
the communicator comm
.prefer concept-based run-time polymorphism to inheritance-based polymorphism (flexible, cleaner, faster):
/// Models the _CONCEPT_NAME_ concept:
/// - fun1: Num -> bool
/// - fun2: NumA<3> -> Num
struct Interface {
/// Adapts T to model the _CONCEPT_NAME_ concept:
/// - can be specialized and overloaded for different T's
/// - a shared_ptr with type-erasure can be used to provide reference semantics
/// - an unique_ptr with type-erasure can be used to provide value semantics
template<class T> explicit Interface(const T& t)
: fun1([&](Num x){ return t.ts_fun1(x); }) // ts_fun1 -> fun1
, fun2([&}(NumA<3> x){ return t.ts_fun2(x); }) // ts_fun2 -> fun2
{}
/// The easiest way to provide reference semantics is to use generic functors
/// and capture by reference:
std::function<bool(Num)> fun1;
std::function<Num(NumA<3>)> fun2;
/// The easiest way to provide value semantics is to use a model sub-struct
/// with type-erasure.
};
traits
namespace,simple EnableIf
/ DisableIf
implementation (see Remastered
enable_if
and Beating overload resolution into
submission),
template<SInd nd, EnableIf<traits::equal<SInd,nd,2>> = traits::dummy>
void do_something(const NumA<nd> x) { /* exists only for 2D input arrays */ }
EnableIf<>...
doesn't work due to this bug in clang.noexcept(expr) -> decltype(expr) { return expr; }
,expr
can't contain a lambda!noexcept(expr) -> { return expr; }
.The RETURNS
macro does the right thing:
inline auto ghost_cells() const RETURNS(cell_ids() | ghosts);
cold_do(f)
executes computation f
but preventing its inlining.Know your attributes: [[attribute]]
[[gnu::noreturn]]
function cannot return.[[gnu::noinline]]
function is not inlined.[[gnu::always_inline]]
inlines inline
function even with
optimizations disabled.[[gnu::flatten]]
every call inside the function is inlined (not the
function itself).[[gnu::pure]]
function has no side-effects and its result depends on
arguments and/or global state.[[gnu::const]]
function has no side-effects and its result depends
only on its arguments.[[gnu::no_instrument_function]]
if instrumentation is enabled, this
function is not instrumented.[[gnu::hot]]
function is a hot spot (executed very often).[[gnu::cold]]
function is unlikely to be executed.[[aligned (alignment)]]
minimum alignment (in bytes)[[clang::fallthrough]]
denote intentional fall-through between switch
labels.Know your builtins:
long __builtin_expect(long exp, long c)
, it is expected that
exp == c
):if (likely (expr)) { /*...*/ }
if (unlikely(expr)) { /*...*/ }
// #define likely(x) __builtin_expect (!!(x), 1)
// #define unlikely(x) __builtin_expect (!!(x), 0)
void* __builtin_assume_alligned(const void *exp, size_t align, ...)
returns exp
, and allows the compiler to assume that the returned pointer
is at least align
bytes aligned.void __builtin_prefetch (const void *addr, rw, locality)
where addr
is
the address of the memory to prefetch, rw = {0, 1}
indicates an expected
read (0
) or an expected write (1
), and locality = {0, 1, 2, 3}
expresses the temporal locality of the data, where 0
means that the data
need not be left in the cache after the access, 3
means that the data
should be left in all levels of cache, and 1-2
are values in between.