web-dev-qa-db-fra.com

instruction switch / case en C ++ avec un type QString

Je veux utiliser switch-case dans mon programme mais le compilateur me donne cette erreur:

switch expression of type 'QString' is illegal

Comment puis-je utiliser l'instruction switch avec un QString?

Mon code est le suivant:

bool isStopWord( QString Word )
{
bool flag = false ;

switch( 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 ;
}
25
woody

Comment puis-je utiliser l'instruction switch avec une QString?

Tu ne peux pas. En langage C++, l'instruction switch ne peut être utilisée qu'avec des types intégraux ou enum. Vous pouvez formellement placer un objet de type classe dans une instruction switch, mais cela signifie simplement que le compilateur recherchera une conversion définie par l'utilisateur pour le convertir en type intégral ou enum.

29
AnT

Vous pouvez, en créant une QStringList avant l'itération, comme ceci:

QStringList myOptions;
myOptions << "goLogin" << "goAway" << "goRegister";

/*
goLogin = 0
goAway = 1
goRegister = 2
*/

Alors:

switch(myOptions.indexOf("goRegister")){
  case 0:
    // go to login...
    break;

  case 1:
    // go away...
    break;

  case 2:
    //Go to Register...
    break;

  default:
    ...
    break;
}
25
Lauro Oliveira

Il n'est pas possible de basculer directement sur les chaînes en C++. Cependant, il est possible dans Qt d'utiliser QMetaEnum comme indiqué ici: Q_ENUM et comment activer une chaîne

Pour ce faire, commencez par déclarer une énumération avec les chaînes à utiliser dans les cas de commutation comme nom d'énumérateur dans votre classe déclaration. Ajoutez ensuite l'énumération aux métadonnées avec Q_ENUMS pour que le programme recherche plus tard.

#include <QMetaEnum>

class TestCase : public QObject
{
    Q_OBJECT
    Q_ENUMS(Cases)        // metadata declaration

public:
    explicit Test(QObject *parent = 0);

    enum Cases
    {
        THE, AT, IN, THIS // ... ==> strings to search, case sensitive
    };

public slots:
    void SwitchString(QString Word);
};

Puis dans le .cpp fichier implémenter le commutateur nécessaire après la conversion de la chaîne à la valeur correspondante avec .

La comparaison est sensible à la casse, donc si vous voulez une recherche qui ne respecte pas la casse, convertissez d'abord la chaîne d'entrée en majuscules/minuscules. Vous pouvez également effectuer d'autres transformations nécessaires à la chaîne. Par exemple, si vous devez changer de chaîne avec des espaces vides ou des caractères non autorisés dans les identificateurs C/C++, vous pouvez convertir/supprimer/remplacer ces caractères pour faire de la chaîne un identificateur valide.

void TestCase::SwitchString(QString Word)
{
    // get information about the enum named "Cases"
    QMetaObject MetaObject = this->staticMetaObject;
    QMetaEnum MetaEnum = MetaObject.enumerator(MetaObject.indexOfEnumerator("Cases"));

    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;
    }
}

Ensuite, utilisez simplement la classe pour changer les chaînes. Par exemple:

TestCase test;
test.SwitchString("At");
test.SwitchString("the");
test.SwitchString("aBCdxx");
4
phuclv

Si vous pouvez utiliser un compilateur C++ moderne, vous pouvez calculer une valeur de hachage de compilation pour vos chaînes. Dans cette réponse il y a un exemple d'une fonction de hachage constexpr assez simple.

Une solution peut donc ressembler à ceci:

// function from https://stackoverflow.com/a/2112111/1150303
// (or use some other constexpr hash functions from this thread)
unsigned constexpr const_hash(char const *input) {
    return *input ?
    static_cast<unsigned int>(*input) + 33 * const_hash(input + 1) :
    5381;
}

QString switchStr = "...";
switch(const_hash(switchStr.toStdString().c_str()))
{
case const_hash("Test"):
    qDebug() << "Test triggered";
    break;
case const_hash("asdf"):
    qDebug() << "asdf triggered";
    break;
default:
    qDebug() << "nothing found";
    break;
}

Ce n'est toujours pas une solution parfaite. Il peut y avoir des collisions de hachage (testez donc votre programme chaque fois que vous ajoutez/modifiez case) et vous devez être prudent dans la conversion de QString en char* si vous souhaitez utiliser des caractères exotiques ou utf, par exemple.

Pour c ++ 11, ajoutez CONFIG += c++11 à votre projet, pour Qt5. Qt4: QMAKE_CXXFLAGS += -std=c++11

3
DomTomCat

La réponse de @ DomTomCat a déjà abordé ce sujet, mais comme la question concerne spécifiquement Qt, il existe une meilleure solution.

Qt a déjà une fonction de hachage pour QStrings, mais malheureusement qHash de Qt4 n'est pas qualifié de constexpr. Heureusement, Qt est open source, nous pouvons donc copier la fonctionnalité qHash pour QStrings dans notre propre fonction de hachage constexpr et l'utiliser!

source qHash de Qt4

Je l'ai modifié pour n'avoir besoin que d'un paramètre (les littéraux de chaîne sont toujours terminés par un caractère nul):

uint constexpr qConstHash(const char *string)
{
    uint h = 0;

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

Une fois que vous l'avez défini, vous pouvez l'utiliser dans des instructions switch comme ceci:

QString string;
// Populate the QString somehow.

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

Comme cette méthode utilise le même code que Qt utilise pour calculer les hachages, elle aura la même résistance aux collisions de hachage que QHash, ce qui est généralement très bon. L'inconvénient est que cela nécessite un compilateur assez récent - car il a des instructions de non-retour dans la fonction de hachage constexpr, il nécessite C++ 14.

3
Anthony Hilyard

essaye ça:

// 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

comment utiliser:

#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);
             })
             )
}

il y a une simple magie macro. après le prétraitement:

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

fonctionnera comme ceci:

// 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);
1
moskk

Comme indiqué précédemment, ce n'est pas un problème Qt, les instructions switch ne peuvent utiliser que des expressions constantes, regardez les classes de collection a QSet est une bonne 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);
}
1
Harald Scheirich

Je suggère d'utiliser if et break. Cela permettrait de changer de casse dans le calcul.

QString a="one"
if (a.contains("one"))
{

   break;
}
if (a.contains("two"))
{

   break;
}
0
Drl Sherif Omran
case "the":
    //^^^ case label must lead to a constant expression

Je ne suis pas au courant de qt, mais vous pouvez essayer ceci. Vous pouvez éviter switch et utiliser directement == Pour comparaison, si QString n'est pas différent d'un std::string Normal.

if( Word == "the" )
{
   // ..
}
else if( Word == "at" )
{
   // ..
}
// ....
0
Mahesh

Cela semble un peu plus sain à mon humble avis.

bool isStopWord( QString w ) {
    return (
        w == "the" ||
        w == "at" ||
        w == "in" ||
        w == "your" ||
        w == "near" ||
        w == "all" ||
        w == "this"
    );
}
0
weberc2