Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #150254 > unrolled thread

clarification about enums in C

Started byThiago Adams <thiago.adams@gmail.com>
First post2020-02-25 15:38 -0800
Last post2020-02-26 21:04 +0000
Articles 11 on this page of 31 — 7 participants

Back to article view | Back to comp.lang.c


Contents

  clarification about enums in C Thiago Adams <thiago.adams@gmail.com> - 2020-02-25 15:38 -0800
    Re: clarification about enums in C luser droog <luser.droog@gmail.com> - 2020-02-25 19:19 -0800
    Re: clarification about enums in C James Kuyper <jameskuyper@alumni.caltech.edu> - 2020-02-25 21:17 -0800
      Re: clarification about enums in C Thiago Adams <thiago.adams@gmail.com> - 2020-02-27 04:01 -0800
    Re: clarification about enums in C David Brown <david.brown@hesbynett.no> - 2020-02-26 11:29 +0100
      Re: clarification about enums in C already5chosen@yahoo.com - 2020-02-26 14:05 -0800
        Re: clarification about enums in C Ben Bacarisse <ben.usenet@bsb.me.uk> - 2020-02-26 23:03 +0000
          Re: clarification about enums in C already5chosen@yahoo.com - 2020-02-26 23:29 -0800
            Re: clarification about enums in C already5chosen@yahoo.com - 2020-02-27 01:42 -0800
              Re: clarification about enums in C David Brown <david.brown@hesbynett.no> - 2020-02-27 11:02 +0100
                Re: clarification about enums in C Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-02-27 06:37 -0800
                  Re: clarification about enums in C David Brown <david.brown@hesbynett.no> - 2020-02-27 16:18 +0100
                    Re: clarification about enums in C James Kuyper <jameskuyper@alumni.caltech.edu> - 2020-02-27 23:24 -0500
        Re: clarification about enums in C David Brown <david.brown@hesbynett.no> - 2020-02-27 09:50 +0100
          Re: clarification about enums in C Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-02-27 06:33 -0800
            Re: clarification about enums in C already5chosen@yahoo.com - 2020-02-27 06:53 -0800
            Re: clarification about enums in C Ben Bacarisse <ben.usenet@bsb.me.uk> - 2020-02-27 15:05 +0000
            Re: clarification about enums in C David Brown <david.brown@hesbynett.no> - 2020-02-27 16:24 +0100
            Re: clarification about enums in C Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-02-28 01:27 -0800
        Re: clarification about enums in C already5chosen@yahoo.com - 2020-02-27 01:29 -0800
          Re: clarification about enums in C David Brown <david.brown@hesbynett.no> - 2020-02-27 11:12 +0100
            Re: clarification about enums in C already5chosen@yahoo.com - 2020-02-27 03:09 -0800
              Re: clarification about enums in C David Brown <david.brown@hesbynett.no> - 2020-02-27 14:33 +0100
                Re: clarification about enums in C already5chosen@yahoo.com - 2020-02-27 05:44 -0800
                  Re: clarification about enums in C David Brown <david.brown@hesbynett.no> - 2020-02-27 14:57 +0100
          Re: clarification about enums in C Ben Bacarisse <ben.usenet@bsb.me.uk> - 2020-02-27 11:53 +0000
            Re: clarification about enums in C James Kuyper <jameskuyper@alumni.caltech.edu> - 2020-02-27 23:52 -0500
              Re: clarification about enums in C Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-02-28 01:30 -0800
    Re: clarification about enums in C Ben Bacarisse <ben.usenet@bsb.me.uk> - 2020-02-26 14:09 +0000
      Re: clarification about enums in C Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-02-26 09:47 -0800
        Re: clarification about enums in C Ben Bacarisse <ben.usenet@bsb.me.uk> - 2020-02-26 21:04 +0000

Page 2 of 2 — ← Prev page 1 [2]


#150287

