I suspect that I can't do this, but figured I'd ask the wise community here first.
I want to check if any of a handful (say ten, though probably just two or three) of variables are equal to the same specific value. e.g.
if (X == 3 || Y == 3 || Z == 3 || W == 3) ...
In Python I'm used to simply doing if 3 in (X, Y, Z, W):
, but whatever.
Anyway, I'm wondering if it's possible to abstract it into开发者_StackOverflow中文版 a variadic macro such as EQU_ANY(3, X, Y, Z, W)
instead of writing a bunch of EQU_ANYX
macros where X is the number of args.
[The solution using a macro is at the end, because it's rather horrible and yucky.]
If you don't mind copies being made, it's probably cleaner to use std::find
:
std::array<int, 4> values = { X, Y, Z, W };
if (std::find(values.begin(), values.end(), 3) != values.end()) { }
It's often nice to wrap this line into a function:
template <typename Container, typename Value>
bool contains(const Container& c, const Value& v)
{
return std::find(c.begin(), c.end(), v) != c.end();
}
used as:
std::array<int, 4> values = { X, Y, Z, W };
if (contains(values, 3)) { }
In C++0x, you can use an initializer list instead of creating a temporary array:
if (contains({ X, Y, Z, W }, 3)) { }
(This works in gcc 4.5+; I'm not aware of any other compilers that support this C++0x feature yet.)
If you really want to avoid copying the objects (e.g., if they are large or expensive to copy), you can use an indirect version of the same function:
#include <boost/iterator/indirect_iterator.hpp>
template <typename Container, typename Value>
bool indirect_contains(const Container& c, const Value& v)
{
return std::find(boost::make_indirect_iterator(c.begin()),
boost::make_indirect_iterator(c.end()),
v)
!= boost::make_indirect_iterator(c.end());
}
Used as:
std::array<int*, 4> values = { &X, &Y, &Z, &W };
if (indirect_contains(values, 3)) { }
Or, with a C++0x initializer list:
if (indirect_contains({ &X, &Y, &Z, &W }, 3)) { }
Since Jonathan Leffler mentioned Boost.Preprocessor, here is what that solution would look like:
#include <boost/preprocessor.hpp>
#define SEQUENCE_CONTAINS_IMPL(r, data, i, elem) \
BOOST_PP_IIF(BOOST_PP_EQUAL(i, 0), BOOST_PP_EMPTY(), ||) \
((elem) == (data))
#define SEQUENCE_CONTAINS(elements, value) \
(BOOST_PP_SEQ_FOR_EACH_I(SEQUENCE_CONTAINS_IMPL, value, elements))
Used as:
if (SEQUENCE_CONTAINS((X)(Y)(Z)(W), 3)) { }
Which expands to:
if ((((X) == (3)) ||
((Y) == (3)) ||
((Z) == (3)) ||
((W) == (3)))) { }
(This is ugly and horrible; I wouldn't use this in my code, but if you're really worried about copies of two or three values being made, you probably don't want to chance a function call being made either.)
Try using a switch statement. You can specify the same behavior for multiple conditions like so:
switch (n) {
case 1:
case 2:
case 3:
// Do something
break;
}
That's the equivalent of
if (x == 1 || x == 2 || x == 3)
// Do something
That's the cleanest way to do this. You might be able to write a variadic switch macro (or something that accomplishes something like that), but please, for everyone around you... don't :P
Since you also tagged your question with C, here is an answer that only works with C99 and variadic macros, C++ doesn't have that. Use P99 to do statement unrolling
#define TESTIT(WHAT, X, I) X == WHAT
#define TEST_MORE(WHAT, ...) P99_FOR(WHAT, P99_NARG(__VA_ARGS__), P00_OR, TESTIT, __VA_ARGS__)
This here
TEST_MORE(A, b, c, d, e, f)
TEST_MORE(3, b, c, d, e, f)
becomes then
((((((((b == A) || (c == A))) || (d == A))) || (e == A))) || (f == A))
((((((((b == 3) || (c == 3))) || (d == 3))) || (e == 3))) || (f == 3))
Compared to boost this has the advantage that the final call to TEST_MORE
is simple and readable.
It most likely can be done using the facilities in Boost's boost::preprocessor package (which can be used with C as well as C++).
精彩评论