No syntax like: if(theInput IN {"A", "B", "F"}) { --- right?

Including any extra c++ standards, including but not limited to C++11, there's no quick way to see if a variable matches a value in a list, is there?

I mean, instead of writing this:
1
2
3
4
5
6
7
if(theInput == "A" || theInput == "B" || theInput == "F") {
   // do something
} else if(theInput == "C" || theInput == "G") {
   // do something else
} else {
   // do something else
}


Is there a better way, like this:
1
2
3
4
5
6
7
if(theInput IN{"A", "B", "F"}) {
   // do something
} else if(theInput IN{"C", "G"}) {
   // do something else
} else {
   // do something else
}


I don't want to use a function that uses va_list, because that ads extra overhead. I don't want to use anything else that ads extra overhead.

I'm open to using something in c++, any of the new standards including C++11, or a #define macro.

Hoping there's a way, besides creating a #define macro for each number of arguments I want to be able to support.

Here's some code showing how to use find to check an array of char for a given char. The variable charList1 and charList2 would be the lists of letters you're trying to match against. Note that changing the charList by adding or removing char objects will require no extra changes to keep the code working. I think this meets your needs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int main () {
  char charList1[] = { 'A', 'B' , 'C' , 'D' };
  char charList2[] = { 'Z', 'Y' , 'X' , 'W', 'V', 'U' };

  if (find(charList1,charList1+sizeof(charList1),'D') == charList1+sizeof(charList1))
    { cout << "Did not find letter in charList1";}
  else
    { cout << "Found letter in charList1";}

  cout << endl;

 if (find(charList2,charList2+sizeof(charList2),'D') == charList2+sizeof(charList2))
    { cout << "Did not find letter in charList2";}
  else
    { cout << "Found letter in charList2";}

  return 0;
}
Last edited on
I apologize, I should have been more clear. I really appreciate that you put all that together, and feel bad!

I used one characters in my string for simplification.

However, I'm looking for a way to see if a variable of any type matches at least one element in a list. Could be enumerated types, longer strings, numbers, even classes.

There could be a solution in any of the new c++ extensions.

Or, there could be a solution using some type of #define macro, template, variadic macros, variadic template, or something like that.


Basically, mySQL syntax allows: "SomeVariableName IN(someValue, someOtherValue, someEvenOtherValue...)" syntax, and I'm looking for a functional equivalent without additional overhead.
I used an array of char for simplicity. It can be enumerated types, longer strings, numbers, even classes. Are you saying the list could be different kinds of things? One element could be an int, and another a string, and so on, or will all elements in the list be of the same type?

There's nothing stopping you taking this example code:

if (find(charList2,charList2+sizeof(charList2),'D') == charList2+sizeof(charList2))

and turning it into a simpler-to-use function (although it's pretty simple already).

I'm looking for a functional equivalent without additional overhead.


It is a functional equivalent, with little overhead. Do you mean you want the exact same syntax?
Last edited on
If you have C++11 you could use forward list:
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <algorithm>
#include <forward_list>

int main()
{
	std::forward_list<int> il {1, 4, 6, 7};

	for(size_t i = 0; i < 10; ++i)
		if(std::count(il.begin(), il.end(), i))
			std::cout << "found: " << i << '\n';
}

Or make a function and use a std::initializer_list:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <algorithm>
#include <initializer_list>

template<typename T>
bool is_in(std::initializer_list<T> il, const T& v)
{
	return std::count(il.begin(), il.end(), v);
}

int main()
{
	char input;

	std::cout << "Type in a char: ";
	std::cin >> input;

	if(is_in({'A', 'B', 'F'}, input))
	{
		std::cout << "One" << '\n';
	}
	else if(is_in({'C', 'G'}, input))
	{
		std::cout << "Two" << '\n';
	}
	else
	{
		std::cout << "Three" << '\n';
	}
}
Last edited on
I am now spending my time thinking of more and more ways to do this. Argh! Too much C++!
Indeed, you could also do something like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <algorithm>
#include <initializer_list>

template<typename T>
struct in_list
: public std::initializer_list<T>
{
	in_list(const std::initializer_list<T>& il): std::initializer_list<T>(il) {}
};

template<typename T>
bool operator,(const T& v, const in_list<T>& il)
{
	return std::count(il.begin(), il.end(), v);
}

int main()
{
	char input;

	std::cout << "Type in a char: ";
	std::cin >> input;

	if(input, in_list<char>{'A', 'B', 'C'})
	{
		std::cout << "One" << '\n';
	}
	else if(input, in_list<char>{'C', 'G'})
	{
		std::cout << "Two" << '\n';
	}
	else
	{
		std::cout << "Three" << '\n';
	}
}
Moschops --

By functional equivalent without additional overhead, I don't necessarily mean the same exact syntax... just that it gets that job done, and that it's easy to read quickly / uses less characters to do.



Galik --

Thanks! I hadn't read up on initializer_list yet. This is close to what I was looking for and figuring should be able to be done.

There's a few situations that I'll be using this method.

There is unfortunately a considerable amount of additional overhead, at least on g++ 4.6.0. However, I realize that it's a small amount, and unless it's being done a bazillion times, it's negligible and unnoticable. I just mention it because when I try different methods like this, I'm always curious about performance.

I'll mention my results because I have them. I'm not complaining about it, and again know it's negligible unless it's in a bottleneck. :) Both initializer_list methods take about 4 times longer to run than the if method. To make sure I wasn't dealing with fractional of a second numbers that had high variation, I ran a for loop so the slower initialization_list method took about 10 seconds. This method took an average of 10.225sec, and the if method took 2.79sec.

So, I'll be using it as long as it's not in a bottleneck situation. Still wish there was a way to do this without a performance hit, but I realize that often bugs me even when I won't see a difference in program run speed.
Can you post the code you are using to time the results? If you put optimization (-O3) on then the performance should be very good.
uses less characters to do.


If the bottleneck in your programming is the speed at which you can type, I am extraordinarily impressed! The proportion of my programming time spent actually typing is tiny; easily less than 5%.
Again, thanks everyone for your replies. I'm using a variant on the initializer_list method, and am happy to have it.



Moschops - typing is definitely not the bottleneck. I just like reducing the amount of code and making it easier to read later when possible. I know some would ridicule it, but I've created a few things that act as my own c++ extensions. I know the reasons arguing against doing such things, but I like it. :) Like define macros like FOREACH_CONST_VECTOR_NOITER so I can type:
1
2
3
4
vector<objectType*> objects;
FOREACH_CONST_VECTOR_NOITER(objects, objectType*, object) {
   object->doSomething();
} FEND

Instead of having to do:
1
2
3
4
vector<objectType*> objects;
for(vector<objectType*>::iterator objectIter = objects.begin() ; objectIter != objects.end() ; ++objectIter) {
   (*object)->doSomething();
}

... Now, not needed with c++11's range-based for-loops, but I wanted it earlier.



Galik - sure, code is below.

Compiling using -O3. gcc 4.6.0, although I'm in the middle of upgrading that atm.

I'll point out again, that I know it's iterating 20 million times, and that's a lot more than my programs will be handling. I'm not using that to be representative of the amount of times my program will do the process, I'm using that to make the results from "time ./comparesWithInitializerList" larger, for better comparison purposes, so I'm not comparing two small numbers with lots of variance. I'll also point out that I know for the number of times my program will be doing this, that the time difference is fractions of a second. It's more out of curiosity that I sometimes benchmark things like this.

comparesWithoutInitializerList.cpp :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <string>
using namespace std;

int main() {
   for(unsigned long i = 0 ; i < 20000000 ; ++i) {
      string someInput = "blah";

      if("blah" == someInput || "zzzz" == someInput || "rewk" == someInput) {
         //would do something here
      } else if("fadsljk" == someInput) {
         throw false;
      } else {
         throw false;
      }
   }
}


comparesWithInitializerList.cpp :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <string>
#include <algorithm>
#include <initializer_list>
using namespace std;

template<typename T>
bool is_in(initializer_list<T> il, const T& v) {
   return count(il.begin(), il.end(), v);
}
bool str_is_in(initializer_list<string> il, const string& v) {
   // using is_in below instead of str_is_in gives: "error: no matching function for call to 'is_in(<brace-enclosed initializer list>, std::string&)'
   // i believe compiler is correctly seing literal char *'s as the template type, then getting confused why there's a string
   // (using is_in on integers compiles fine)
   // this function allows the calling line to not have to typecast the literal char*'s to strings
   return count(il.begin(), il.end(), v);
}

int main() {
   for(unsigned long i = 0 ; i < 20000000 ; ++i) {
      string someInput = "blah";

      if(str_is_in({"blah", "zzzz", "rewk"}, someInput)) {
         // would do something here
      } else if(str_is_in({"fadsljk"}, someInput)) {
         throw false;
      } else {
         throw false;
      }
   }
}
Last edited on
This is the most interesting forum thread I've read in a very long time. It reminded me why I like C and C++ so much. :)
EDIT: Nvm, i only readed first post.
Last edited on
phoemi wrote:
Like define macros like FOREACH_CONST_VECTOR_NOITER so I can type:
For your info: It's a bad version of BOOST_FOREACH(). It is like so:
1
2
3
4
vector<objectType*> objects;
BOOST_FOREACH(const objectType *const object, objects) {
   object->doSomething();
}


EDIT: Or so:
1
2
3
4
vector<objectType*> objects;
BOOST_FOREACH(objectType *object, objects) {
   object->doSomething();
}
(like you will)
Last edited on
closed account (DSLq5Di1)
Some variadic template lovin' -> http://ideone.com/3mPdA.
How about this?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
template <class myType> //myType is template for DOUBLE, INT, STRING, whatever
class myCompare
{
private:
	set <myType> goodValues; 
public:
        //constructor(s) & desctructor 
        SetPossibleValues(std::string const & listOfValues)
//listOfValues is something like "str1;sr2;str_n" or "int1;int2;int_n"
        {
            myType currVar;                        //holds 
            std::isstringstream iss;
            iss.str(listOfValues);
            while (!iss.eof())
            {
                iss>>currVar;
                iss.ignore (1)                   //you might want to play with delimiter  (default is EOF );
                goodValues.insert(currVar);
             }
         }
        bool Contains(myType const & valueToSearch)
        {
              return goodValues.find(valueToSearch) != goodValues::end;
         }
}


It might not compile (i was just sketching the solution)
this should work with any type of object that has an intrinsic sort (even objects, if you define it properly): in this case your object must implement the >> operator in order to extract from a stream
For simple types it (should) work fine (refinments required)
You can define for your object also a clear method...
Last edited on
closed account (DSLq5Di1)
I really liked the syntax of Galiks code, so I've modified it slightly to improve on performance, and added a helper function for type inference, test cases here:- http://ideone.com/vB4TO.
Thanks everyone, I can't believe the response I got on this post. I'm a new member, but love this forum already!

sloppy9 - The program you linked last certainly gets around most of the performance hit. Where the original initializer_list code ran in 3.7 times the amount of time the straight up if..else..else code took, your code runs in only 1.8 times. But, there seems to be a bug in the program you linked last, at least when I compile it on gcc 4.7.0 (build 20111106). It is, of course, an experimental source build from their svn repository, so things can happen... If I change your line 55 from "}" to "} else { throw false; }", the program throws false, when that wouldn't be the expected behavior. (Without something like this, it appears to run correctly, but it's just looping and deciding it's never in the set, again, at least on my compiler.)

BTW, I also love your timing method. Much nicer than running ./time <programName> for programs compiled using alternate methods.

I have figured out that the last program you linked is failing on the first iteration of the for loop.

It's driving me nuts, I don't see what's wrong with it. If I ad to the "operatror," function:
for(auto x : il) { cout << x << endl; } // or auto &x

Then I get garbage random output here, including high ascii characters.

Inspired by your variadic template idea, I read up on them, and created a method that doesn't use initializer_list. It runs in the same amount of time that your code does -- about 1.8 times the amount of time the straight up if..else..else code took

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template<typename T>
inline bool in(const T& compareValue, const T& thisValue) {
   return(compareValue == thisValue);
}

template<typename T, typename... Args>
bool in(const T& compareValue, const T& thisValue, const Args&... args) {
   return(compareValue == thisValue || in(compareValue, args...));
}

// these are needed because compiler can't sort out in(string, const literal char*, ...)
inline bool in(const string& compareValue, const string& thisValue) {
   return(compareValue == thisValue);
}

template<typename... Args>
inline bool in(const string& compareValue, const string& thisValue, const Args&... args) {
   return(compareValue == thisValue || in(compareValue, args...));
}


I still have to figure out your helper function for type inference idea, so I don't need the second set of functions to handle the string vs char* case.
closed account (DSLq5Di1)
Mmm that is odd.. I'm not really sure what the problem could be with GCC 4.7, and I lack the resources to do any testing at the moment sorry.

compiler can't sort out in(string, const literal char*, ...)
This is a result of passing a string literal as a reference, the type is an array rather than a pointer to the first element. I think providing an overload for string literals as you have is a good idea, though I'd replace the type of thisValue with const char*.
Topic archived. No new replies allowed.