Coding style - Steelhead-Games/CharmQuarkEngine-wiki GitHub Wiki
General advices
- Try to adhere to the KISS and DRY principles whenever possible.
- Prefer offensive programming to defensive programming. Asserts are your best friends in developing a robust architecture and writing clean code.
- Try avoiding the auto keyword whenever possible. The only exception is the places where the data type can be easily deducted.
- Comments are the code smell. Try writing the code so clean that comments are redundant.
- Smart pointers are also the code smell. If you are using
shared_ptr
orweak_ptr
, that means that you don't have a proper manager for that resource and/or you are unsure about the lifetime of the resource. In that case, reach out to Aleksei Lesovoi for help. - Don't leave your code unclear. Even if you think it is clear because you are knowledgeable about C++, it is not fair for everybody else. Declare variables const if they will be unchangeable, declare classes final if the class is not assumed to be inherited, etc.
- Try making code performant as if there will never be another opportunity to work on it.
- Don't leave a mess. That's it. Even if it wasn't you who wrote this code, you are free to improve it, delete comments, refactor to make further modifications easier.
- If you see the code that you can improve by refactoring it or fixing the possible bug - do it, unless it takes a lot of time to do so, in that case reach out to
Aleksei Lesovoi
for advice. - Use the top-down design approach. Start with the convenient interface and then try to implement the system that could provide you with such an interface. Also, don't hesitate to go back and modify the interface whenever you think you can achieve a better result with a different interface that could be utilized with the simpler system.
- Use the right solutions for the valid issues. Don't overthink the problem. If you think of adding some fancy feature because it is fancy, or may be useful, don't add it until you are sure that it will be a good idea or unless you have a real problem. E.g. if you think that the engine needs the stack allocator, until you know it is 100% helpful for the project and will be 100% used, it will be a bad idea to implement it. On the other hand, sometimes solving the issue will require you to think ahead. E.g. you are thinking of adding a new bool for the material, and you are not sure if this would be needed later. Sometimes you cannot predict the usage of your feature, and just try to minimize the future work on refactoring the code without messing with the low-level systems a lot.
Commit messages
This is super important when creating an informative commit message. While we cannot create a webhook on the commit messages, look for each other during the code review: the commit message is a part of it.
Bad: "Fix."
Good: "Fixed the sync bug that included the std::barrier
."
Excellent: "Fixed the sync bug that included the std::barrier
; now std::barrier
inner value is reset properly."
Naming Convention
Generally, use camelCase.
int timeDuration;
Use prefix m_
for data members of a class.
bool m_Enabled;
Use prefix s_
for static variables.
int s_Count;
Use prefix g_
for global variables.
void* g_WindowPtr;
Use prefix k_
for global constant variables.
uint32_t k_FrameBufferCount;
If statements
If statements' brackets should be placed on their own separate lines
if (condition)
{
// body
}
If you are creating a branch that will fire only once or twice during the game's lifetime, don't hesitate to specify it it with the likely attribute
if (something wrong has happened) [unlikely](/Steelhead-Games/CharmQuarkEngine-wiki/wiki/unlikely)
{
// do this
}
else
{
// do that
}
switch statements
Always use the braces to highlight the switch body
. Switch
statements should always contain the default
branch
If you have a switch-case
that simply just returns the value, you can ignore the braces for the case body
. Example:
switch (variable)
{
case condition1:
return result1;
case condition2:
return result2;
default:
assert(false);
return result1;
}
Otherwise, case body
may also be highlighted with the braces, and break
s should be on a separate line:
switch (variable)
{
case condition1:
{
...
}
break;
case condition2:
{
...
}
break;
default:
{
...
}
}
include directives
Use <>
instead of ""
.
DO:
#include <RenderCore.h>
DO NOT:
#include "RenderCore.h"
try-catch exceptions
Strictly prohibited. All the exceptions are expensive performance-wise. If there is something wrong with the game that requires you to have exceptions, it should just fail — no need to try to fix the problem at runtime.
Default parameters
This is a debatable part, but on this project, we will be avoiding default parameters until special occasions. Why so?
- Making users explicitly set all the parameters, even if they are not used, will make them actually understand what parameters are passed and what they are missing;
- It helps avoid bugs when the function signature changes. E.g. while making a game, we decided to change the water surface behaviour Before
void UpdateWaterSurface(Texture* RainRipples = nullptr) {}
After
void UpdateWaterSurface(Texture* FlowMap = nullptr, Texture* RainRipples = nullptr) {}
If you had a function call in the engine
UpdateWaterSurface(GetRainRipples());
And you didn't update the code (which can happen), and some assets were not tested in time; this type of bug is tough to debug later.