4

This is pretty much the same question as Does adding enumerators into enum break ABI?, but with enum class introduced by C++11.

For what I understand by this page I can simply have a stable ABI by defining an underlying type for my enumerative:

enum class Foo : uint32_t
{
    x, y, z
}

I would say this works fine, as for instance something like

enum class Foo : uint8_t { x = 257 }

Will not compile. This implies that the compiler is no longer silently changing the size of my enumerative, thus I don't end up in breaking binary compatibility.

Am I correct?

Dacav
  • 13,590
  • 11
  • 60
  • 87
  • Are you implicitly assuming that the added enum values are always appended at the __end__ of the list? –  Nov 19 '18 at 20:49
  • The question feels a little narrower than the title suggests. – Shafik Yaghmour Nov 20 '18 at 04:27
  • @Frank, I'm not implicitly assuming that, but if I got it correctly, having an enumerative with a big value helps the compiler in deciding what backing type to use. If we provide explicitly a backing integral type, we will ask the compiler of use a specific size for our enumerative, hence achieving binary compatibility (well, if we don't change the **order** of enum values, of course) – Dacav Nov 20 '18 at 12:41
  • @ShafikYaghmour, Yeah, maybe you're right. I'll rename that into… – Dacav Nov 20 '18 at 12:41
  • It was implementation-defined before, it still is when you don't specify the underlying type explicitly. The only requirement is that it is big enough to represent the enumerator values. Which is why the second snippet failed. If you need to guarantee binary compatibility then it does depend on what your compiler did previously. Seeing a 32-bit type work is certainly not unusual. That will almost certainly still work when you don't specify it. – Hans Passant Nov 20 '18 at 13:11
  • @HansPassant - Of course if we have a library using old-style enums one can't be sure, and I guess it is wise to bump the soname while switching to `class enum` with a underlying type. Once this is done, is the enumerative going to be stable forever? – Dacav Nov 20 '18 at 13:22

2 Answers2

1

I believe the accepted answer to the referenced question answers most of the issues with respect to ABI compatibility. I could repeat it here but I don't see much value there.

To address you specific question about C++11 scoped enumerations your example:

enum class Foo : uint8_t { x = 257 }

is ill-formed since it requires a narrowing conversion, the implementation is required to provide a diagnostic but it may compile if the implementation only makes it warning for example. As you ask, the compiler won't silently change the size of underlying type.

We can see this from the draft C++ standard dcl.enump5:

Each enumeration defines a type that is different from all other types. Each enumeration also has an underlying type. The underlying type can be explicitly specified using an enum-base. For a scoped enumeration type, the underlying type is int if it is not explicitly specified. In both of these cases, the underlying type is said to be fixed. Following the closing brace of an enum-specifier, each enumerator has the type of its enumeration. If the underlying type is fixed, the type of each enumerator prior to the closing brace is the underlying type and the constant-expression in the enumerator-definition shall be a converted constant expression of the underlying type. If the underlying type is not fixed, the type of each enumerator prior to the closing brace is determined as follows: ...

and converted constant expression forbid narrowing conversion, from expr.constp5:

A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only

...

  • integral conversions other than narrowing conversions,

...

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 1
    This does not answer the question, which is about ABI. – rubenvb Nov 20 '18 at 06:21
  • Well, the answer is not about ABI, but it is possibly relevant: if the implementation only raises a warning, could it be that the compiler will allow me to break binary compatibility, should I use a value outside the ranges of my enum type? – Dacav Nov 20 '18 at 12:46
  • @rubenvb - Well, if as the OP put it "the compiler no longer silently changes the underlying type", one can conclude the ABI will stay the same. – StoryTeller - Unslander Monica Nov 20 '18 at 12:47
  • @rubenvb I don't see the value of repeating the accepted answer to the referenced question. I feel like the real question is quite narrow and I have addressed that. I updated my answer to reflect that more explicitly. – Shafik Yaghmour Nov 21 '18 at 02:22
0

OP here.

I found out, and I think it would be relevant to mention, that C++11 has this nice std::underlying_type.

I think it can be used, together with is_same and static assertions, to create some guards against unexpected changes of ABI due to enumeratives.

I think (but I didn't try) that this is especially relevant if one is working with some library, and a pre-existing enumerative is not specifying an underlying type.

Dacav
  • 13,590
  • 11
  • 60
  • 87