Concise way to say equal to set of values in C++ - c++

For example I have the following string,
if (str[i] == '(' ||
str[i] == ')' ||
str[i] == '+' ||
str[i] == '-' ||
str[i] == '/' ||
str[i] == '*')
My question is there a concise way to say if this value one of these set of values in c++?

You can search for single character str[i] in a string with your special characters:
std::string("()+-/*").find(str[i]) != std::string::npos

Not glorious because it is C instead of C++, but the C standard library is always accessible from C++ code, and my first idea as an old dinosaur would be:
if (strchr("()+-/*", str[i]) != NULL)
Simple and compact

You may use the following:
const char s[] = "()+-/*";
if (std::any_of(std::begin(s), std::end(s), [&](char c){ return c == str[i]})) {
// ...
}

It really depends on your application actually. For such a small check and depending the context, one acceptable option could be to use a macro
#include <iostream>
#define IS_DELIMITER(c) ((c == '(') || \
(c == ')') || \
(c == '+') || \
(c == '-') || \
(c == '/') || \
(c == '*') )
int main(void)
{
std::string s("TEST(a*b)");
for(int i = 0; i < s.size(); i ++)
std::cout << "s[" << i << "] = " << s[i] << " => "
<< (IS_DELIMITER(s[i]) ? "Y" : "N") << std::endl;
return 0;
}
A more C++ish way of doing it would be to use an inline function
inline bool isDelimiter(const char & c)
{
return ((c == '(') || (c == ')') || (c == '+') ||
(c == '-') || (c == '/') || (c == '*') );
}
This post might be interesting then : Inline functions vs Preprocessor macros

Maybe not "more concise", but I think this style is succinct and expressive at the point of the test.
Of course is_arithmetic_punctuation needn't be a lambda if you're going to use it more than once. It could be a function or a function object.
auto is_arithmetic_punctuation = [](char c)
{
switch(c)
{
case '(':
case ')':
case '+':
case '-':
case '/':
case '*':
return true;
default:
return false;
}
};
if (is_arithmetic_punctuation(str[i]))
{
// ...
}

Related

iterate char by char through vector of strings

I want to iterate char by char in a vector of strings. In my code I created a nested loop to iterate over the string, but somehow I get an out of range vector.
void splitVowFromCons(std::vector<std::string>& userData, std::vector<std::string>& allCons, std::vector<std::string>& allVows){
for ( int q = 0; q < userData.size(); q++){
std::string userDataCheck = userData.at(q);
for ( int r = 0; r < userDataCheck.size(); r++){
if ((userDataCheck.at(r) == 'a') || (userDataCheck.at(r) == 'A') || (userDataCheck.at(r) == 'e') || (userDataCheck.at(r) == 'E') || (userDataCheck.at(r) == 'i') || (userDataCheck.at(r) == 'I') || (userDataCheck.at(r) == 'o') || (userDataCheck.at(r) == 'O') || (userDataCheck.at(r) == 'u') || (userDataCheck.at(r) == 'U')){
allVows.push_back(userData.at(r));
}
else if ((userDataCheck.at(r) >= 'A' && userDataCheck.at(r) <= 'Z') || (userDataCheck.at(r) >= 'a' && userDataCheck.at(r) <= 'z')){
allCons.push_back(userData.at(r));
}
else {
continue;;
}
}
}
}
The error here is in these lines:
allVows.push_back(userData.at(r));
allCons.push_back(userData.at(r));
the r variable is your index into the current string, but here you're using it to index into the vector, which looks like a typo to me. You can make this less error prone using range-for loops:
for (const std::string& str : userData) {
for (char c : str) {
if (c == 'a' || c == 'A' || ...) {
allVows.push_back(c);
}
else if (...) {
....
}
}
}
which I hope you'll agree also has the benefit of being more readable due to less noise. You can further simplify your checks with a few standard library functions:
for (const std::string& str : userData) {
for (char c : str) {
if (!std::isalpha(c)) continue; // skip non-alphabetical
char cap = std::toupper(c); // capitalise the char
if (cap == 'A' || cap == 'E' || cap == 'I' || cap == 'O' || cap == 'U') {
allVows.push_back(c);
}
else {
allCons.push_back(c);
}
}
}
Since this question is about debugging actually, I think it is a nice illustration of how the usage of std::algorithms of C++ can decrease the effort needed to see what is wrong with a non working code.
Here is how it can be restructured:
bool isVowel(char letter)
{
return letter == 'A' || letter == 'a' ||
letter == 'E' || letter == 'e'||
letter == 'O' || letter == 'o'||
letter == 'Y' || letter == 'y'||
letter == 'U' || letter == 'u';
}
bool isConsonant(char letter)
{
return std::isalpha(letter) && !isVowel(letter);
}
void categorizeLetters(const std::vector<std::string> &words, std::vector<char> &vowels, std::vector<char> &consonants)
{
for( const std::string &word : words){
std::copy_if(word.begin(), word.end(), std::back_inserter(vowels), isVowel);
std::copy_if(word.begin(), word.end(), std::back_inserter(consonants), isConsonant);
}
}
With a solution like this, you avoid the error-prone access-with-index that lead to your problem. Also, code is readable and comprehensive

