> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-fbfa8bee.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> إرشادات أسلوب البرمجة لتطوير ClickHouse بلغة C++

# دليل أسلوب C++

<div id="general-recommendations">
  ## توصيات عامة
</div>

فيما يلي توصيات وليست متطلبات.
إذا كنت تُحرِّر الشيفرة، فمن المنطقي اتباع تنسيق الشيفرة الحالية.
يُعد أسلوب كتابة الشيفرة ضروريًا لتحقيق الاتساق. فالانسجام في الأسلوب يجعل قراءة الشيفرة أسهل، كما يجعل البحث فيها أسهل أيضًا.
كثير من القواعد ليست لها أسباب منطقية، بل تفرضها الممارسات الراسخة.

<div id="formatting">
  ## التنسيق
</div>

**1.** يتم تنسيق معظم الكود تلقائيًا عبر `clang-format`.

**2.** المسافة البادئة هي 4 مسافات. قم بضبط بيئة التطوير لديك بحيث تُضيف مفتاح Tab أربع مسافات.

**3.** يجب أن تكون الأقواس المعقوفة الفاتحة والغالقة في سطر منفصل.

```cpp theme={null}
inline void readBoolText(bool & x, ReadBuffer & buf)
{
    char tmp = '0';
    readChar(tmp, buf);
    x = tmp != '0';
}
```

**4.** إذا كان متن الدالة بأكمله عبارة عن `statement` واحدة، يمكن وضعه في سطر واحد. ضع مسافات حول الأقواس المعقوفة (باستثناء المسافة في نهاية السطر).

```cpp theme={null}
inline size_t mask() const                { return buf_size() - 1; }
inline size_t place(HashValue x) const    { return x & mask(); }
```

**5.** للدوال. لا تضع مسافات حول الأقواس.

```cpp theme={null}
void reinsert(const Value & x)
```

```cpp theme={null}
memcpy(&buf[place_value], &x, sizeof(x));
```

**6.** في تعبيرات `if` و`for` و`while` وغيرها، تُضاف مسافة قبل القوس الافتتاحي (خلافًا لاستدعاءات الدوال).

```cpp theme={null}
for (size_t i = 0; i < rows; i += storage.index_granularity)
```

**7.** أضف مسافات حول العوامل الثنائية (`+`, `-`, `*`, `/`, `%`, ...) والعامل الثلاثي `?:`.

```cpp theme={null}
UInt16 year = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + (s[3] - '0');
UInt8 month = (s[5] - '0') * 10 + (s[6] - '0');
UInt8 day = (s[8] - '0') * 10 + (s[9] - '0');
```

**8.** عند إدخال حرف تغذية السطر، ضع المعامل في سطر جديد وزد المسافة البادئة قبله.

```cpp theme={null}
if (elapsed_ns)
    message << " ("
        << rows_read_on_server * 1000000000 / elapsed_ns << " rows/s., "
        << bytes_read_on_server * 1000.0 / elapsed_ns << " MB/s.) ";
```

**9.** يمكنك استخدام المسافات للمحاذاة داخل السطر عند الحاجة.

```cpp theme={null}
dst.ClickLogID         = click.LogID;
dst.ClickEventID       = click.EventID;
dst.ClickGoodEvent     = click.GoodEvent;
```

**10.** لا تستخدم مسافات حول المعاملات `.`، `->`.

إذا لزم الأمر، يمكن نقل المعامل إلى السطر التالي. في هذه الحالة، تزداد المسافة البادئة أمامه.

**11.** لا تستخدم مسافة للفصل بين العوامل الأحادية (`--`, `++`, `*`, `&`, ...) والمعامل.

**12.** ضع مسافة بعد الفاصلة، لا قبلها. وتسري القاعدة ذاتها على الفاصلة المنقوطة داخل تعبير `for`.

**13.** لا تستخدم مسافات للفصل في المعامل `[]`.

**14.** في تعبير `template <...>`، ضع مسافة بين `template` و`<`؛ ولا مسافات بعد `<` أو قبل `>`.

```cpp theme={null}
template <typename TKey, typename TValue>
struct AggregatedStatElement
{}
```

**15.** في الأصناف والبنى، اكتب `public` و`private` و`protected` على نفس مستوى `class/struct`، وأضف مسافة بادئة لبقية الكود.

```cpp theme={null}
template <typename T>
class MultiVersion
{
public:
    /// Version of object for usage. shared_ptr manage lifetime of version.
    using Version = std::shared_ptr<const T>;
    ...
}
```

**16.** إذا كان نفس `namespace` مستخدمًا في الملف بأكمله ولم يكن هناك شيء آخر ذو أهمية، فلا حاجة إلى مسافة بادئة داخل `namespace`.

