-
Notifications
You must be signed in to change notification settings - Fork 9
Code Style Guidelines
Phil Miller edited this page Aug 13, 2020
·
2 revisions
Unless a constructor is intended to have implicit conversion semantics, use explicit
for single argument constructors for all classes.
- Unless you are using a normal 32-bit
int
, use the typedefs incstdint
for all other combinations. Usingunsigned
,long long
,long
, and so on can vary across platforms are in general and less descriptive when reading the code. Thus, use the following except for the special case of a normalint
:
Signed | Unsigned |
---|---|
int8_t |
uint8_t |
int16_t |
uint16_t |
int32_t |
uint32_t |
int64_t |
uint64_t |
- For integers that must hold a pointer, always use
uintptr_t
orintptr_t
.
Even if your constructors sets them, always initialize them. This way if another constructor is added we never have to worry about uninitialized variables.
struct A {
A() : a(10), b(4.f) { }
int a = 0;
float b = 0;
};
struct A {
A() { } // don't do this
A() = default; // do this!
A(A const&) = default;
A(A&&) = default;
A& operator=(A const&) = default;
};
When someone reading the code sees default
they know exactly what it means rather than an empty constructor. It reduces the overhead of reading code and chance of mistakes. If the default behavior is appropriate, don't include them at all.
Please read the rule of 3/5/0
- Try to keep lines under 80 cols in length with a few exceptions when it makes sense.
- Wrap parens and templates on the next line to keep them aligned
void test() {
auto result = function(
param1, param2, param3, this->getIndex(), param4, param5, param6,
param7, param8
);
return result + 1;
}
template <
typename A, typename B = typename std::enable_if_t<std::is_same<A, long>::value>,
typename C
>
struct Test { };
- Avoid using
typedef
, preferring use ofusing
. Theusing
construct makes the order more sane to read. Make type aliases whenever it seems useful. If you think a type might change later or is more useable, create a type alias.
using NodeType = uint16_t;
struct A {
using ActionListType = std::list<std::function<void()>>;
private:
ActionListType actions_;
};
- Avoid usage of
using namespace X
anywhere in the code (except maybe in tests) - Import names you need precisely within a scope where required
namespace X {
using NodeType = int16_t;
} /* end namespace X */
void myFunction(int x) {
if (x == 100) {
using X::NodeType;
NodeType y = static_cast<NodeType>(x);
}
}
- Do not indent for new namespaces, and keep them on one line
namespace vt { namespace group {
struct MyGoup { };
}} /* end namespace vt::group */
- Use an
enum struct
when possible. Degrading automatically (happens with non-strongly typed enums) can introduce bugs and provides less type checking.
enum struct CallbackEnum : int8_t {
ConstantOne = 0,
ConstantTwo = 1
};
- To get the underlying enum type for a strongly typed enum call
std::underlying_type<T>
For single-line blocks use curly braces.
if (done) {
return 10;
}
while (not done) {
vt::runScheduler();
}
/**
* \struct OtherClass
*
* \brief See preferred spacing on pure virtual method.
*/
struct OtherClass {
/**
* \brief A pure virtual method
*
* \param[in] num_times number of times the scheduler should be called
*/
virtual void myTestMethod(int num_times) = 0;
};
/**
* \struct MyClassName
*
* \brief Prefer "struct" over "class", reducing boilerplate
*
* Using "struct" gives you a default public visibility for the inheritance
* and class members
*/
template <typename T, typename U = void>
struct MyClassName : OtherClass {
using MyIntType = int32_t;
using IteratorType = typename std::vector<MyIntType>::const_reverse_iterator;
// Use defaults when possible and needed
MyClassName() = default;
MyClassName(MyClassName&&) = default;
MyClassName(MyClassName const&) = default;
// Often your destructor should be virtual
virtual ~MyClassName() = default;
// if it is above 80 cols, wrap to next line with regular indenting
static inline typename Abc<T,X>::type mySimpleMethod(
int a, int b, int c, int d
);
// Use the override keyword, it can only help you
void myTestMethod(int a) override {
// Initialize stack variables as much as possible (and static ones when possible)
int a = 0, tmp = 0;
int my_local_count = 0;
std::vector<int> my_vec = {};
/*
* Consider using "and", "or", "not" instead of "&&", "||", "!".
* Often it's more readable. It's fully C++ standard compliant.
* Not required, just a personal style opinion.
*/
if (my_local_count != 0 and a == 0) {
a = 10;
}
// Braces instead of semicolon when empty
// Prefer "true" instead of "1"
while (true) { }
// Prefer spacing here
for (int i = 0; i < 10; i++) {
}
for (auto&& elm : my_vec) { }
}
// if function returns bool, avoid type aliases
bool valid() const;
private:
// Private members have trailing underscore
int my_data_ = 0;
bool is_coll_ = true; /**< for booleans prefix with "is"/"has"/etc */
};
/**
* \struct ManyTemplateParams
*
* \brief A example class with many template parameters
*
* Wrap typename args when they get long, start ">" on next line, indented as
* so. Use "typename" unless you have to use "class": exception being template
* templates. Space goes here: "template <". Ellipses on on LHS for parameter
* packs.
*/
template <
typename CollectionT, typename IndexT, typename TupleT, typename RetT,
typename... Args
>
struct ManyTemplateParams {
// Wrap function args when they get long, indented as so
RetT operator()(
CollectionT const& col, IndexT const& idx, TupleT&& tup,
std::tuple<Args...> tup, std::unique_ptr<intt64_t> ptr
) {
return {};
}
};
- Refer to doxygen style guidelines
- Personally I use
doxymacs
in emacs for generating doxygen statements automatically for functions/members- To generate for a function/method:
doxymacs-insert-function-comment
- To generate for a member:
doxymacs-insert-member-comment
- For a general multiline comment:
doxymacs-insert-blank-multiline-comment
- I (Jonathan) have modified the plugin to follow the preferred doxygen style guidelines. If you want to use it, contact me and I will give you the elisp file.
- To generate for a function/method: