Re: enums that are really just sets of constants



On Wed, 2017-04-19 at 10:30 +0100, Jonathan Wakely wrote:
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?

Both.

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

Yes, that's what we are doing right now. It's a bit ugly.

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).

Good point.

I'm leaning towards this at the moment.

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.

OK. I wouldn't like that.

 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?

Not really. These are generally just ints.

 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.

Yes, I saw that mentioned in the C++ Core Guidelines. I might need to
adapt my habits. But that's something to deal with later.

-- 
Murray Cumming
murrayc murrayc com
www.murrayc.com



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