Menu
CoddyTech
flag Ar iconالعربيةdown icon

optional و variant و any

جزء من قسم البرمجة كائنية التوجه في رحلة C++ على Coddy. الدرس 89 من 104.

توفر لغة C++ الحديثة ثلاثة أنواع مفردات في الترويسات <optional> و<variant> و<any>، وتساعدك على التعامل مع القيم التي قد تكون غير موجودة، أو تحتوي على نوع واحد من عدة أنواع، أو تخزن أي نوع على الإطلاق.

std::optional<T> يمثّل قيمة قد تكون موجودة أو غير موجودة - وهو مثالي للدوال التي قد تفشل من دون طرح استثناء:

#include <iostream>
#include <optional>
std::optional<int> findIndex(const std::string& str, char c) {
    for (size_t i = 0; i < str.size(); ++i) {
        if (str[i] == c) return i;
    }
    return std::nullopt;  // لا قيمة
}
int main() {
    auto result = findIndex("hello", 'l');
    if (result.has_value()) {
        std::cout << "Found at: " << *result << "\n";  // 2
    }
}

std::variant<Types...> هو اتحاد آمن من حيث النوع يحتفظ بنوع واحد بالضبط من الأنواع المحددة في أي وقت:

#include <iostream>
#include <variant>
#include <string>
int main() {
    std::variant<int, double, std::string> data;
    data = 42;
    std::cout << std::get<int>(data) << "\n";
    data = "hello";
    if (std::holds_alternative<std::string>(data)) {
        std::cout << std::get<std::string>(data) << "\n";
    }
}

يمكن لـ std::any الاحتفاظ بقيمة من أي نوع، ويُحدَّد ذلك في وقت التشغيل. استخدم std::any_cast لاسترجاع القيمة:

#include <iostream>
#include <any>
int main() {
    std::any value = 10;
    std::cout << std::any_cast<int>(value) << "\n";
    value = std::string("text");
    std::cout << std::any_cast<std::string>(value) << "\n";
}

اختر std::optional للقيم القابلة لأن تكون فارغة، وstd::variant عندما تعرف الأنواع المحتملة أثناء الترجمة، وstd::any فقط عندما تحتاج حقًا إلى مرونة في الأنواع أثناء التشغيل.

challenge icon

التحدي

سهل

لنَبْنِ نظامًا للإعدادات يوضّح قوة أنواع المفردات الحديثة في C++. ستنشئ مديرًا مرنًا للإعدادات يتعامل مع قيم قد تكون مفقودة، أو قد تحتوي على أنواع مختلفة، أو قد تحتاج إلى مرونة في النوع وقت التشغيل.

