1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
|
This document specifies how to contribute code to CAF.
Git Workflow
============
Please adhere to the following Git naming and commit message conventions.
Having a consistent work flow and style reduces friction and makes organizing
contributions a lot easier for all sides.
Branches
--------
- Our main branch is `master`. It reflects the latest development changes for
the next release and should always compile. Nightly versions use the
`master`. Users looking for a production-ready state are encouraged to use
the latest release version instead.
- Push trivial bugfixes (e.g. typos, missing includes, etc.) consisting of a
single commit directly to `master`. Otherwise, implement your changes in a
topic or bugfix branch and file a pull request on GitHub.
- Implement new features and non-trivial changes in a *topic branch* with
naming convention `topic/short-description`.
- Implement fixes for existing issues in a *bugfix branch* with naming
convention `issue/$num`, where `$num` is the issue ID on GitHub.
- Simply use a fork of CAF if you are an external contributor.
Pull Requests
-------------
Check the following steps to prepare for a merge into `master` after completing
work in a topic or bugfix branch (or fork).
- Squash your commits into a single one if necessary. Each commit should
represent a coherent set of changes.
- Wait for a code review and the test results of our CI.
- Address any review feedback and fix all issues reported by the CI.
- A maintainer will merge the pull request when all issues are resolved.
Commit Message Style
--------------------
- Summarize the changes in no more than 50 characters in the first line.
Capitalize and write in an imperative present tense, e.g., "Fix bug" as
opposed to "Fixes bug" or "Fixed bug".
- Suppress the dot at the end of the first line. Think of it as the header of
the following description.
- Leave the second line empty.
- Optionally add a long description written in full sentences beginning on the
third line. Indent at 72 characters per line.
Coding Style
============
When contributing source code, please adhere to the following coding style,
which is loosely based on the [Google C++ Style
Guide](https://google.github.io/styleguide/cppguide.html) and the coding
conventions used by the C++ Standard Library.
Example for the Impatient
-------------------------
```c++
// libcaf_example/caf/example/my_class.hpp
#pragma once
#include <string>
// use "//" for regular comments and "///" for doxygen
namespace caf {
namespace example {
/// This class is only being used as style guide example.
class my_class {
public:
/// Brief description. More description. Note that CAF uses the
/// "JavaDoc-style" autobrief option, i.e., everything up until the
/// first dot is the brief description.
my_class();
/// Destructs `my_class`. Please use Markdown in comments.
~my_class();
// suppress redundant @return if you start the brief description with "Returns"
/// Returns the name of this instance.
inline const std::string& name() const noexcept {
return name_;
}
/// Sets the name of this instance.
inline void name(const std::string& new_name) {
name_ = new_name;
}
/// Prints the name to `std::cout`.
void print_name() const;
/// Does something (maybe).
void do_something();
/// Does something else but is guaranteed to never throw.
void do_something_else() noexcept;
private:
std::string name_;
};
} // namespace example
} // namespace caf
```
```c++
// libcaf_example/src/example/my_class.cpp
#include "caf/example/my_class.hpp"
#include <iostream>
namespace caf {
namespace example {
namespace {
constexpr const char default_name[] = "my object";
} // namespace
my_class::my_class() : name_(default_name) {
// nop
}
my_class::~my_class() {
// nop
}
void my_class::print_name() const {
std::cout << name() << std::endl;
}
void my_class::do_something() {
if (name() == default_name) {
std::cout << "You didn't gave me a proper name, so I "
<< "refuse to do something."
<< std::endl;
} else {
std::cout << "You gave me the name \"" << name()
<< "\"... Do you really think I'm willing to do something "
"for you after insulting me like that?"
<< std::endl;
}
}
void my_class::do_something_else() noexcept {
// Do nothing if we don't have a name.
if (name().empty())
return;
switch (name.front()) {
case 'a':
// handle a
break;
case 'b':
// handle b
break;
default:
handle_default();
}
}
} // namespace example
} // namespace caf
```
```c++
// libcaf_example/test/example/my_class.cpp
#define CAF_SUITE example.my_class // name of this test suite
#include "caf/example/my_class.hpp" // header-under-test
#include "caf/test/dsl.hpp" // caf::test includes
#include <iostream> // standard includes
// ... // other CAF includes
namespace {
struct fixture {};
} // namespace
CAF_TEST_FIXTURE_SCOPE(my_class_tests, fixture)
// ... any number of CAF_TEST ...
CAF_TEST_FIXTURE_SCOPE_END()
```
General
-------
- Use 2 spaces per indentation level.
- Use at most 80 characters per line.
- Never use tabs.
- Never use C-style casts.
- Never declare more than one variable per line.
- Only separate functions with vertical whitespaces and use comments to
document logical blocks inside functions.
- Use `.hpp` as suffix for header files and `.cpp` as suffix for implementation
files.
- Bind `*` and `&` to the *type*, e.g., `const std::string& arg`.
- Never increase the indentation level for namespaces and access modifiers.
- Use the order `public`, `protected`, and then `private` in classes.
- Always use `auto` to declare a variable unless you cannot initialize it
immediately or if you actually want a type conversion. In the latter case,
provide a comment why this conversion is necessary.
- Never use unwrapped, manual resource management such as `new` and `delete`.
- Prefer `using T = X` over `typedef X T`.
- Insert a whitespaces after keywords: `if (...)`, `template <...>`,
`while (...)`, etc.
- Put opening braces on the same line:
```c++
void foo() {
// ...
}
```
- Use standard order for readability: C standard libraries, C++ standard
libraries, OS-specific headers (usually guarded by `ifdef`), other libraries,
and finally (your) CAF headers. Include `caf/config.hpp` before the standard
headers if you need to include platform-dependent headers. Use angle brackets
for system includes and doublequotes otherwise.
```c++
// example.hpp
#include <vector>
#include <sys/types.h>
#include "3rd/party.h"
#include "caf/fwd.hpp"
```
Put the implemented header always first in a `.cpp` file.
```c++
// example.cpp
#include "caf/example.hpp" // header for this .cpp file
#include "caf/config.hpp" // needed for #ifdef guards
#include <algorithm>
#ifdef CAF_WINDOWS
#include <windows.h>
#else
#include <sys/socket.h>
#endif
#include "some/other/library.h"
#include "caf/actor.hpp"
```
- Put output parameters in functions before input parameters if unavoidable.
This follows the parameter order from the STL.
- Protect single-argument constructors with `explicit` to avoid implicit
conversions.
- Use `noexcept` whenever it makes sense and as long as it does not limit future
design space. Move construction and assignment are natural candidates for
`noexcept`.
Naming
------
- All names except macros and template parameters should be
lower case and delimited by underscores.
- Template parameter names should be written in CamelCase.
- Types and variables should be nouns, while functions performing an action
should be "command" verbs. Classes used to implement metaprogramming
functions also should use verbs, e.g., `remove_const`.
- Private and protected member variables use the suffix `_` while getter *and*
setter functions use the name without suffix:
```c++
class person {
public:
person(std::string name) : name_(std::move(name)) {
// nop
}
const std::string& name() const {
return name_
}
void name(const std::string& new_name) {
name_ = new_name;
}
private:
std::string name_;
};
```
- Use `T` for generic, unconstrained template parameters and `x`
for generic function arguments. Suffix both with `s` for
template parameter packs and lists:
```c++
template <class... Ts>
void print(const Ts&... xs) {
// ...
}
void print(const std::vector<T>& xs) {
// ...
}
```
Headers
-------
- Each `.cpp` file has an associated `.hpp` file.
Exceptions to this rule are unit tests and `main.cpp` files.
- Each class has its own pair of header and implementation files and the
relative path for the files are derived from the full class name. For
example, the header file for `caf::example::my_class` of `libcaf_example` is
located at `libcaf_example/caf/example/my_class.hpp` and the source file at
`libcaf_example/src/example/my_class.cpp`.
- All header files use `#pragma once` to prevent multiple inclusion.
- Do not `#include` when a forward declaration suffices.
- Each library component must provide a `fwd.hpp` header providing forward
declarations for all types used in the user API.
- Each library component should provide an `all.hpp` header that contains the
main page for the documentation and includes all headers for the user API.
- Use `inline` for small functions (rule of thumb: 10 lines or less).
Breaking Statements
-------------------
- Break constructor initializers after the comma, use two spaces for
indentation, and place each initializer on its own line (unless you don't
need to break at all):
```c++
my_class::my_class()
: my_base_class(some_function()),
greeting_("Hello there! This is my_class!"),
some_bool_flag_(false) {
// ok
}
other_class::other_class() : name_("tommy"), buddy_("michael") {
// ok
}
```
- Break function arguments after the comma for both declaration and invocation:
```c++
intptr_t channel::compare(const abstract_channel* lhs,
const abstract_channel* rhs) {
// ...
}
```
- Break before tenary operators and before binary operators:
```c++
if (today_is_a_sunny_day()
&& it_is_not_too_hot_to_go_swimming()) {
// ...
}
```
Template Metaprogramming
------------------------
Despite its power, template metaprogramming came to the language pretty
much by accident. Templates were never meant to be used for compile-time
algorithms and type transformations. This is why C++ punishes metaprogramming
with an insane amount of syntax noise. In CAF, we make excessive use of
templates. To keep the code readable despite all the syntax noise, we have some
extra rules for formatting metaprogramming code.
- Break `using name = ...` statements always directly after `=` if it
does not fit in one line.
- Consider the *semantics* of a metaprogramming function. For example,
`std::conditional` is an if-then-else construct. Hence, place the if-clause
on its own line and do the same for the two cases.
- Use one level of indentation per "open" template and place the closing `>`,
`>::type` or `>::value` on its own line. For example:
```c++
using optional_result_type =
typename std::conditional<
std::is_same<result_type, void>::value,
bool,
optional<result_type>
>::type;
// think of it as the following (not valid C++):
auto optional_result_type =
conditional {
if result_type == void
then bool
else optional<result_type>
};
```
- Note that this is not necessary when simply defining a type alias.
When dealing with "ordinary" templates, indenting based on the position of
the opening `<` is ok, e.g.:
```c++
using response_handle_type = response_handle<Subtype, message,
ResponseHandleTag>;
```
Preprocessor Macros
-------------------
- Use macros if and only if you can't get the same result by using inline
functions or proper constants.
- Macro names use the form `CAF_<COMPONENT>_<NAME>`.
Comments
--------
- Start Doxygen comments with `///`.
- Use Markdown instead of Doxygen formatters.
- Use `@cmd` rather than `\cmd`.
- Use `//` to define basic comments that should not be processed by Doxygen.
|