sbi
sbi

Reputation: 224069

Compile-time list of enum values

In modern C++ (GCC 5.1.0, so C++14, I guess), what is the quickest way to pass, at compile-time, a list of values of an enum, and then, at runtime, check which values are in there?

enum foobar { foo, bar, baz };

template<????>
void f() {
  if( contains<????, foo>() )
    std::cout << "foo!";
  if( contains<????, bar>() )
    std::cout << "bar!";
  if( contains<????, baz>() )
    std::cout << "baz!";
}

f<foo,bar>();

Note: This is for unit tests, so speed etc. are mainly irrelevant and the main objective is for it to be decipherable by someone who is not familiar with the code.

Upvotes: 5

Views: 5387

Answers (3)

max66
max66

Reputation: 66200

I don't know if you're still interested but... to answer your original question... if you switch the order of template argument for contain... I mean

if( contains<foo, ????>() )

instead of

if( contains<????, foo>() )

your contains (renamed containsFB in my following examples) can be, starting from C++14, a simple constexpr function

template <foobar F0, foobar ... Fs>
constexpr bool containsFB ()
 {
   using unused = bool[];

   bool ret { false };

   (void)unused { false, ret |= F0 == Fs ... };

   return ret;
 }

Starting from C++17 you can also use template folding and constantFB can be written as a type traits that inherit from std::true_type or std::false_type.

template <foobar F0, foobar ... Fs>
struct containsFB : public std::integral_constant<bool, ((F0 == Fs) || ...)>
 { };

The following is a full working C++14 example

#include <iostream>
#include <utility>

enum foobar { foo, bar, baz };


template <foobar F0, foobar ... Fs>
constexpr bool containsFB ()
 {
   using unused = bool[];

   bool ret { false };

   (void)unused { false, ret |= F0 == Fs ... };

   return ret;
 }

/* C++17 alternative
template <foobar F0, foobar ... Fs>
struct containsFB : public std::integral_constant<bool, ((F0 == Fs) || ...)>
 { };
 */

template <foobar ... Fs>
void f ()
 {
   if( containsFB<foo, Fs...>() ) std::cout << "foo!";
   if( containsFB<bar, Fs...>() ) std::cout << "bar!";
   if( containsFB<baz, Fs...>() ) std::cout << "baz!";
 }

int main ()
 {
   f<foo,bar>();
 }

If you want to make containsFB (function or struct) a little more generic, you can templatize the type of the values; so

template <typename T, T F0, T ... Fs>

instead of

template <foobar F0, foobar ... Fs>

Upvotes: 2

Mikhail
Mikhail

Reputation: 8028

Here is one proposal

#include <initializer_list>// pulled in by a lot of stuff

enum class options  { foo,bar,baz };
void test_func(options opt)
{

}
int main()
{
    auto test_vector = { options::foo, options::bar  };
    for (auto option : test_vector)
    {
        test_func(option);
    }
    return 0;
}

Slightly more complicated to check that the supplied test vectors contain what they should contain:

#include <initializer_list>
#include <algorithm>
#include <stdexcept>

enum class options { foo, bar, baz, wuz };
void test_func(options)
{

}

template<typename AT, typename BT>
void assert_test_vectors(AT a, BT check_these_items)
{
    for (auto item : check_these_items)
    {
        auto test = std::find(a.begin(), a.end(), item) != a.end();
        if (!test)
        {
            return throw std::runtime_error("You suck");
        }
    }
}

template<typename T>
void run_tests(T tests)
{
    const auto better_have_these = { options::foo, options::bar };
    assert_test_vectors(tests, better_have_these);
    for (auto test : tests)
    {
        test_func(test);
    }
}

int main()
{
    const auto test_vectors = { options::foo, options::wuz };
    run_tests(test_vectors);
    return 0;
}

Upvotes: 3

login_not_failed
login_not_failed

Reputation: 1251

Variadic option. It prints «foo!bar!baz!foo!», as expected.

enum class foobar { foo, bar, baz };

void test() {}

template< typename F, typename... Fs >
void test( F Foo, Fs const&... args )
{
    switch( Foo )
    {
    case foobar::foo:
        std::cout << "foo!";
        break;
    case foobar::bar:
        std::cout << "bar!";
        break;
    case foobar::baz:
        std::cout << "baz!";
        break;
    }
    test( args... );
}

int main() {
    test( foobar::foo, foobar::bar, foobar::baz, foobar::foo );
}

Upvotes: 1

Related Questions