ستنظّم شفرتك عبر ثلاثة ملفات:

  • ConfigTypes.h: عرّف أنواع قيم الإعدادات والدوال المساعدة.

    أنشئ دالة تُسمّى parseValue تأخذ سلسلة نصية وتحاول تحليلها كعدد صحيح. إذا كانت السلسلة تمثّل عددًا صحيحًا صالحًا، فأعِد العدد الصحيح مغلّفًا داخل std::optional. إذا فشل التحليل (أي إذا احتوت السلسلة على محارف غير رقمية)، فأعِد std::nullopt. للتبسيط، اعتبر السلسلة صالحة إذا كانت تحتوي فقط على أرقام (وعلامة ناقص في البداية اختياريًا).

    أنشئ أيضًا اسمًا مستعارًا للنوع يُسمّى Setting باستخدام std::variant، ويمكنه احتواء int أو double أو std::string.

    وأخيرًا، أنشئ دالة تُسمّى describeSetting تأخذ قيمة من النوع Setting وتُعيد سلسلة نصية تصف نوع القيمة التي تحتوي عليها وقيمتها بالتنسيق التالي:

    • للنوع int: Integer: [value]
    • للنوع double: Double: [value]
    • للسلسلة النصية: String: [value]
  • DynamicStore.h: أنشئ مخزنًا بسيطًا للمفاتيح والقيم باستخدام std::any.

    عرّف فئة DynamicStore يمكنها تخزين قيم من أي نوع. يجب أن تحتوي على:

    • دالة set تأخذ مفتاحًا نصيًا وقيمة من النوع std::any، وتخزّنهما داخليًا (استخدم std::map)
    • دالة get تأخذ مفتاحًا وتُعيد قيمة std::any (وتُعيد قيمة std::any فارغة إذا لم يكن المفتاح موجودًا)
    • دالة hasKey تُعيد القيمة true إذا كان المفتاح موجودًا
  • main.cpp: اقرأ ثلاثة مدخلات:
    1. سلسلة نصية قد تكون عددًا (لاختبار std::optional)
    2. مؤشر نوع: int أو double أو string
    3. قيمة تتوافق مع ذلك النوع

    وضّح استخدام أنواع المفردات الثلاثة:

    أولًا، استخدم دالة parseValue مع المدخل الأول. إذا كان يحتوي على قيمة، اطبع Parsed: [value]. وإذا كان فارغًا، اطبع Parse failed.

    ثانيًا، أنشئ متغيرًا من نوع Setting استنادًا إلى مؤشر النوع. إذا كان النوع هو int، فخزّن القيمة كعدد صحيح. وإذا كان double، فخزّنها كعدد عشري. وإذا كان string، فخزّنها كسلسلة نصية. ثم استدعِ describeSetting واطبع النتيجة.

    ثالثًا، أنشئ DynamicStore، وخزّن السلسلة "config_loaded" تحت المفتاح "status"، ثم استرجعها واطبع: Status: [value]. استخدم std::any_cast<std::string> لاستخراج القيمة.

على سبيل المثال، عند استخدام المدخلات 42 وint و100:

Parsed: 42
Integer: 100
Status: config_loaded

وعند استخدام المدخلات hello وdouble و3.14:

Parse failed
Double: 3.14
Status: config_loaded

وعند استخدام المدخلات -15 وstring وusername:

Parsed: -15
String: username
Status: config_loaded

تذكّر تضمين الترويسات المناسبة: <optional> و<variant> و<any> و<string> و<map> حيثما يلزم. استخدم std::holds_alternative وstd::get للعمل مع النوع المتغيّر، واستخدم has_value() أو التحويل المباشر إلى قيمة منطقية للتحقق من قيمة optional.

جرّب بنفسك

#include <iostream>
#include <string>
#include <any>
#include "ConfigTypes.h"
#include "DynamicStore.h"

int main() {
    // اقرأ ثلاثة مدخلات
    std::string input1;  // سلسلة نصية قد تكون رقماً (لاختبار std::optional)
    std::string typeIndicator;  // مؤشر النوع: int أو double أو string
    std::string value;  // القيمة المقابلة لذلك النوع
    
    std::cin >> input1;
    std::cin >> typeIndicator;
    std::cin >> value;
    
    // TODO: الجزء 1 - اختبر std::optional مع parseValue
    // استخدم الدالة parseValue مع input1
    // إذا كانت تحتوي على قيمة، اطبع "Parsed: [value]"
    // If it's empty, print "Parse failed"
    
    
    // TODO: الجزء 2 - اختبر std::variant مع Setting
    // أنشئ متغير Setting بناءً على typeIndicator
    // إذا كان النوع "int"، خزّن القيمة كعدد صحيح
    // إذا كان النوع "double"، خزّن القيمة كـ double
    // إذا كان النوع "string"، خزّن القيمة كسلسلة نصية
    // ثم استدعِ describeSetting واطبع النتيجة
    
    
    // TODO: الجزء 3 - اختبر std::any مع DynamicStore
    // أنشئ DynamicStore
    // خزّن السلسلة "config_loaded" تحت المفتاح "status"
    // استرجعه واطبع: "Status: [value]"
    // استخدم std::any_cast<std::string> لاستخراج القيمة
    
    
    return 0;
}
quiz iconاختبر نفسك

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

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم C++ عبر الإنترنت