Variable arguments of enum type

Discussion in 'C Programming' started by Harald van Dijk, Apr 1, 2008.

  1. Hello all,

    Is it possible to know what an enumeration type promotes to? Given this
    code,

    #include <stdio.h>
    #include <stdarg.h>

    enum EnumerationType
    {
    Value1,
    Value2,
    Value3
    };

    void VarArgFunc(int dummy, ...)
    {
    va_list ap;
    va_start(ap, dummy);
    printf("VarArgFunc called with dummy=%d, ...=", dummy);
    switch (va_arg(ap, /* ??? */))
    {
    case Value1: puts("Value1"); break;
    case Value2: puts("Value2"); break;
    case Value3: puts("Value3"); break;
    }
    va_end(ap);
    }

    int main(void)
    {
    enum EnumerationType variable = Value1;
    VarArgFunc(0, variable);
    }

    what's the right type to pass to va_arg? I've been looking at code that
    does just this, using va_arg(ap, enum EnumerationType), which fails
    horribly at runtime when compiled on systems where enum EnumerationType
    turns out to be compatible with unsigned char. What's the right way of
    dealing with this? Will an enumeration type always promote to signed or
    unsigned int, or can it also promote to something larger (both in theory
    and in practise)?
    Harald van Dijk, Apr 1, 2008
    #1
    1. Advertising

  2. Harald van Dijk

    Ben Pfaff Guest

    Harald van Dijk <> writes:

    > Will an enumeration type always promote to signed or unsigned
    > int, or can it also promote to something larger (both in theory
    > and in practise)?


    In theory, an implementation may choose any suitable integer
    type:

    Each enumerated type shall be compatible with char, a signed
    integer type, or an unsigned integer type. The choice of type
    is implementation-defined,108) but shall be capable of
    representing the values of all the members of the
    enumeration.

    In practice, all the values of an enumeration must be in the
    range of "int", so I would not expect a compiler to choose a type
    wider than "int".

    You could always remove all risk by transforming this code:

    enum EnumerationType
    {
    Value1,
    Value2,
    Value3
    };

    into this:

    enum
    {
    Value1,
    Value2,
    Value3
    };
    typedef int EnumerationType;

    which is just about equivalent except that EnumerationType is
    always int.
    --
    char a[]="\n .CJacehknorstu";int putchar(int);int main(void){unsigned long b[]
    ={0x67dffdff,0x9aa9aa6a,0xa77ffda9,0x7da6aa6a,0xa67f6aaa,0xaa9aa9f6,0x11f6},*p
    =b,i=24;for(;p+=!*p;*p/=4)switch(0[p]&3)case 0:{return 0;for(p--;i--;i--)case+
    2:{i++;if(i)break;else default:continue;if(0)case 1:putchar(a[i&15]);break;}}}
    Ben Pfaff, Apr 1, 2008
    #2
    1. Advertising

  3. In article <b603a$47f291a5$541dfcd3$1.nb.home.nl>,
    Harald van =?UTF-8?b?RMSzaw==?= <> wrote:

    >Is it possible to know what an enumeration type promotes to?


    According to C89, each enumeration identifier is a constant of
    type int . enumeration types are compatible with an integer type,
    but the choice of integer type is implementation defined.


    >int main(void)
    >{
    > enum EnumerationType variable = Value1;
    > VarArgFunc(0, variable);
    >}



    If you were to use

    VarArgFunc(0, (unsigned int)variable);

    then because the value of each identifier is of type int, you
    know that if your variable 'variable' was set to one of the enumeration
    identifiers that the cast will have a predictable result no matter
    what the implementation type used for the enumeration type as a whole.

    I used (unsigned int) instead of (int) against the off chance that
    the implementation choose an unsigned integral type for the enumeration
    type, in which case you could potentially run into issues with (int)
    if the enumeration value currently exceeded INT_MAX. I can't think of
    why an implementation would think it a good idea to use an unsigned
    integral type to hold values that are allowed to be both negative and
    positive (it'd need a lot of fixups to work right!) but the C89 verbage
    doesn't prohibit it. I would imagine that -in practice-,
    using (int) would be fine (and much easier to program with).
    The C89 verbage is probably there to allow char or short to be
    used if the declared constants fit.

    Note that an enumeration type is not restricted to holding one
    of the enumerated values -- anything between the minimum and maximum
    of them is valid to store in the enumeration variable (but anything
    outside that range might or might not fit.)
    --
    "t lacks context, and may or may not make sense."
    -- Walter J. Phillips
    Walter Roberson, Apr 1, 2008
    #3
  4. On Tue, 01 Apr 2008 13:13:50 -0700, Ben Pfaff wrote:
    > Harald van Dijk <> writes:
    >> Will an enumeration type always promote to signed or unsigned int, or
    >> can it also promote to something larger (both in theory and in
    >> practise)?

    >
    > In theory, an implementation may choose any suitable integer type:
    >
    > Each enumerated type shall be compatible with char, a signed integer
    > type, or an unsigned integer type. The choice of type is
    > implementation-defined,108) but shall be capable of representing the
    > values of all the members of the enumeration.
    >
    > In practice, all the values of an enumeration must be in the range of
    > "int", so I would not expect a compiler to choose a type wider than
    > "int".


    That's a good point, thanks.

    > You could always remove all risk by transforming this code:
    >
    > enum EnumerationType
    > {
    > Value1,
    > Value2,
    > Value3
    > };
    >
    > into this:
    >
    > enum
    > {
    > Value1,
    > Value2,
    > Value3
    > };
    > typedef int EnumerationType;
    >
    > which is just about equivalent except that EnumerationType is always
    > int.


    Unfortunately, this disables very useful compiler warnings. With

    EnumerationType dummy;
    switch (dummy)
    {
    case Value1: /*...*/ break;
    case Value2: /*...*/ break;
    }

    I can get the compiler to warn me that I'm missing a case for Value3,
    which is a useful enough warning that I don't want to disable it.
    Harald van Dijk, Apr 1, 2008
    #4
  5. On Tue, 01 Apr 2008 20:18:10 +0000, Walter Roberson wrote:
    > In article <b603a$47f291a5$541dfcd3$1.nb.home.nl>,
    > Harald van =?UTF-8?b?RMSzaw==?= <> wrote:
    >>int main(void)
    >>{
    >> enum EnumerationType variable = Value1; VarArgFunc(0, variable);
    >>}

    >
    >
    > If you were to use
    >
    > VarArgFunc(0, (unsigned int)variable);
    >
    > then because the value of each identifier is of type int, you know that
    > if your variable 'variable' was set to one of the enumeration
    > identifiers that the cast will have a predictable result no matter what
    > the implementation type used for the enumeration type as a whole.
    >
    > I used (unsigned int) instead of (int) against the off chance that the
    > implementation choose an unsigned integral type for the enumeration
    > type, in which case you could potentially run into issues with (int) if
    > the enumeration value currently exceeded INT_MAX.


    Luckily, I don't have to worry too much about that, there are plenty of
    assert(enum_value >= min_enum_value && enum_value <= max_enum_value);

    > I can't think of why
    > an implementation would think it a good idea to use an unsigned integral
    > type to hold values that are allowed to be both negative and positive
    > (it'd need a lot of fixups to work right!) but the C89 verbage doesn't
    > prohibit it. I would imagine that -in practice-, using (int) would be
    > fine (and much easier to program with). The C89 verbage is probably
    > there to allow char or short to be used if the declared constants fit.


    This I don't understand. Different enumeration types can have different
    sizes. When a compiler sees

    enum EnumType1 {
    Value1 = 1, Value2 = 2
    };

    it knows that there aren't any negative values to worry about. When that
    compiler later sees

    enum EnumType2 {
    Value1 = 1, Value2 = -1
    };

    enum EnumType2 can be distinct from enum EnumType1; there's no
    requirement that all enumeration types are compatible. So why does the
    compiler have to worry about enum EnumType1 holding negative values?

    > Note that an enumeration type is not restricted to holding one of the
    > enumerated values -- anything between the minimum and maximum of them is
    > valid to store in the enumeration variable (but anything outside that
    > range might or might not fit.)


    Mostly true, and I've run into that already, with the assumption of real
    code that 0x100 is a valid value for an enum which turned out to have a
    range of 0x00 to 0xFF (unsigned char).

    I say mostly true because

    enum {
    Value1 = 1,
    Value2 = 2 };

    must be capable of storing 3. The enum must be an integer type with at
    least two value bits, and an integer type with at least two value bits
    can _always_ store the value 3.
    Harald van Dijk, Apr 1, 2008
    #5
  6. Harald van Dijk

    Guest

    On Apr 1, 3:13 pm, Ben Pfaff <> wrote:
    > Harald van D©¦k <> writes:
    >
    > > Will an enumeration type always promote to signed or unsigned
    > > int, or can it also promote to something larger (both in theory
    > > and in practise)?

    >
    > In theory, an implementation may choose any suitable integer
    > type:
    >
    > Each enumerated type shall be compatible with char, a signed
    > integer type, or an unsigned integer type. The choice of type
    > is implementation-defined,108) but shall be capable of
    > representing the values of all the members of the
    > enumeration.
    >
    > In practice, all the values of an enumeration must be in the
    > range of "int", so I would not expect a compiler to choose a type
    > wider than "int".
    >
    > You could always remove all risk by transforming this code:
    >
    > enum EnumerationType
    > {
    > Value1,
    > Value2,
    > Value3
    > };
    >
    > into this:
    >
    > enum
    > {
    > Value1,
    > Value2,
    > Value3
    > };
    > typedef int EnumerationType;
    >
    > which is just about equivalent except that EnumerationType is
    > always int.


    Why would you use an enum type in the first place then?
    I mean, if you are using an enum *type*, then that's
    because you want a separate type, no? If varargs stuff
    forces you to do something unnatural, then it's better
    to cast the function arguments to int instead, just as
    in other cases where you got to cast varargs function
    arguments.

    By the way, I've seen libraries which assume that enum
    types are always promoted to int or unsigned int, so it
    must be so with all "popular" compilers.

    Yevgen
    , Apr 1, 2008
    #6
  7. Harald van Dijk

    Ben Pfaff Guest

    writes:

    > On Apr 1, 3:13 pm, Ben Pfaff <> wrote:
    >> You could always remove all risk by transforming this code:
    >>
    >> enum EnumerationType
    >> {
    >> Value1,
    >> Value2,
    >> Value3
    >> };
    >>
    >> into this:
    >>
    >> enum
    >> {
    >> Value1,
    >> Value2,
    >> Value3
    >> };
    >> typedef int EnumerationType;
    >>
    >> which is just about equivalent except that EnumerationType is
    >> always int.

    >
    > Why would you use an enum type in the first place then?


    A number of reasons. Consider the alternatives: enums behave
    better than #defines (they are scoped) and unlike "const int"
    objects, they can be used in constant expressions. They are also
    more convenient than either of those alternatives in that it is
    easier to add, remove, and reorder elements.

    > I mean, if you are using an enum *type*, then that's because
    > you want a separate type, no?


    There is very little separate about an enum type, since every
    enum type is compatible with some integer type. The elements of
    an enumeration type don't even have the type of that enumeration!
    They can be freely mixed with integer types in expressions.
    There's not much to lose by using "int" instead. Loss of useful
    warnings in switch statements from a compiler is about the only
    thing I can think of.
    --
    char a[]="\n .CJacehknorstu";int putchar(int);int main(void){unsigned long b[]
    ={0x67dffdff,0x9aa9aa6a,0xa77ffda9,0x7da6aa6a,0xa67f6aaa,0xaa9aa9f6,0x11f6},*p
    =b,i=24;for(;p+=!*p;*p/=4)switch(0[p]&3)case 0:{return 0;for(p--;i--;i--)case+
    2:{i++;if(i)break;else default:continue;if(0)case 1:putchar(a[i&15]);break;}}}
    Ben Pfaff, Apr 1, 2008
    #7
  8. Harald van Dijk

    Kaz Kylheku Guest

    On Apr 1, 1:18 pm, -cnrc.gc.ca (Walter Roberson)
    wrote:
    > In article <b603a$47f291a5$541dfcd3$1.nb.home.nl>,
    > Harald van =?UTF-8?b?RMSzaw==?=  <> wrote:
    >
    > >Is it possible to know what an enumeration type promotes to?

    >
    > According to C89, each enumeration identifier is a constant of
    > type int . enumeration types are compatible with an integer type,
    > but the choice of integer type is implementation defined.


    That sucks, because passing the constant as a trailing argument has
    different semantics form passing an enum constant from the declaration
    of that type. Doh!

    Enum should have been properly designed from the start:

    // proposed:
    unsigned long enum foo { a, b, c };

    foo is compatible with unsigned int, and a b c are constants of
    unsigned int type.

    This would actually be a backward-compatible extension, because the
    combination of type specifiers is a constraint violation.

    C++'s safer enums are not such a bad idea either, though orthogonal to
    this problem. Safer enums would make the a, b, c constants themselves
    of the enum type, and forbid implicit conversions from integral to
    enum type. That could still be combined with the additional type
    specifiers to request compatibility with a given integral type.

    The restrict qualifier could be borrowed for enum for this purpose:

    // proposed:
    unsigned long restrict enum foo { a, b, c };

    Now a, b, and c have ``enum foo'' type rather than unsigned long.
    Moreover, assigments from arithmetic types to ``enum foo'' are
    forbidden without a cast.

    Since ``types other than pointer types ... shall not be restrict
    qualified'', this is a backward-compatible extension.
    Kaz Kylheku, Apr 1, 2008
    #8
  9. Harald van Dijk

    Guest

    On Apr 1, 3:48 pm, Ben Pfaff <> wrote:
    > writes:
    > > On Apr 1, 3:13 pm, Ben Pfaff <> wrote:
    > >> You could always remove all risk by transforming this code:

    >
    > >> enum EnumerationType
    > >> {
    > >> Value1,
    > >> Value2,
    > >> Value3
    > >> };

    >
    > >> into this:

    >
    > >> enum
    > >> {
    > >> Value1,
    > >> Value2,
    > >> Value3
    > >> };
    > >> typedef int EnumerationType;

    >
    > >> which is just about equivalent except that EnumerationType is
    > >> always int.

    >
    > > Why would you use an enum type in the first place then?

    >
    > A number of reasons. Consider the alternatives: enums behave
    > better than #defines (they are scoped) and unlike "const int"
    > objects, they can be used in constant expressions. They are also
    > more convenient than either of those alternatives in that it is
    > easier to add, remove, and reorder elements.


    No, those are reasons to use enumerators themselves, not
    the type. I.e. you get the very same benefits in
    enum {A, B}; int a = A;

    >
    > > I mean, if you are using an enum *type*, then that's because
    > > you want a separate type, no?

    >
    > There is very little separate about an enum type, since every
    > enum type is compatible with some integer type.


    Well, it's still a separate type, which allows gcc know
    when to warn :)

    > The elements of
    > an enumeration type don't even have the type of that enumeration!
    > They can be freely mixed with integer types in expressions.


    Even worse, code like the following will bite:

    int a = -1;
    enum {X, Y} b = X;
    if (a > b) oops;

    > There's not much to lose by using "int" instead. Loss of useful
    > warnings in switch statements from a compiler is about the only
    > thing I can think of.


    I guess it's psychological. I can easily imagine
    how every enum type is replaced by int and nothing
    changes, yet I find it very weird if something
    supposed to hold only members of an enum (or values
    made of those members) is not an object of that enum
    type. Yet it's weird if someone uses an enum for
    booleans instead of int. It's certainly psychological :)

    Yevgen
    , Apr 1, 2008
    #9
  10. Harald van Dijk

    CBFalconer Guest

    Harald van D?k wrote:
    >

    .... snip ...
    >
    > This I don't understand. Different enumeration types can have
    > different sizes. When a compiler sees
    >
    > enum EnumType1 {
    > Value1 = 1, Value2 = 2
    > };
    >
    > it knows that there aren't any negative values to worry about.
    > When that compiler later sees


    Not so. You can follow that with:

    enum EnumType x, y, z;

    x = -1; y = 1000; z = Value1;

    and all is legitimate.

    --
    [mail]: Chuck F (cbfalconer at maineline dot net)
    [page]: <http://cbfalconer.home.att.net>
    Try the download section.



    --
    Posted via a free Usenet account from http://www.teranews.com
    CBFalconer, Apr 1, 2008
    #10
  11. Harald van Dijk

    Ben Pfaff Guest

    writes:

    > On Apr 1, 3:48 pm, Ben Pfaff <> wrote:
    >> writes:
    >> > Why would you use an enum type in the first place then?

    >>
    >> A number of reasons. Consider the alternatives: enums behave
    >> better than #defines (they are scoped) and unlike "const int"
    >> objects, they can be used in constant expressions. They are also
    >> more convenient than either of those alternatives in that it is
    >> easier to add, remove, and reorder elements.

    >
    > No, those are reasons to use enumerators themselves, not
    > the type. I.e. you get the very same benefits in
    > enum {A, B}; int a = A;


    I usually use enum types more for documentation than for anything
    else. Seeing the enum type in the function prototype can be
    enlightening sometimes.
    --
    char a[]="\n .CJacehknorstu";int putchar(int);int main(void){unsigned long b[]
    ={0x67dffdff,0x9aa9aa6a,0xa77ffda9,0x7da6aa6a,0xa67f6aaa,0xaa9aa9f6,0x11f6},*p
    =b,i=24;for(;p+=!*p;*p/=4)switch(0[p]&3)case 0:{return 0;for(p--;i--;i--)case+
    2:{i++;if(i)break;else default:continue;if(0)case 1:putchar(a[i&15]);break;}}}
    Ben Pfaff, Apr 2, 2008
    #11
  12. On Tue, 01 Apr 2008 17:33:16 -0500, CBFalconer wrote:
    > Harald van D?k wrote:
    > ... snip ...
    >>
    >> This I don't understand. Different enumeration types can have different
    >> sizes. When a compiler sees
    >>
    >> enum EnumType1 {
    >> Value1 = 1, Value2 = 2
    >> };
    >>
    >> it knows that there aren't any negative values to worry about. When
    >> that compiler later sees

    >
    > Not so. You can follow that with:
    >
    > enum EnumType x, y, z;
    >
    > x = -1; y = 1000; z = Value1;
    >
    > and all is legitimate.


    Yes, yet this shows nothing of interest. None of x, y, or z is going to
    be set to a negative value on my system, and there is no conformance
    problem in that regard. The first assignment will set x to 255. The
    second assignment will set y to 232. The third assignment will set z to
    1. All perfectly valid: since none of the enumeration constants are
    negative, the compiler doesn't have to make the enumeration type support
    negative values.
    Harald van Dijk, Apr 2, 2008
    #12
  13. Harald van Dijk

    Ben Pfaff Guest

    Jack Klein <> writes:

    > I have had a few situations where I had to port code that depended on
    > enumerated types being ints. One solution is:
    >
    > enum EnumerationType
    > {
    > EnumerationType_Min = INT_MIN, /* include <limits.h>, of course */
    > Value1 = 0,
    > Value2,
    > Value3,
    > EnumerationType_Max = INT_MAX
    > };


    This definitely forces the compiler to choose a type whose range
    is at least as wide as int for the enumerated type. But the
    compiler could choose short if short and int have the same range,
    and DeathC could choose long or long long.
    --
    "The expression isn't unclear *at all* and only an expert could actually
    have doubts about it"
    --Dan Pop
    Ben Pfaff, Apr 2, 2008
    #13
  14. CBFalconer <> writes:
    > Harald van D?k wrote:
    >>

    > ... snip ...
    >>
    >> This I don't understand. Different enumeration types can have
    >> different sizes. When a compiler sees
    >>
    >> enum EnumType1 {
    >> Value1 = 1, Value2 = 2
    >> };
    >>
    >> it knows that there aren't any negative values to worry about.
    >> When that compiler later sees

    >
    > Not so. You can follow that with:
    >
    > enum EnumType x, y, z;
    >
    > x = -1; y = 1000; z = Value1;
    >
    > and all is legitimate.


    Depends on what you mean by "legitimate".

    Suppose enum EnumType is compatible with signed char, and suppose
    signed char has a range of -128 to 127. Then the conversion of -1 or
    1000 overflows, resulting in an implementation-defined value (or
    possibly an implementation-defined signal in C99).

    --
    Keith Thompson (The_Other_Keith) <>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Apr 3, 2008
    #14
  15. On Wed, 02 Apr 2008 21:51:42 -0700, Keith Thompson wrote:
    > Suppose enum EnumType is compatible with signed char, and suppose signed
    > char has a range of -128 to 127. Then the conversion of -1 or 1000
    > overflows, resulting in an implementation-defined value (or possibly an
    > implementation-defined signal in C99).


    If enum EnumType is compatible with signed char, there's no problem
    storing -1 in it. Were you thinking of unsigned char first, and did you
    change it to signed char later?
    Harald van Dijk, Apr 3, 2008
    #15
  16. Harald van Dijk

    Ben Pfaff Guest

    Keith Thompson <> writes:

    > CBFalconer <> writes:
    >> Not so. You can follow that with:
    >>
    >> enum EnumType x, y, z;
    >>
    >> x = -1; y = 1000; z = Value1;
    >>
    >> and all is legitimate.

    >
    > Depends on what you mean by "legitimate".
    >
    > Suppose enum EnumType is compatible with signed char, and suppose
    > signed char has a range of -128 to 127. Then the conversion of -1 or
    > 1000 overflows, resulting in an implementation-defined value (or
    > possibly an implementation-defined signal in C99).


    -1 is in the range -128 to 127.
    --
    Ben Pfaff
    http://benpfaff.org
    Ben Pfaff, Apr 3, 2008
    #16
  17. Harald van Dijk <> writes:
    > On Wed, 02 Apr 2008 21:51:42 -0700, Keith Thompson wrote:
    >> Suppose enum EnumType is compatible with signed char, and suppose signed
    >> char has a range of -128 to 127. Then the conversion of -1 or 1000
    >> overflows, resulting in an implementation-defined value (or possibly an
    >> implementation-defined signal in C99).

    >
    > If enum EnumType is compatible with signed char, there's no problem
    > storing -1 in it. Were you thinking of unsigned char first, and did you
    > change it to signed char later?


    Whoops. Yeah, something like that.

    --
    Keith Thompson (The_Other_Keith) <>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Apr 3, 2008
    #17
    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. Eric Sosman
    Replies:
    3
    Views:
    775
    Mayeul
    Feb 26, 2010
  2. Wojtek
    Replies:
    1
    Views:
    493
  3. Lew
    Replies:
    0
    Views:
    532
  4. Roedy Green
    Replies:
    0
    Views:
    726
    Roedy Green
    Feb 27, 2010
  5. Navaneeth
    Replies:
    4
    Views:
    550
    Kenny McCormack
    Nov 20, 2010
Loading...

Share This Page