FromDavid Brown <david.brown@hesbynett.no>
Date2020-02-27 11:12 +0100
Message-ID<r384n9$j0c$1@dont-email.me>
In reply to#150280
On 27/02/2020 10:29, already5chosen@yahoo.com wrote:
> On Thursday, February 27, 2020 at 12:05:12 AM UTC+2, already...@yahoo.com wrote:
>> On Wednesday, February 26, 2020 at 12:29:40 PM UTC+2, David Brown wrote:
>>> On 26/02/2020 00:38, Thiago Adams wrote:
>>>> From the standard:
>>>>
>>>> "The identifiers in an enumerator list are declared as constants 
>>>> that have type int and may appear wherever such are permitted.127)"
>>>>
>>>> But there is a foot note:
>>>>
>>>> "Each enumerated type shall be compatible with char, a signed 
>>>> integer type, or an unsigned integer type. The choice of type 
>>>> is implementation-defined,128)"
>>>>
>>>> "128) An implementation may delay the choice of which integer 
>>>> type until all enumeration constants have been seen."
>>>>
>>>> I am confused. 
>>>>
>>>
>>> You are not the first person to feel that way.
>>>
>>>> enum E { A, B};
>>>
>>> The key weirdness of "enum" in C is that "enum E" is a type, but A and B
>>> are not objects of that type.  They are, however, members of the
>>> enumeration "E".
>>>
>>> "A" and "B" are named integer constant values (much like 'a' and 'b').
>>> So they have type "int".
>>>
>>
>> That' how language of the standard sounds.
>> But it's not how gcc and clang work.
>>
>> enum enum_t {
>>  uns = (unsigned)-1,
>>  neg = -1,
>> };
>> Both in gcc and in clang uns has type 'unsigned int' rather than 'int'.
>> And indeed it differs from type of 'enum_t' which in ths case is 'int'.
>>
>> Microsoft's compiler (VS2015) is easier to understand and don't violate (my understanding of) Standard, but it is less flexible - everything, including uns, is 'int'.
>>
>>
>>> "enum E" is an enumerated type.  It is an odd thing - it is an integer
>>> type (6.2.5p17), and it is required to be compatible with a char, a
>>> signed integer type, or an unsigned integer type (6.7.2.2p4).  I am sure
>>> it was not intended that this requirement be recursive here!
>>>
>>> It is up to the compiler to decide which integer type "enum E" should be
>>> compatible with.  Once it is picked (gcc picks "unsigned int" here,
>>> unless at least one of the enumerators is negative, when it picks
>>> "int"), the type "enum E" is indistinguishable from that type.  So
>>> _Generic() on gcc would match "enum E" as "unsigned int".  As far as I
>>> understand it (and I could be wrong), "enum E" is therefore a new type
>>> that is in every way identical and compatible to an existing type - but
>>> is not an alias for it (like a typedef).
>>>
>>> (Of course, compilers and other tools can track a distinction here, and
>>> use it for extra warnings or analysis.)
>>>
>>>>
>>>> Can A and B be int and E not int?
>>>
>>> A and B are always "int".  E might not be.
>>>
>>> (Many compilers have extensions to allow A and B to be "long int".)
>>>
> 
> In case of Windows port of gcc and clang, even 'long long int',
> This case is especially funny:
> 
> enum en_t {
>   A = 1,
>   B = -1,
>   C = (unsigned)-1,
> };
> 
> type of en_t is 'long long'

