Re: enums that are really just sets of constants



On 19 April 2017 at 09:37, Murray Cumming wrote:
In the ABI-breaking versions of glibmm and gtkmm, we have recently
changed the enums to be C++11 "enum class"s. This is generally a
welcome improvement.
- Their values are, for instance, Things::ONE, rather than THINGS_ONE.
- Their values do not convert implicitly to other types such as int or
bool, helping the compiler to find some programmer mistakes.

We are also gradually moving the enums inside classes when they are
typically only used with that class.

However, some of the C enums that we wrap are actually meant to be
integer constants, so it seems that we cannot and should not use "enum
class" for these.

How are they used? Are they compared to integers? Are they passed to
gtk APIs that expect integers?

You can use scoped enums anyway, but you'd need an explicit cast to an
integer, so it might not be appropriate.

So I wonder what is the most "modern C++" way to do
this while keeping the new-style syntax. Here are two possibilities:

1. Use old-style enums inside a class:

class ResponseType {
public:
  enum Enum {
    NONE = -1,
    REJECT = -2,
    ACCEPT = -3,
    ...
  };
};

You can also use a namespace to give them their own scope, which has
less overhead than a class type (no RTTI for the class, no way for
users to create instances of the class type, or pointers to it, which
would have no purpose if it's just being used to create a new scope
for the enumerators).

But shouldn't we just avoid old-style enums completely?

Why? Both types still have uses in modern C++. Old doesn't mean bad.

N.B. since C++11 you can use the enumeration type's name as a
qualification even for unscoped (i.e. old-style, non-class) enums:

enum E { Foo, Bar, Baz };

auto e = E::Foo;

So you can use the new syntax even with old-style enums. The problem
is that you're not *required* to do that, you can still just say Foo,
so the names still leak into the surrounding scope. With a scoped
(i.e. new-style, class) enum the qualification is required.


2. Use constexpr int constants inside a class:

class ResponseType {
public:
  constexpr int NONE = -1;
  constexpr int
REJECT = -2;
  constexpr int ACCEPT = -3;
  ...
};

But shouldn't we use some kind of enum to group the values together?

Is their type actually important? i.e. are these different values of
the same logical type? If yes, then using an enumeration makes sense.
Otherwise just using an enumeration type to create an arbitrary group
doesn't add any benefit.

The example above looks like these are different values for the same
"thing" so giving them the same type makes sense to me. It allows
overloading on that type, for example.

Aside: IMHO the shouty ALL_CAPS naming is not good style in C++, see
https://accu.org/index.php/articles/1923 for my reasoning.


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]