tags:

views:

179

answers:

4

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"?

+12  A: 

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 :>

Kornel Kisielewicz
Manuel
@Manuel - I didn't say that he wouldn't have to change anything :P
Kornel Kisielewicz
@Kornel: fair enough. I'd tone down my comment but I can't seem to edit it.
Manuel
@Manuel, I included the fact from your comment in the answer.
Kornel Kisielewicz
I actually have another remark: I fear that this array is going to be reevaluated at each call... and thus you're better off defining a constant (even though the constant could definitely be declared using the `Boost.Assign` library).
Matthieu M.
@Matthieu, I strongly believe that expression templates come into play here, so the reevaluation isn't that much of an issue.
Kornel Kisielewicz
@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();
Manuel
+1  A: 

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;
}
Notinlist
Notinlist typing a INLIST macro, oh the irony ;)
Kornel Kisielewicz
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.
UncleBens
change (Array)[0] to 0[Array] for it to detect types that override the [] operator
dman
+4  A: 

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;
}
xtofl
+1: for the code, but while nice, I wouldn't carelessly overload the `,` operator just for syntactic sugar :>
Kornel Kisielewicz
Interesting approach! The copy() make me cringe enough that I think I would look elsewhere for a solution. Thanks :)
Mordachai
@Mordachai: take a look at Boost.Assign if you find this code interesting. Lots of equally cool stuff there.
Manuel
@Kornel: Carelessly, yes, but What else would you overload it for?
Jon Purdy
+1  A: 

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.

Emile Cormier
Cool! This seems like a solid approach as well. It does result in a recursive run time algorithm, no?
Mordachai
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. :-)
Emile Cormier
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.
Emile Cormier