**17.** إذا كانت الكتلة الخاصة بـ `if` أو `for` أو `while` أو أي تعبير آخر تحتوي على جملة واحدة `statement` فقط، فإن الأقواس المعقوفة تكون اختيارية. ضع الجملة `statement` في سطر منفصل بدلاً من ذلك. تنطبق هذه القاعدة أيضاً على `if` و`for` و`while` المتداخلة ...

لكن إذا احتوت `statement` الداخلية على أقواس معقوفة أو `else`، وجب كتابة الكتلة الخارجية بين أقواس معقوفة.

```cpp theme={null}
/// Finish write.
for (auto & stream : streams)
    stream.second->finalize();
```

**18.** يجب ألا تكون هناك مسافات في نهايات الأسطر.

**19.** ملفات المصدر مُرمَّزة بـ UTF-8.

**20.** يمكن استخدام الأحرف غير ASCII في القيم الحرفية للسلاسل النصية.

```cpp theme={null}
<< ", " << (timer.elapsed() / chunks_stats.hits) << " μsec/hit.";
```

**21.** لا تكتب عدة تعبيرات في سطر واحد.

**22.** اجمع أجزاء الشيفرة داخل الدوال، وافصل بينها بسطر فارغ واحد كحد أقصى.

**23.** افصل بين الدوال والأصناف وما شابهها بسطر فارغ واحد أو سطرين فارغين.

**24.** يجب أن يُكتب `A const` (المرتبط بقيمة) قبل اسم النوع.

```cpp theme={null}
//correct
const char * pos
const std::string & s
//incorrect
char const * pos
```

**25.** عند التصريح بمؤشر أو مرجع، يجب وضع مسافات على جانبي الرمزين `*` و `&`.

```cpp theme={null}
//correct
const char * pos
//incorrect
const char* pos
const char *pos
```

**26.** عند استخدام أنواع القوالب، استخدم الكلمة المفتاحية `using` لتعريف أسماء مستعارة لها (باستثناء أبسط الحالات).

بعبارة أخرى، لا تُحدَّد معلمات القالب إلا في `using` ولا تُكرَّر في الشيفرة.

يمكن التصريح عن `using` محليًا، مثلًا داخل دالة.

```cpp theme={null}
//correct
using FileStreams = std::map<std::string, std::shared_ptr<Stream>>;
FileStreams streams;
//incorrect
std::map<std::string, std::shared_ptr<Stream>> streams;
```

**27.** لا تُعلن عدة متغيرات من أنواع مختلفة في تعليمة واحدة.

```cpp theme={null}
//incorrect
int x, *y;
```

**28.** لا تستخدم تحويلات الأنواع بصيغة C.

```cpp theme={null}
//incorrect
std::cerr << (int)c <<; std::endl;
//correct
std::cerr << static_cast<int>(c) << std::endl;
```

**29.** في `صنف` و`بنية`، اجمع الأعضاء والدوال كلًّا على حدة داخل كل مستوى من مستويات الإتاحة.

**30.** في `صنف` و`بنية` الصغيرة، ليس من الضروري فصل تعريف `method` عن تنفيذها.

وينطبق الأمر نفسه على `method` الصغيرة في أي `صنف` أو `بنية`.

في `Template صنف` و`بنية`، لا تفصل تعريفات `method` عن تنفيذها (لأنه بخلاف ذلك يجب تعريفها في وحدة الترجمة نفسها).

**31.** يمكنك لفّ الأسطر عند 140 حرفًا بدلًا من 80.

**32.** استخدم دائمًا معاملي الزيادة/الإنقاص السابقين إذا لم تكن الصيغة اللاحقة مطلوبة.

```cpp theme={null}
for (Names::const_iterator it = column_names.begin(); it != column_names.end(); ++it)
```

<div id="comments">
  ## التعليقات
</div>

**1.** احرص على إضافة تعليقات إلى كل أجزاء الشيفرة غير الواضحة.

هذا مهم جدًا. فقد تساعدك كتابة تعليق على إدراك أن الشيفرة غير ضرورية، أو أن تصميمها خاطئ.

```cpp theme={null}
/** Part of piece of memory, that can be used.
  * For example, if internal_buffer is 1MB, and there was only 10 bytes loaded to buffer from file for reading,
  * then working_buffer will have size of only 10 bytes
  * (working_buffer.end() will point to position right after those 10 bytes available for read).
  */
```

**2.** يمكن أن تكون التعليقات مفصّلة بالقدر المطلوب.

**3.** ضع التعليقات قبل الشيفرة التي تشرحها. وفي حالات نادرة، يمكن أن تأتي التعليقات بعد الشيفرة، في السطر نفسه.

