I\'ve come to a pattern when writing enums in C++. It is like this:
class Player
{
public:
class State
{
public:
typedef enum
{
No there isn't, and if you need this, you probably shouldn't be using an enum
in the first place.
In your particular case, what's a use case where you'd ever want to call Count
?
AFAIK there's no automatic compiler-supported keyword to get the total amount of elements in enum
. OTOH this usually doesn't make sense: you may have multiple values with the same value, as long as the values don't have to have consequent values (i.e. you may assign the values manually, rather than rely on automatic numbering).
One common practice is to declare the enum
in the following way:
typedef enum
{
Stopped,
Playing,
Paused,
count
}PossibleValues;
This way, if count
is always defined last - it will give you the count of the enum elements, assuming the numbering starts from 0 and is consequent.
Reposting an answer from a similar question (What is the best way for non sequencial integer c++ enums) because it was kind of relevant to an otherwise pretty much unanswered question.
A pattern you could use to get what you want is to use an std::initializer_list to store all the values of your enum.
namespace PossibleValues
{
enum Type
{
ZERO= 0,
PLUS180= 180,
PLUS90= 90,
MINUS90= -90
};
constexpr auto Values = {ZERO, PLUS180, PLUS90, MINUS90};
size_t Count() { return Values.size(); }
Type Default() { return *begin(Values); }
}
This also has the benefit of being able to iterate of the values of the enum even if they don't have linear values.
And I think you could possibly generate both the enum, the initializer list and the functions from a single macro with a variadic macro, though in the best of worlds this kind of thing should be in the standard.
Edit: When I used PossibleValues as an enum or used a struct for PossibleValues, my compiler would complain about incomplete type. Using a namespace for an enum is a bit unusual, but it works fine.
The solution stackoverflow.com/a/60216003/12894563 can be improved. We can save enums expressions in a static vector and iterate, obtain min/max and etc
Usage:
#include <type_traits>
#include <algorithm>
#include <vector>
#include <iostream>
#define make_enum(Name, Type, ...) \
struct Name { \
enum : Type { \
__VA_ARGS__ \
}; \
static auto count() { return values.size(); } \
\
static inline const std::vector<Type> values = [] { \
static Type __VA_ARGS__; return std::vector<Type>({__VA_ARGS__}); \
}(); \
static Type min() \
{ \
static const Type result = *std::min_element(values.begin(), values.end()); \
return result; \
} \
static Type max() \
{ \
static const Type result = *std::max_element(values.begin(), values.end()); \
return result; \
} \
}
make_enum(FakeEnum, int, A = 1, B = 0, C = 2, D);
int main(int argc, char *argv[])
{
std::cout << FakeEnum::A << std::endl
<< FakeEnum::min() << std::endl
<< FakeEnum::max() << std::endl
<< FakeEnum::count() << std::endl;
return 0;
}
Must the type of PossibleValues be an enum? If you just need something that behaves like an enum, you can do the following:
#include <iostream>
#include <functional>
#include <set>
template <typename Representation, typename T>
class Iterable_Strong_Enum
{
private:
struct T_Ptr_Less : public std::binary_function<T const *, T const *, bool>
{
bool operator()(T const * x, T const * y) const
{
return x->get_representation() < y->get_representation();
}
};
public:
typedef std::set<T const *, T_Ptr_Less> instances_list;
typedef typename instances_list::const_iterator const_iterator;
Representation const & get_representation() const { return _value; }
static Representation const & min() { return (*_instances.begin())->_value; }
static Representation const & max() { return (*_instances.rbegin())->_value; }
static T const * corresponding_enum(Representation const & value)
{
const_iterator it = std::find_if(_instances.begin(), _instances.end(), [&](T const * e) -> bool
{
return e->get_representation() == value;
});
if (it != _instances.end())
{
return *it;
}
else
{
return nullptr;
}
}
bool operator==(T const & other) const { return _value == other._value; }
bool operator!=(T const & other) const { return _value != other._value; }
bool operator< (T const & other) const { return _value < other._value; }
bool operator<=(T const & other) const { return _value <= other._value; }
bool operator> (T const & other) const { return _value > other._value; }
bool operator>=(T const & other) const { return _value >= other._value; }
static bool is_valid_value(Representation const & value) { return corresponding_enum(value) != nullptr; }
static typename instances_list::size_type size() { return _instances.size(); }
static const_iterator begin() { return _instances.begin(); }
static const_iterator end() { return _instances.end(); }
protected:
explicit Iterable_Strong_Enum(Representation const & value);
private:
Representation _value;
static instances_list _instances;
};
template <typename Representation, typename T>
Iterable_Strong_Enum<Representation, T>::Iterable_Strong_Enum(Representation const & value)
: _value(value)
{
_instances.insert(static_cast<T const *>(this));
}
class PossibleValues : public Iterable_Strong_Enum<int, PossibleValues>
{
public:
static const PossibleValues Stopped;
static const PossibleValues Playing;
static const PossibleValues Pause;
protected:
private:
explicit PossibleValues(int value);
};
PossibleValues::PossibleValues(int value) : Iterable_Strong_Enum<int, PossibleValues>(value) { }
// you need to call that explicitly
Iterable_Strong_Enum<int, PossibleValues>::instances_list Iterable_Strong_Enum<int, PossibleValues>::_instances;
const PossibleValues PossibleValues::Stopped(0);
const PossibleValues PossibleValues::Playing(1);
const PossibleValues PossibleValues::Pause(2);
void stackoverflow()
{
std::cout << "There are " << PossibleValues::size() << " different possible values with representation: " << std::endl;
for (auto pv = PossibleValues::begin(); pv != PossibleValues::end(); ++pv)
{
PossibleValues possible_value = **pv;
std::cout << possible_value.get_representation() << std::endl;
}
}
I'm kinda torn about that solution. On the one hand, its pretty general and on the other hand its a big hammer for a small problem.