12

I would like to define a simple template function which takes a runtime value and determines if it is a member of some set of possible values.

Usage:

int x; // <- pretend this came from elsewhere... if (isoneof(x, {5,3,9,25}) ... 

Something like:

template <typename T, size_t size> bool isoneof(T value, T (&arr)[size]) { for (size_t i = 0; i < size; ++i) if (value == arr[i]) return true; return false; } 

I assume that this is doomed to failure, as I don't see how one can create a static array inline.

I can use:

int kPossibilities[] = {5,3,9,25}; if (isoneodf(6, kPossibilities)) ... 

With a minor change to isoneof:

template <typename T1, typename T2, size_t size> bool isoneof(T1 value, const T2 (&arr)[size]) { for (size_t i = 0; i < size; ++i) if (value == arr[i]) return true; return false; } 

Which also makes it a tad more flexible.

Does anyone have an improvement to offer? A better way to define a "set of static values inline"?

1
  • 6
    It's about time for C++1x, isn't it? Array literals are perfectly valid there... Commented Feb 5, 2010 at 15:25

6 Answers 6

12

If you like such things, then you will be a very happy user of Boost.Assign.

Boost.Assign actually proves that such semantics are possible, however one look at the source of assign will convince you that you don't want to do that by yourself :)

You will be able to create something like this however:

if (isoneof(x, list_of(2)(3)(5)(7)(11)) { ... 

... the downside being you'd have to use boost::array as the parameter instead of a built-in array (thanks, Manuel) -- however, that's a nice moment to actually start using them :>

Sign up to request clarification or add additional context in comments.

7 Comments

I'm afraid you're wrong. Boost.Assign works only with boost::array, not with built-in arrays. So the OP would have to change his function to take a "const boost::array<T, size> & arr" ir order to be able to use "list_of" as you suggest.
@Manuel - I didn't say that he wouldn't have to change anything :P
@Kornel: fair enough. I'd tone down my comment but I can't seem to edit it.
@Matthieu, I strongly believe that expression templates come into play here, so the reevaluation isn't that much of an issue.
@Kornel - No expression templates are involved, "list_of" just populates a deque under the hoods and then copies it to the destination container with an overload of template<class T> operator T();
|
5

It's possible in the next C++ standard.

Up till then, you can work around it by e.g. overloading operator, for a static object that starts a static array.

Note: this implementation is O(n^2) and may be optimized - it's just to get the idea.

using namespace std; template< typename T, size_t N > struct CHead { T values[N]; template< typename T > CHead<T,N+1> operator,( T t ) { CHead<T,N+1> newhead; copy( values, values+N, newhead.values); newhead.values[N]=t; return newhead; } bool contains( T t ) const { return find( values, values+N, t ) != values+N; } }; struct CHeadProto { template< typename T > CHead<T,1> operator,( T t ) { CHead<T,1> h = {t}; return h; } } head; int main() { assert( (head, 1,2,3,4).contains(1) ); return 0; } 

4 Comments

+1: for the code, but while nice, I wouldn't carelessly overload the , operator just for syntactic sugar :>
Interesting approach! The copy() make me cringe enough that I think I would look elsewhere for a solution. Thanks :)
@Mordachai: take a look at Boost.Assign if you find this code interesting. Lots of equally cool stuff there.
@Kornel: Carelessly, yes, but What else would you overload it for?
2

For the sake of completeness, I'll post a solution that uses Boost.MPL. The following works, but I think Kornel's solution is best.

#include <iostream> #include <boost/mpl/for_each.hpp> #include <boost/mpl/vector_c.hpp> struct Contains { Contains(int value, bool& result) : value(value), result(result) { result = false; } template< typename T > void operator()(T x) { result = result || (x == value); } int value; bool& result; }; template <class IntList> bool isoneof(int val) { namespace mpl = boost::mpl; bool result; mpl::for_each<IntList>(Contains(val, result)); return result; } int main() { namespace mpl = boost::mpl; std::cout << isoneof< mpl::vector_c<int, 1,2,3,5,7,11> >(4) << "\n"; std::cout << isoneof< mpl::vector_c<int, 1,2,3,5,7,11> >(5) << "\n"; } 

As you can see, the compile-time array is passed inline as a template argument to isoneof.

3 Comments

Cool! This seems like a solid approach as well. It does result in a recursive run time algorithm, no?
Looking at boost/mpl/for_each.hpp, I see that it calls for_each_impl::execute() recursively. Since it's all inline, perhaps optimizers are smart enough now to "unroll" the recursive calls? Maybe not. I don't know. :-)
Also note that the comparison will be performed for all elements in the mpl::vector_c. It will not "break" out on the first match.
1

This one?

int ints[] = {2,3,5,7,11}; #define ARRAY_SIZE(Array) (sizeof(Array)/sizeof((Array)[0])) #define INLIST(x,array) isoneof(x,array,ARRAY_SIZE(array)) 

ADDITION:

template <typename T> bool isoneof(const T& x, T *array, int n) { for(int i=0; i<n; ++i) if(x==array[i]) return true; return false; } 

3 Comments

Notinlist typing a INLIST macro, oh the irony ;)
This is a step back from what OP himself suggested, though. OP's version is guaranteed to deduce the size of the array properly (or fail to compile) whereas this would just produce wrong results if the array has decayed to pointer.
change (Array)[0] to 0[Array] for it to detect types that override the [] operator
1

Using C++11, this would be written like this:

template <typename T> bool isoneof(T value, std::initializer_list<T> arr) { using namespace std; return any_of(begin(arr), end(arr), [&](const T& x) { return x == value; }); } 

1 Comment

Thanks. Yeah, C++11 (and 14) make this sort of thing a bit nicer.
0

Just FYI - I solved my particular problem using vararg templates and initializer lists now that I have access to C++14:

template <typename T, typename U> bool isoneof(T v, U v1) { return v == v1; } template <typename T, typename U, typename... Args> bool isoneof(T v, U v1, Args ... others) { return isoneof(v, v1) || isoneof(v, others...); } template <typename T, typename U> bool isoneof(T value, std::initializer_list<U> values) { for (const auto & e : values) if (value == e) return true; return false; } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.