Let\'s say I have a bunch of well-known values, like this (but const char *
is just an example, it could be more complicated):
const char *A = \"A\"
You could factor your current best solution into a template:
template<class A, class B, size_t n>
inline bool is_in(const A &a, B (&bs)[n]) {
return std::find(bs, bs + n, a) != bs + n;
}
which you can use like
X items[] = { A, C, E, G };
if (is_in(some_complicated_expression_with_ugly_return_type, items))
…
If not switch, maybe something like this, I didn't use it, but may be a draft to something working?
template <class ReturnType>
bool average(ReturnType expression, int count, ...)
{
va_list ap;
va_start(ap, count); //Requires the last fixed parameter (to get the address)
for(int j=0; j<count; j++)
if(expression==va_arg(ap, ReturnType))
return true;
return false
va_end(ap);
}
If you have C++11:
auto res = some_complicated_expression_with_ugly_return_type;
if (res == A
|| res == C
|| res == E
|| res == G) {
}
if not, you can still eliminate the type declaration by using a template function:
template <class T>
bool matches(T t) {
return t == A || t == C || t == E || t == G;
}
if (matches(some_complicated_expression_with_ugly_return_type)) {
}
Expressions of the type
if (some_complicated_expression_with_ugly_return_type == A ||
some_complicated_expression_with_ugly_return_type == C ||
some_complicated_expression_with_ugly_return_type == E ||
some_complicated_expression_with_ugly_return_type == G)
{
...
}
are quite common in code (well, a pre-computed expression is anyway). I think the best you can do for readability is pre-compute the expression and keep it as is.
ugly_return_type x = some_complicated_expression_with_ugly_return_type;
if (x == A ||
x == C ||
x == E ||
x == G)
{
...
}
Developers are used to this type of syntax. This makes it a whole lot easier to understand when someone else is reading your code
It also expresses what you want perfectly. There's a reason this type of syntax is so widely used in existing code - because other alternatives are worse for readability.
Of course, you could wrap the condition in a function, but only if it's reusable and it logically makes sense (besides the point IMO).
C++11:
template<typename T1, typename T2>
bool equalsOneOf (T1&& value, T2&& candidate) {
return std::forward<T1>(value) == std::forward<T2>(candidate);
}
template<typename T1, typename T2, typename ...T>
bool equalsOneOf (T1&& value, T2&& firstCandidate, T&&...otherCandidates) {
return (std::forward<T1>(value) == std::forward<T2>(firstCandidate))
|| equalsOneOf (std::forward<T1> (value), std::forward<T>(otherCandidates)...);
}
if (equalsOneOf (complexExpression, A, D, E)) { ... }
C++03:
template<typename T, typename C>
bool equalsOneOf (const T& value, const C& c) { return value == c; }
template<typename T, typename C1, typename C2>
bool equalsOneOf (const T& value, const C1& c1, const C2& c2) {
return (value == c2) || equalsOneOf (value, c1);
}
template<typename T, typename C1, typename C2, typename C3>
bool equalsOneOf (const T& value, const C1& c1, const C2& c2, const C3& c3) {
return (value == c3) || equalsOneOf (value, c1, c2);
}
template<typename T, typename C1, typename C2, typename C3, typename C4>
bool equalsOneOf (const T& value, const C1& c1, const C2& c2, const C3& c3, const C4& c4) {
return (value == c4) || equalsOneOf (value, c1, c2, c3);
}
template<typename T, typename C1, typename C2, typename C3, typename C4, typename C5>
bool equalsOneOf (const T& value, const C1& c1, const C2& c2, const C3& c3, const C4& c4, const C5& c5) {
return (value == c5) || equalsOneOf (value, c1, c2, c3, c4);
}
// and so on, as many as you need
You could use a switch
:
switch (some_complicated_expression_with_ugly_return_type) {
case A: case C: case E: case G:
// do something
default:
// no-op
}
This only works with integer and enum types, note.
For more complex types, you can use C++11's auto
, or for C++03, boost's BOOST_AUTO:
auto tmp = some_complicated_expression_with_ugly_return_type;
// or
BOOST_AUTO(tmp, some_complicated_expression_with_ugly_return_type);
if (tmp == A || tmp == C || tmp == E || tmp == G) {
// ...
}