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 ;
}
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.
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;
}
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");
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
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!
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.
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);
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);
}
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;
}
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" )
{
// ..
}
// ....
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"
);
}