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

Wstęp

Typy wyliczeniowe potrafią być bardzo przydatne w trakcie programowania – choć coraz częściej czuję, że są one często wykorzystywane aż nadto. Jednak nie dziś będę zajmował się dobrymi praktykami wykorzystania enum'ów, dziś będziemy mówić o dostępie do nich za pomocą refleksji Qt.

Przykład roboczy

class EnumExample : QObject {
    Q_OBJECT
public:
    enum EnumValues {
        Value_1,
        Value_2,
        Value_3
    };
    Q_ENUM(EnumValues)
};
Aby móc korzystać z mocy enumów potrzebujemy następujących rzeczy:
  • obiektu dziedziczącego po QObject (oraz standardowego makra Q_OBJECT), 
  • naszego wyliczenia (tutaj EnumValues),
  • oraz makra Q_OBJECT(«nazwa wyliczenia»), które daje nam dostęp do refleksji.
Można pominąć punkt pierwszy i ostatni (na temat QObject), stosując Q_GADGET – więcej o tym makrze innym razem.

Wyszukiwanie wartości wyliczenia za pomocą ciągu znakowego

Bardzo często przydaje mi się funkcja pozwalająca znaleźć wartość typu wyliczeniowego za pomocą string'a. Może nie jest to najpiękniejsze rozwiązanie, jednak wydaje mi się niemalże idealne przy konwersji obiektów z i na tekst (użyłbym słowa serializacja, ale pojęcia nie mam co do tego mają seriale – jeśli ktoś ma jakiś sensowny polski odpowiednik to poproszę). Także kod funkcji wygląda tak (nie jest mój – znalazłem go gdzieś na StackOverflow):
template <class T>
T tryFindValueFromString(const QString nameboolok = nullptr)
{
    static_assert(std::is_enum<T>::value"tryFindValueFromString działa tylko dla enumów!");
    QMetaEnum metaEnum = QMetaEnum::fromType<T>();
    for (int i = 0; i < metaEnum.keyCount(); i++)
    {
        if (name.compare(metaEnum.key(i), Qt::CaseInsensitive) == 0)
        {
            if (ok) *ok = true;
            return ((T)metaEnum.value(i));
        }
    }
    if (ok)*ok = false;
    T retData;
    return retData;
}
Jak widać, jest to klasa generyczna, która przyjmuje ciąg tekstowy, zwraca wartość znalezionego wyliczenia, oraz przez parametr 'ok' komunikuje, czy udało się go w ogóle znaleźć. Jak jej używać?

bool result = false;
auto code = tryFindValueFromString<EnumExample::EnumValues>("Value_1", &result);

Konwersja wartości enum'a na ciąg znaków

Inną funkcją, którą przywitałem z radością w kontekście testów jednostkowych jest tryFindStringFromValue. Pozwala ona na znalezienie surowej (takiej jak zapisana w kodzie źródłowym) nazwy enum'a na podstawie wartości liczbowej reprezentowanej przez typ wyliczeniowy. Poniższy kod został oparty, przez analogię, na powyższym:
template <typename T>
QString tryFindStringFromValue(int enumValueboolok = nullptr) 
{
    static_assert(std::is_enum<T>::value"tryFindStringFromValue działa tylko dla enumów!");
    
    QMetaEnum metaEnum = QMetaEnum::fromType<T>();
    QByteArray retData = metaEnum.valueToKey(enumValue);
    if (retData.isEmpty() || retData.isNull()) {
        if (ok != nullptr) *ok = false;
    }
    else if (ok != nullptr) *ok = true;
    return QString(retData);
}
Podobnie jak wyżej, użycie tego jest proste:
bool ok = false;
QString retVal = tryFindStringFromValue<EnumExample::EnumValues>((int)EnumExample::Value_1, &ok);

Komentarze

  1. Jak tak dobrze się znasz, to wrzuć jeszcze coś na temat zegarków sportowych z możliwością programowania

    OdpowiedzUsuń

Prześlij komentarz