Related
I have objects that I need to hash with SHA256. The object has several fields as follows:
class Foo {
// some methods
protected:
std::array<32,int> x;
char y[32];
long z;
}
Is there a way I can directly access the bytes representing the 3 member variables in memory as I would a struct ? These hashes need to be computed as quickly as possible so I want to avoid malloc'ing a new set of bytes and copying to a heap allocated array. Or is the answer to simply embed a struct within the class?
It is critical that I get the exact binary representation of these variables so that the SHA256 comes out exactly the same given that the 3 variables are equal (so I can't have any extra padding bytes etc included going into the hash function)
Most Hash classes are able to take multiple regions before returning the hash, e.g. as in:
class Hash {
public:
void update(const void *data, size_t size) = 0;
std::vector<uint8_t> digest() = 0;
}
So your hash method could look like this:
std::vector<uint8_t> Foo::hash(Hash *hash) const {
hash->update(&x, sizeof(x));
hash->update(&y, sizeof(y));
hash->update(&z, sizeof(z));
return hash->digest();
}
You can solve this by making an iterator that knows the layout of your member variables. Make Foo::begin() and Foo::end() functions and you can even take advantage of range-based for loops.
If you can increment it and dereference it, you can use it any other place you're able to use a LegacyForwardIterator.
Add in comparison functions to get access to the common it = X.begin(); it != X.end(); ++it idiom.
Some downsides include: ugly library code, poor maintainability, and (in this current form) no regard for endianess.
The solution to the latter downside is left as an exercise to the reader.
#include <array>
#include <iostream>
class Foo {
friend class FooByteIter;
public:
FooByteIter begin() const;
FooByteIter end() const;
Foo(const std::array<int, 2>& x, const char (&y)[2], long z)
: x_{x}
, y_{y[0], y[1]}
, z_{z}
{}
protected:
std::array<int, 2> x_;
char y_[2];
long z_;
};
class FooByteIter {
public:
FooByteIter(const Foo& foo)
: ptr_{reinterpret_cast<const char*>(&(foo.x_))}
, x_end_{reinterpret_cast<const char*>(&(foo.x_)) + sizeof(foo.x_)}
, y_begin_{reinterpret_cast<const char*>(&(foo.y_))}
, y_end_{reinterpret_cast<const char*>(&(foo.y_)) + sizeof(foo.y_)}
, z_begin_{reinterpret_cast<const char*>(&(foo.z_))}
{}
static FooByteIter end(const Foo& foo) {
FooByteIter fbi{foo};
fbi.ptr_ = reinterpret_cast<const char*>(&foo.z_) + sizeof(foo.z_);
return fbi;
}
bool operator==(const FooByteIter& other) const { return ptr_ == other.ptr_; }
bool operator!=(const FooByteIter& other) const { return ! (*this == other); }
FooByteIter& operator++() {
ptr_++;
if (ptr_ == x_end_) {
ptr_ = y_begin_;
}
else if (ptr_ == y_end_) {
ptr_ = z_begin_;
}
return *this;
}
FooByteIter operator++(int) {
FooByteIter pre = *this;
(*this)++;
return pre;
}
char operator*() const {
return *ptr_;
}
private:
const char* ptr_;
const char* const x_end_;
const char* const y_begin_;
const char* const y_end_;
const char* const z_begin_;
};
FooByteIter Foo::begin() const {
return FooByteIter(*this);
}
FooByteIter Foo::end() const {
return FooByteIter::end(*this);
}
template <typename InputIt>
char checksum(InputIt first, InputIt last) {
char check = 0;
while (first != last) {
check += (*first);
++first;
}
return check;
}
int main() {
Foo f{{1, 2}, {3, 4}, 5};
for (const auto b : f) {
std::cout << (int)b << ' ';
}
std::cout << std::endl;
std::cout << "Checksum is: " << (int)checksum(f.begin(), f.end()) << std::endl;
}
You can generalize this further by making serialization functions for all data types you might care about, allowing serialization of classes that aren't plain-old-data types.
Warning
This code assumes that the underlying types being serialized have no internal padding, themselves. This answer works for this datatype because it is made of types which themselves do not pad. To make this work for datatypes that have datatypes that have padding, this method would need to be recursed all the way down.
Just cast a pointer to object to a pointer to char. You can iterate through the bytes by increment. Use sizeof(foo) to check overflow.
As long as you're able to make your class an aggregate, i.e. std::is_aggregate_v<T> == true, you can actually sort-of reflect the members of the structure.
This allows you to easily hash the members without actually having to name them. (also you don't have to remember updating your hash function every time you add a new member)
Step 1: Getting the number of members inside the aggregate
First we need to know how many members a given aggregate type has.
We can check this by (ab-)using aggregate initialization.
Example:
Given struct Foo { int i; int j; };:
Foo a{}; // ok
Foo b{{}}; // ok
Foo c{{}, {}}; // ok
Foo d{{}, {}, {}}; // error: too many initializers for 'Foo'
We can use this to get the number of members inside the struct, by trying to add more initializers until we get an error:
template<class T>
concept aggregate = std::is_aggregate_v<T>;
struct any_type {
template<class T>
operator T() {}
};
template<aggregate T>
consteval std::size_t count_members(auto ...members) {
if constexpr (requires { T{ {members}... }; } == false)
return sizeof...(members) - 1;
else
return count_members<T>(members..., any_type{});
}
Notice that i used {members}... instead of members....
This is because of arrays - a structure like struct Bar{int i[2];}; could be initialized with 2 elements, e.g. Bar b{1, 2}, so our function would have returned 2 for Bar if we had used members....
Step 2: Extracting the members
Now that we know how many members our structure has, we can use structured bindings to extract them.
Unfortunately there is no way in the current standard to create a structured binding expression with a variable amount of expressions, so we have to add a few extra lines of code for each additional member we want to support.
For this example i've only added a max of 4 members, but you can add as many as you like / need:
template<aggregate T>
constexpr auto tie_struct(T const& data) {
constexpr std::size_t fieldCount = count_members<T>();
if constexpr(fieldCount == 0) {
return std::tie();
} else if constexpr (fieldCount == 1) {
auto const& [m1] = data;
return std::tie(m1);
} else if constexpr (fieldCount == 2) {
auto const& [m1, m2] = data;
return std::tie(m1, m2);
} else if constexpr (fieldCount == 3) {
auto const& [m1, m2, m3] = data;
return std::tie(m1, m2, m3);
} else if constexpr (fieldCount == 4) {
auto const& [m1, m2, m3, m4] = data;
return std::tie(m1, m2, m3, m4);
} else {
static_assert(fieldCount!=fieldCount, "Too many fields for tie_struct! add more if statements!");
}
}
The fieldCount!=fieldCount in the static_assert is intentional, this prevents the compiler from evaluating it prematurely (it only complains if the else case is actually hit)
Now we have a function that can give us references to each member of an arbitrary aggregate.
Example:
struct Foo {int i; float j; std::string s; };
Foo f{1, 2, "miau"};
// tup is of type std::tuple<int const&, float const&, std::string const&>
auto tup = tie_struct(f);
// this will output "12miau"
std::cout << std::get<0>(tup) << std::get<1>(tup) << std::get<2>(tup) << std::endl;
Step 3: hashing the members
Now that we can convert any aggregate into a tuple of its members, hashing it shouldn't be a big problem.
You can basically hash the individual types like you want and then combine the individual hashes:
// for merging two hash values
std::size_t hash_combine(std::size_t h1, std::size_t h2)
{
return (h2 + 0x9e3779b9 + (h1<<6) + (h1>>2)) ^ h1;
}
// Handling primitives
template <class T, class = void>
struct is_std_hashable : std::false_type { };
template <class T>
struct is_std_hashable<T, std::void_t<decltype(std::declval<std::hash<T>>()(std::declval<T>()))>> : std::true_type { };
template <class T>
concept std_hashable = is_std_hashable<T>::value;
template<std_hashable T>
std::size_t hash(T value) {
return std::hash<T>{}(value);
}
// Handling tuples
template<class... Members>
std::size_t hash(std::tuple<Members...> const& tuple) {
return std::apply([](auto const&... members) {
std::size_t result = 0;
((result = hash_combine(result, hash(members))), ...);
return result;
}, tuple);
}
template<class T, std::size_t I>
using Arr = T[I];
// Handling arrays
template<class T, std::size_t I>
std::size_t hash(Arr<T, I> const& arr) {
std::size_t result = 0;
for(T const& elem : arr) {
std::size_t h = hash(elem);
result = hash_combine(result, h);
}
return result;
};
// Handling structs
template<aggregate T>
std::size_t hash(T const& agg) {
return hash(tie_struct(agg));
}
This allows you to hash basically any aggregate struct, even with arrays and nested structs:
struct Foo{ int i; double d; std::string s; };
struct Bar { Foo k[10]; float f; };
std::cout << hash(Foo{1, 1.2f, "miau"}) << std::endl;
std::cout << hash(Bar{}) << std::endl;
full example on godbolt
Footnotes
This only works with aggregates
No need to worry about padding because we access the members directly.
You have to add a few more ifs into tie_struct if you need more than 4 members
The provided hash() function doesn't handle all types - if you need e.g. std::array, std::pair, etc... you need to add overloads for those.
It's a lot of boilerplate code, but it's insanely powerful.
You can also use Boost.PFR for the aggregate-to-tuple part, if you are allowed to use boost
I am abstracting my problem now but I am in this kind of situation.
Let's say I have 2 enums that are in the same namespace.
namespace some_namespace {
enum class Something{
S,
O,
M,
E,
T,
H,
I,
N,
G
};
enum class Else{
E,
L,
S
};
}
Now let's create 2 structs which have as a static field some value of these enums.
struct SomeStruct{
static constexpr int enumValue = static_cast<int>(some_namespace::Something::T);
};
struct AnotherStruct {
static constexpr int enumValue = static_cast<int>(some_namespace::Something::O);
};
How can I check whether the enumValues of this 2 structs are from the same enum class or not? I think I shall give some values to my enum fields and use some bitwise operator on enum values to distinguish? Can someone help? thanks)
Using just the static_casted enumValue it is not possible to distinguish the originating enum(as pointed out by #Sam). What you can instead do is introduce a new field that stores the underlying enum class type and leverage that to make the distinction.
enum class E1{ One, Two };
enum class E2{Three, Four };
struct SomeStruct{
static constexpr int enumValue = static_cast<int>(E1::One);
using UnderlyingT = E1;
};
struct AnotherStruct {
static constexpr int enumValue = static_cast<int>(E2::Three);
using UnderlyingT = E2;
};
static_assert(!std::is_same_v<SomeStruct::UnderlyingT, AnotherStruct::UnderlyingT>);
Code Link
As already indicated in the comments, you don't want to convert your enum to an int if you still want to know the type.
There might be some reasons to keep the type and the scoping while wanting to implicit conversion to the underlying type.
What you could do is to wrap the enum into a class that converts to both the underlying type AND the actual enum.
#include <type_traits>
template<typename TEnum>
class ImplicitEnumConverter final
{
public:
TEnum e;
using TUnderlyingType = std::underlying_type_t<TEnum>;
constexpr ImplicitEnumConverter(TEnum e) : e{e} {}
constexpr operator TUnderlyingType() const noexcept
{
return static_cast<TUnderlyingType>(e);
}
constexpr operator TEnum() const noexcept
{
return e;
}
};
struct SomeStruct{
static constexpr auto enumValue = ImplicitEnumConverter{some_namespace::Something::T};
};
Code at compiler-explorer
This way, you are able to keep all the type-safe information where this is relevant AND you are able to convert it implicitly to an int.
The answer was simpler than I thought.
So I declare enum like this.
namespace some_namespace {
enum class Something{
S = 0,
O = 2,
M = 4,
E = 6,
T = 8,
H = 10,
I = 12,
N = 14,
G = 16
};
enum class Else{
E = 1,
L = 3,
S = 5
};
}
And then this leads me to check whether two integer values have the same parity or not . Thus
if((SomeStruct::enum_value + AnotherStruct::enum_value) % 2 == 0) {
// Same enum
} else {
// not same enum.
}
I have a requirement where there's a enum and there are template functions defined for all the possible enum combinations upto length l.
Say the enum is
Enum typenum {A, B, C}
And all these template functions are defined and available at runtime (i.e, compiler creates these functions at the compile time)
Alpha<A>::f()
Alpha<B>::f()
Alpha<C>::f()
Alpha<A,A>::f()
Alpha<A,B>::f()
Alpha<A,C>::f()
Alpha<B,A>::f()
Alpha<B,B>::f()
Alpha<B,C>::f()
Alpha<C,A>::f()
Alpha<C,B>::f()
Alpha<C,C>::f()
and combination of 3 enums, 4 enums...
Now I've to choose the right function as per an input vector
void f(vector<enum> eVec){
Alpha::f<eVec[0], eVec[1],... eVec[eVec.size() - 1]>() // <-------
How do I do this? One way to do this would be to define for every size. Eg:
if(eVec.size() == 1)
Alpha<eVec[0]>::f()
else if(eVec.size() == 2)
Alpha<eVec[0], eVec[1]>::f()
This won't scale though. Is there any elegant, scalable way of doing this.
And all these template functions are defined and available at runtime (i.e, compiler creates these functions at the compile time)
Are you sure it's a good idea?
Because, if you want select run-time template values, you have to implement, compile time, all possible Alpha<typeNumsValues...>::f() combinations.
That is impossible if you don't impose a length limit for the variadic list, but is very computationally expensive also when there is relatively low limit.
Anyway... suppose you have an enum as follows
enum typeEnum { A, B, C };
and variadic template Alpha class, with typeEnum template values and a static method f() as follows
template <typeEnum ...>
struct Alpha
{ static void f () { /* do something */ } };
your f() can call a variadic f_helper()
void f (std::vector<typeEnum> const & eVec)
{ f_helper<>(eVec, 0u); }
realized as follows
template <typeEnum ...>
void f_helper (std::vector<typeEnum> const &, ...)
{ }
template <typeEnum ... Tes>
typename std::enable_if<(sizeof...(Tes) < 6u)>::type
f_helper (std::vector<typeEnum> const & eVec, std::size_t index)
{
if ( index < eVec.size() )
switch ( eVec[index++] )
{
case A: f_helper<Tes..., A>(eVec, index); break;
case B: f_helper<Tes..., B>(eVec, index); break;
case C: f_helper<Tes..., C>(eVec, index); break;
}
else
Alpha<Tes...>::f();
}
Observe that I've posed a very low limit (5, sizeof...(Tes) < 6u) to the length of the variadic list because the number of developed Alpha grows exponentially.
Also observe that I've added a do-nothing version of f_helper(); it's necessary because the less-than-6-length recursively call f_helper(), can call it with a variadic list of 6 enums that must be managed in some way.
The following is a full compiling example
#include <vector>
enum typeEnum { A, B, C };
template <typeEnum ...>
struct Alpha
{ static void f () { } };
template <typeEnum ...>
void f_helper (std::vector<typeEnum> const &, ...)
{ }
template <typeEnum ... Tes>
typename std::enable_if<(sizeof...(Tes) < 6u)>::type
f_helper (std::vector<typeEnum> const & eVec, std::size_t index)
{
if ( index < eVec.size() )
switch ( eVec[index++] )
{
case A: f_helper<Tes..., A>(eVec, index); break;
case B: f_helper<Tes..., B>(eVec, index); break;
case C: f_helper<Tes..., C>(eVec, index); break;
}
else
Alpha<Tes...>::f();
}
void f (std::vector<typeEnum> const & eVec)
{ f_helper<>(eVec, 0u); }
int main ()
{
f({A, B, C, A});
}
If you want specific functions from runtime variable then use a map instead. Template is the wrong tool for the job as you have to write lot just to transform variable into constants.
Assuming that you enum have a default value None and that you have up to say 5 arguments, you can define a map like this:
enum MyEnum { None = 0, A, B, C, D... };
using MyKey = std::tuple<MyEnum, MyEnum, MyEnum, MyEnum, MyEnum>;
using MyFunction = std::function<void()>;
Then you have somewhere a map of function (a singleton)
std::map<MyKey, MyFunction> myMap;
A utility function could be helpful to create a key from a variable number of arguments:
MyKey MakeKey(MyEnum e1, MyEnum e2 = None, MyEnum e3 = None, MyEnum e4 = None, MyEnum e5 = None)
{
return std::make_tuple(e1, e2, e3, e4, e5);
}
myMap.emplace(MakeKey(A, B), [](){ /* some code */ });
MyEnum AtOrDefault(const vector<enum> &eVec, int index)
{
return index < eVec.size() ? eVec[index] : None;
}
Then assuming you want to call the appropriate function from a vector, you could do:
void f(const vector<enum> &eVec)
{
if (eVec.size() > 5) throw some_exception;
MyKey key = std::make_typle(
AtOrDefault(eVec, 0),
AtOrDefault(eVec, 1),
AtOrDefault(eVec, 2),
AtOrDefault(eVec, 3),
AtOrDefault(eVec, 4));
auto &fn = myMap[key];
fn();
}
You could also use the idea of calculating a value assuming that you know the maximum number of elements in the enum. You could then create a CombinedEnumType:
enum CombinedEnumType : uint32_t { };
And defined a function
CombinedEnumType MakeCombinedEnumType(MyEnum e1, … MyEnum e5 = None)
{
const int MyEnumEcount = 5;
MyEnum a[] = { e1, e2, e3, e4, e5 };
uint32_t result = 0;
for (auto & item : a) { result *= MyEnumEcount; result += item; }
return static_cast<CombinedEnumType>(result);
}
This code is just for ideas. In real production code, you have to use constants, proper variable names, validate that a function exist for a given combination…
Answering my own question. I've realized that this approach won't work. Because when we write something like
Alpha<eVec[0]>::f()
Compiler throws an error - " error: expression must have a constant value"
So the only alternative left is
if(eVec.size() == 1){
switch(eVec[0]){
case A:
Alpha<A>::f()
.....
This is because the compiler needs to know all possible parameter types using which templates would be called during compile time.
I came across these lines of code:
template< int I > struct arg
{
arg()
{
}
template< class T > arg( T const & /* t */ )
{
// static assert I == is_placeholder<T>::value
typedef char T_must_be_placeholder[ I == is_placeholder<T>::value? 1: -1 ];
}
};
template< class T > struct is_placeholder
{
enum _vt { value = 0 };
};
What could be the reason the struct is_placeholder is templated while typename T is not used anywhere inside?
Why T_must_be_placeholder is defined in such a way so that it can have invalid size -1. To realise this, I called arg<1>(1) and it gave error: size of array is negative as expected. Is it some sort of sanity-check technique? Why doesn't the compiler report this issue if the arg<1>(1) call is not made?
While
int i = 0;
char a[i == 1 ? 1 : -1]; //No error
If sanity check works for 1st example, then how does it fail for second one?
is_placeholder is a template so that it can be specialized for different T. Say I create a struct example and want is_placeholder<example>::value to be 42. I would do this:
struct example {};
template< class T > struct is_placeholder
{
enum _vt { value = 0 };
};
template<>
struct is_placeholder<example>
{
enum _vt { value = 42 };
};
template< int I > struct arg
{
arg()
{
}
template< class T > arg( T const & /* t */ )
{
// static assert I == is_placeholder<T>::value
typedef char T_must_be_placeholder[ I == is_placeholder<T>::value ? 1: -1 ];
}
};
void test()
{
auto x = arg<42>(example()); // compiles
auto y = arg<43>(example()); // assertion
return 0;
}
In this case T_must_be_placeholder checks if is_placeholder<example>::value (which we just specialized to be 42) is the same as I. So if I is 42 it compiles, but if I is anything else, it won't.
Attempting to create an array with negative size is a way of deliberately preventing the code from compiling, so yes, this is probably a sanity check (static assertion, as it says in the comment).
Is there a generic way to cast int to enum in C++?
If int falls in range of an enum it should return an enum value, otherwise throw an exception. Is there a way to write it generically? More than one enum type should be supported.
Background: I have an external enum type and no control over the source code. I'd like to store this value in a database and retrieve it.
The obvious thing is to annotate your enum:
// generic code
#include <algorithm>
template <typename T>
struct enum_traits {};
template<typename T, size_t N>
T *endof(T (&ra)[N]) {
return ra + N;
}
template<typename T, typename ValType>
T check(ValType v) {
typedef enum_traits<T> traits;
const T *first = traits::enumerators;
const T *last = endof(traits::enumerators);
if (traits::sorted) { // probably premature optimization
if (std::binary_search(first, last, v)) return T(v);
} else if (std::find(first, last, v) != last) {
return T(v);
}
throw "exception";
}
// "enhanced" definition of enum
enum e {
x = 1,
y = 4,
z = 10,
};
template<>
struct enum_traits<e> {
static const e enumerators[];
static const bool sorted = true;
};
// must appear in only one TU,
// so if the above is in a header then it will need the array size
const e enum_traits<e>::enumerators[] = {x, y, z};
// usage
int main() {
e good = check<e>(1);
e bad = check<e>(2);
}
You need the array to be kept up to date with e, which is a nuisance if you're not the author of e. As Sjoerd says, it can probably be automated with any decent build system.
In any case, you're up against 7.2/6:
For an enumeration where emin is the
smallest enumerator and emax is the
largest, the values of the enumeration
are the values of the underlying type
in the range bmin to bmax, where bmin
and bmax are, respectively, the
smallest and largest values of the
smallest bit-field that can store emin
and emax. It is possible to define an
enumeration that has values not
defined by any of its enumerators.
So if you aren't the author of e, you may or may not have a guarantee that valid values of e actually appear in its definition.
Ugly.
enum MyEnum { one = 1, two = 2 };
MyEnum to_enum(int n)
{
switch( n )
{
case 1 : return one;
case 2 : return two;
}
throw something();
}
Now for the real question. Why do you need this? The code is ugly, not easy to write (*?) and not easy to maintain, and not easy to incorporate in to your code. The code it telling you that it's wrong. Why fight it?
EDIT:
Alternatively, given that enums are integral types in C++:
enum my_enum_val = static_cast<MyEnum>(my_int_val);
but this is even uglier that above, much more prone to errors, and it won't throw as you desire.
If, as you describe, the values are in a database, why not write a code generator that reads this table and creates a .h and .cpp file with both the enum and a to_enum(int) function?
Advantages:
Easy to add a to_string(my_enum) function.
Little maintenance required
Database and code are in synch
No- there's no introspection in C++, nor is there any built in "domain check" facility.
What do you think about this one?
#include <iostream>
#include <stdexcept>
#include <set>
#include <string>
using namespace std;
template<typename T>
class Enum
{
public:
static void insert(int value)
{
_set.insert(value);
}
static T buildFrom(int value)
{
if (_set.find(value) != _set.end()) {
T retval;
retval.assign(value);
return retval;
}
throw std::runtime_error("unexpected value");
}
operator int() const { return _value; }
private:
void assign(int value)
{
_value = value;
}
int _value;
static std::set<int> _set;
};
template<typename T> std::set<int> Enum<T>::_set;
class Apples: public Enum<Apples> {};
class Oranges: public Enum<Oranges> {};
class Proxy
{
public:
Proxy(int value): _value(value) {}
template<typename T>
operator T()
{
T theEnum;
return theEnum.buildFrom(_value);
}
int _value;
};
Proxy convert(int value)
{
return Proxy(value);
}
int main()
{
Apples::insert(4);
Apples::insert(8);
Apples a = convert(4); // works
std::cout << a << std::endl; // prints 4
try {
Apples b = convert(9); // throws
}
catch (std::exception const& e) {
std::cout << e.what() << std::endl; // prints "unexpected value"
}
try {
Oranges b = convert(4); // also throws
}
catch (std::exception const& e) {
std::cout << e.what() << std::endl; // prints "unexpected value"
}
}
You could then use code I posted here to switch on values.
You should not want something like what you describe to exist, I fear there are problems in your code design.
Also, you assume that enums come in a range, but that's not always the case:
enum Flags { one = 1, two = 2, four = 4, eigh = 8, big = 2000000000 };
This is not in a range: even if it was possible, are you supposed to check every integer from 0 to 2^n to see if they match some enum's value?
If you are prepared to list your enum values as template parameters you can do this in C++ 11 with varadic templates. You can look at this as a good thing, allowing you to accept subsets of the valid enum values in different contexts; often useful when parsing codes from external sources.
Perhaps not quite as generic as you'd like, but the checking code itself is generalised, you just need to specify the set of values. This approach handles gaps, arbitrary values, etc.
template<typename EnumType, EnumType... Values> class EnumCheck;
template<typename EnumType> class EnumCheck<EnumType>
{
public:
template<typename IntType>
static bool constexpr is_value(IntType) { return false; }
};
template<typename EnumType, EnumType V, EnumType... Next>
class EnumCheck<EnumType, V, Next...> : private EnumCheck<EnumType, Next...>
{
using super = EnumCheck<EnumType, Next...>;
public:
template<typename IntType>
static bool constexpr is_value(IntType v)
{
return v == static_cast<typename std::underlying_type<EnumType>::type>(V) || super::is_value(v);
}
EnumType convert(IntType v)
{
if (!is_value(v)) throw std::runtime_error("Enum value out of range");
return static_cast<EnumType>(v);
};
enum class Test {
A = 1,
C = 3,
E = 5
};
using TestCheck = EnumCheck<Test, Test::A, Test::C, Test::E>;
void check_value(int v)
{
if (TestCheck::is_value(v))
printf("%d is OK\n", v);
else
printf("%d is not OK\n", v);
}
int main()
{
for (int i = 0; i < 10; ++i)
check_value(i);
}
C++0x alternative to the "ugly" version, allows for multiple enums. Uses initializer lists rather than switches, a bit cleaner IMO. Unfortunately, this doesn't work around the need to hard-code the enum values.
#include <cassert> // assert
namespace // unnamed namespace
{
enum class e1 { value_1 = 1, value_2 = 2 };
enum class e2 { value_3 = 3, value_4 = 4 };
template <typename T>
int valid_enum( const int val, const T& vec )
{
for ( const auto item : vec )
if ( static_cast<int>( item ) == val ) return val;
throw std::exception( "invalid enum value!" ); // throw something useful here
} // valid_enum
} // ns
int main()
{
// generate list of valid values
const auto e1_valid_values = { e1::value_1, e1::value_2 };
const auto e2_valid_values = { e2::value_3, e2::value_4 };
auto result1 = static_cast<e1>( valid_enum( 1, e1_valid_values ) );
assert( result1 == e1::value_1 );
auto result2 = static_cast<e2>( valid_enum( 3, e2_valid_values ) );
assert( result2 == e2::value_3 );
// test throw on invalid value
try
{
auto result3 = static_cast<e1>( valid_enum( 9999999, e1_valid_values ) );
assert( false );
}
catch ( ... )
{
assert( true );
}
}