How can I evaluate a QChar object in a switch?

Going through a bunch of code, looking to improve it.
I came across this bit:
if (c == '<' || c == '>') {
pattern.append("\\b");
} else if (c == 'a') {
pattern.append("[a-zA-Z]");
} else if (c == 'A') {
pattern.append("[^a-zA-Z]");
} else if (c == 'h') {
pattern.append("[A-Za-z_]");
} else if (c == 'H') {
pattern.append("[^A-Za-z_]");
} else if (c == 'c' || c == 'C') {
ignorecase = (c == 'c');
} else if (c == 'l') {
pattern.append("[a-z]");
} else if (c == 'L') {
pattern.append("[^a-z]");
} else if (c == 'o') {
pattern.append("[0-7]");
} else if (c == 'O') {
pattern.append("[^0-7]");
} else if (c == 'u') {
pattern.append("[A-Z]");
} else if (c == 'U') {
pattern.append("[^A-Z]");
} else if (c == 'x') {
pattern.append("[0-9A-Fa-f]");
} else if (c == 'X') {
pattern.append("[^0-9A-Fa-f]");
} else if (c == '=') {
pattern.append("?");
} else {
pattern.append('\\');
pattern.append(c);
}
If c was a char, this would be easy to turn into a switch. c is a QChar;
How should I turn QChar into an interger and reliably compare it to the various cases >, = etc?
A QChar is a wrapper for a 16-bit UTF-16 character.
You can retrieve the value using QChar::unicode() that returns an unsigned short.
You can the write your switch like this:
QChar c;
switch (c.unicode()) {
case u'a':
...
}
Be careful with your case statements as if you use 8-bit char literals, it might not work as expected.
For instance é might be 0xE9 (Latin-1, UTF16), or 0x82 (CP437) or even 0xC3 0xA9 (UTF-8, which will not compile as it needs 2 characters).
The solution is to use UTF-16 literals that are part of C++ since C++11.
For exampleu'é' will always be compiled as a char16_t (~unsigned short) of value 0x00E9.
you can define something like a dictionary, and I mean a Map:
int main(int argc, char* argv[])
{
QMap<QChar, QString> myMap{{'a', "[a-zA-Z]"},{'X', "[^0-9A-Fa-f]"}, {'h', "[A-Za-z_]"}};
QString regex{};
regex.append(myMap.value('a', ""));
regex.append(myMap.value('5', ""));
regex.append(myMap.value('X', ""));
qDebug() << "myRegex: " << regex;
return 0;

Algorithm for template argument deduction (as strings)?

For some reason, I want to implement something simliar to the what C++ compilers used to deduce template arguments. With a known set of template parameters like
"T0", "T1", "T2"...
Given 2 strings like:
str_param = "vector<T0>"
str_arg = "vector<float>"
The result should be that "T0" is mapped to "float":
map["T0"]=="float"
I don't need a full-featured template preprocessor, which means, I'll be satisfied if I can just handle the cases where the template argument can be literally deduced. No need to consider things like "typedef" in the context.
In other words, if I use the resulted map to replace template parameters in str_param, it should become str_arg. If that is not possible, I consider it as "fail to match".
I currenlty have problem handling cases like:
str_param = "T1*"
str_arg = "int**"
Where expected result is:
map["T1"]=="int*"
My algorithm mistakes it as:
map["T1"]=="int"
Putting my poor algorithm here:
std::vector<std::string> templ_params({"T1"});
std::vector<std::string> templ_args(templ_params.size());
std::string str_param = "T1*";
std::string str_arg = "int**";
const char* p_str_param = str_param.c_str();
const char* p_str_arg = str_arg.c_str();
while (*p_str_param != 0 && *p_str_arg != 0)
{
while (*p_str_param == ' ' || *p_str_param == '\t') p_str_param++;
while (*p_str_arg == ' ' || *p_str_arg == '\t') p_str_arg++;
if (*p_str_param == 0 || *p_str_arg == 0) break;
if (*p_str_param != *p_str_arg)
{
std::string templ_param;
std::string templ_arg;
while (*p_str_param == '_' ||
(*p_str_param >= 'a' && *p_str_param <= 'z') ||
(*p_str_param >= 'A' && *p_str_param <= 'Z') ||
(*p_str_param >= '0' && *p_str_param <= '9'))
templ_param += *(p_str_param++);
while (*p_str_param == ' ' || *p_str_param == '\t') p_str_param++;
char end_marker = *p_str_param;
const char* p_str_arg_end = p_str_arg;
while (*p_str_arg_end != end_marker) p_str_arg_end++;
while (*(p_str_arg_end - 1) == ' ' || *(p_str_arg_end - 1) == '\t')
p_str_arg_end--;
while (p_str_arg<p_str_arg_end) templ_arg += *(p_str_arg++);
for (size_t i=0; i < templ_params.size(); j++)
{
if (templ_params[i]==templ_param)
{
templ_args[i]=templ_arg;
break;
}
}
}
else
{
p_str_param++;
p_str_arg++;
}
}

Find the first printf format sequence in a C++ string

I search the most concise and efficient way to find the first printf format sequence (conversion specification) in a C++ string (I cannot use std::regex as they are not yet implement in most in compilers).
So the problem is to write an optimized function that will return the beginning of the first printf-format sequence pos and its length n from an input string str:
inline void detect(const std::string& str, int& pos, int& n);
For example, for:
%d -> pos = 0 and n = 2
the answer is: %05d -> pos = 15 and n = 4
the answer is: %% %4.2f haha -> pos = 18 and n = 5
How to do that (clever and tricky ways are welcome)?
Scan forward for %, then parse the content from there. There are some quirky ones, but not THAT bad (not sure you want to make it an inline tho').
General principle (I'm just typing as I go along, so probably not the BEST form of code ever written - and I haven't tried to compile it at all).
inline void detect(const std::string& str, int& pos, int& n)
{
std::string::size_type last_pos = 0;
for(;;)
{
last_pos = str.find('%', last_pos)
if (last_pos == std::string::npos)
break; // Not found anythin.
if (last_pos == str.length()-1)
break; // Found stray '%' at the end of the string.
char ch = str[last_pos+1];
if (ch == '%') // double percent -> escaped %. Go on for next.
{
last_pos += 2;
continue;
}
pos = last_pos;
do
{
if (isdigit(ch)) || ch == '.' || ch == '-' || ch == '*' ||
ch == '+' || ch == 'l' || ch == 'L' || ch == 'z' ||
ch == 'h' || ch == 't' || ch == 'j' || ch == ' ' ||
ch == '#' || ch == '\'')
{
last_pos++;
ch = str[last_pos+1];
}
else
{
// The below string may need appending to depending on version
// of printf.
if (string("AacdeEfFgGiopusxX").find(ch) != std::string::npos)
{
// Do something about invalid string?
}
n = last_pos - pos;
return;
}
} while (last_pos < str.length());
}
}
edit2: This bit is probably better written as:
if (isdigit(ch)) || ch == '.' || ch == '-' || ch == '*' ||
ch == '+' || ch == 'l' || ch == 'L' || ch == 'z' ||
ch == 'h' || ch == 't' || ch == 'j' || ch == ' ' ||
ch == '#' || ch == '\'') ...
if (string("0123456789.-*+lLzhtj #'").find(ch) != std::string::npos) ...
Now, that's your homework done. please report back with what grade you get.
Edit: It should be noted that some things that a regular printf will "reject" is accepted by the above code, e.g. "%.......5......6f", "%5.8d", "%-5-6d" or "%-----09---5555555555555555llllld". If you want the code to reject these sort of things, it's not a huge amount of extra work, just need a little bit of logic to check "have we seen this character before" in the "check for special characters or digit", and in most cases the special character should only be allowed once. And as the comment says, I may have missed a couple of valid format specifiers. It gets further trickier if you also need to cope with "this 'l' is not allowed with 'c'" or such rules. But if the input isn't "malicious" (e.g. you want to annotate where on which line there are format specifiers in a working C source file), the above should work reasonably well.

