switch/case statement in C++ with a QString type

后端 未结 14 1892
南笙
南笙 2020-12-16 10:19

I want to use switch-case in my program but the compiler gives me this error:

switch expression of type \'QString\' is illegal

How can I us

相关标签:
14条回答
  • 2020-12-16 10:24

    @DomTomCat's answer already touched on this, but since the question is specifically asking about Qt, there is a better way.

    Qt already has a hashing function for QStrings, but unfortunately Qt4's qHash is not qualified as a constexpr. Luckily Qt is open source, so we can copy the qHash functionality for QStrings into our own constexpr hashing function and use that!

    Qt4's qHash source

    I've modified it to only need one parameter (string literals are always null-terminated):

    uint constexpr qConstHash(const char *string)
    {
        uint h = 0;
    
        while (*string != 0)
        {
            h = (h << 4) + *string++;
            h ^= (h & 0xf0000000) >> 23;
            h &= 0x0fffffff;
        }
        return h;
    }
    

    Once you've defined this, you can use it in switch statements like so:

    QString string;
    // Populate the QString somehow.
    
    switch (qHash(string))
    {
        case qConstHash("a"):
            // Do something.
            break;
        case qConstHash("b"):
            // Do something else.
            break;
    }
    

    Since this method uses the same code Qt uses to calculate hashes, it will have the same hash collision resistance as QHash, which is generally very good. The downside is that this requires a fairly recent compiler--since it has non-return statements in the constexpr hashing function, it requires C++14.

    0 讨论(0)
  • 2020-12-16 10:25

    Check out this, it helps me

    int main(int, char **)
    {
        static const uint red_hash = 30900;
        static const uint green_hash = 7244734;
        static const uint blue_hash = 431029;
      else  
        static const uint red_hash = 112785;  
        static const uint green_hash = 98619139;  
        static const uint blue_hash = 3027034;
      endif
    
        QTextStream in(stdin), out(stdout);
        out << "Enter color: " << flush;
        const QString color = in.readLine();
        out << "Hash=" << qHash(color) << endl;
    
        QString answer;
        switch (qHash(color)) {
        case red_hash:
            answer="Chose red";
            break;
        case green_hash:
            answer="Chose green";
            break;
        case blue_hash:
            answer="Chose blue";
            break;
        default:
            answer="Chose something else";
            break;
        }
        out << answer << endl;
    }
    
    0 讨论(0)
  • 2020-12-16 10:30

    try this:

    // file qsswitch.h
    #ifndef QSSWITCH_H
    #define QSSWITCH_H
    
    #define QSSWITCH(__switch_value__, __switch_cases__) do{\
        const QString& ___switch_value___(__switch_value__);\
        {__switch_cases__}\
        }while(0);\
    
    #define QSCASE(__str__, __whattodo__)\
        if(___switch_value___ == __str__)\
        {\
        __whattodo__\
        break;\
        }\
    
    #define QSDEFAULT(__whattodo__)\
        {__whattodo__}\
    
    #endif // QSSWITCH_H
    

    how to use:

    #include "qsswitch.h"
    
    QString sW1 = "widget1";
    QString sW2 = "widget2";
    
    class WidgetDerived1 : public QWidget
    {...};
    
    class WidgetDerived2 : public QWidget
    {...};
    
    QWidget* defaultWidget(QWidget* parent)
    {
        return new QWidget(...);
    }
    
    QWidget* NewWidget(const QString &widgetName, QWidget *parent) const
    {
        QSSWITCH(widgetName,
                 QSCASE(sW1,
                 {
                     return new WidgetDerived1(parent);
                 })
                 QSCASE(sW2,
                 {
                     return new WidgetDerived2(parent);
                 })
                 QSDEFAULT(
                 {
                     return defaultWidget(parent);
                 })
                 )
    }
    

    there is some simple macro magic. after preprocessing this:

    QSSWITCH(widgetName,
             QSCASE(sW1,
             {
                 return new WidgetDerived1(parent);
             })
             QSCASE(sW2,
             {
                 return new WidgetDerived2(parent);
             })
             QSDEFAULT(
             {
                 return defaultWidget(parent);
             })
             )
    

    will work like this:

    // QSSWITCH
    do{
            const QString& ___switch_value___(widgetName);
            // QSCASE 1
            if(___switch_value___ == sW1)
            {
                return new WidgetDerived1(parent);
                break;
            }
    
            // QSCASE 2
            if(___switch_value___ == sW2)
            {
                return new WidgetDerived2(parent);
                break;
            }
    
            // QSDEFAULT
            return defaultWidget(parent);
    }while(0);
    
    0 讨论(0)
  • 2020-12-16 10:33

    It's not possible to switch directly on strings in C++. However it's possible in Qt using QMetaEnum as shown here: Q_ENUM and how to switch on a string. You don't even need C++14 like in Anthony Hilyard's answer, and the matching cases are not the hashes of the strings so there's zero chance of hash collision

    Basically QMetaEnum can convert from string to enum value and vice versa so we'll use that to jump to the correct branch. One small limitation is that strings are enum values so the string must be a valid C++ identifier. But that's easy to workaround, just replace the special characters with a specific rule if necessary

    To do that, first declare an enum with the strings to be used in switch cases as enumerator name in your class declaration. Then add the enum to the metadata with Q_ENUMS in order for the program to search later.

    #include <QMetaEnum>
    
    class TestCase : public QObject
    {
        Q_OBJECT
        Q_ENUMS(Cases)        // metadata declaration
    
        QMetaObject MetaObject;
        QMetaEnum MetaEnum;   // enum metadata
    
        TestCase() :
        // get information about the enum named "Cases"
        MetaObject(this->staticMetaObject),
        MetaEnum(MetaObject.enumerator(MetaObject.indexOfEnumerator("Cases"))
        {}
    
    public:
        explicit Test(QObject *parent = 0);
    
        enum Cases
        {
            THE, AT, IN, THIS // ... ==> strings to search, case sensitive
        };
    
    public slots:
        void SwitchString(const QString &word);
    };
    

    Then just implement the needed switch inside SwitchString after converting the string to the corresponding value with QMetaEnum::keyToValue.

    The comparison is case sensitive so if you want a case insensitive search, convert the input string to upper/lower case first. You can also do other transformations necessary to the string. For example in case you need to switch strings with blank spaces or forbidden characters in C++ identifiers, you may convert/remove/replace those characters to make the string a valid identifier.

    void TestCase::SwitchString(const QString &word)
    {
        switch (MetaEnum.keyToValue(word.toUpper().toLatin1()))
        // or simply switch (MetaEnum.keyToValue(word)) if no string modification is needed
        {
            case THE:  /* do something */ break;
            case AT:   /* do something */ break;
            case IN:   /* do something */ break;
            case THIS: /* do something */ break;
            default:   /* do something */ break;
        }
    }
    

    Then just use the class for switching the strings. For example:

    TestCase test;
    test.SwitchString("At");
    test.SwitchString("the");
    test.SwitchString("aBCdxx");
    
    0 讨论(0)
  • 2020-12-16 10:34

    As previously noted this is not a Qt problem, switch statements can only use constant expressions, look at the collection classes a QSet is a good solution

    void initStopQwords(QSet<QString>& stopSet)
    {
        // Ideally you want to read these from a file
        stopSet << "the";
        stopSet << "at";
        ...
    
    }
    
    bool isStopWord(const QSet<QString>& stopSet, const QString& word)
    {
        return stopSet.contains(word);
    }
    
    0 讨论(0)
  • 2020-12-16 10:34

    Late to the party, here's a solution I came up with some time ago, which completely abides to the requested syntax and works with c++11 onwards.

    #include <uberswitch/uberswitch.hpp>
    
    bool isStopWord( QString word )
    {
    bool flag = false ;
    
    uberswitch( word )
    {
    case ("the"):
        flag = true ;
        break ;
    case ("at") :
        flag = true ;
        break ;
    case ("in") :
        flag = true ;
        break ;
    case ("your"):
        flag = true ;
        break ;
    case ("near"):
        flag = true ;
        break ;
    case ("all"):
        flag = true ;
        break ;
    case ("this"):
        flag = true ;
        break ;
    }
    
    return flag ;
    }
    

    The only differences to be noticed are the usage of uberswitch in place of switch and the parenthesis around the case value (needed, baucause that's a macro).

    Here's the code: https://github.com/falemagn/uberswitch

    0 讨论(0)
提交回复
热议问题