GCC and forward declaration of enum

  • Thread starter Alexander Grigoriev
  • Start date
A

Alexander Grigoriev

Not quite new version of GCC that I have to use, craps with the following
code:

enum E;

enum E { e };

That is, it doesn't accept forward declaration of enum. C++ standard text
doesn't explicitly say about enum's forward declaration, but one example
shows it in 18.2.1, clause 4:

enum float_round_style;
enum float_denorm_style;

Is it a defect in the standard?
 
A

Attila Feher

Alexander said:
Not quite new version of GCC that I have to use, craps with the
following code:

enum E;

enum E { e };

That is, it doesn't accept forward declaration of enum. C++ standard
text doesn't explicitly say about enum's forward declaration, but one
example shows it in 18.2.1, clause 4:

Enums cannot be forward declared. First of all the C++ standard does not
define forward declaration. It defines incomplete types. in 3.9 paragraph
6 you can read:

"A class that has been declared but not defined, or an array of unknown size
or of incomplete element type, is an incompletely-defined object type.
Incompletely-defined object types and the void types are incomplete types
(3.9.1)."

(3.9.1 only refers to the section defining void)

So it seems that this paragraph (and its non-normative example) defines what
can be an incomplete type (what can be forward declared). The enum compound
type is not there.

In addition to this, the section 7.2 (describing enum declarations) does not
mention any possibility to declare an incomplete enum type. Therefore we
can conclude that it is simply not allowed.

Background: in C++ enum types can be represented by using _any_ integral
type, not only int (see 7.2/5). It is implementation defined *what* that
type is, and the implementation figures it out from the complete definition
of the enum type. So in case of:

enum ColorsEnum;
....
struct foo {
ColorsEnum *p
};

The compiler would have no idea if the pointer there should point to an int,
a (signed/unsigned) char, an unsigned int or a long int or... any other
integral type. While on most architectures it may not be an issue, on some
architectures the pointer will have a different size, in case it is a char
pointer. So finally our imaginary compiler would have no idea what to put
there to get a ColorsEnum*.

enum float_round_style;
enum float_denorm_style;

Is it a defect in the standard?

Examples are not part of the Standard text. They are non-normative.

But what you do show is not an example, it is a synopsis. Meaning
"outline", "design", "summary", "conspectus". It is not C++ code, but some
parts of the code showing those things which are governed by the standard.

According to the public WG21 web site there is no issue raised about this.
I suggest that you ask in comp.std.c++ for the reasons of this C++ like, but
ill-formed notation.
 
A

Alexander Grigoriev

