New coding guidelines: Difference between revisions

From Multi Theft Auto: Wiki
Jump to navigation Jump to search
m (typo)
(Replaced content with "{{Delete|Moved to mtasa-docs GitHub repository}}")
Tag: Replaced
 
(4 intermediate revisions by one other user not shown)
Line 1: Line 1:
This page contains new and unofficial coding guidelines.
{{Delete|Moved to mtasa-docs GitHub repository}}
<br><br>
 
<ol>
<li>Use '''const''' and '''constexpr''' wherever 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>Functions that only return a value should be placed in header files. For example, instead of:
<syntaxhighlight lang="cpp">
// In .cpp file
int GetGameSpeed()
{
    return m_iGameSpeed;
}
</syntaxhighlight>
Do it in the header:
<syntaxhighlight lang="cpp">
int GetGameSpeed() const { return m_iGameSpeed; }
</syntaxhighlight>
</li>
 
<li>Use early-return to improve code readability. For example, instead of:
<syntaxhighlight lang="cpp">
bool CStaticFunctionDefinitions::RespawnObject(CElement* pElement)
{
    if (IS_OBJECT(pElement))
    {
        CObject* pObject = static_cast<CObject*>(pElement);
        if (pObject)
        {
            pObject->Respawn();
            return true;
        }
    }
 
    return false;
}
</syntaxhighlight>
It's clearer to do:
 
<syntaxhighlight lang="cpp">
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;
}
</syntaxhighlight>
</li>
 
<li>Always strive to maintain code readability. If a type is lengthy to write, you can use '''auto''' to improve readability.
<syntaxhighlight lang="cpp">
CDeatchmatchObject* pObject = static_cast<CDeathmatchObject*>(pEntity);
// Can be
auto* pObject = static_cast<CDeathmatchObject*>(pEntity);
</syntaxhighlight>
</li>
 
<li>If you use addresses of values or functions, document what the address is with a comment or use a '''#define''' macro.
<syntaxhighlight lang="cpp">
float CWeatherSA::GetWetRoads() const
{
    return *(float*)0xC81308; // CWeather::WetRoads
}
</syntaxhighlight>
<syntaxhighlight lang="cpp">
// In the header
#define NUM_WETROADS    0xC81308
 
// In the .cpp
float CWeatherSA::GetWetRoads() const
{
    return *(float*)NUM_WETROADS;
}
</syntaxhighlight>
 
Both methods are '''OK'''.
</li>
 
<li>
When using the '''#define''' macro for addresses, you should use an appropriate prefix that indicates what the address represents.
<syntaxhighlight lang="cpp">
#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)
</syntaxhighlight>
</li>
 
<li>For readability, if a loop or condition is short, omit curly braces. For example, instead of:
<syntaxhighlight lang="cpp">
if (!bStillRunning)
{
    StopMoving();
}
</syntaxhighlight>
You can do:
 
<syntaxhighlight lang="cpp">
if (!bStillRunning)
    StopMoving();
</syntaxhighlight>
 
However, do not omit curly braces for larger blocks:
<syntaxhighlight lang="cpp">
for (dont)
    for (do)
        for (this)
            ...
</syntaxhighlight>
</li>
 
<li>Use [https://en.cppreference.com/w/cpp/language/range-for range-based for loops] when possible.
<syntaxhighlight lang="cpp">
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++)
</syntaxhighlight>
</li>
 
<li>Define and call functions without arguments simply with empty parentheses.
<syntaxhighlight lang="cpp">
// Bad
void MyFunction(void);
MyFunction(void);
 
// Good
void MyFunction();
MyFunction();
</syntaxhighlight>
</li>
 
<li>When possible, use a logical condition for code readability. Instead of:
<syntaxhighlight lang="cpp">
const CPositionRotationAnimation* CObject::GetMoveAnimation()
{
    if (IsMoving())
    {
        return m_pMoveAnimation;
    }
    else
    {
        return nullptr;
    }
}
</syntaxhighlight>
Do:
 
<syntaxhighlight lang="cpp">
const CPositionRotationAnimation* CObject::GetMoveAnimation()
{
    return IsMoving() ? m_pMoveAnimation : nullptr;
}
</syntaxhighlight>
</li>
 
<li>Use lower camel case for variable names of types like custom structs and enums:
<syntaxhighlight lang="cpp">
SSomeStruct  valueOne;
ESomeEnum    m_valueTwo;
</syntaxhighlight>
</li>
 
<li>Functions and classes use UpperCamelCase:
<syntaxhighlight lang="cpp">
void UpperCamelCase();
class Vector;
</syntaxhighlight>
</li>
 
<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">
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
</syntaxhighlight>
</li>
 
<li>
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.
 
By adhering to these practices, you ensure that your codebase remains clean, consistent, and adheres to modern C++ standards, which ultimately contributes to better maintainability and fewer integration issues.
</li>
 
<li>
Don't use unnecessary parenthesis. Instead of:
<syntaxhighlight lang="cpp">
bool CClientPed::IsDead()
{
    (return (m_status == STATUS_DEAD));
}
</syntaxhighlight>
Do:
<syntaxhighlight lang="cpp">
bool CClientPed::IsDead()
{
    return m_status == STATUS_DEAD;
}
</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>
 
</ol>
 
[[Category: Development]]

Latest revision as of 14:26, 20 July 2024

Edit-delete.png This page is marked for deletion.

Reason: Moved to mtasa-docs GitHub repository
Actions: Delete (Administrators) - Discuss - What links here - Category