Yes, that makes sense (as an extension) - it is the first type that
supports all the values you gave as initialisers.  (On Linux, it is
"long" since "long" is 64-bit - on Windows, to get 64-bit you need "long
long".)

> A == 1, type is either 'int' or 'unsigned', I can't tell for sure

"int", of course.

> B == -1, type is 'int

Yes.

> C == 2**32-1, type is 'long long' or 'unsigned long long', I can't tell for sure.

"long long" (or "long" on Linux).  It is the first type that can hold
the value while remaining sign-compatible with the other members.


These are not the only choice of types to use that would be practical
and consistent, but they are a reasonable choice.  (Making A, B, C and
en_t all the same type "long long" would also be a good option.)

> 
> Again, that's on Windows/Msys2. 
> On Linux, I wouldn't know how to distinguish between rvalues of types 'long' and 'long long'.
> 

Use _Generic.


Code for testing types, practical for <https://godbolt.org>
Compiled with gcc -O2 -x c -std=c11 -Wall -Wpedantic

<https://godbolt.org/z/FSZ9FP>


typedef enum X { A = 1, B = -1, C = (unsigned) -1 } X;

#define typeno(X) _Generic((X), \
    int : 1, \
    short int : 2, \
    char : 3, \
    signed char : 4, \
    unsigned char : 5, \
    unsigned int : 6, \
    unsigned short int : 7, \
    long int : 8, \
    unsigned long int : 9, \
    long long int : 10, \
    unsigned long long int : 11 \
)

enum X x;

int testX(void) {
    return typeno(x);
}

int testA(void) {
    return typeno(A);
}

int testB(void) {
    return typeno(B);
}

int testC(void) {
    return typeno(C);
}

[toc] | [prev] | [next] | [standalone]


#150289

Fromalready5chosen@yahoo.com
Date2020-02-27 03:09 -0800
Message-ID<66b4b494-68c6-420a-b0e3-c4cb4b357d9b@googlegroups.com>
In reply to#150287
On Thursday, February 27, 2020 at 12:13:06 PM UTC+2, David Brown wrote:
> On 27/02/2020 10:29, already5chosen@yahoo.com wrote:
> > On Thursday, February 27, 2020 at 12:05:12 AM UTC+2, already...@yahoo.com wrote:
> >> On Wednesday, February 26, 2020 at 12:29:40 PM UTC+2, David Brown wrote:
> >>> On 26/02/2020 00:38, Thiago Adams wrote:
> >>>> From the standard:
> >>>>
> >>>> "The identifiers in an enumerator list are declared as constants 
> >>>> that have type int and may appear wherever such are permitted.127)"
> >>>>
> >>>> But there is a foot note:
> >>>>
> >>>> "Each enumerated type shall be compatible with char, a signed 
> >>>> integer type, or an unsigned integer type. The choice of type 
> >>>> is implementation-defined,128)"
> >>>>
> >>>> "128) An implementation may delay the choice of which integer 
> >>>> type until all enumeration constants have been seen."
> >>>>
> >>>> I am confused. 
> >>>>
> >>>
> >>> You are not the first person to feel that way.
> >>>
> >>>> enum E { A, B};
> >>>
> >>> The key weirdness of "enum" in C is that "enum E" is a type, but A and B
> >>> are not objects of that type.  They are, however, members of the
> >>> enumeration "E".
> >>>
> >>> "A" and "B" are named integer constant values (much like 'a' and 'b').
> >>> So they have type "int".
> >>>
> >>
> >> That' how language of the standard sounds.
> >> But it's not how gcc and clang work.
> >>
> >> enum enum_t {
> >>  uns = (unsigned)-1,
> >>  neg = -1,
> >> };
> >> Both in gcc and in clang uns has type 'unsigned int' rather than 'int'.
> >> And indeed it differs from type of 'enum_t' which in ths case is 'int'.
> >>
> >> Microsoft's compiler (VS2015) is easier to understand and don't violate (my understanding of) Standard, but it is less flexible - everything, including uns, is 'int'.
> >>
> >>
> >>> "enum E" is an enumerated type.  It is an odd thing - it is an integer
> >>> type (6.2.5p17), and it is required to be compatible with a char, a
> >>> signed integer type, or an unsigned integer type (6.7.2.2p4).  I am sure
> >>> it was not intended that this requirement be recursive here!
> >>>
> >>> It is up to the compiler to decide which integer type "enum E" should be
> >>> compatible with.  Once it is picked (gcc picks "unsigned int" here,
> >>> unless at least one of the enumerators is negative, when it picks
> >>> "int"), the type "enum E" is indistinguishable from that type.  So
> >>> _Generic() on gcc would match "enum E" as "unsigned int".  As far as I
> >>> understand it (and I could be wrong), "enum E" is therefore a new type
> >>> that is in every way identical and compatible to an existing type - but
> >>> is not an alias for it (like a typedef).
> >>>
> >>> (Of course, compilers and other tools can track a distinction here, and
> >>> use it for extra warnings or analysis.)
> >>>
> >>>>
> >>>> Can A and B be int and E not int?
> >>>
> >>> A and B are always "int".  E might not be.
> >>>
> >>> (Many compilers have extensions to allow A and B to be "long int".)
> >>>
> > 
> > In case of Windows port of gcc and clang, even 'long long int',
> > This case is especially funny:
> > 
> > enum en_t {
> >   A = 1,
> >   B = -1,
> >   C = (unsigned)-1,
> > };
> > 
> > type of en_t is 'long long'
> 
> Yes, that makes sense (as an extension) 

