4

I am using C++ for particle physics and I find myself commondly writing lines like this:

bool isItCorrect(int i){
 if(i==11 || i == 62 || i==-11 || i == 11002 || i==22002) return True
 else return false;
}

What is the easiest way for me to make this shorter in C++. In python I could do:

def isItCorrect( i ):
    if (i is in [11,62,-11,11002,22002]) return True
    else return False
1
  • There are 5 answers! Commented Mar 17, 2018 at 4:37

5 Answers 5

5

You can use variadic templates in C++ 11 and define:

template <typename T, typename T1>
bool isItCorrect(T t, T1 t1) {
  return t == t1;
}
template <typename T, typename T1, typename... T2>
bool isItCorrect(T t, T1 t1, T2... t2) {
  return t == t1 || isItCorrect(t, t2...);
}

and use:

bool isItCorrect(int i) {
    return isItCorrect(i, 62, -11, 110022, 22002);
}
1
  • This differs from what OP's code does, and would requiring changing all the call sites. Commented Mar 14, 2018 at 5:01
4

It may not be the "simplest", but I generally just use a switch, eg:

bool isItCorrect(int i)
{
    switch (i)
    {
        case 11:
        case 62:
        case -11:
        case 11002:
        case 22002:
            return true;
        default:
            return false;
    }
}
2

std::set provides a count function which works much like the is in from python.

bool isItCorrect(int i) {
  return std::set<int>({11, 62, -11, 110022, 22002}).count(i);
}
6
  • However it probably has pretty different performance characteristics than the collection of if conditions, depending on how set stores it's data. Might be relevant for physics code. Commented Mar 14, 2018 at 3:45
  • 1
    Yeah. A hash map of some kind would probably make more sense. But the general structure here wouldn't change. It'd also help if the hash map could be statically allocated.
    – Bill Lynch
    Commented Mar 14, 2018 at 3:47
  • The most efficient approach is to use std::binary_search over ordered array. However, the boilerplate looks rather ugly. :-)
    – oakad
    Commented Mar 14, 2018 at 3:50
  • I guess some kind of variadic macro or template function which expands to a switch or if statements which get optimized by the compiler might be the best approach. However I'm too stupid to write that. Commented Mar 14, 2018 at 3:51
  • This looks pretty bad. You construct a new std::set every time in order to do a fast lookup. But the price you pay for the fast lookup is a slow construction. And with one lookup per constructed set, that is a bad tradeoff.
    – MSalters
    Commented Mar 14, 2018 at 7:46
1

You might be able to use something like this: You can wrap a std vector into a struct and then write a function that takes the value to check against the field. This will work as is if you know all the elements of the field a head of time.

#include <iostream>
#include <vector>

struct field {
    std::vector<int> data { 11, 62, -11, 11002, 22002 };
};

bool isItCorrect( int i, const field& f ) {
    for ( auto& d : f.data ) 
        if ( d == i ) return true;
    return false;
}

int main() {
    field f;

    std::cout << std::boolalpha;
    std::cout << isItCorrect( 2, f ) << std::endl;
    std::cout << isItCorrect( 62, f ) << std::endl;

    std::cout << "\nPress any key to quit.";
    std::cin.get();
    return 0;
}

Output

false
true

Press any key to quit.

If you are working with arbitrary types, and you don't know how many elements to check against you can template it as such using variadic function templates and parameter packs:

fieldT.h

#ifndef FIELD_T_H
#define FIELD_T_H

#include <vector>
#include <type_traits>

template<class T>
class fieldT {
private:
    std::vector<T> _data;

public:
    explicit fieldT( std::vector<T>& data ) : _data( data ) {}

    template<class... Args>
    fieldT( Args&&... args ) : 
      _data { std::forward<Args>( args )... } {}

    std::vector<T> data() {
        return _data;
    }
};

template<class T>
bool isItCorrectT( T t, fieldT<T>& f );

template<class T, class... Args>
bool isItCorrectT( T t, Args... args );

#include "fieldT.inl"  

#endif // FIELD_T_H 

fieldT.inl

template<class T>
bool isItCorrectT( T t, fieldT<T>& f ) {
    for ( auto& d : f.data() )
        if ( d == t ) return true;
    return false;
}

template<class T, class... Args>
bool isItCorrectT( T t, Args... args ) {
    fieldT<T> f( args... );
    for ( auto& d : f.data() )
        if ( d == t ) return true;
    return false;
} 

Here I turned the class into a class template and then I also template the function isItCorrect only I created 2 overloads. One that will accept the value to check against and the fieldT<T> object where the 2nd overload will accept the value to check against and any arbitrary amount of arguments for that type.


Here is a demo of using the above class template using both overloaded methods for each constructor of the class:

#include <iostream>
#include <vector>

#include "fieldT.h"

int main() {
    std::cout << std::boolalpha;

    // First create a vector of floats and instantiate a field<float> passing in that vector.
    std::vector<float> floats { 1.0f, 1.1f, 1.2f, 1.3f, 1.4f };
    fieldT<float> ff( floats );

    // checking for both true and false case using the fieldT<T> above
    std::cout << isItCorrectT( 2.0f, ff ) << std::endl;
    std::cout << isItCorrectT( 1.2f, ff ) << std::endl;

    // Or we can pass the values directly into the function as the
    // overloaded version will take all but the first parameter
    // and create a field<T> object populating its internal vector.
    std::cout << isItCorrectT( 1.5f, 2.9f, 7.5f, 3.4f ) << std::endl;
    std::cout << isItCorrectT( 1.5f, 2.9f, -3.7f, 1.5f, 8.9f ) << std::endl;

    std::cout << "\nPress any key to quit.";
    std::cin.get();
    return 0;
} 

Output

false
true
false
true

Press any key to quit.

What is real nice about this is that it remains generic and portable and can be used for any arbitrary type <T> with any amount of arguments of <T>.

I did not add in any tests to make sure that the values after the first are of type <T> but one should be able to easily assert that.

As you can see from within the main. The actual function call is very clean, easy to read and use. Even the class and the functions that operate on it are very clean and readable, easy to follow and understand and most important they are generic and portable, reusable.

1
bool isItCorrect(int i)
{
    set<int> correctNums { 11, 62, -11, 11002, 22002 };

    return correctNums.find(i) != correctNums.end();
}

I believe it's the simplest way.

Not the answer you're looking for? Browse other questions tagged or ask your own question.