```cpp theme={null}
/** Parses and executes the query.
*/
void executeQuery(
    ReadBuffer & istr, /// Where to read the query from (and data for INSERT, if applicable)
    WriteBuffer & ostr, /// Where to write the result
    Context & context, /// DB, tables, data types, engines, functions, aggregate functions...
    BlockInputStreamPtr & query_plan, /// Here could be written the description on how query was executed
    QueryProcessingStage::Enum stage = QueryProcessingStage::Complete /// Up to which stage process the SELECT query
    )
```

**4.** يجب أن تكون التعليقات مكتوبة باللغة الإنجليزية فقط.

**5.** إذا كنت تكتب مكتبة، فأدرج في ملف الترويسة الرئيسي تعليقات تفصيلية تشرحها.

**6.** لا تُضِف تعليقات لا توفّر معلومات إضافية. وعلى وجه الخصوص، لا تترك تعليقات فارغة مثل هذه:

```cpp theme={null}
/*
* Procedure Name:
* Original procedure name:
* Author:
* Date of creation:
* Dates of modification:
* Modification authors:
* Original file name:
* Purpose:
* Intent:
* Designation:
* Classes used:
* Constants:
* Local variables:
* Parameters:
* Date of creation:
* Purpose:
*/
```

المثال مقتبس من المصدر [http://home.tamk.fi/\~jaalto/course/coding-style/doc/unmaintainable-code/](http://home.tamk.fi/~jaalto/course/coding-style/doc/unmaintainable-code/).

**7.** لا تكتب تعليقات عديمة الفائدة (اسم المؤلف، تاريخ الإنشاء ..) في بداية كل ملف.

**8.** تبدأ التعليقات أحادية السطر بثلاث شرطات مائلة: `///`، وتبدأ التعليقات متعددة الأسطر بـ `/**`. وتُعد هذه التعليقات "توثيقًا".

ملاحظة: يمكنك استخدام Doxygen لإنشاء التوثيق من هذه التعليقات. لكن Doxygen لا يُستخدم عمومًا لأن التنقل في الشيفرة داخل بيئة التطوير المتكاملة (IDE) أكثر ملاءمة.

**9.** يجب ألا تحتوي التعليقات متعددة الأسطر على أسطر فارغة في البداية أو النهاية (باستثناء السطر الذي يُغلق التعليق متعدد الأسطر).

**10.** عند التعليق على الشيفرة لإلغائها مؤقتًا، استخدم التعليقات العادية، وليس تعليقات "التوثيق".

**11.** احذف الأجزاء المُعلَّق عليها من الشيفرة قبل إجراء commit.

**12.** لا تستخدم ألفاظًا نابية في التعليقات أو الشيفرة.

**13.** لا تستخدم الأحرف الكبيرة. ولا تُكثر من علامات الترقيم.

```cpp theme={null}
/// WHAT THE FAIL???
```

**14.** لا تستخدم التعليقات كفواصل.

```cpp theme={null}
///******************************************************
```

**15.** لا تفتح نقاشات في التعليقات.

```cpp theme={null}
/// Why did you do this stuff?
```

**16.** لا حاجة لكتابة تعليق في نهاية كتلة يوضح ما الذي كانت تتناوله.

```cpp theme={null}
/// for
```

<div id="names">
  ## الأسماء
</div>

**1.** استخدم الأحرف الصغيرة والشرطات السفلية في أسماء المتغيرات وأعضاء الأصناف.

```cpp theme={null}
size_t max_block_size;
```

**2.** بالنسبة إلى أسماء الدوال (الطرائق)، استخدم `camelCase` بحيث يبدأ بحرف صغير.

```cpp theme={null}
std::string getName() const override { return "Memory"; }
```

**3.** بالنسبة إلى أسماء الأصناف (البُنى)، استخدم أسلوب CamelCase على أن يبدأ بحرف كبير. ولا تُستخدم للواجهات أي بادئات باستثناء I.

```cpp theme={null}
class StorageMemory : public IStorage
```

**4.** تُسمّى عبارات `using` بالطريقة نفسها التي تُسمّى بها الأصناف.

**5.** أسماء وسيطات أنواع القوالب: في الحالات البسيطة، استخدم `T`؛ `T`، `U`؛ `T1`، `T2`.

في الحالات الأكثر تعقيدًا، اتبع قواعد تسمية الأصناف، أو أضف البادئة `T`.

```cpp theme={null}
template <typename TKey, typename TValue>
struct AggregatedStatElement
```

**6.** أسماء وسيطات الثوابت في القوالب: إمّا أن تتبع قواعد تسمية المتغيّرات، أو استخدام `N` في الحالات البسيطة.

```cpp theme={null}
template <bool without_www>
struct ExtractDomain
```

**7.** بالنسبة إلى الأصناف المجرّدة (الواجهات)، يمكنك إضافة البادئة `I`.

```cpp theme={null}
class IProcessor
```

**8.** إذا كنت تستخدم متغيّرًا محليًا، فيمكنك استخدام الاسم المختصر.

في جميع الحالات الأخرى، استخدم اسمًا يعبّر عن المعنى.

```cpp theme={null}
bool info_successfully_loaded = false;
```

**9.** تُكتب أسماء `define` والثوابت العامة بصيغة ALL\_CAPS مع استخدام الشرطة السفلية.

```cpp theme={null}
#define MAX_SRC_TABLE_NAMES_TO_STORE 1000
```

**10.** يجب أن تتبع أسماء الملفات النمط نفسه المستخدم في محتواها.

إذا كان الملف يحتوي على صنف واحدة، فسمِّ الملف بالطريقة نفسها التي تُسمّى بها الصنف (CamelCase).

إذا كان الملف يحتوي على دالة واحدة، فسمِّ الملف بالطريقة نفسها التي تُسمّى بها الدالة (camelCase).

**11.** إذا كان الاسم يحتوي على اختصار، فحينها:

* بالنسبة إلى أسماء المتغيرات، يجب أن يُكتب الاختصار بأحرف صغيرة `mysql_connection` (وليس `mySQL_connection`).
* بالنسبة إلى أسماء الأصناف والدوال، احتفظ بالأحرف الكبيرة في الاختصار `MySQLConnection` (وليس `MySqlConnection`).

**12.** يجب أن تُسمّى وسائط المُنشئ التي تُستخدم فقط لتهيئة أعضاء الصنف بالطريقة نفسها التي تُسمّى بها أعضاء الصنف، ولكن مع إضافة شرطة سفلية في النهاية.

```cpp theme={null}
FileQueueProcessor(
    const std::string & path_,
    const std::string & prefix_,
    std::shared_ptr<FileHandler> handler_)
    : path(path_),
    prefix(prefix_),
    handler(handler_),
    log(&Logger::get("FileQueueProcessor"))
{
}
```

يمكن حذف اللاحقة \_ إذا لم تُستخدم الوسيطة داخل جسم المُنشئ.

**13.** لا يوجد فرق بين أسماء المتغيرات المحلية وأعضاء الصنف (ولا حاجة إلى أي بادئات).

```cpp theme={null}
timer (not m_timer)
```

**14.** بالنسبة إلى الثوابت في `enum`، استخدم CamelCase بحيث يبدأ بحرف كبير. كما يُقبل أيضًا استخدام ALL\_CAPS. إذا كان `enum` غير محلي، فاستخدم `enum class`.

```cpp theme={null}
enum class CompressionMethod
{
    QuickLZ = 0,
    LZ4     = 1,
};
```

**15.** يجب أن تكون جميع الأسماء باللغة الإنجليزية. لا يُسمح بالنقل الصوتي للكلمات العبرية.

ليس T\_PAAMAYIM\_NEKUDOTAYIM

**16.** الاختصارات مقبولة إذا كانت معروفة على نطاق واسع (عندما يمكنك العثور بسهولة على معنى الاختصار في Wikipedia أو عبر محرك بحث).

`AST`، `SQL`.

ليس `NVDH` (مجرد حروف عشوائية)

الكلمات غير المكتملة مقبولة إذا كانت الصيغة المختصرة شائعة الاستخدام.

يمكنك أيضًا استخدام اختصار إذا كان الاسم الكامل مذكورًا بجواره في التعليقات.

**17.** يجب أن تحمل أسماء الملفات التي تحتوي على شيفرة مصدر C++ الامتداد `.cpp`. ويجب أن تحمل ملفات الترويسة الامتداد `.h`.

<div id="how-to-write-code">
  ## كيفية كتابة الشيفرة
</div>

**1.** إدارة الذاكرة.

لا يجوز استخدام التحرير اليدوي للذاكرة (`delete`) إلا في شيفرة المكتبة.

في شيفرة المكتبة، لا يجوز استخدام العامل `delete` إلا داخل الدوال الهدّامة.

في شيفرة التطبيق، يجب أن يحرّر الذاكرةَ الكائنُ الذي يملكها.

أمثلة:

* أسهل طريقة هي وضع كائن على المكدس، أو جعله عضوًا في `صنف` أخرى.
* عند وجود عدد كبير من الكائنات الصغيرة، استخدم الحاويات.
* للتحرير التلقائي لعدد قليل من الكائنات الموجودة على الكومة، استخدم `shared_ptr/unique_ptr`.

**2.** إدارة الموارد.

استخدم `RAII` وراجع ما سبق.

**3.** معالجة الأخطاء.

استخدم الاستثناءات. في معظم الحالات، كل ما تحتاج إليه هو رمي استثناء، ولا تحتاج إلى التقاطه (بفضل `RAII`).

في تطبيقات معالجة البيانات غير المتصلة، يكون من المقبول غالبًا عدم التقاط الاستثناءات.

في الخوادم التي تتعامل مع طلبات المستخدمين، يكفي عادةً التقاط الاستثناءات عند المستوى الأعلى من معالج الاتصال.

في دوال الخيوط، ينبغي التقاط جميع الاستثناءات والاحتفاظ بها لإعادة رميها في الخيط الرئيسي بعد `join`.

```cpp theme={null}
/// If there weren't any calculations yet, calculate the first block synchronously
if (!started)
{
    calculate();
    started = true;
}
else /// If calculations are already in progress, wait for the result
    pool.wait();

if (exception)
    exception->rethrow();
```

لا تُخفِ الاستثناءات أبدًا من دون معالجتها. ولا تكتفِ بمجرد تسجيل جميع الاستثناءات عشوائيًا.

```cpp theme={null}
//Not correct
catch (...) {}
```

إذا كنت بحاجة إلى تجاهل بعض الاستثناءات، فافعل ذلك فقط مع استثناءات محددة، وأعِد رمي البقية.

```cpp theme={null}
catch (const DB::Exception & e)
{
    if (e.code() == ErrorCodes::UNKNOWN_AGGREGATE_FUNCTION)
        return nullptr;
    else
        throw;
}
```

عند استخدام الدوال مع رموز الاستجابة أو `errno`، تحقّق دائمًا من النتيجة وألقِ استثناءً في حال حدوث خطأ.

```cpp theme={null}
if (0 != close(fd))
    throw ErrnoException(ErrorCodes::CANNOT_CLOSE_FILE, "Cannot close file {}", file_name);
```

يمكنك استخدام assert للتحقق من الشروط الثابتة في الشيفرة.

**4.** أنواع الاستثناءات.

لا حاجة إلى استخدام تسلسل هرمي معقد للاستثناءات في شيفرة التطبيق. يجب أن يكون نص الاستثناء مفهومًا لمسؤول النظام.

**5.** إطلاق الاستثناءات من الـ destructor.

لا يُنصح بهذا، لكنه مسموح.

استخدم الخيارات التالية:

* أنشئ دالة (`done()` أو `finalize()`) تنفّذ مسبقًا كل العمل الذي قد يؤدي إلى استثناء. إذا جرى استدعاء تلك الدالة، فلا ينبغي أن تحدث أي استثناءات لاحقًا في الـ destructor.
* يمكن وضع المهام شديدة التعقيد (مثل إرسال الرسائل عبر الشبكة) في دالة منفصلة يجب على مستخدم الـ صنف استدعاؤها قبل التدمير.
* إذا حدث استثناء في الـ destructor، فمن الأفضل تسجيله بدلًا من إخفائه (إذا كان logger متاحًا).
* في التطبيقات البسيطة، من المقبول الاعتماد على `std::terminate` (في حالات `noexcept` الافتراضية في C++11) للتعامل مع الاستثناءات.

**6.** كتل الشيفرة المجهولة.

يمكنك إنشاء block شيفرة منفصلة داخل دالة واحدة لجعل بعض المتغيرات محلية النطاق، بحيث تُستدعى الـ destructors عند الخروج من الـ block.

```cpp theme={null}
Block block = data.in->read();

{
    std::lock_guard<std::mutex> lock(mutex);
    data.ready = true;
    data.block = block;
}

ready_any.set();
```

**7.** تعدد الخيوط.

في برامج معالجة البيانات دون اتصال:

* حاول الحصول على أفضل أداء ممكن على نواة CPU واحدة. بعد ذلك يمكنك جعل الشيفرة متوازية إذا لزم الأمر.

في تطبيقات الخادم:

* استخدم thread pool لمعالجة الطلبات. حتى الآن، لم تكن لدينا أي مهام تتطلب تبديل السياق في فضاء المستخدم.

لا تُستخدم عملية fork للتوازي.

**8.** مزامنة الخيوط.

غالبًا ما يكون من الممكن جعل الخيوط المختلفة تستخدم خلايا ذاكرة مختلفة (والأفضل من ذلك: خطوط cache مختلفة)، وألا تستخدم أي مزامنة بين الخيوط (باستثناء `joinAll`).

إذا كانت المزامنة مطلوبة، ففي معظم الحالات يكفي استخدام mutex ضمن `lock_guard`.

في الحالات الأخرى، استخدم بدائيات المزامنة الخاصة بالنظام. لا تستخدم الانتظار النشط.

يجب استخدام العمليات الذرية فقط في أبسط الحالات.

لا تحاول تنفيذ هياكل بيانات خالية من الأقفال إلا إذا كان ذلك مجال خبرتك الأساسي.

**9.** المؤشرات مقابل المراجع.

في معظم الحالات، فضّل المراجع.

**10.** `const`.

استخدم المراجع الثابتة، والمؤشرات إلى الثوابت، و`const_iterator`، ودوال `const`.

اعتبر `const` هو الخيار الافتراضي، ولا تستخدم غير `const` إلا عند الضرورة.

عند تمرير المتغيرات بالقيمة، لا يكون استخدام `const` منطقيًا عادةً.

**11.** unsigned.

استخدم `unsigned` عند الحاجة.

**12.** الأنواع العددية.

استخدم الأنواع `UInt8` و`UInt16` و`UInt32` و`UInt64` و`Int8` و`Int16` و`Int32` و`Int64`، بالإضافة إلى `size_t` و`ssize_t` و`ptrdiff_t`.

لا تستخدم هذه الأنواع للأعداد: `signed/unsigned long` و`long long` و`short` و`signed/unsigned char` و`char`.

**13.** تمرير الوسائط.

مرّر القيم المعقّدة بالقيمة إذا كان سيتم نقلها، واستخدم `std::move`؛ ومرّرها بالمرجع إذا كنت تريد تحديث القيمة داخل حلقة.

إذا كانت الدالة تنقل ملكية كائن أُنشئ على `heap`، فاجعل نوع الوسيط `shared_ptr` أو `unique_ptr`.

**14.** قيم الإرجاع.

في معظم الحالات، استخدم فقط `return`. لا تكتب `return std::move(res)`.

إذا كانت الدالة تخصّص كائنًا على `heap` ثم تعيده، فاستخدم `shared_ptr` أو `unique_ptr`.

في حالات نادرة (مثل تحديث قيمة داخل حلقة)، قد تحتاج إلى إرجاع القيمة عبر وسيط. في هذه الحالة، يجب أن يكون الوسيط مرجعًا.

```cpp theme={null}
using AggregateFunctionPtr = std::shared_ptr<IAggregateFunction>;

/** Allows creating an aggregate function by its name.
  */
class AggregateFunctionFactory
{
public:
    AggregateFunctionFactory();
    AggregateFunctionPtr get(const String & name, const DataTypes & argument_types) const;
```

**15.** `namespace`.

لا حاجة إلى استخدام `namespace` منفصل لشيفرة التطبيق.

ولا تحتاج المكتبات الصغيرة إلى ذلك أيضًا.

أما في المكتبات المتوسطة والكبيرة، فضع كل شيء داخل `namespace`.

في ملف `.h` الخاص بالمكتبة، يمكنك استخدام `namespace detail` لإخفاء تفاصيل التنفيذ غير اللازمة لشيفرة التطبيق.

وفي ملف `.cpp`، يمكنك استخدام `static` أو `namespace` مجهول لإخفاء الرموز.

كذلك، يمكن استخدام `namespace` مع `enum` لمنع تسرّب الأسماء المقابلة إلى `namespace` خارجي (لكن من الأفضل استخدام `enum class`).

**16.** التهيئة المؤجلة.

إذا كانت التهيئة تتطلب معاملات، فعادةً لا ينبغي لك كتابة مُنشئ افتراضي.

وإذا احتجت لاحقًا إلى تأجيل التهيئة، فيمكنك إضافة مُنشئ افتراضي ينشئ كائنًا غير صالح. أو، إذا كان عدد الكائنات قليلًا، يمكنك استخدام `shared_ptr/unique_ptr`.

```cpp theme={null}
Loader(DB::Connection * connection_, const std::string & query, size_t max_block_size_);

/// For deferred initialization
Loader() {}
```

**17.** الدوال الافتراضية.

إذا لم يكن الصنف مخصّصًا للاستخدام متعدد الأشكال، فلا حاجة إلى جعل الدوال افتراضية. وينطبق ذلك أيضًا على الدالة الهادمة.

**18.** الترميزات.

استخدم UTF-8 في كل مكان. استخدم `std::string` و `char *`. لا تستخدم `std::wstring` و `wchar_t`.

**19.** التسجيل.

راجع الأمثلة في مختلف أنحاء الشيفرة.

قبل إجراء `commit`، احذف كل رسائل السجل غير المفيدة ورسائل Debug، وأي أنواع أخرى من مخرجات Debug.

يجب تجنّب التسجيل داخل الحلقات، حتى على مستوى Trace.

يجب أن تكون السجلات قابلة للقراءة عند أي مستوى من مستويات التسجيل.

ينبغي استخدام التسجيل في شيفرة التطبيق فقط، في الغالب.

يجب أن تُكتب رسائل السجل باللغة الإنجليزية.

ويُفضَّل أن يكون السجل مفهومًا لمسؤول النظام.

لا تستخدم الألفاظ النابية في السجل.

استخدم ترميز UTF-8 في السجل. وفي حالات نادرة، يمكنك استخدام محارف غير ASCII في السجل.

**20.** الإدخال والإخراج.

لا تستخدم `iostreams` في الحلقات الداخلية الحسّاسة لأداء التطبيق (ولا تستخدم `stringstream` أبدًا).

استخدم مكتبة `DB/IO` بدلًا من ذلك.

**21.** التاريخ والوقت.

راجع مكتبة `DateLUT`.

**22.** include.

استخدم دائمًا `#pragma once` بدلًا من حواجز التضمين.

**23.** using.

لا تستخدم `using namespace`. يمكنك استخدام `using` لشيء محدد، لكن اجعله محليًا داخل صنف أو دالة.

**24.** لا تستخدم `trailing return type` للدوال إلا عند الضرورة.

```cpp theme={null}
auto f() -> void
```

**25.** التصريح عن المتغيرات وتهيئتها.

```cpp theme={null}
//right way
std::string s = "Hello";
std::string s{"Hello"};

//wrong way
auto s = std::string{"Hello"};
```

**26.** بالنسبة إلى الدوال الافتراضية، اكتب `virtual` في الصنف الأساسي، لكن اكتب `override` بدلًا من `virtual` في الأصناف الفرعية.

<div id="unused-features-of-c">
  ## ميزات C++ غير المستخدمة
</div>

**1.** لا يُستخدم التوريث الافتراضي.

**2.** التراكيب التي تتوفر لها اختصارات نحوية مريحة في C++ الحديثة، مثلًا.

```cpp theme={null}
// Traditional way without syntactic sugar
template <typename G, typename = std::enable_if_t<std::is_same<G, F>::value, void>> // SFINAE via std::enable_if, usage of ::value
std::pair<int, int> func(const E<G> & e) // explicitly specified return type
{
    if (elements.count(e)) // .count() membership test
    {
        // ...
    }

    elements.erase(
        std::remove_if(
            elements.begin(), elements.end(),
            [&](const auto x){
                return x == 1;
            }),
        elements.end()); // remove-erase idiom

    return std::make_pair(1, 2); // create pair via make_pair()
}

// With syntactic sugar (C++14/17/20)
template <typename G>
requires std::same_v<G, F> // SFINAE via C++20 concept, usage of C++14 template alias
auto func(const E<G> & e) // auto return type (C++14)
{
    if (elements.contains(e)) // C++20 .contains membership test
    {
        // ...
    }

    elements.erase_if(
        elements,
        [&](const auto x){
            return x == 1;
        }); // C++20 std::erase_if

    return {1, 2}; // or: return std::pair(1, 2); // create pair via initialization list or value initialization (C++17)
}
```

<div id="platform">
  ## المنصة
</div>

**1.** نكتب الشفرة البرمجية لمنصة محددة.

لكن عند تساوي العوامل الأخرى، تُفضَّل الشفرة متعددة المنصات أو القابلة للنقل.

**2.** اللغة: C++20 (راجع قائمة [ميزات C++20](https://en.cppreference.com/w/cpp/compiler_support#C.2B.2B20_features) المتاحة).

**3.** المترجم: `clang`. وقت كتابة هذا النص (مارس 2025)، تُترجم الشفرة البرمجية باستخدام clang بالإصدار >= 19.

تُستخدم المكتبة القياسية (`libc++`).

**4.** نظام التشغيل: Ubuntu Linux، على ألا يكون أقدم من Precise.

**5.** تُكتب الشفرة البرمجية لمعمارية CPU من نوع x86\_64.

مجموعة تعليمات CPU هي الحد الأدنى المدعوم بين خوادمنا. وهي حاليًا SSE 4.2.

**6.** استخدم أعلام الترجمة `-Wall -Wextra -Werror -Weverything` مع بعض الاستثناءات.

**7.** استخدم الربط الثابت مع جميع المكتبات باستثناء تلك التي يصعب ربطها ربطًا ثابتًا (راجع خرج الأمر `ldd`).

**8.** تُطوَّر الشفرة البرمجية ويُجرى Debug لها باستخدام إعدادات الإصدار.

<div id="tools">
  ## الأدوات
</div>

**1.** يُعد KDevelop بيئة تطوير متكاملة جيدة.

**2.** لتصحيح الأخطاء، استخدم `gdb` و`valgrind` (`memcheck`) و`strace` و`-fsanitize=...` أو `tcmalloc_minimal_debug`.

**3.** لتحليل الأداء، استخدم `Linux Perf` أو `valgrind` (`callgrind`) أو `strace -cf`.

**4.** الشيفرة المصدرية موجودة في Git.

**5.** يُستخدم `CMake` للبناء.

**6.** تُصدر البرامج باستخدام حزم `deb`.

**7.** يجب ألا تؤدي عمليات commit على `master` إلى كسر عملية البناء.

مع أن مراجعات محددة فقط تُعد صالحة للعمل.

**8.** نفّذ عمليات commit بأكبر قدر ممكن من التكرار، حتى لو كانت الشيفرة جاهزة جزئيًا فقط.

استخدم الفروع لهذا الغرض.

إذا كانت الشيفرة الخاصة بك في فرع `master` غير قابلة للبناء بعد، فاستبعدها من عملية البناء قبل `push`. ستحتاج إلى إكمالها أو إزالتها خلال بضعة أيام.

**9.** بالنسبة إلى التغييرات غير البسيطة، استخدم الفروع وانشرها على الخادم.

**10.** تُزال الشيفرة غير المستخدمة من المستودع.

<div id="libraries">
  ## المكتبات
</div>

**1.** تُستخدم مكتبة C++20 القياسية (ويُسمح بالامتدادات التجريبية)، وكذلك إطارا العمل `boost` و`Poco`.

**2.** لا يُسمح باستخدام مكتبات من حزم نظام التشغيل، كما لا يُسمح باستخدام المكتبات المثبّتة مسبقًا. يجب وضع جميع المكتبات على هيئة شيفرة مصدرية في دليل `contrib` وبنائها مع ClickHouse. راجع [إرشادات إضافة مكتبات خارجية جديدة وصيانتها](/ar/resources/develop-contribute/contribute/contrib#adding-and-maintaining-third-party-libraries) لمزيد من التفاصيل.

**3.** تُمنح الأفضلية دائمًا للمكتبات المستخدمة مسبقًا.

<div id="general-recommendations">
  ## توصيات عامة
</div>

**1.** اكتب أقل قدر ممكن من الشيفرة.

**2.** جرّب أبسط حل ممكن.

**3.** لا تكتب شيفرة قبل أن تعرف كيف ستعمل، وكيف ستعمل الحلقة الداخلية.

**4.** في أبسط الحالات، استخدم `using` بدلًا من الأصناف أو البُنى.

**5.** إذا أمكن، فلا تكتب copy constructors أو assignment operators أو destructors (باستثناء destructor افتراضي، إذا كان الصنف يحتوي على دالة افتراضية واحدة على الأقل) أو move constructors أو move assignment operators. وبعبارة أخرى، يجب أن تعمل الدوال التي يُنشئها المصرّف تلقائيًا بشكل صحيح. يمكنك استخدام `default`.

**6.** يُشجَّع تبسيط الشيفرة. قلّل حجم الشيفرة قدر الإمكان.

<div id="additional-recommendations">
  ## توصيات إضافية
</div>

**1.** عدم التصريح بـ `std::` صراحةً للأنواع القادمة من `stddef.h`

غير مستحسن. بمعنى آخر، نوصي بكتابة `size_t` بدلًا من `std::size_t` لأنه أقصر.

ومع ذلك، لا بأس بإضافة `std::`.

**2.** عدم التصريح بـ `std::` صراحةً للدوال القادمة من مكتبة C القياسية

غير مستحسن. بمعنى آخر، اكتب `memcpy` بدلًا من `std::memcpy`.

والسبب هو وجود دوال مشابهة غير قياسية، مثل `memmem`. ونحن نستخدم هذه الدوال أحيانًا. وهذه الدوال غير موجودة في `namespace std`.

إذا كتبت `std::memcpy` بدلًا من `memcpy` في كل مكان، فسيبدو `memmem` من دون `std::` غريبًا.

ومع ذلك، يمكنك استخدام `std::` إذا كنت تفضل ذلك.

**3.** استخدام دوال من C عندما تكون الدوال نفسها متاحة في مكتبة C++ القياسية.

هذا مقبول إذا كان أكثر كفاءة.

على سبيل المثال، استخدم `memcpy` بدلًا من `std::copy` لنسخ كتل كبيرة من الذاكرة.

**4.** وسيطات الدالة متعددة الأسطر.

أيٌّ من أنماط التفاف الأسطر التالية مسموح به:

```cpp theme={null}
function(
  T1 x1,
  T2 x2)
```

```cpp theme={null}
function(
  size_t left, size_t right,
  const & RangesInDataParts ranges,
  size_t limit)
```

```cpp theme={null}
function(size_t left, size_t right,
  const & RangesInDataParts ranges,
  size_t limit)
```

```cpp theme={null}
function(size_t left, size_t right,
      const & RangesInDataParts ranges,
      size_t limit)
```

```cpp theme={null}
function(
      size_t left,
      size_t right,
      const & RangesInDataParts ranges,
      size_t limit)
```
