switch/case statement in C++ with a QString type
I want to use switch-case in my program but the compiler gives me this error:
switch expression of type '开发者_StackOverflow社区QString' is illegal
How can I use the switch
statement with a QString
?
My code is as follows:
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 ;
}
You can, creating an QStringList before iteration, like this:
QStringList myOptions;
myOptions << "goLogin" << "goAway" << "goRegister";
/*
goLogin = 0
goAway = 1
goRegister = 2
*/
Then:
switch(myOptions.indexOf("goRegister")){
case 0:
// go to login...
break;
case 1:
// go away...
break;
case 2:
//Go to Register...
break;
default:
...
break;
}
How can I use the switch statement with a QString?
You can't. In C++ language switch
statement can only be used with integral or enum types. You can formally put an object of class type into a switch
statement, but that simply means that the compiler will look for a user-defined conversion to convert it to integral or enum type.
@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.
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");
If you can use a modern C++ compiler then you could compute a compile time hash value for your strings. In this answer there's an example of a rather simple constexpr
hashing function.
So a solution can look like this:
// 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;
}
It is still not a perfect solution. There can be hash collisions (hence test your program whenever you add/change case
) and you have to be careful in the conversion from QString
to char*
if you want to use exotic or utf
characters, for instance.
For c++ 11 add CONFIG += c++11
to your project, for Qt5. Qt4: QMAKE_CXXFLAGS += -std=c++11
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);
}
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);
case "the":
//^^^ case label must lead to a constant expression
I am not aware of qt, but you can give this a try. You can avoid switch
and directly use ==
for comparison, if QString
is no different than a normal std::string
.
if( word == "the" )
{
// ..
}
else if( word == "at" )
{
// ..
}
// ....
This seems a little saner IMHO.
bool isStopWord( QString w ) {
return (
w == "the" ||
w == "at" ||
w == "in" ||
w == "your" ||
w == "near" ||
w == "all" ||
w == "this"
);
}
I would suggest to use if and break. This would make it near to switch case in the computation.
QString a="one"
if (a.contains("one"))
{
break;
}
if (a.contains("two"))
{
break;
}
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
This has nothing to do with Qt, just as it has nothing to do with the colour of your socks.
C++ switch syntax is as follows:
char c = getc();
switch( c ) {
case 'a':
a();
break;
case 'b':
b();
break;
default:
neither();
}
If that doesn't help then please list in detail the error message, possible along with the colour of you socks.
Edit: to respond to your reply, you can't use switch
with none-integral types. In particular, you can't use class types. Not objects of type QString
and not objects of any other type. You can use an if-else if-else
construct instead, or you can use runtime or compile time polymorphism, or overloading, or any of the array of alternatives to a switch
.
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;
}
It is identical to a C++ switch statement.
switch(var){
case(option1):
doesStuff();
break;
case(option2):
etc();
break;
}
精彩评论