Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a shorter way to write compound 'if' conditions? [duplicate]

Just instead of:

if  ( ch == 'A' || ch == 'B' || ch == 'C' || .....

For example, to do it like:

if  ( ch == 'A', 'B', 'C', ...

is there even a shorter way to summarize conditions?

like image 878
OptimusMaximus Avatar asked Aug 25 '16 16:08

OptimusMaximus


3 Answers

strchr() can be used to see if the character is in a list.

const char* list = "ABCXZ";
if (strchr(list, ch)) {
  // 'ch' is 'A', 'B', 'C', 'X', or 'Z'
}
like image 53
P.P Avatar answered Nov 20 '22 12:11

P.P


In this case you could use a switch:

switch (ch) {
case 'A':
case 'B':
case 'C':
    // do something
    break;
case 'D':
case 'E':
case 'F':
    // do something else
    break;
...
}

While this is slightly more verbose than using strchr, it doesn't involve any function calls. It also works for both C and C++.

Note that the alternate syntax you suggested won't work as you might expect because of the use of the comma operator:

if  ( ch == 'A', 'B', 'C', 'D', 'E', 'F' )

This first compares ch to 'A' and then discards the result. Then 'B' is evaluated and discarded, then 'C', and so forth until 'F' is evaluated. Then 'F' becomes the value of the conditional. Since any non-zero value evaluated to true in a boolean context (and 'F' is non-zero), then the above expression will always be true.

like image 45
dbush Avatar answered Nov 20 '22 14:11

dbush


Templates allow us to express ourselves in this way:

if (range("A-F").contains(ch)) { ... }

It requires a little plumbing, which you can put in a library.

This actually compiles out to be incredibly efficient (at least on gcc and clang).

#include <cstdint>
#include <tuple>
#include <utility>
#include <iostream>

namespace detail {
    template<class T>
    struct range
    {
        constexpr range(T first, T last)
        : _begin(first), _end(last)
        {}

        constexpr T begin() const { return _begin; }
        constexpr T end() const { return _end; }

        template<class U>
        constexpr bool contains(const U& u) const
        {
            return _begin <= u and u <= _end;
        }

    private:
        T _begin;
        T _end;
    };

    template<class...Ranges>
    struct ranges
    {
        constexpr ranges(Ranges...ranges) : _ranges(std::make_tuple(ranges...)) {}

        template<class U>
        struct range_check
        {
            template<std::size_t I>
            bool contains_impl(std::integral_constant<std::size_t, I>,
                               const U& u,
                               const std::tuple<Ranges...>& ranges) const
            {
                return std::get<I>(ranges).contains(u)
                or contains_impl(std::integral_constant<std::size_t, I+1>(),u, ranges);
            }

            bool contains_impl(std::integral_constant<std::size_t, sizeof...(Ranges)>,
                               const U& u,
                               const std::tuple<Ranges...>& ranges) const
            {
                return false;
            }


            constexpr bool operator()(const U& u, std::tuple<Ranges...> const& ranges) const
            {
                return contains_impl(std::integral_constant<std::size_t, 0>(), u, ranges);
            }
        };

        template<class U>
        constexpr bool contains(const U& u) const
        {
            range_check<U> check {};
            return check(u, _ranges);
        }

        std::tuple<Ranges...> _ranges;
    };
}

template<class T>
constexpr auto range(T t) { return detail::range<T>(t, t); }

template<class T>
constexpr auto range(T from, T to) { return detail::range<T>(from, to); }

// this is the little trick which turns an ascii string into
// a range of characters at compile time. It's probably a bit naughty
// as I am not checking syntax. You could write "ApZ" and it would be
// interpreted as "A-Z".
constexpr auto range(const char (&s)[4])
{
    return range(s[0], s[2]);
}

template<class...Rs>
constexpr auto ranges(Rs...rs)
{
    return detail::ranges<Rs...>(rs...);
}

int main()
{
    std::cout << range(1,7).contains(5) << std::endl;
    std::cout << range("a-f").contains('b') << std::endl;

    auto az = ranges(range('a'), range('z'));
    std::cout << az.contains('a') << std::endl;
    std::cout << az.contains('z') << std::endl;
    std::cout << az.contains('p') << std::endl;

    auto rs = ranges(range("a-f"), range("p-z"));
    for (char ch = 'a' ; ch <= 'z' ; ++ch)
    {
        std::cout << ch << rs.contains(ch) << " ";
    }
    std::cout << std::endl;

    return 0;
}

expected output:

1
1
1
1
0
a1 b1 c1 d1 e1 f1 g0 h0 i0 j0 k0 l0 m0 n0 o0 p1 q1 r1 s1 t1 u1 v1 w1 x1 y1 z1 

For reference, here was my original answer:

template<class X, class Y>
bool in(X const& x, Y const& y)
{
    return x == y;
}

template<class X, class Y, class...Rest>
bool in(X const& x, Y const& y, Rest const&...rest)
{
    return in(x, y) or in(x, rest...);
}

int main()
{
    int ch = 6;
    std::cout << in(ch, 1,2,3,4,5,6,7) << std::endl;

    std::string foo = "foo";
    std::cout << in(foo, "bar", "foo", "baz") << std::endl;

    std::cout << in(foo, "bar", "baz") << std::endl;
}
like image 34
Richard Hodges Avatar answered Nov 20 '22 12:11

Richard Hodges