tags:

views:

170

answers:

6

motivation: I would like to create a utility class so that instead of having to write:

if( someVal ==  val1 || someVal == val2 || someVal == val3 )

I could instead write:

if( is(someVal).in(val1, val2, val3) )

which is much closer to the mathematical 'a is an element of (b,c,d)' and also would save on a lot of typing when the variable name 'someVal' is long.

Here is the code I have so far (for 2 and 3 values):

template<class T>
class is {
private:
    T t_;
public:
    is(T t) : t_(t) { }

    bool in(const T& v1, const T& v2) { 
        return t_ == v1 || t_ == v2; 
    }
    bool in(const T& v1, const T& v2, const T& v3) { 
        return t_ == v1 || t_ == v2 || t_ == v3; 
    }
};

However it fails to compile if I write:

is(1).in(3,4,5);

instead I have to write

is<int>(1).in(3,4,5);

Which isn't too bad, but it would be better if somehow the compiler could figure out that the type is int with out me having to explicitly specify it.
Is there anyway to do this or I am stuck with specifying it explicitly?

A: 

You are stuck with it - the constructor requires the type to be provided as a template parameter. And I should observe that I really don't like your idea (especially the class name). Why not use a std:;set? Or even a template function - something like:

template <typename  T>
bool IsIn( T v, T a, T b, T c ) {
   ...
}
anon
Actually I started with something similar to IsIn because a class called 'in' seemed like asking for trouble, but in the end I decided I preferred is(a).in(b,c) for readability. Re using std::set - my use case is I want to be able to do this in one line, not have to add members to the set and then check for membership. (BTW I didn't downvote you)
hamishmcn
Regarding the class name: Put it in a namespace and it would be OK? (How likely is it that C++ would get an **is** keyword?! Isn't that what everybody calls their istreams?)
UncleBens
+13  A: 

If you want to keep this syntax, you can use a helper function like :

template<class T>
class is_op {
private:
    T t_;
public:
    is_op(T t) : t_(t) { }

    bool in(const T& v1, const T& v2) { 
        return t_ == v1 || t_ == v2; 
    }
    bool in(const T& v1, const T& v2, const T& v3) { 
        return t_ == v1 || t_ == v2 || t_ == v3; 
    }
};


template< class U >
inline is_op<U> is( U const& v )
{
    return is_op<U>( v );
}

int main(int argc, char* argv[])
{
    is( 1 ).in( 1 , 2 , 4 );
}
fa.
Typical way, write the class which provides the functionality and then write a function to create the instance of the class since functions support type deduction.
Matthieu M.
+2  A: 
template<typename T>
is<T> is_value(T value)
{
    return is<T>(value);
}

int main()
{
    bool r ;

    r = is_value(1).in(3,4,5);
    r = is_value(3).in(3,4,5);

    return 0;
}
skwllsp
+4  A: 

The problem is quite amusing, it's true that boolean conditions can get hairy.

I myself tend to prefer writing special functions though, because the meaning here is hard to convey. What does:

if (someVal == val1 || someVal == val2 || someVal == val3)

means ?

Is

if ( is(someval).in(val1, val2, val3) )
// or
if ( is(someval).in(val1)(val2)(val3) ) // implements short-circuiting
                                        // and removes arity issue
                                        // using a proxy object

better?

I think it would be easier to read with:

bool isToBeLogged(const Foo& foo)
{
  // Either
  static std::set<Foo> ValuesToLog = /* some boost assign magic or whatever */;
  return ValuesToLog.find(foo) != ValuesToLog.end();

  // Or
  return foo == val1 || foo == val2 || foo == val3;
}


if (isToBeLogged(someVal))

I guess it's a matter of style.

The advantages of the second method including:

  • In case the test is done more than once, the logic is not scattered all around (so changes are easy)
  • No need to comment the test, the method name does it already

Inconvients ? I guess it's more typing... oh well :p

Matthieu M.
I wonder how you would implement short-circuiting? You won't pass the result of a function call, but an object that calls a function when looked up? - But generally, couldn't you combine the approaches (use is.in to implement isToBeLogged)?
UncleBens
A: 

a modification for your comparison class is perhaps to use varargs, to make it general to n elements of the set.

Ricardo Ferreira
varargs don't work for most types of T? Variadic templates or `initializer_list` in C++0x would be another matter.
UncleBens
+1  A: 

Regarding the general problem, a utility function like contains might be handy:

#include <boost/range.hpp>
template <class Range, class T>
bool contains(const Range& range, const T& value)
{
    return std::find(boost::begin(range), boost::end(range), value) != boost::end(range);
}

(Boost usage makes it also accept arrays, although one might write that overload separately. This could also be overloaded for containers with the find member function.)

In C++0x this could be extended to support std::initializer_list<T>* allowing rather nice usage:

if (contains({1, 2, 3}, value) {...}

* Not sure if it shouldn't work already, but my compiler requires an overload to make it work.

UncleBens