Reputation: 224069
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
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
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
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