Stupid C++ question about if-else

I have this code:
#include <iostream>
using namespace std;
int main()
{ char c='6';
if(c == '+' || '-' || '*' || '^' || '/' || '%')
{
cout<<"good";
}
else {cout<<"bad";}
return 0;
}
I want to write "good" if the char is '+' or '-' etc, and write "bad" if the char is anything else.
But this code writes "good" always with any char.
Where is the problem? Thanks.
if(c == '+' || '-' || '*' || '^' || '/' || '%')
parses to
if( (c == '+') || ('-'!=0) || ('*'!=0 || ('^'!=0) || ('/'!=0) || ('%'!=0))
It will always evaluate to true because '-' is indeed not equal to zero. Of course it's a sort of flaw with the type-safety that a char "degrades" to a boolean and evaluates to true. (The proper type-safe solution would be simply not to compile your code unless you explicitly cast).
What you wanted to know was whether c is one of those values. There are many ways to do that. Apart from an indented if you could use a library feature:
C function strchr:
if( strchr( "+-*^/%", c ) != NULL )
switch statement
switch (c )
{
case '+': case '-': case '*': case '^': case '/': case '%':
// true logic
break;
default:
// false logic
};
regex
(overkill here but purists would like it).
std::bitset
This takes a lot of "setup" but if you have a fixed set of chars and lots of variable chars to see if it exists in the set, this is the quickest way to do it.
// one-time setup
std::bitset<256> myCharSet;
myCharSet.set('+');
myCharSet.set('-');
myCharSet.set('*');
myCharSet.set('^');
myCharSet.set('/');
myCharSet.set('%');
// subsequently
if( myCharSet.test( static_cast<unsigned char>(c) ) )
{
// true logic
}
else
{
// false logic
}
static array
Similar solution to bitset but you don't mind wasting a few bytes.
static bool charset[256] = { false };
static bool init = false;
if( !init )
{
charset['+'] = true; // etc
init = true;
}
if( charset[ static_cast<unsigned char>(c) ] )
{
// true logic
}
else
{
// false logic
}
And you could make a class that does this that initialises from a string of the characters you want to check for (plus some logic as to whether a 0 byte is true or false, if the string you pass is null-terminated).
As with bitset this is constant-time lookup.
There are other options (eg with C++ std::string class using find and std::find) but these will do for now.
Change your if to:
if(c == '+' || c == '-' || c == '*' || c == '^' || c == '/' || c == '%')
or better yet:
switch (c)
{
case '+': case '-': case '*' : case '^' : case '/' : case '%':
cout << "good\n"; break;
default: cout << "bad\n"; break;
}
it should be
if(c == '+' || c == '-' || c == '*' || c == '^' || c == '/' || c == '%')
...
otherwise the expression is always evaluating to true. Any of those characters have a value different from 0, so true for c++.
Simple answer:'-' is the ASCII (the way the computer encodes character) value of that character - which is not 0 therefore it's true (computer's logic).
Same for all the other characters. So we got:
c == '+' || true || true || true || true || true which is always true.
What you should've done:
c == '+' || c == '-' || c == '*' || c == '^' || c == '/' || c == '%'
The mistake was probably you thinking "It needs to be equal to this or that or that or that" and so on. This happened to everyone, just remember that computers don't speak English.