Skip to content

Latest commit

 

History

History
135 lines (95 loc) · 3.82 KB

style_guide.md

File metadata and controls

135 lines (95 loc) · 3.82 KB

C++ Style Guide

Please follow these guidelines when submitting patches.

Whitespace

Use 4 spaces. Format if as follows:

if (d.find(t) == d.end()) {
    d[t] = coef;
} else {
    d[t] = d[t] + coef;
}

Pointers

Never use raw C++ pointers and never use the raw new and delete. Always use the smart pointers provided by symengine_rcp.h (depending on WITH_SYMENGINE_RCP, those are either Teuchos::RCP, or our own, faster implementation), i.e. Ptr and RCP (and do not use the .get() method, only the .ptr() method). In Debug mode, the pointers are 100% safe, i.e. no matter how you use them the code will not segfault, but instead raise a nice exception if a pointer becomes dangling or null. In Release mode, the Ptr is as fast as a raw pointer and RCP is a lot faster, but no checks are done (so the code can segfault).

Declaration

In the .cpp files you can declare:

using SymEngine::RCP;
using SymEngine::Ptr;
using SymEngine::outArg;
using SymEngine::make_rcp;
using SymEngine::rcp_dynamic_cast;

and then just use RCP or Ptr.

In the .h header files use the full name like SymEngine::RCP or SymEngine::Ptr.

Initialization

Initialize as follows:

RCP<Basic> x  = make_rcp<Symbol>("x");

Never call the naked new, nor use the naked rcp. If available, use the factory functions, e.g. in this case symbol() as follows:

RCP<Basic> x  = symbol("x");

This does the same thing (internally it calls make_rcp), but it is easier to use.

Freeing

The RCP pointer is released automatically. You never call the naked delete.

Passing To/From Functions

Use C++ references for objects that you are not passing around. If the object is not modified, use const A &a:

RCP<const Integer> gcd(const Integer &a, const Integer &b)
{
    integer_class g;
    mp_gcd(g, a.as_integer_class(), b.as_integer_class());
    return integer(std::move(g));
}

If it is modified, use A &a (see the first argument):

void Add::dict_add_term(umap_basic_num &d, const RCP<Integer> &coef,
        const RCP<Basic> &t)
{
    if (d.find(t) == d.end()) {
        d[t] = coef;
    } else {
        d[t] = d[t] + coef;
    }
}

If the objects are passed around, you have to use RCP. You also need to use RCP whenever you call some function that uses RCP.

Declare functions with two input arguments (and one return value) as follows:

RCP<const Basic> multiply(const RCP<const Basic> &a,
        const RCP<const Basic> &b)
{
    ...
    return make_rcp<const Integer>(1);
}

Functions with one input and two output arguments are declared:

void as_coef_term(const RCP<const Basic> &self,
    const Ptr<RCP<const Integer>> &coef,
    const Ptr<RCP<const Basic>> &term)
{
    ...
    *coef = make_rcp<const Integer>(1);
    *term = self
    ...
}

and used as follows:

RCP<Integer> coef;
RCP<Basic> t;
as_coef_term(b, outArg(coef), outArg(t));

SymEngine objects are always immutable, so you always declare them as const. And RCP is only used with SymEngine's objects, so you always use const RCP<const Integer> &i. But if the Integer was somehow mutable (it's not in SymEngine), you would use const RCP<Integer> &i.

For returning objects from functions, simply declare the return type as RCP<const Basic> as shown above.

Casting

You can use dynamic cast as follows:

RCP<Basic> tmp;
RCP<Integer> coef;
coef = rcp_dynamic_cast<Integer>(tmp);

Namespaces

Never use "implicit imports": using namespace std;.

In cpp files, either use the full name of the symbol (e.g. SymEngine::RCP), or use "explicit import" as follows: using SymEngine::RCP;.

In header files, always use the full name (never import symbols there).