[Studium] Rozeznanie w możliwościach refleksji Qt/C++ część 1 - Q_PROPERTY #RefleksjaWQt



Wstęp

Ten artykuł ma być początkiem serii pomniejszych na temat możliwości refleksji w Qt/C++. Rozpoczynam to studium ze względu na potrzebę zbudowania w miarę uniwersalnego narzędzie do zapisu danych z obiektów do XML w sposób automatyczny. Aktualnie posługuję się względnie ubogą implementacją opartą na szablonach, jednak wierzę, że zautomatyzowanie tego za pomocą refleksji stanowczo przyśpieszy prace w przyszłości.
Chciałbym zaznaczyć, że seria ta nie na darmo nazywa się „studium”! W trakcie realizacji tych artykułów uczę się sam i dzielę się wiedzą, która raczej nie ma wielkiego potwierdzenia w praktyce (chyba, że zaznaczę inaczej). Także proszę miej na uwadze, że rzeczy przedstawione w tej serii mogą mieć bliżej nieznane konsekwencje. Oczywiście nie planuję tutaj całkowicie wyłączyć swojego rozumu i ignorować już zdobytego doświadczenia – nie będę tutaj publikował kodu ewidentnie złego, chyba, że zaznaczę to wcześniej i zostanie zrobione to dla uproszczenia przykładu, co czasem jest konieczne.

Właściwości - przykład

Zacznę od najważniejszego, czyli podstawy. Aby móc w ogóle zrobić cokolwiek refleksyjnego w Qt musimy użyć ichnich właściwości – wszystkie szczegóły na ich temat znajdziemy w dokumentacji (https://doc.qt.io/qt-5/properties.html). W dalszych częściach studium mam nadzieję, że uda się stworzyć praktyczny przykład wykorzystania wszystkich elementów, które wchodzą w skład definicji właściwości.
Poniżej prezentuję najprostszy przykład.

class PropertyExample : QObject {
    Q_OBJECT
    Q_PROPERTY(bool isEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged)
private:
    bool _isEnabled;
public:
    bool isEnabled() { return _isEnabled; }
    void setIsEnabled(bool value)
    {
        if (_isEnabled == value) return;

        _isEnabled = value;
        emit isEnabledChanged();
    }
signals:
    void isEnabledChanged();
};
Zaczynając od początku o tym co się dzieje w kodzie:
  • Aby możliwe było użycie właściwości (a co za tym idzie – refleksji) niezbędne jest dziedziczenie po QObject (możliwe, że można to ominąć za pomocą Q_GADGET, ale to jest jeszcze do zbadania),
  • Obowiązkowe makro Q_OBJECT, które nie powinno Cię dziwić jeśli pracowałeś wcześniej z Qt,
  • Definicja właściwości – rozbijemy ją szczegółowo poniżej,
  • prywatny magazyn dla właściwości (bool _isEnabled),
  • funkcja READ dla właściwości (bool isEnabled()),
  • funkcja WRITE dla właściwości (void setIsEnabled(bool value)) - zaczynanie takich funkcji od słowa 'set' jest jedną z konwencji stosowanej w Qt,
  • sygnał, który informuje o tym, że wartość się zmieniła.
To jest minimalny działający przykład, który prezentuje jak stworzyć właściwość wewnątrz klasy Qt. Zwróć jeszcze tylko uwagę, na fakt, że funkcja WRITE sprawdza, czy wartość przechowywana w magazynie jest inna niż ta, którą ktoś próbuje ustawić – zaleca się stosowanie takiej konstrukcji praktycznie zawsze, gdyż chroni to przed zapętleniem, kiedy połączysz dwie właściwości a one będą się zmieniać na okrągło.

Definicja właściwości

Nie będę tutaj opisywał wszystkich możliwości, bo i po co, skoro można znaleźć je w przytoczonej wcześniej dokumentacji. Dla porządku opiszę tylko to co pojawiło się w przykładzie:
Q_PROPERTY(
bool isEnabled
READ isEnabled
WRITE setIsEnabled
NOTFIY isEnabledChanged)
Makro.
typ i nazwa właściwości dostępna dla systemu refleksji.
nazwa funkcji, która służy do odczytu wartości.
nazwa funkcji, która służy do zapisu nowej wartości.
Nazwa sygnału, który jest emitowany, gdy wartość właściwości zostanie zmieniona.

Podsumowanie

Oto krótka ściągawka na temat tego jako powinna wyglądać najprostsza właściwość w klasie Qt. Dalej mam zamiar w "podrozdziałach" pochylić się nad resztą elementów, które można dołożyć do definicji Q_PROPERTY, natomiast w następnej części będę próbował odczytać wartości za pomocą systemu refleksji. Także pewno będę musiał również dodać jakiś mały spis treści aby się nie pogubić.

Komentarze