GCC and forward declaration of enum

Discussion in 'C++' started by Alexander Grigoriev, Sep 12, 2003.

  1. 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?
     
    Alexander Grigoriev, Sep 12, 2003
    #1
    1. Advertising

  2. Alexander Grigoriev

    Attila Feher Guest

    Alexander Grigoriev wrote:
    > 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.

    --
    Attila aka WW
     
    Attila Feher, Sep 12, 2003
    #2
    1. Advertising

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

    "Attila Feher" <> wrote in message
    news:bjrqiq$6ss$...
    > 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*.
     
    Alexander Grigoriev, Sep 12, 2003
    #3
  4. Alexander Grigoriev

    White Wolf Guest

    Alexander Grigoriev wrote:
    > 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.

    --
    WW aka Attila
     
    White Wolf, Sep 12, 2003
    #4
  5. White Wolf wrote:
    > Alexander Grigoriev wrote:
    >
    >>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?



    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.
     
    Gianni Mariani, Sep 12, 2003
    #5
  6. Alexander Grigoriev

    White Wolf Guest

    Gianni Mariani wrote:
    > 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???

    --
    WW aka Attila
     
    White Wolf, Sep 12, 2003
    #6
  7. White Wolf wrote:
    > Gianni Mariani wrote:
    >
    >>White Wolf wrote:

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


    Implementation detail.

    Export a template for me and then we'll talk.
     
    Gianni Mariani, Sep 12, 2003
    #7
  8. Alexander Grigoriev

    White Wolf Guest

    Gianni Mariani wrote:
    > White Wolf wrote:
    >> Gianni Mariani wrote:
    >>
    >>> White Wolf wrote:

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

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

    --
    WW aka Attila
     
    White Wolf, Sep 12, 2003
    #8
  9. Alexander Grigoriev wrote:
    >
    > 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.
     
    Alexander Terekhov, Sep 12, 2003
    #9
  10. White Wolf wrote:
    > Gianni Mariani wrote:
    >
    >>White Wolf wrote:
    >>
    >>>Gianni Mariani wrote:
    >>>
    >>>
    >>>>White Wolf wrote:

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

    >>
    >>Implementation detail.

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

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


    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.
     
    Gianni Mariani, Sep 12, 2003
    #10
  11. Alexander Grigoriev

    White Wolf Guest

    Gianni Mariani wrote:
    [SNIP]
    >>> Implementation detail.

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

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

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


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

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


    It depends on the reader. :)

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


    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
    >> more sense to use an incomplete class type, since into that one may
    >> put "variable number of arguments".

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

    --
    WW aka Attila
     
    White Wolf, Sep 13, 2003
    #11
  12. White Wolf wrote:
    > 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.

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


    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.

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


    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.

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

    >
    >
    > 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).
     
    Gianni Mariani, Sep 14, 2003
    #12
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. jobseeker
    Replies:
    2
    Views:
    1,095
    DarkSpy
    Oct 15, 2003
  2. qazmlp
    Replies:
    1
    Views:
    604
    Jonathan Turkanis
    Feb 15, 2004
  3. Replies:
    3
    Views:
    465
    Ron Natalie
    Aug 11, 2006
  4. Juha Nieminen
    Replies:
    3
    Views:
    4,735
    Lionel B
    Mar 5, 2007
  5. Brian
    Replies:
    4
    Views:
    2,656
    Brian
    Feb 27, 2010
Loading...

Share This Page