It makes non-zero sense, but not a whole lot of it.
I'd very much prefer a different language extension as solution to the same practical problem: a keyword __const, with semantics identical to C++ keyword const.
With a hope of inclusion into next revision of the standard.

> - it is the first type that
> supports all the values you gave as initialisers.  (On Linux, it is
> "long" since "long" is 64-bit - on Windows, to get 64-bit you need "long
> long".)
> 
> > A == 1, type is either 'int' or 'unsigned', I can't tell for sure
> 
> "int", of course.
> 
> > B == -1, type is 'int
> 
> Yes.
> 
> > C == 2**32-1, type is 'long long' or 'unsigned long long', I can't tell for sure.
> 
> "long long" (or "long" on Linux).  It is the first type that can hold
> the value while remaining sign-compatible with the other members.
> 
> 
> These are not the only choice of types to use that would be practical
> and consistent, but they are a reasonable choice.  (Making A, B, C and
> en_t all the same type "long long" would also be a good option.)
>

IMHO, a better option. But not as good as my proposal above.

> > 
> > Again, that's on Windows/Msys2. 
> > On Linux, I wouldn't know how to distinguish between rvalues of types 'long' and 'long long'.
> > 
> 
> Use _Generic.
> 
> 
> Code for testing types, practical for <https://godbolt.org>
> Compiled with gcc -O2 -x c -std=c11 -Wall -Wpedantic
> 
> <https://godbolt.org/z/FSZ9FP>
> 
> 
> typedef enum X { A = 1, B = -1, C = (unsigned) -1 } X;
> 
> #define typeno(X) _Generic((X), \
>     int : 1, \
>     short int : 2, \
>     char : 3, \
>     signed char : 4, \
>     unsigned char : 5, \
>     unsigned int : 6, \
>     unsigned short int : 7, \
>     long int : 8, \
>     unsigned long int : 9, \
>     long long int : 10, \
>     unsigned long long int : 11 \
> )
> 
> enum X x;
> 
> int testX(void) {
>     return typeno(x);
> }
> 
> int testA(void) {
>     return typeno(A);
> }
> 
> int testB(void) {
>     return typeno(B);
> }
> 
> int testC(void) {
>     return typeno(C);
> }