Your example is not quite correct, as your argument can also be applied to
incomplete struct/class types ("The compiler would have no idea if the
pointer there should point to" struct/class of what size?), and it doesn't
stand in that case.

The actual problem is that enums are usually passed by value, and the
compiler can't decide what size of enum shuld be passed to the function when
called, if the type is still incomplete.
 
W

White Wolf

Alexander said:
Your example is not quite correct, as your argument can also be
applied to incomplete struct/class types ("The compiler would have no
idea if the pointer there should point to" struct/class of what
size?), and it doesn't stand in that case.

PLEASE do NOT top post. Thank you.

There is a very very very very important difference. For classes it makes
sense to forward declare them, since they can refer to each other, in which
case the code could not be written. Enums cannot refer to each other that
way. So in class types there is a need for unifying the pointer being used.
Since enum declarations cannot get "recursive", there it makes no sense, so
why would you make restrictions?
The actual problem is that enums are usually passed by value, and the
compiler can't decide what size of enum shuld be passed to the
function when called, if the type is still incomplete.

Incomplete types *cannot* be passed by value. Sorry, but you talk B/S here.
And that B/S is *not* Bjarne Stroustrup, but has something to do with the
end of the digestion process in big animals Spanish like to kill in arenas.
 
G

Gianni Mariani

White said:
PLEASE do NOT top post. Thank you.

There is a very very very very important difference. For classes it makes
sense to forward declare them, since they can refer to each other, in which
case the code could not be written. Enums cannot refer to each other that
way. So in class types there is a need for unifying the pointer being used.
Since enum declarations cannot get "recursive", there it makes no sense, so
why would you make restrictions?


It may make sense to do this: (not that I've ever needed or will ever need)

enum X::XValue; // bad syntax ... forward declare an XValue in enum X.

enum Y
{
YValue = X::Value + 1,
};

enum X
{
XValue = 2;
XXValue = YValue + 1;
};

I can see how this *may* be useful. I'm not holding my breath to see
anyone care though.
 
W

White Wolf

Gianni said:
White Wolf wrote:
It may make sense to do this: (not that I've ever needed or will ever
need)

enum X::XValue; // bad syntax ... forward declare an XValue in enum
X.

enum Y
{
YValue = X::Value + 1,
};

enum X
{
XValue = 2;
XXValue = YValue + 1;
};

I can see how this *may* be useful. I'm not holding my breath to see
anyone care though.

And how is the compiler supposed to know what will be the value (compile
time constant!!!) of that label, without showing the *whole* enum???
 
G

Gianni Mariani

White said:
And how is the compiler supposed to know what will be the value (compile
time constant!!!) of that label, without showing the *whole* enum???

Implementation detail.

Export a template for me and then we'll talk.
 
W

White Wolf

Gianni said:
Implementation detail.

Ahh. You must be a politician. Sorry for that, but I had to do it.

Since you seem to know so well what can and what cannot be done and what
makes sense I hope you will show up in Kona, HI on this falls WG21 meeting.
See you there.
Export a template for me and then we'll talk.

No problemo. I will calling up Greg and ask him for one of his compilers.

Just to make you even more angry and arrogant here is one legit reason for
wanting to pass around enum as a pointer: inside a framework of some kind or
other code, which only delegates one might want to simply pass
(transparently) the enum through. But in case of such a setup it makes much
more sense to use an incomplete class type, since into that one may put
"variable number of arguments".

Have a nice day.
 
A

Alexander Terekhov

Alexander said:
Not quite new version of GCC that I have to use, craps with the following
code:

enum E;

enum E { e };

That is, it doesn't accept forward declaration of enum.

If you want to have a pointer or reference, you can forward declare
"enum wrapper" CLASS. Something like that:

#include <iostream>

#define ENUM(E, T) class T { \
\
E m_e; \
\
public: \
\
T() : m_e() { } \
\
T(E e) : m_e(e) { } \
\
operator E () const { \
return m_e; \
} \
\
E & operator=(E e) { \
return m_e = e; \
} \
\
}

class MyEnumClass;

MyEnumClass & f();

enum MyEnum { zero, one };

MyEnum operator!(MyEnum e) {
return e ? zero : one;
}

ENUM(MyEnum, MyEnumClass);

MyEnumClass & f() {
static MyEnumClass thing;
return thing;
}

int main() {
std::cout << !f() << " ** " << !(f() = !f()) << "\n";
}

regards,
alexander.
 
G

Gianni Mariani

White said:
Ahh. You must be a politician. Sorry for that, but I had to do it.

I can see the seething sincerity in that apology.

Since you seem to know so well what can and what cannot be done and what
makes sense I hope you will show up in Kona, HI on this falls WG21 meeting.
See you there.

Hawaii is a nice place. Have a drink for me, I won't be there.
No problemo. I will calling up Greg and ask him for one of his compilers.

Just to make you even more angry and arrogant

I hope this is a joke .... he says, quivering in blinding fury, as he
reaches back for a counter to snipe arrogantly and relentlessly at the
cowering fool. .... sorry, got carried away.


.... here is one legit reason for
wanting to pass around enum as a pointer: inside a framework of some kind or
other code, which only delegates one might want to simply pass
(transparently) the enum through.


There are alternatives.

enums are not allways the best way to solve the problem.

I don't know if you remember or came across, one of my philosphies of
good design is to keep the threshold for creating a new class to a minimum.

I have done this in the past.

replaced the enum:

enum States
{
start_state,
finish_state,
some_other_state,
};

with this:

class State;

namespace Machine
{

extern State start;
extern State finish;
extern State some_other;

};

inline bool IsSameState( const State & a, const State & b )
{
return (&a) == (&b);
}



But in case of such a setup it makes much
more sense to use an incomplete class type, since into that one may put
"variable number of arguments".


Like I said above.
 
W

White Wolf

Gianni Mariani wrote:
[SNIP]
I can see the seething sincerity in that apology.

That is what they used to say when they are asked how will they spend
1000billions out of 700 and keep the balance of the budget... :)
Hawaii is a nice place. Have a drink for me, I won't be there.

You quitter! :) (What should one drink there?)
I hope this is a joke .... he says, quivering in blinding fury, as he
reaches back for a counter to snipe arrogantly and relentlessly at the
cowering fool. .... sorry, got carried away.

It depends on the reader. :)
... here is one legit reason for

There are alternatives.

Of course. And IMHO they are better.
enums are not allways the best way to solve the problem.

