When writing code for FiberTaskingLib, I always try to follow these conventions. Sticking to a common set of formatting / indention rules makes it easier to read through the source base. Therefore, if you want to submit patches, please try to adhere to these rules.
I highly recommend just using clang-format to auto-format all your changes. Then you can skip most of the details of this page. I personally have clang-format set to run on save in my IDE. But there is also a Makefile command format
that will run clang format on the entire code-base using docker.
In the following examples, tabs are replaced by spaces for visual consistency on the web. But in actual code, use tabs for indentations. See below for more details.
Braces in your code should look like the following example:
for (int i = 0; i < t; ++i) {
[...]
}
if (j < k) {
[...]
} else if (j > k) {
[...]
} else {
[...]
}
class Dummy {
[...]
};
for (int i = 0; i < 100; ++i) {
→ if ((i % 4) == 0) {
→ → FunctionWithManyArgs(width, height,
→ → •••••••••••••••••••••nullptr, &windowHandle, &windowColor,
→ → •••••••••••••••••••••foo, bar);
→ }
}
class BusWheel : public RubberInflatable {
(isNight) ? ColorMeDark() : ColorMeBright();
namespace FiberTaskingLib {
class MyClass {
public:
void Bar(int arg);
};
void MyClass::Bar(int arg) {
printf("Arg is: %i", arg);
}
} // End of namespace FiberTaskingLib
-
For namespace clauses that are longer than a few lines, it’s suggested to add a "End of namespace XXX" comment, to aid readability.
class MyClass {
public:
MyClass();
private:
int m_foo;
float m_bar;
};
In addition, the content of the preprocessor statements should be indented, including any nested preprocessor statements
void SwitchLevels(int nextLevel) {
int currentLevel = GetCurrentLevel();
#if defined(FTL_SPECIAL_HEADER)
#if (FTL_HEADER_VERSION > 5)
m_saveFile.WriteByte(1);
#else
m_saveFile.WriteByte(19);
#endif
#endif
m_saveFile.WriteFile();
// ....
}
No whitespace between template keyword and '<'. The template definition should be on the previous line to the class or function it applies to.
template<typename T>
class Queue {
public:
T Pop();
};
template<typename foo>
void MyFunc(foo arg) {
// ...
}
Operator keyword is NOT separated from the name, except for type conversion operators where it is required.
struct Foo {
void operator()() {
// ...
}
Foo &operator+=(Foo &other) {
// ...
}
operator bool() {
return true;
}
};
No whitespace after a cast; and in a pointer, we use a whitespace before the star but not after it.
const char *ptr = (const char *)foobar;
We use the same rule for references as we do for pointers: use a whitespace before the "&" but not after it.
int i = 0;
int &ref = i;
case keywords are aligned with the switch keyword. case contents are indented.
switch (cmd) {
case 'a':
SomeCmd();
// Fall Through intended
case 'A':
SomeMoreCmd();
break;
case 's':
Save();
break;
case 'l':
case 'p':
Close();
break;
default:
Dialog::HandleCommand(sender, cmd, data);
}
-
Note comment on whether fall through is intentional.
Prefixed with 'FTL_' and in all uppercase, underscore-separated.
FTL_THREAD_FUNC_RETURN_TYPE
Camel case starting with uppercase.
class MyClass { /* ... */ };
struct MyStruct { /* ... */ };
typedef int MyInt;
Prefixed with 'm_' and in camel case, starting with lowercase.
class Foo {
public:
Foo() {}
private:
char *m_somePrivateVariableName;
};
No prefix, camel case, starting with uppercase.
struct Bar {
int Width;
int Height;
};
Camel case, starting with uppercase.
void ThisIsMyFancyFunction();
class MyClass {
public:
MyClass() {}
};
In general you should avoid global variables, but if it can’t be avoided, use 'g_' as prefix, camel case, and start with lowercase
int g_someGlobalVariable;
Namespaces should strive to be descriptive. If you choose to use multiple words, they should be camelcase, starting with an uppercase. However, public namespaces will be used and typed A LOT, so they should be as easy to type as possible, without sacrificing readability.
namespace FiberTaskingLib {
}
or
namespace ftl {
}
if / for / while / etc. should always have braces, even if the content of the statement is only one line. This helps to prevent future bugs if/when the code is modified.
if (bar == 0) {
return true;
}
for (int i = 0; i < 20; ++i) {
printf("%i", i);
}
do {
foo = Update(foo);
} while (foo < 20);
Initializer lists should start on a new line from the constructor definition. In addition, each entry should be on its own line. Each entry should be aligned with the previous one, using spaces for alignment.
class Fiber {
public:
Fiber()
: m_stack(nullptr),
m_systemPageSize(0),
m_stackSize(0),
m_context(nullptr),
m_arg(0) {
}
// ...
};
The initializer list as a whole should be indented once if the contructor has no content, and indented twice if it does.
class WaitFreeQueue {
public:
WaitFreeQueue()
: m_top(1),
m_bottom(1),
m_array(new CircularArray(32)) {
m_array.Grow();
m_top += 1;
}
// ...
};
Classes / Structs should be laid out as follows:
class ExampleClass {
public:
// Constructors
ExampleClass();
ExampleClass(ExampleClass &&other);
public:
// Public member variables
int Width;
int Height;
private:
// Private member variables
float m_deltaTime;
public:
// Public methods
void Rotate();
private:
// Private methods
int DecrementHeight(float amount);
};
Classes, structs, functions are documented using the javadoc style
/**
* @brief Adds a group of tasks to the internal queue
*
* @param numTasks The number of tasks
* @param tasks The tasks to queue
* @return An atomic counter corresponding to the task group as a whole. Initially it will equal numTasks. When each task completes, it will be decremented.
*/
std::shared_ptr<std::atomic_uint> AddTasks(uint numTasks, Task *tasks);
Mostly, this is just common sense. However, the main philosopy is:
-
Naming should be used to explain What is going on
-
Comments should be used to explain Why.
The following goes slightly beyond code formatting: We use certain keywords (together with an explanatory text) to mark certain sections of our code. In particular:
-
FIXME: marks code that contains hacks or bad/temporary workarounds, things that really should be revised at a later point.
-
TODO: marks incomplete code, or things that could be done better but are left for the future.