开发者

C++ Variadic Macro To Specify Multiple Conditions

开发者 https://www.devze.com 2023-02-17 03:32 出处:网络
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 sam

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++).

0

精彩评论

暂无评论...
验证码 换一张
取 消