C ++ Variadic Macro для указания нескольких условий - PullRequest
2 голосов
/ 17 марта 2011

Я подозреваю, что не могу этого сделать, но решил, что сначала спрошу мудрое сообщество.

Я хочу проверить, равны ли какие-либо из нескольких (скажем, десять, хотя, вероятно, всего две или три) переменных одному и тому же конкретному значению. например,

if (X == 3 || Y == 3 || Z == 3 || W == 3) ...

В Python я привык просто делать if 3 in (X, Y, Z, W):, но что угодно.

В любом случае, мне интересно, можно ли абстрагировать его в макрос с переменным значением, например EQU_ANY(3, X, Y, Z, W), вместо записи набора макросов EQU_ANYX, где X - количество аргументов.

Ответы [ 4 ]

4 голосов
/ 17 марта 2011

[Решение с использованием макроса в конце, потому что оно довольно ужасное и отвратительное.]


Если вы не возражаете против создания копий, возможно, лучше использовать std::find:

std::array<int, 4> values = { X, Y, Z, W };
if (std::find(values.begin(), values.end(), 3) != values.end()) { }

Часто приятно обернуть эту строку в функцию:

template <typename Container, typename Value>
bool contains(const Container& c, const Value& v) 
{
    return std::find(c.begin(), c.end(), v) != c.end();
}

используется как:

std::array<int, 4> values = { X, Y, Z, W };
if (contains(values, 3)) { }

В C ++ 0x вы можете использоватьсписок инициализаторов вместо создания временного массива:

if (contains({ X, Y, Z, W }, 3)) { }

(Это работает в gcc 4.5+; я не знаю ни одного другого компилятора, поддерживающего эту функцию C ++ 0x.)


Если вы действительно хотите избежать копирования объектов (например, если они большие или дорогостоящие для копирования), вы можете использовать косвенную версию той же функции:

#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());
}

Используется как:

std::array<int*, 4> values = { &X, &Y, &Z, &W };
if (indirect_contains(values, 3)) { }

Или со списком инициализатора C ++ 0x:

if (indirect_contains({ &X, &Y, &Z, &W }, 3)) { }

Поскольку Джонатан Леффлер упомянул Boost.Preprocessor, вот как это решение будет выглядеть:

#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))

Используется как:

if (SEQUENCE_CONTAINS((X)(Y)(Z)(W), 3)) { }

Что расширяется до:

if ((((X) == (3)) || 
     ((Y) == (3)) || 
     ((Z) == (3)) || 
     ((W) == (3)))) { }

(Это ужасно и ужасно; я бы не стал использовать тего в моем коде, но если вы действительно беспокоитесь о том, что создаются копии двух или трех значений, вы, вероятно, тоже не захотите делать вызов функции.)

1 голос
/ 17 марта 2011

Поскольку вы также пометили свой вопрос буквой C, вот ответ, который работает только с C99 и переменными макросами, в C ++ его нет. Используйте P99 , чтобы развернуть оператор

#define TESTIT(WHAT, X, I) X == WHAT
#define TEST_MORE(WHAT, ...) P99_FOR(WHAT, P99_NARG(__VA_ARGS__), P00_OR, TESTIT, __VA_ARGS__)

Это здесь

TEST_MORE(A, b, c, d, e, f)
TEST_MORE(3, b, c, d, e, f)

становится потом

((((((((b == A) || (c == A))) || (d == A))) || (e == A))) || (f == A))
((((((((b == 3) || (c == 3))) || (d == 3))) || (e == 3))) || (f == 3))

По сравнению с усилением это имеет то преимущество, что последний вызов TEST_MORE прост и удобочитаем.

1 голос
/ 17 марта 2011

Попробуйте использовать оператор switch.Вы можете указать одно и то же поведение для нескольких условий, например:

switch (n) {
    case 1:
    case 2:
    case 3:
        // Do something
        break;
}

Это эквивалентно

if (x == 1 || x == 2 || x == 3)
    // Do something

Это самый чистый способ сделать это.Возможно, вы сможете написать макрос с переменным переключением (или что-то подобное), но, пожалуйста, для всех, кто вас окружает ... не надо: P

0 голосов
/ 17 марта 2011

Скорее всего, это можно сделать с помощью средств в пакете Boost boost :: препроцессор (который может использоваться как с C, так и с C ++).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...