conventions(7) | conventions(7) |
conventions - cfl conventions
CFL files and conventions for naming, object lifetimes and manual pages.
The file config.hpp
contains some hardware dependent settings, such as whether
the program is intended run distributed (on more than one process) or
accelerated (using GPU:s). cfl.hpp
is a master header including all available
files.
First, the CFL manual pages normally use a function template to describe what
really is an instantiation of a class overloading operator ()
, or a function
object.
struct F
{
template <typename U>
auto operator () (U && u)
{
return u;
}
} f;
assert (f (1) == 2);
Is written as
template <typename U> auto f (U && u);
f (1) == 2;
Function templates are not suitable for a library such as CFL where functions appear as stand-alone values. Below is a list of further simplified notations used in the documentation. Although not always valid C++, they should make the manual pages less verbose and easier to read.
==
instead of full assert
expressions.auto
declaration although CFL requires only C++11.noexcept
function specifiers.std::declval
.Throughout CFL, function arguments are always passed by reference. If the
callee must store argument values to use after the full expression has ended,
storage is selected from the argument lifetime and ownership encoded in the
regular const
and reference specification (using the terminology from c++
specification [basic.lval]):
const && arg ==> store arg as copy-constructed value
&& arg ==> store arg as move-constructed value
const & arg ==> store arg by const lvalue reference
& arg ==> store arg by lvalue reference
This is called standard CFL storage and normally applies to all containers in CFL, such as tuples, asynchronous functions and closures. Functions evaluating to a return value immediately just use references to arguments.
The rationale for this choice of storage, is the fact that with no manual memory management in the user code (i.e. using Scope Based Resource Management), an lvalue will outlive any later expression in scope.
Argument lifetime may be manipulated through functions move
and copy
, which
basically are utility functions for casting to non-const and const rvalue
references respectively.
The storage for a function may be changed using glfn
. Then values are stored
by reference only, lvalues as lvalue references and rvalues as rvalue
references. The name glfn
refers to glvalue function - a function using
generalized lvalues for storage. Note, glfn
operates on the function itself,
not its arguments.
glfn (bind (1__, 0)) ([] (int arg) { return arg; })
The above example would store the argument 0
by rvalue reference in the
closure, instead of move-constructing a value. This a manual optimization, done
when the caller can guarantee argument lifetimes.
Analogously, the storage for a function may be changed to only use prvalues
using prfn
.
At the other end, return values are passed back to the caller using the same
conventions: lvalues as lvalue references and rvalues as values. To avoid
unnecessary copy- or move-construction, return values should preferably be
assigned to auto &&
declared variables.
auto && r = f ();
Here, neither glvalues nor prvalues returned from f
are copy- or
move-constructed into r
, but instead bound by reference immediately. As
usual, the lifetime of a returned prvalue is prolonged until the end of scope.
However, it is the users responsibility to guarantee the lifetime of inner
references in return values. Normally, such inner references would be a
consequence of previous lifetime manipulation by the user - the default argument
and return value lifetime convention should be safe.
An exception to this is when asynchronous values from an outer scope are assigned references bound to values in the inner scope. Then the caller must manually copy-construct such left-hand-side inner scope references.
In short, using the default c++ scope based resource management and assigning
function return value using auto &&
, object lifetimes are guaranteed without
unnecessary copy- or move-construction, also for asynchronous function.
Functions have plain names without prefix or suffix.
int plus (int u, int b) { return u + v; }
Function classes are classes which overload operator ()
. They are suffixed
with _f
. Instantiations thereof (function objects) are named as functions and
uses no prefix or suffix.
struct plus_f
{
template <typename U, typename V>
auto operator () (U && u, V && v)
{
return u + v;
}
} plus;
Factories are possibly templated classes with at least a member type type
,
and is normally suffixed with an underscore _. A factory may have an optional
static member function value
returning type
, where parameters by convention
have the same type as the class template argument list. For distinction, the
former is called a type factory and the latter a type-value factory.
template <typename U, typename V>
struct plus_
{
typedef int type;
static type value (U && u, V && v)
{
return u + v;
}
}
The added rvalue reference declarators for U
and V
are part of the object
lifetime scheme, which is treated in [OBJECT LIFETIMES][].
As type-factories can not have a function variant, the underscore suffix is
sometimes omitted to please the eye. Similar to std
, CFL uses the _t
suffix
to denote factory members type type
(although this violates the POSIX
reservation of the _t
suffix).
A factory may also have a template class as result. Then the member holding
the result is called factory
instead of type
. As usual in templated C++
code, when accessing a class member the user has to explicitly specify if it is
of typename
or template
type. So not using type
as name does not break any
generality in the factory conventions.
Some functions taking another function as argument come with a variant prefixed
with f
, then replacing the function argument with a factory as template
argument. This is mostly relevant for the CFL implementation, but an end user
example is enable_if
and fenable_if
, where the first has a function as
predicate argument and the latter a factory template.
Containers are classes holding other values. They are parametrized by the
types they are holding and are suffixed with _c
. An example is tuple_c
, a
fixed-size collection of heterogeneous values.
template <typename... U> struct tuple_c;
U, V, X...
u, v, x...
F, G, H...
and f, ,g, h...
respectively.Y
, such as FY, GY, HY...
. Normally, factories are not
instantiated, so there are no corresponding lowercase parameters.H
or T
respectivelyR, S, T...
I, J, K...
K
.R
.RY
.T
refers to the enclosing class type.cfl_
are markers used for external inspection,
such as predicates.