New coding guidelines: Difference between revisions
Jump to navigation
Jump to search
m (Added another entry: Unnecesasry parenthesis) |
m (typo) |
||
(7 intermediate revisions by 3 users not shown) | |||
Line 5: | Line 5: | ||
<li>Use '''const''' and '''constexpr''' wherever possible.</li> | <li>Use '''const''' and '''constexpr''' wherever possible.</li> | ||
<li>Use the '''noexcept''' specifier whenever possible.</li> | <li>Use the '''noexcept''' specifier whenever possible, but be cautious as it can cause the program to terminate if an exception is thrown.</li> | ||
<li>Use '''nullptr''' instead of '''NULL''' when setting or returning a null pointer.</li> | <li>Use '''nullptr''' instead of '''NULL''' when setting or returning a null pointer.</li> | ||
Line 176: | Line 176: | ||
const CPositionRotationAnimation* CObject::GetMoveAnimation() | const CPositionRotationAnimation* CObject::GetMoveAnimation() | ||
{ | { | ||
return | return IsMoving() ? m_pMoveAnimation : nullptr; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 195: | Line 195: | ||
</li> | </li> | ||
<li>Use Hungarian notation | <li>Use Hungarian notation only when necessary to maintain consistency with existing code; however, try to avoid using it in entirely new code (new files, etc.). | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
float fValue; // Local variable | float fValue; // Local variable | ||
Line 240: | Line 240: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
</li> | |||
<li>In new codes, try not to use the '''SString''' type anymore, use '''std::string''' instead.</li> | |||
<li>Class member, should start with the prefix '''m_''' | |||
<syntaxhighlight lang="cpp"> | |||
CVector m_vecPosition; | |||
CVector m_vecRotation; | |||
bool m_isVisible; | |||
bool m_isFrozen; | |||
</syntaxhighlight> | |||
</li> | |||
<li>Use member initialization lists whenever possible. Instead of doing: | |||
<syntaxhighlight lang="cpp"> | |||
CObject::CObject(CElement* pParent, CObjectManager* pObjectManager, bool bIsLowLod) | |||
: CElement(pParent), m_bIsLowLod(bIsLowLod), m_pLowLodObject(NULL) | |||
{ | |||
// Init | |||
m_iType = CElement::OBJECT; | |||
SetTypeName("object"); | |||
m_pObjectManager = pObjectManager; | |||
m_usModel = 0xFFFF; | |||
m_pMoveAnimation = NULL; | |||
m_ucAlpha = 255; | |||
m_vecScale = CVector(1.0f, 1.0f, 1.0f); | |||
m_fHealth = 1000.0f; | |||
m_bSyncable = true; | |||
m_pSyncer = NULL; | |||
m_bIsFrozen = false; | |||
m_bDoubleSided = false; | |||
m_bBreakable = false; | |||
m_bCollisionsEnabled = true; | |||
// Add us to the manager's list | |||
pObjectManager->AddToList(this); | |||
} | |||
</syntaxhighlight> | |||
Do: | |||
<syntaxhighlight lang="cpp"> | |||
CObject::CObject(CElement* pParent, CObjectManager* pObjectManager, bool bIsLowLod) | |||
: CElement(pParent), | |||
m_bIsLowLod(bIsLowLod), | |||
m_pLowLodObject(nullptr), | |||
m_iType(CElement::OBJECT), | |||
m_pObjectManager(pObjectManager), | |||
m_usModel(0xFFFF), | |||
m_pMoveAnimation(nullptr), | |||
m_ucAlpha(255), | |||
m_vecScale(1.0f, 1.0f, 1.0f), | |||
m_fHealth(1000.0f), | |||
m_bSyncable(true), | |||
m_pSyncer(nullptr), | |||
m_bIsFrozen(false), | |||
m_bDoubleSided(false), | |||
m_bBreakable(false), | |||
m_bCollisionsEnabled(true) | |||
{ | |||
SetTypeName("object"); | |||
// Add us to the manager's list | |||
pObjectManager->AddToList(this); | |||
} | |||
</syntaxhighlight> | |||
</li> | |||
<li>If you want to return an error to the debug script, throw a '''std::invalid_argument''' exception or use '''std::logic_error'''. | |||
<syntaxhighlight lang="cpp"> | |||
if (!CClientBuildingManager::IsValidModel(modelId)) | |||
throw std::invalid_argument("Invalid building model id"); | |||
if (!CClientBuildingManager::IsValidPosition(pos)) | |||
throw std::invalid_argument("Position is outside of game world"); | |||
</syntaxhighlight> | |||
If you want to throw a warning instead of an error, use [https://github.com/multitheftauto/mtasa-blue/blob/master/Shared/mods/deathmatch/logic/lua/CLuaFunctionParser.h#L22 LuaFunctionError] class with <code>throwWarning = true</code> | |||
</li> | </li> | ||
</ol> | </ol> | ||
[[Category: Development]] |
Latest revision as of 13:32, 26 June 2024
This page contains new and unofficial coding guidelines.
- Use const and constexpr wherever possible.
- Use the noexcept specifier whenever possible, but be cautious as it can cause the program to terminate if an exception is thrown.
- Use nullptr instead of NULL when setting or returning a null pointer.
- Functions that only return a value should be placed in header files. For example, instead of:
// In .cpp file int GetGameSpeed() { return m_iGameSpeed; }
Do it in the header:
int GetGameSpeed() const { return m_iGameSpeed; }
- Use early-return to improve code readability. For example, instead of:
bool CStaticFunctionDefinitions::RespawnObject(CElement* pElement) { if (IS_OBJECT(pElement)) { CObject* pObject = static_cast<CObject*>(pElement); if (pObject) { pObject->Respawn(); return true; } } return false; }
It's clearer to do:
bool CStaticFunctionDefinitions::RespawnObject(CElement* pElement) { if (!IS_OBJECT(pElement)) return false; CObject* pObject = static_cast<CObject*>(pElement); if (!pObject) return false; pObject->Respawn(); return true; }
- Always strive to maintain code readability. If a type is lengthy to write, you can use auto to improve readability.
CDeatchmatchObject* pObject = static_cast<CDeathmatchObject*>(pEntity); // Can be auto* pObject = static_cast<CDeathmatchObject*>(pEntity);
- If you use addresses of values or functions, document what the address is with a comment or use a #define macro.
float CWeatherSA::GetWetRoads() const { return *(float*)0xC81308; // CWeather::WetRoads }
// In the header #define NUM_WETROADS 0xC81308 // In the .cpp float CWeatherSA::GetWetRoads() const { return *(float*)NUM_WETROADS; }
Both methods are OK.
-
When using the #define macro for addresses, you should use an appropriate prefix that indicates what the address represents.
#define FUNC_RemoveRef 0x4C4BB0 // Function address #define ARRAY_aCannons 0xC80740 // Array address #define STRUCT_CAESoundManager 0xB62CB0 // Struct address #define SIZE_CWaterCannon 0x3CC // Size of object (e.g., struct object) #define CLASSSIZE_WeaponInfo 112 // Class size #define NUM_CWaterCannon_Audio_Offset 0x32C // Number (e.g., offsets, integers, etc.) #define CLASS_CText 0xC1B340 // Class address #define VAR_CTempColModels_ModelPed1 0x968DF0 // Variable address (CColModel colModelPeds)
- For readability, if a loop or condition is short, omit curly braces. For example, instead of:
if (!bStillRunning) { StopMoving(); }
You can do:
if (!bStillRunning) StopMoving();
However, do not omit curly braces for larger blocks:
for (dont) for (do) for (this) ...
- Use range-based for loops when possible.
std::vector<int> vec; // Example std container // Bad: for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); it++) // Good: for (const auto& v : vec) for (const int& v : vec) // In case of maps you can use structured binding: std::map<std::string, int> myMap; for (const auto& [k, v] : myMap) // There are situations where you must use old style loops, in this case use `auto` for (auto it = vec.begin(); it != vec.end(); it++)
- Define and call functions without arguments simply with empty parentheses.
// Bad void MyFunction(void); MyFunction(void); // Good void MyFunction(); MyFunction();
- When possible, use a logical condition for code readability. Instead of:
const CPositionRotationAnimation* CObject::GetMoveAnimation() { if (IsMoving()) { return m_pMoveAnimation; } else { return nullptr; } }
Do:
const CPositionRotationAnimation* CObject::GetMoveAnimation() { return IsMoving() ? m_pMoveAnimation : nullptr; }
- Use lower camel case for variable names of types like custom structs and enums:
SSomeStruct valueOne; ESomeEnum m_valueTwo;
- Functions and classes use UpperCamelCase:
void UpperCamelCase(); class Vector;
- Use Hungarian notation only when necessary to maintain consistency with existing code; however, try to avoid using it in entirely new code (new files, etc.).
float fValue; // Local variable unsigned char m_ucValue; // Class member variable char ms_cValue; // Class static member variable bool g_bCrashTwiceAnHour; // Global variable char* szUsername; // Zero-terminated string SString strUsername; // String CVector vecPosition; // 3D Vector
-
In C++, prefer using types from the std namespace provided by appropriate headers (such as <cstdint>, <cstddef>, etc.). This is recommended for several reasons:
- Namespace Safety: Types defined in these headers are encapsulated within the std namespace, which helps avoid naming conflicts with user-defined types or macros. This follows the C++ standard practice of minimizing global namespace pollution.
- Consistency and Readability: Using std:: types ensures consistency and improves code readability by making it clear that these types are part of the standard library.
- C++ Standard Compliance: The C++ standard (C++11 and later) includes headers like <cstdint> and <cstddef>, which provide standardized types:
- <cstdint> includes exact-width integer types such as 'std::uint32_t, std::int32_t, etc.
- <cstddef> includes types like std::size_t, std::ptrdiff_t, etc.
- Portability and Maintainability: Using these headers makes your code more portable across different compilers and platforms, as it guarantees that these types are defined in a standardized way. This is especially important in a C++ environment, where the focus is on maintainability and cross-platform compatibility.
-
Don't use unnecessary parenthesis. Instead of:
bool CClientPed::IsDead() { (return (m_status == STATUS_DEAD)); }
Do:
bool CClientPed::IsDead() { return m_status == STATUS_DEAD; }
- In new codes, try not to use the SString type anymore, use std::string instead.
- Class member, should start with the prefix m_
CVector m_vecPosition; CVector m_vecRotation; bool m_isVisible; bool m_isFrozen;
- Use member initialization lists whenever possible. Instead of doing:
CObject::CObject(CElement* pParent, CObjectManager* pObjectManager, bool bIsLowLod) : CElement(pParent), m_bIsLowLod(bIsLowLod), m_pLowLodObject(NULL) { // Init m_iType = CElement::OBJECT; SetTypeName("object"); m_pObjectManager = pObjectManager; m_usModel = 0xFFFF; m_pMoveAnimation = NULL; m_ucAlpha = 255; m_vecScale = CVector(1.0f, 1.0f, 1.0f); m_fHealth = 1000.0f; m_bSyncable = true; m_pSyncer = NULL; m_bIsFrozen = false; m_bDoubleSided = false; m_bBreakable = false; m_bCollisionsEnabled = true; // Add us to the manager's list pObjectManager->AddToList(this); }
Do:
CObject::CObject(CElement* pParent, CObjectManager* pObjectManager, bool bIsLowLod) : CElement(pParent), m_bIsLowLod(bIsLowLod), m_pLowLodObject(nullptr), m_iType(CElement::OBJECT), m_pObjectManager(pObjectManager), m_usModel(0xFFFF), m_pMoveAnimation(nullptr), m_ucAlpha(255), m_vecScale(1.0f, 1.0f, 1.0f), m_fHealth(1000.0f), m_bSyncable(true), m_pSyncer(nullptr), m_bIsFrozen(false), m_bDoubleSided(false), m_bBreakable(false), m_bCollisionsEnabled(true) { SetTypeName("object"); // Add us to the manager's list pObjectManager->AddToList(this); }
- If you want to return an error to the debug script, throw a std::invalid_argument exception or use std::logic_error.
if (!CClientBuildingManager::IsValidModel(modelId)) throw std::invalid_argument("Invalid building model id"); if (!CClientBuildingManager::IsValidPosition(pos)) throw std::invalid_argument("Position is outside of game world");
If you want to throw a warning instead of an error, use LuaFunctionError class with
throwWarning = true