_Generic is way too advanced for me :( Gimme 5 more years, to become accustomed to its presence in the language. That is, assuming that until that it would not be declared optional.

[toc] | [prev] | [next] | [standalone]


#150293

FromDavid Brown <david.brown@hesbynett.no>
Date2020-02-27 14:33 +0100
Message-ID<r38gfh$1a9$1@dont-email.me>
In reply to#150289
On 27/02/2020 12:09, already5chosen@yahoo.com wrote:
> On Thursday, February 27, 2020 at 12:13:06 PM UTC+2, David Brown wrote:
>> On 27/02/2020 10:29, already5chosen@yahoo.com wrote:
<snip>

>>>
>>> In case of Windows port of gcc and clang, even 'long long int',
>>> This case is especially funny:
>>>
>>> enum en_t {
>>>   A = 1,
>>>   B = -1,
>>>   C = (unsigned)-1,
>>> };
>>>
>>> type of en_t is 'long long'
>>
>> Yes, that makes sense (as an extension) 
> 
> It makes non-zero sense, but not a whole lot of it.
> I'd very much prefer a different language extension as solution to the same practical problem: a keyword __const, with semantics identical to C++ keyword const.
> With a hope of inclusion into next revision of the standard.
> 

While I too would like a C++ style "const" in C, I think "enum" is a
different situation.  Sometimes people use "enum" simply to make
independent constants that in C++ can be made with "const int A = 1;".
In C++, you can use a "const int" like that for the size of an array, or
for initialising other file-scope objects - in C you can't, so you use
either "enum { A = 1 };" or "#define A 1".

However, a big use of "enum" is to group a collection of states or
values - I use it as an enumerated type.  (C++ improves on this (for
many uses) with enum classes.)  Compilers and static analysis can do
additional checks with things like switch cases or warning about legal
but probably incorrect assignments or operations on enumerated types.
Debuggers can show values as text, rather than as a number.  You can't
get any of that with a bunch of "const int" declarations.

If C is ever to take on C++'s "const" features, something I too would
like, I think it can be done without any new keyword.  For the most
part, the difference is simple that some things are allowed in C++ and
not in C.  The only key point where "const" means one thing in a valid C
program and a different thing in a valid C++ program is that plain
"const int A = 1;" at file-scope in C is effectively "extern", while in
C++ it is "static".  That would have to remain unchanged, and you'd have
to write "static const" more in C than in C++.

<snip>

> 
>>>
>>> Again, that's on Windows/Msys2. 
>>> On Linux, I wouldn't know how to distinguish between rvalues of types 'long' and 'long long'.
>>>
>>
>> Use _Generic.
>>
>>
>> Code for testing types, practical for <https://godbolt.org>
>> Compiled with gcc -O2 -x c -std=c11 -Wall -Wpedantic
>>
>> <https://godbolt.org/z/FSZ9FP>
>>
<snip>
> _Generic is way too advanced for me :( Gimme 5 more years, to become
> accustomed to its presence in the language. That is, assuming that
> until that it would not be declared optional.
> 

To be honest, I haven't found a need for _Generic in real code.  But it
is very handy for this kind of thing.

[toc] | [prev] | [next] | [standalone]


#150295

Fromalready5chosen@yahoo.com
Date2020-02-27 05:44 -0800
Message-ID<2e1f4ca8-2045-4d4f-bdda-426cec6b44f7@googlegroups.com>
In reply to#150293
On Thursday, February 27, 2020 at 3:33:45 PM UTC+2, David Brown wrote:
> On 27/02/2020 12:09, already5chosen@yahoo.com wrote:
> > On Thursday, February 27, 2020 at 12:13:06 PM UTC+2, David Brown wrote:
> >> On 27/02/2020 10:29, already5chosen@yahoo.com wrote:
> <snip>
> 
> >>>
> >>> In case of Windows port of gcc and clang, even 'long long int',
> >>> This case is especially funny:
> >>>
> >>> enum en_t {
> >>>   A = 1,
> >>>   B = -1,
> >>>   C = (unsigned)-1,
> >>> };
> >>>
> >>> type of en_t is 'long long'
> >>
> >> Yes, that makes sense (as an extension) 
> > 
> > It makes non-zero sense, but not a whole lot of it.
> > I'd very much prefer a different language extension as solution to the same practical problem: a keyword __const, with semantics identical to C++ keyword const.
> > With a hope of inclusion into next revision of the standard.
> > 
> 
> While I too would like a C++ style "const" in C, I think "enum" is a
> different situation.  Sometimes people use "enum" simply to make
> independent constants that in C++ can be made with "const int A = 1;".
> In C++, you can use a "const int" like that for the size of an array, or
> for initialising other file-scope objects - in C you can't, so you use
> either "enum { A = 1 };" or "#define A 1".
> 
> However, a big use of "enum" is to group a collection of states or
> values - I use it as an enumerated type.  (C++ improves on this (for
> many uses) with enum classes.)  Compilers and static analysis can do
> additional checks with things like switch cases or warning about legal
> but probably incorrect assignments or operations on enumerated types.
> Debuggers can show values as text, rather than as a number.  You can't
> get any of that with a bunch of "const int" declarations.
> 

But for this use of enum 'int' range, specified in the Standard, appears fully adequate.
So, if it remains the only use, I see no need for extension of the range beyond 'int'.

> If C is ever to take on C++'s "const" features, something I too would
> like, I think it can be done without any new keyword.  For the most
> part, the difference is simple that some things are allowed in C++ and
> not in C.  The only key point where "const" means one thing in a valid C
> program and a different thing in a valid C++ program is that plain
> "const int A = 1;" at file-scope in C is effectively "extern", while in
> C++ it is "static".  That would have to remain unchanged, and you'd have
> to write "static const" more in C than in C++.
> 
> <snip>
> 
> > 
> >>>
> >>> Again, that's on Windows/Msys2. 
> >>> On Linux, I wouldn't know how to distinguish between rvalues of types 'long' and 'long long'.
> >>>
> >>
> >> Use _Generic.
> >>
> >>
> >> Code for testing types, practical for <https://godbolt.org>
> >> Compiled with gcc -O2 -x c -std=c11 -Wall -Wpedantic
> >>
> >> <https://godbolt.org/z/FSZ9FP>
> >>
> <snip>
> > _Generic is way too advanced for me :( Gimme 5 more years, to become
> > accustomed to its presence in the language. That is, assuming that
> > until that it would not be declared optional.
> > 
> 
> To be honest, I haven't found a need for _Generic in real code.  But it
> is very handy for this kind of thing.

[toc] | [prev] | [next] | [standalone]


#150296

FromDavid Brown <david.brown@hesbynett.no>
Date2020-02-27 14:57 +0100
Message-ID<r38hsr$fcs$1@dont-email.me>
In reply to#150295
On 27/02/2020 14:44, already5chosen@yahoo.com wrote:
> On Thursday, February 27, 2020 at 3:33:45 PM UTC+2, David Brown wrote:
>> On 27/02/2020 12:09, already5chosen@yahoo.com wrote:
>>> On Thursday, February 27, 2020 at 12:13:06 PM UTC+2, David Brown wrote:
>>>> On 27/02/2020 10:29, already5chosen@yahoo.com wrote:
>> <snip>
>>
>>>>>
>>>>> In case of Windows port of gcc and clang, even 'long long int',
>>>>> This case is especially funny:
>>>>>
>>>>> enum en_t {
>>>>>   A = 1,
>>>>>   B = -1,
>>>>>   C = (unsigned)-1,
>>>>> };
>>>>>
>>>>> type of en_t is 'long long'
>>>>
>>>> Yes, that makes sense (as an extension) 
>>>
>>> It makes non-zero sense, but not a whole lot of it.
>>> I'd very much prefer a different language extension as solution to the same practical problem: a keyword __const, with semantics identical to C++ keyword const.
>>> With a hope of inclusion into next revision of the standard.
>>>
>>
>> While I too would like a C++ style "const" in C, I think "enum" is a
>> different situation.  Sometimes people use "enum" simply to make
>> independent constants that in C++ can be made with "const int A = 1;".
>> In C++, you can use a "const int" like that for the size of an array, or
>> for initialising other file-scope objects - in C you can't, so you use
>> either "enum { A = 1 };" or "#define A 1".
>>
>> However, a big use of "enum" is to group a collection of states or
>> values - I use it as an enumerated type.  (C++ improves on this (for
>> many uses) with enum classes.)  Compilers and static analysis can do
>> additional checks with things like switch cases or warning about legal
>> but probably incorrect assignments or operations on enumerated types.
>> Debuggers can show values as text, rather than as a number.  You can't
>> get any of that with a bunch of "const int" declarations.
>>
> 
> But for this use of enum 'int' range, specified in the Standard, appears fully adequate.
> So, if it remains the only use, I see no need for extension of the range beyond 'int'.
> 

Well, yes.  However, there are more uses for enum.  An example is for
flags that you want to combine:

enum colour {
    black = 0,
    red = 1,
    blue = 2,
    green = 4,
    magenta = red + blue,
    cyan = blue + green,
    yellow = green + red,
    white = red + blue + green
};

With bigger sets of flags, you might easily go beyond the size of "int".

Generally speaking, "enum" is the C way to group a bunch of named
constant integer values.  While "int" is big enough for most uses, it
seems arbitrary to restrict it to "int" when it is so obvious and simple
to extend it.

[toc] | [prev] | [next] | [standalone]


#150290

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2020-02-27 11:53 +0000
Message-ID<87o8tkw8j2.fsf@bsb.me.uk>
In reply to#150280
already5chosen@yahoo.com writes:
<cut>
> In case of Windows port of gcc and clang, even 'long long int',
> This case is especially funny:
>
> enum en_t {
>   A = 1,
>   B = -1,
>   C = (unsigned)-1,
> };
>
> type of en_t is 'long long'

(nit: the type can be compatible with long long, but it should not
actually be long long.)

> A == 1, type is either 'int' or 'unsigned', I can't tell for sure

You could use _Generic or do some mixed-sign arithmetic.

> B == -1, type is 'int
> C == 2**32-1, type is 'long long' or 'unsigned long long', I can't
> tell for sure.

With gcc in Linux, it turns out they use the enum type for C:

  #include <stdio.h>
   
  #define TS(t) t: #t
  #define T(e) printf(#e" is %s\n", \
                      _Generic((e), TS(int), TS(unsigned), TS(enum en_t)))
   
  #define PT(e) printf(#e" is %ssigned\n", (e)-(e) - 1 >= 0 ? "un" : "")
   
  int main(void)
  {
       enum en_t {
            A = 1,
            B = -1,
            C = (unsigned)-1,
       };
       T(A);
       T(B);
       T(C);
       PT(A);
       PT(B);
       PT(C);
  }

prints

  A is int
  B is int
  C is enum en_t
  A is signed
  B is signed
  C is signed

<cut>
-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#150324

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2020-02-27 23:52 -0500
Message-ID<r3a6b3$u09$1@dont-email.me>
In reply to#150290
On 2/27/20 6:53 AM, Ben Bacarisse wrote:
...
> With gcc in Linux, it turns out they use the enum type for C:
> 
>   #include <stdio.h>
>    
>   #define TS(t) t: #t
>   #define T(e) printf(#e" is %s\n", \
>                       _Generic((e), TS(int), TS(unsigned), TS(enum en_t)))

"No two generic associations in the same generic selection shall specify
compatible types." (6.5.1.1p2). That "shall" occurs in a "Constraints"
paragraph section, and therefore mandates a diagnostic.

"Each enumerated type shall be compatible with char, a signed integer
type, or an unsigned integer type. The choice of type is
implementation-defined, ...". (6.7.2.2p4). Since the type is
implementation-defined, that "shall" is a requirement imposed on the
implementation's choice, not on the developer.

As a general rule, putting an enumerated type in a _Generic selection
with other integer types is a bad idea unless you're certain that it
can't be compatible with any of those other types.

>   #define PT(e) printf(#e" is %ssigned\n", (e)-(e) - 1 >= 0 ? "un" : "")
>    
>   int main(void)
>   {
>        enum en_t {
>             A = 1,
>             B = -1,
>             C = (unsigned)-1,

Of course, providing (unsigned)-1 as the value for C is also a
constraint violation. The standard only mandates that at least one
diagnostic be produced, regardless of how many different reasons there
might be why a diagnostic is mandatory. Which might explain why gcc only
produces one for this code.
Generating an executable is permitted, despite the constraint
violation(s?). If it is generated, and if you choose to execute it, the
resulting behavior is undefined, so your results don't mean as much as
they might seem to.

>        };
>        T(A);

[toc] | [prev] | [next] | [standalone]


#150327

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2020-02-28 01:30 -0800
Message-ID<875zfrt5xl.fsf@nosuchdomain.example.com>
In reply to#150324
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
> On 2/27/20 6:53 AM, Ben Bacarisse wrote:
[...]
>>   #define PT(e) printf(#e" is %ssigned\n", (e)-(e) - 1 >= 0 ? "un" : "")
>>    
>>   int main(void)
>>   {
>>        enum en_t {
>>             A = 1,
>>             B = -1,
>>             C = (unsigned)-1,
>
> Of course, providing (unsigned)-1 as the value for C is also a
> constraint violation.

Unless INT_MAX == UINT_MAX (possible if unsigned int has at least one
padding bit).  There might not be an existing implementation where this
is the case.

-- 
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#150266

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2020-02-26 14:09 +0000
Message-ID<877e09zbha.fsf@bsb.me.uk>
In reply to#150254
Thiago Adams <thiago.adams@gmail.com> writes:

> From the standard:
>
> "The identifiers in an enumerator list are declared as constants 
> that have type int and may appear wherever such are permitted.127)"

Clear enough, yes?

> But there is a foot note:

It's just some more text.  It's not a footnote.

> "Each enumerated type shall be compatible with char, a signed 
> integer type, or an unsigned integer type. The choice of type 
> is implementation-defined,128)"

So the type enum xxx is compatible with (almost the same as being the
same type as) one of the integer types no wider than int.

> "128) An implementation may delay the choice of which integer 
> type until all enumeration constants have been seen."

This is so that it can decide how with the type needs to be.

> I am confused. 
>
> enum E { A, B};
>
> Can A and B be int and E not int?
> Can A B and E be != int?

A and B are of type int.  enum E is some type that might not be as wide
as int.

That's a bit like 'a' and 'b'.  They look like characters but they are
constants of type int.  char c = 'a'; is fine even when char is a type
narrow than int.

Consider this program:

#include <stdio.h>

int main(void)
{
     enum E { A, B } e;
     printf("sizeof A: %zu, sizeof e: %zu\n", sizeof A, sizeof e);
}

with some compilers (and some compiler options) it prints

$ ./e                   
sizeof A: 4, sizeof e: 1

and that's allowed.

-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#150271

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2020-02-26 09:47 -0800
Message-ID<87pne1tf3o.fsf@nosuchdomain.example.com>
In reply to#150266
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
> Thiago Adams <thiago.adams@gmail.com> writes:
[...]
>> "Each enumerated type shall be compatible with char, a signed 
>> integer type, or an unsigned integer type. The choice of type 
>> is implementation-defined,128)"
>
> So the type enum xxx is compatible with (almost the same as being the
> same type as) one of the integer types no wider than int.

There's no requirement for the integer type to be no wider than int.
(Though since the enumeration constants are of type int, there's
probably no good reason to choose a type wider than int, unless a
wider type is requested via an extension.)

[...]

-- 
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#150273

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2020-02-26 21:04 +0000
Message-ID<87mu95xdq2.fsf@bsb.me.uk>
In reply to#150271
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>> Thiago Adams <thiago.adams@gmail.com> writes:
> [...]
>>> "Each enumerated type shall be compatible with char, a signed 
>>> integer type, or an unsigned integer type. The choice of type 
>>> is implementation-defined,128)"
>>
>> So the type enum xxx is compatible with (almost the same as being the
>> same type as) one of the integer types no wider than int.
>
> There's no requirement for the integer type to be no wider than int.

Yes, it can be compatible with any integer type.

> (Though since the enumeration constants are of type int, there's
> probably no good reason to choose a type wider than int, unless a
> wider type is requested via an extension.)

I think I extrapolated form this logic.

-- 
Ben.

[toc] | [prev] | [standalone]


Page 2 of 2 — ← Prev page 1 [2]

Back to top | Article view | comp.lang.c


csiph-web