Well, enum labels are "sort of global". So unless they are hidden inside
the scope of a class...
I don't know if you remember or came across, one of my philosphies of
good design is to keep the threshold for creating a new class to a
minimum.

My philosophy for good design is to not write a single character more code
than necessary. The art is to find that balance. Because another rule
comes in as well: if its definition is not exactly like the one for int, do
not use an int - you need a class.
I have done this in the past.

replaced the enum:

enum States
{
start_state,
finish_state,
some_other_state,
};


I would not use an enum to identify states of a state machine. That creates
strong coupling between the states. And those sould be independent as much
as possible, so that only those states know about each other which need to.
In this case the enum must know about all possible cases. It is not
necessarily bad, but it collects unrelated information into one place. It
strongly couples non-coherent concepts.

IMO in a state machine the Context needs to know its starting state. That
state needs to know the states it uses and so fort. The only place where I
think such information can or should be together is a sort of factory, which
is able to create/retrieve those state objects.

But that leads to far way. There are many different kinds of state
machines, some with pretty much fixed state-space while others might need to
change frequently.
with this:

class State;

namespace Machine
{

extern State start;
extern State finish;
extern State some_other;

};

inline bool IsSameState( const State & a, const State & b )
{
return (&a) == (&b);
}

In here I think you are better off comparing the type_id of those states.
Your function name indicates "is this the same state", and not "is this the
same object".
But in case of such a setup it makes much

Like I said above.

I am not really sure what did you say above. Using a class type (which has
to be defined by the user of the library) gives a lot of flexibility for a
small price. Of course - OTOH - the scenario I have described is very rare.
It is even more rare that the argument you need to pass through
transparently is an enum. So IMO it is not worth to change the language
because of it - or even to discuss it too much. :)

State machines: I - time to time - start to think about the ultimate state
machine implementation. But so far I bailed out, I think because I have not
seen enough real state machines from different problem domains to really see
the best way(s). And my feeling that this is a non-trivial problem is
backed up by the fact that several state-machine code generators exist. And
I am yet to see a code generator, which has no (serious) limitations and
does not sell itself by taking away the responsibility from programmers to
look at a very complex and frustrating problem. Lex et. al. is a bit
exception, but only as long as you do not need a runtime extensible grammar.
:)
 
G

Gianni Mariani

White said:
Gianni Mariani wrote: ....

You quitter! :) (What should one drink there?)

Somthing cold an alchoholic. After all, we can't be making too many
decisions that make sense.

....
My philosophy for good design is to not write a single character more code
than necessary. The art is to find that balance. Because another rule
comes in as well: if its definition is not exactly like the one for int, do
not use an int - you need a class.

hmm. I'll think about that.
....

But that leads to far way. There are many different kinds of state
machines, some with pretty much fixed state-space while others might need to
change frequently.

A state machine is a collection of states and a state is a collection of
transitions.

I can discuss state machines forever. I'd better stop here.
In here I think you are better off comparing the type_id of those states.
Your function name indicates "is this the same state", and not "is this the
same object".

The type State is an incomplete type. start, finish etc are just some
random state. You can't "copy" a state, it makes no sense. The only
thing you care about is if the "current" state is a particular state.
I am not really sure what did you say above. Using a class type (which has
to be defined by the user of the library) gives a lot of flexibility for a
small price. Of course - OTOH - the scenario I have described is very rare.
It is even more rare that the argument you need to pass through
transparently is an enum.

100% agree.


So IMO it is not worth to change the language
because of it - or even to discuss it too much. :)

Beer time.

State machines: I - time to time - start to think about the ultimate state
machine implementation. But so far I bailed out, I think because I have not
seen enough real state machines from different problem domains to really see
the best way(s).

There is no 1 kind of state machine fits all. Some state machines have
very complex transitions but few states while others (lexers) have large
numbers of states but uniformly described transitions. Some state
machines are simple and an abstraction to a uniform state machine would
be prohibitive on performace grounds. Then we have LR parser state
machines which stack states which are simply genious. So far, the
complex transitions with relativly few states is the one that could use
a nice framework. I have build one in a recent past life, I might put
it into the library I'm working on.


And my feeling that this is a non-trivial problem is
backed up by the fact that several state-machine code generators exist. And
I am yet to see a code generator, which has no (serious) limitations and
does not sell itself by taking away the responsibility from programmers to
look at a very complex and frustrating problem. Lex et. al. is a bit
exception, but only as long as you do not need a runtime extensible grammar.
:)

Lex is an interesting one in that you could possible make the grammar
run-time extensible. There are ways of going from regular expression to
DFA directly (if they're minimal DFA's is a different story).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top