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


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

bugprone-switch-missing-default-case

Started bypozz <pozzugno@gmail.com>
First post2025-10-22 10:56 +0200
Last post2025-10-22 18:13 +0000
Articles 20 on this page of 38 — 11 participants

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


Contents

  bugprone-switch-missing-default-case pozz <pozzugno@gmail.com> - 2025-10-22 10:56 +0200
    Re: bugprone-switch-missing-default-case Janis Papanagnou <janis_papanagnou+ng@hotmail.com> - 2025-10-22 11:32 +0200
      Re: bugprone-switch-missing-default-case Richard Harnden <richard.nospam@gmail.invalid> - 2025-10-22 12:44 +0100
        Re: bugprone-switch-missing-default-case Thiago Adams <thiago.adams@gmail.com> - 2025-10-22 10:05 -0300
          Re: bugprone-switch-missing-default-case Kaz Kylheku <643-408-1753@kylheku.com> - 2025-10-22 18:22 +0000
            Re: bugprone-switch-missing-default-case David Brown <david.brown@hesbynett.no> - 2025-10-23 09:12 +0200
              Re: bugprone-switch-missing-default-case Thiago Adams <thiago.adams@gmail.com> - 2025-10-23 08:03 -0300
                Re: bugprone-switch-missing-default-case David Brown <david.brown@hesbynett.no> - 2025-10-23 17:06 +0200
                  Re: bugprone-switch-missing-default-case Thiago Adams <thiago.adams@gmail.com> - 2025-10-23 13:15 -0300
                    Re: bugprone-switch-missing-default-case bart <bc@freeuk.com> - 2025-10-23 17:46 +0100
                      Re: bugprone-switch-missing-default-case Thiago Adams <thiago.adams@gmail.com> - 2025-10-23 14:12 -0300
                    Re: bugprone-switch-missing-default-case David Brown <david.brown@hesbynett.no> - 2025-10-23 22:40 +0200
                      Re: bugprone-switch-missing-default-case scott@slp53.sl.home (Scott Lurndal) - 2025-10-23 20:51 +0000
                        Re: bugprone-switch-missing-default-case Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2025-10-23 16:21 -0700
                        Re: bugprone-switch-missing-default-case David Brown <david.brown@hesbynett.no> - 2025-10-24 08:59 +0200
                          Re: bugprone-switch-missing-default-case scott@slp53.sl.home (Scott Lurndal) - 2025-10-24 15:51 +0000
                      Re: bugprone-switch-missing-default-case tTh <tth@none.invalid> - 2025-10-23 23:57 +0200
                        Re: bugprone-switch-missing-default-case David Brown <david.brown@hesbynett.no> - 2025-10-24 09:22 +0200
                    Re: bugprone-switch-missing-default-case antispam@fricas.org (Waldek Hebisch) - 2025-10-23 23:23 +0000
                      Re: bugprone-switch-missing-default-case David Brown <david.brown@hesbynett.no> - 2025-10-24 09:53 +0200
        Re: bugprone-switch-missing-default-case David Brown <david.brown@hesbynett.no> - 2025-10-22 15:41 +0200
          Re: bugprone-switch-missing-default-case Janis Papanagnou <janis_papanagnou+ng@hotmail.com> - 2025-10-22 16:05 +0200
            Re: bugprone-switch-missing-default-case David Brown <david.brown@hesbynett.no> - 2025-10-22 17:23 +0200
              Re: bugprone-switch-missing-default-case Janis Papanagnou <janis_papanagnou+ng@hotmail.com> - 2025-10-23 04:54 +0200
                Re: bugprone-switch-missing-default-case David Brown <david.brown@hesbynett.no> - 2025-10-23 09:30 +0200
              Re: bugprone-switch-missing-default-case antispam@fricas.org (Waldek Hebisch) - 2025-10-23 22:40 +0000
          Re: bugprone-switch-missing-default-case Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2025-10-22 12:41 -0700
            Re: bugprone-switch-missing-default-case pozz <pozzugno@gmail.com> - 2025-10-23 08:47 +0200
              Re: bugprone-switch-missing-default-case Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2025-10-23 16:17 -0700
                Re: bugprone-switch-missing-default-case Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2025-10-23 19:13 -0700
            Re: bugprone-switch-missing-default-case David Brown <david.brown@hesbynett.no> - 2025-10-23 10:44 +0200
        Re: bugprone-switch-missing-default-case Janis Papanagnou <janis_papanagnou+ng@hotmail.com> - 2025-10-22 15:56 +0200
          Re: bugprone-switch-missing-default-case David Brown <david.brown@hesbynett.no> - 2025-10-22 17:25 +0200
            Re: bugprone-switch-missing-default-case Janis Papanagnou <janis_papanagnou+ng@hotmail.com> - 2025-10-23 04:39 +0200
              Re: bugprone-switch-missing-default-case David Brown <david.brown@hesbynett.no> - 2025-10-23 09:08 +0200
            Re: bugprone-switch-missing-default-case Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2025-10-23 16:31 -0700
              Re: bugprone-switch-missing-default-case David Brown <david.brown@hesbynett.no> - 2025-10-24 10:09 +0200
    Re: bugprone-switch-missing-default-case Kaz Kylheku <643-408-1753@kylheku.com> - 2025-10-22 18:13 +0000

Page 1 of 2  [1] 2  Next page →


#394647 — bugprone-switch-missing-default-case

Frompozz <pozzugno@gmail.com>
Date2025-10-22 10:56 +0200
Subjectbugprone-switch-missing-default-case
Message-ID<10da67g$3q59f$1@dont-email.me>
 From here[1]:

> Switch statements without a default case can lead to unexpected
> behavior and incomplete handling of all possible cases. When a switch
> statement lacks a default case, if a value is encountered that does
> not match any of the specified cases, the program will continue
> execution without any defined behavior or handling.

Maybe I misunderstood that sentence caused by my bad English. I knew 
that in case the switch value is not present in any case inside the 
switch, the program continues without doing anything (in the switch) and 
without any problem.

int x = 3;
switch(x) {
   case 1: printf("Hello");break;
   case 2: printf("World");break;
}

Will the program execution continue without any defined behaviour?





[1] 
https://clang.llvm.org/extra/clang-tidy/checks/bugprone/switch-missing-default-case.html

[toc] | [next] | [standalone]


#394648

FromJanis Papanagnou <janis_papanagnou+ng@hotmail.com>
Date2025-10-22 11:32 +0200
Message-ID<10da8bo$hker$1@dont-email.me>
In reply to#394647
On 22.10.2025 10:56, pozz wrote:
> 
>> Switch statements without a default case can lead to unexpected
>> behavior and incomplete handling of all possible cases. When a switch
>> statement lacks a default case, if a value is encountered that does
>> not match any of the specified cases, the program will continue
>> execution without any defined behavior or handling.
> 
> Maybe I misunderstood that sentence caused by my bad English. I knew
> that in case the switch value is not present in any case inside the
> switch, the program continues without doing anything (in the switch) and
> without any problem.
> 
> int x = 3;
> switch(x) {
>   case 1: printf("Hello");break;
>   case 2: printf("World");break;
> }
> 
> Will the program execution continue without any defined behaviour?

Your program fragment is well defined.

What the poster certainly tried to express was that in case you
haven't implemented a complete list of all possible cases and
also not provided a 'default' to catch all non-specified cases,
then you might get in troubles with your program, probably by
possible oversights, future extensions, new data, and whatnot.

Personally I have the habit to always define a default branch,
and even if that default is impossible to reach you'll find an
error message (like "internal error with unexpected value...")
generated at that place.

Janis

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


#394649

FromRichard Harnden <richard.nospam@gmail.invalid>
Date2025-10-22 12:44 +0100
Message-ID<10dag3d$jkju$1@dont-email.me>
In reply to#394648
On 22/10/2025 10:32, Janis Papanagnou wrote:
> On 22.10.2025 10:56, pozz wrote:
>>
>>> Switch statements without a default case can lead to unexpected
>>> behavior and incomplete handling of all possible cases. When a switch
>>> statement lacks a default case, if a value is encountered that does
>>> not match any of the specified cases, the program will continue
>>> execution without any defined behavior or handling.
>>
>> Maybe I misunderstood that sentence caused by my bad English. I knew
>> that in case the switch value is not present in any case inside the
>> switch, the program continues without doing anything (in the switch) and
>> without any problem.
>>
>> int x = 3;
>> switch(x) {
>>    case 1: printf("Hello");break;
>>    case 2: printf("World");break;
>> }
>>
>> Will the program execution continue without any defined behaviour?
> 
> Your program fragment is well defined.
> 
> What the poster certainly tried to express was that in case you
> haven't implemented a complete list of all possible cases and
> also not provided a 'default' to catch all non-specified cases,
> then you might get in troubles with your program, probably by
> possible oversights, future extensions, new data, and whatnot.
> 
> Personally I have the habit to always define a default branch,
> and even if that default is impossible to reach you'll find an
> error message (like "internal error with unexpected value...")
> generated at that place.
> 
Use an enum, and the compiler will warn you ...

$ cat x.c
#include <stdio.h>

enum x {A, B, C};

int main(void)
{
     enum x x = C;

     switch (x)
     {
         case A:
             printf("A\n");
             break;

         case B:
             printf("B\n");
             break;
     }

     return 0;
}

$ gcc -Wall x.c
x.c: In function ‘main’:
x.c:9:9: warning: enumeration value ‘C’ not handled in switch [-Wswitch]
     9 |         switch (x)
       |         ^~~~~~


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


#394651

FromThiago Adams <thiago.adams@gmail.com>
Date2025-10-22 10:05 -0300
Message-ID<10dakrm$l3n2$1@dont-email.me>
In reply to#394649
On 10/22/2025 8:44 AM, Richard Harnden wrote:
....
>> Your program fragment is well defined.
>>
>> What the poster certainly tried to express was that in case you
>> haven't implemented a complete list of all possible cases and
>> also not provided a 'default' to catch all non-specified cases,
>> then you might get in troubles with your program, probably by
>> possible oversights, future extensions, new data, and whatnot.
>>
>> Personally I have the habit to always define a default branch,
>> and even if that default is impossible to reach you'll find an
>> error message (like "internal error with unexpected value...")
>> generated at that place.
>>
> Use an enum, and the compiler will warn you ...
> 
> $ cat x.c
> #include <stdio.h>
> 
> enum x {A, B, C};
> 
> int main(void)
> {
>      enum x x = C;
> 
>      switch (x)
>      {
>          case A:
>              printf("A\n");
>              break;
> 
>          case B:
>              printf("B\n");
>              break;
>      }
> 
>      return 0;
> }
> 
> $ gcc -Wall x.c
> x.c: In function ‘main’:
> x.c:9:9: warning: enumeration value ‘C’ not handled in switch [-Wswitch]
>      9 |         switch (x)
>        |         ^~~~~~
> 
> 

The problem with this GCC approach is when there are many enumerators 
but only a few are used.

For instance :

enum E {A, B, C, /*..., Z*/};

1)
void f(enum E e)
{
     switch (e)
     {
         //used
         case A:
         case B:
          break;

         //NON USED
         case C:
         ...
         case Z:
          break;
     };
}

The problem with (1) is when we have too many
non used enumerators, it is impractical to have a lot of switch cases.

One alternative is to use default for all the non used:

2)
void f(enum E e)
{
     switch (e)
     {
         //used
         case A:
         case B:
          break;

         //NON USED (all others)
         default:
          break;
     };
}


The problem with (2) is when we add a new enumerator and
this new enumerations should be used, but there is no warning and it 
goes accidentally for default.


Solution?

In C2Y the new keyword _Countof was introduced.
It works returns the number of elements of array. IT IS FOR ARRAY ONLY.

I did an EXTENSION in my compiler where _Countof(enum E) also returns 
the number of enumerators.


enum E2 {A, B};
static_assert(_Countof(enum E2) == 2);

(It also could be a new keyword.
static_assert(_EnumCount(enum E2) == 2);)

Having this we can do:

3)
void f(enum E e)
{
     switch (e)
     {
         //used
         case A:
         case B:
          break;

         default:
          static_assert(_EnumCount(enum E2) == 20);
          break;
     };

}

Then when adding a new enumerator the programmer will have to review 
this code and update to 21 if it is not used, or handle it in a new case.

This is also useful in other scenarios. For instance:


enum E parse_enum_e(const char* s)
{
     if (strcmp(s, "A") == 0) return A;
     if (strcmp(s, "B") == 0) return B;
     if (strcmp(s, "C") == 0) return C;
     if (strcmp(s, "D") == 0) return D;
     if (strcmp(s, "E") == 0) return E;
     if (strcmp(s, "F") == 0) return F;
     static_assert(_Countof(enum E) == 6);

     return A;
}

If a new enumerator is added we need to include it.

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


#394661

FromKaz Kylheku <643-408-1753@kylheku.com>
Date2025-10-22 18:22 +0000
Message-ID<20251022111414.630@kylheku.com>
In reply to#394651
On 2025-10-22, Thiago Adams <thiago.adams@gmail.com> wrote:
> On 10/22/2025 8:44 AM, Richard Harnden wrote:
> ....
>>> Your program fragment is well defined.
>>>
>>> What the poster certainly tried to express was that in case you
>>> haven't implemented a complete list of all possible cases and
>>> also not provided a 'default' to catch all non-specified cases,
>>> then you might get in troubles with your program, probably by
>>> possible oversights, future extensions, new data, and whatnot.
>>>
>>> Personally I have the habit to always define a default branch,
>>> and even if that default is impossible to reach you'll find an
>>> error message (like "internal error with unexpected value...")
>>> generated at that place.
>>>
>> Use an enum, and the compiler will warn you ...
>> 
>> $ cat x.c
>> #include <stdio.h>
>> 
>> enum x {A, B, C};
>> 
>> int main(void)
>> {
>>      enum x x = C;
>> 
>>      switch (x)
>>      {
>>          case A:
>>              printf("A\n");
>>              break;
>> 
>>          case B:
>>              printf("B\n");
>>              break;
>>      }
>> 
>>      return 0;
>> }
>> 
>> $ gcc -Wall x.c
>> x.c: In function ‘main’:
>> x.c:9:9: warning: enumeration value ‘C’ not handled in switch [-Wswitch]
>>      9 |         switch (x)
>>        |         ^~~~~~
>> 
>> 
>
> The problem with this GCC approach is when there are many enumerators 
> but only a few are used.

The problem with the C and GCC approach is that there is no
one-size-fits all solution.

Some switches are intended to be exhaustive, such that
missing a case is a bug.

Some are not.

You need an "eswitch" for the exhaustively handled enumerations,  and
switch for the others.

GCC can turn on diagnostics over ranges of a file with pragma
and there is also _Pragram, but it's all too clumsy.

In Common Lisp we have case vs ecase:

[1]> (case 3 (0 "zero") (1 "one") (2 "two"))
NIL
[2]> (case 0 (0 "zero") (1 "one") (2 "two"))
"zero"
[3]> (ecase 3 (0 "zero") (1 "one") (2 "two"))

*** - The value of 3 must be one of 0, 1, 2
      The value is: 3

If falling through without hitting a case would be wrong, you use ecase;
problem solved.

-- 
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @Kazinator@mstdn.ca

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


#394674

FromDavid Brown <david.brown@hesbynett.no>
Date2025-10-23 09:12 +0200
Message-ID<10dckhj$1dnn6$2@dont-email.me>
In reply to#394661
On 22/10/2025 20:22, Kaz Kylheku wrote:
> On 2025-10-22, Thiago Adams <thiago.adams@gmail.com> wrote:
>> On 10/22/2025 8:44 AM, Richard Harnden wrote:
>> ....
>>>> Your program fragment is well defined.
>>>>
>>>> What the poster certainly tried to express was that in case you
>>>> haven't implemented a complete list of all possible cases and
>>>> also not provided a 'default' to catch all non-specified cases,
>>>> then you might get in troubles with your program, probably by
>>>> possible oversights, future extensions, new data, and whatnot.
>>>>
>>>> Personally I have the habit to always define a default branch,
>>>> and even if that default is impossible to reach you'll find an
>>>> error message (like "internal error with unexpected value...")
>>>> generated at that place.
>>>>
>>> Use an enum, and the compiler will warn you ...
>>>
>>> $ cat x.c
>>> #include <stdio.h>
>>>
>>> enum x {A, B, C};
>>>
>>> int main(void)
>>> {
>>>       enum x x = C;
>>>
>>>       switch (x)
>>>       {
>>>           case A:
>>>               printf("A\n");
>>>               break;
>>>
>>>           case B:
>>>               printf("B\n");
>>>               break;
>>>       }
>>>
>>>       return 0;
>>> }
>>>
>>> $ gcc -Wall x.c
>>> x.c: In function ‘main’:
>>> x.c:9:9: warning: enumeration value ‘C’ not handled in switch [-Wswitch]
>>>       9 |         switch (x)
>>>         |         ^~~~~~
>>>
>>>
>>
>> The problem with this GCC approach is when there are many enumerators
>> but only a few are used.
> 
> The problem with the C and GCC approach is that there is no
> one-size-fits all solution.
> 
> Some switches are intended to be exhaustive, such that
> missing a case is a bug.
> 
> Some are not.
> 
> You need an "eswitch" for the exhaustively handled enumerations,  and
> switch for the others.
> 
> GCC can turn on diagnostics over ranges of a file with pragma
> and there is also _Pragram, but it's all too clumsy.
> 

The gcc approach works fine in almost all situations - use 
"-Wswitch=error", and add a default case if your switch is not meant to 
handle all enumeration values.  If the default should do nothing, it's 
just "default: // Not all cases need handling".  If the default should 
never happen, "default: __builtin_unreachable();" or "default: 
__builtin_trap();" might be appropriate.


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


#394679

FromThiago Adams <thiago.adams@gmail.com>
Date2025-10-23 08:03 -0300
Message-ID<10dd215$3totj$2@dont-email.me>
In reply to#394674
On 10/23/2025 4:12 AM, David Brown wrote:
> On 22/10/2025 20:22, Kaz Kylheku wrote:
>> On 2025-10-22, Thiago Adams <thiago.adams@gmail.com> wrote:
>>> On 10/22/2025 8:44 AM, Richard Harnden wrote:
>>> ....
>>>>> Your program fragment is well defined.
>>>>>
>>>>> What the poster certainly tried to express was that in case you
>>>>> haven't implemented a complete list of all possible cases and
>>>>> also not provided a 'default' to catch all non-specified cases,
>>>>> then you might get in troubles with your program, probably by
>>>>> possible oversights, future extensions, new data, and whatnot.
>>>>>
>>>>> Personally I have the habit to always define a default branch,
>>>>> and even if that default is impossible to reach you'll find an
>>>>> error message (like "internal error with unexpected value...")
>>>>> generated at that place.
>>>>>
>>>> Use an enum, and the compiler will warn you ...
>>>>
>>>> $ cat x.c
>>>> #include <stdio.h>
>>>>
>>>> enum x {A, B, C};
>>>>
>>>> int main(void)
>>>> {
>>>>       enum x x = C;
>>>>
>>>>       switch (x)
>>>>       {
>>>>           case A:
>>>>               printf("A\n");
>>>>               break;
>>>>
>>>>           case B:
>>>>               printf("B\n");
>>>>               break;
>>>>       }
>>>>
>>>>       return 0;
>>>> }
>>>>
>>>> $ gcc -Wall x.c
>>>> x.c: In function ‘main’:
>>>> x.c:9:9: warning: enumeration value ‘C’ not handled in switch [- 
>>>> Wswitch]
>>>>       9 |         switch (x)
>>>>         |         ^~~~~~
>>>>
>>>>
>>>
>>> The problem with this GCC approach is when there are many enumerators
>>> but only a few are used.
>>
>> The problem with the C and GCC approach is that there is no
>> one-size-fits all solution.
>>
>> Some switches are intended to be exhaustive, such that
>> missing a case is a bug.
>>
>> Some are not.
>>
>> You need an "eswitch" for the exhaustively handled enumerations,  and
>> switch for the others.
>>
>> GCC can turn on diagnostics over ranges of a file with pragma
>> and there is also _Pragram, but it's all too clumsy.
>>
> 
> The gcc approach works fine in almost all situations - use "- 
> Wswitch=error", and add a default case if your switch is not meant to 
> handle all enumeration values.  If the default should do nothing, it's 
> just "default: // Not all cases need handling".  If the default should 
> never happen, "default: __builtin_unreachable();" or "default: 
> __builtin_trap();" might be appropriate.
> 
> 
> 

But then instead a compiler time error (like I suggest) you leave it for 
runtime.

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


#394681

FromDavid Brown <david.brown@hesbynett.no>
Date2025-10-23 17:06 +0200
Message-ID<10ddg8q$1r5el$1@dont-email.me>
In reply to#394679
On 23/10/2025 13:03, Thiago Adams wrote:
> On 10/23/2025 4:12 AM, David Brown wrote:
>> On 22/10/2025 20:22, Kaz Kylheku wrote:
>>> On 2025-10-22, Thiago Adams <thiago.adams@gmail.com> wrote:
>>>> On 10/22/2025 8:44 AM, Richard Harnden wrote:
>>>> ....
>>>>>> Your program fragment is well defined.
>>>>>>
>>>>>> What the poster certainly tried to express was that in case you
>>>>>> haven't implemented a complete list of all possible cases and
>>>>>> also not provided a 'default' to catch all non-specified cases,
>>>>>> then you might get in troubles with your program, probably by
>>>>>> possible oversights, future extensions, new data, and whatnot.
>>>>>>
>>>>>> Personally I have the habit to always define a default branch,
>>>>>> and even if that default is impossible to reach you'll find an
>>>>>> error message (like "internal error with unexpected value...")
>>>>>> generated at that place.
>>>>>>
>>>>> Use an enum, and the compiler will warn you ...
>>>>>
>>>>> $ cat x.c
>>>>> #include <stdio.h>
>>>>>
>>>>> enum x {A, B, C};
>>>>>
>>>>> int main(void)
>>>>> {
>>>>>       enum x x = C;
>>>>>
>>>>>       switch (x)
>>>>>       {
>>>>>           case A:
>>>>>               printf("A\n");
>>>>>               break;
>>>>>
>>>>>           case B:
>>>>>               printf("B\n");
>>>>>               break;
>>>>>       }
>>>>>
>>>>>       return 0;
>>>>> }
>>>>>
>>>>> $ gcc -Wall x.c
>>>>> x.c: In function ‘main’:
>>>>> x.c:9:9: warning: enumeration value ‘C’ not handled in switch [- 
>>>>> Wswitch]
>>>>>       9 |         switch (x)
>>>>>         |         ^~~~~~
>>>>>
>>>>>
>>>>
>>>> The problem with this GCC approach is when there are many enumerators
>>>> but only a few are used.
>>>
>>> The problem with the C and GCC approach is that there is no
>>> one-size-fits all solution.
>>>
>>> Some switches are intended to be exhaustive, such that
>>> missing a case is a bug.
>>>
>>> Some are not.
>>>
>>> You need an "eswitch" for the exhaustively handled enumerations,  and
>>> switch for the others.
>>>
>>> GCC can turn on diagnostics over ranges of a file with pragma
>>> and there is also _Pragram, but it's all too clumsy.
>>>
>>
>> The gcc approach works fine in almost all situations - use "- 
>> Wswitch=error", and add a default case if your switch is not meant to 
>> handle all enumeration values.  If the default should do nothing, it's 
>> just "default: // Not all cases need handling".  If the default should 
>> never happen, "default: __builtin_unreachable();" or "default: 
>> __builtin_trap();" might be appropriate.
>>
>>
>>
> 
> But then instead a compiler time error (like I suggest) you leave it for 
> runtime.
> 
> 

As I said - use "-Wswitch=error".  That gives you a compile-time error - 
not merely a warning, as you had suggested.  But /if/ your switch is not 
meant to handle all cases, which was what Kaz was complaining about, 
then you add a default case.  I agree with you that a compile-time error 
is best when possible, which is why that was my suggestion.


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


#394682

FromThiago Adams <thiago.adams@gmail.com>
Date2025-10-23 13:15 -0300
Message-ID<10ddkb2$1tadp$1@dont-email.me>
In reply to#394681
On 10/23/2025 12:06 PM, David Brown wrote:
> On 23/10/2025 13:03, Thiago Adams wrote:
>> On 10/23/2025 4:12 AM, David Brown wrote:
>>> On 22/10/2025 20:22, Kaz Kylheku wrote:
>>>> On 2025-10-22, Thiago Adams <thiago.adams@gmail.com> wrote:
>>>>> On 10/22/2025 8:44 AM, Richard Harnden wrote:
>>>>> ....
>>>>>>> Your program fragment is well defined.
>>>>>>>
>>>>>>> What the poster certainly tried to express was that in case you
>>>>>>> haven't implemented a complete list of all possible cases and
>>>>>>> also not provided a 'default' to catch all non-specified cases,
>>>>>>> then you might get in troubles with your program, probably by
>>>>>>> possible oversights, future extensions, new data, and whatnot.
>>>>>>>
>>>>>>> Personally I have the habit to always define a default branch,
>>>>>>> and even if that default is impossible to reach you'll find an
>>>>>>> error message (like "internal error with unexpected value...")
>>>>>>> generated at that place.
>>>>>>>
>>>>>> Use an enum, and the compiler will warn you ...
>>>>>>
>>>>>> $ cat x.c
>>>>>> #include <stdio.h>
>>>>>>
>>>>>> enum x {A, B, C};
>>>>>>
>>>>>> int main(void)
>>>>>> {
>>>>>>       enum x x = C;
>>>>>>
>>>>>>       switch (x)
>>>>>>       {
>>>>>>           case A:
>>>>>>               printf("A\n");
>>>>>>               break;
>>>>>>
>>>>>>           case B:
>>>>>>               printf("B\n");
>>>>>>               break;
>>>>>>       }
>>>>>>
>>>>>>       return 0;
>>>>>> }
>>>>>>
>>>>>> $ gcc -Wall x.c
>>>>>> x.c: In function ‘main’:
>>>>>> x.c:9:9: warning: enumeration value ‘C’ not handled in switch [- 
>>>>>> Wswitch]
>>>>>>       9 |         switch (x)
>>>>>>         |         ^~~~~~
>>>>>>
>>>>>>
>>>>>
>>>>> The problem with this GCC approach is when there are many enumerators
>>>>> but only a few are used.
>>>>
>>>> The problem with the C and GCC approach is that there is no
>>>> one-size-fits all solution.
>>>>
>>>> Some switches are intended to be exhaustive, such that
>>>> missing a case is a bug.
>>>>
>>>> Some are not.
>>>>
>>>> You need an "eswitch" for the exhaustively handled enumerations,  and
>>>> switch for the others.
>>>>
>>>> GCC can turn on diagnostics over ranges of a file with pragma
>>>> and there is also _Pragram, but it's all too clumsy.
>>>>
>>>
>>> The gcc approach works fine in almost all situations - use "- 
>>> Wswitch=error", and add a default case if your switch is not meant to 
>>> handle all enumeration values.  If the default should do nothing, 
>>> it's just "default: // Not all cases need handling".  If the default 
>>> should never happen, "default: __builtin_unreachable();" or "default: 
>>> __builtin_trap();" might be appropriate.
>>>
>>>
>>>
>>
>> But then instead a compiler time error (like I suggest) you leave it 
>> for runtime.
>>
>>
> 
> As I said - use "-Wswitch=error".  That gives you a compile-time error - 
> not merely a warning, as you had suggested.  But /if/ your switch is not 
> meant to handle all cases, which was what Kaz was complaining about, 
> then you add a default case.  I agree with you that a compile-time error 
> is best when possible, which is why that was my suggestion.
> 
> 
> 


I think my sample is not covered by any GCC flag


I will copy past it again:
--------
The problem with this GCC approach is when there are many enumerators 
but only a few are used.

For instance :

enum E {A, B, C /*, ...*/, Z};

1)
void f(enum E e)
{
     switch (e)
     {
         //used
         case A:
         case B:
          break;

         //NON USED
         case C:
         ...
         case Z:
          break;
     };
}

The problem with (1) is when we have too many
non used enumerators, it is impractical to have a lot of switch cases.

One alternative is to use default for all the non used:

2)
void f(enum E e)
{
     switch (e)
     {
         //used
         case A:
         case B:
          break;

         //NON USED (all others)
         default:
          break;
     };
}


The problem with (2) is when we add a new enumerator and
this new enumerations should be used, but there is no warning and it 
goes accidentally for default.


Solution?

In C2Y the new keyword _Countof was introduced.
It works returns the number of elements of array. IT IS FOR ARRAY ONLY.

I did an EXTENSION in my compiler where _Countof(enum E) also returns 
the number of enumerators.


enum E2 {A, B};
static_assert(_Countof(enum E2) == 2);

(It also could be a new keyword.
static_assert(_EnumCount(enum E2) == 2);)

Having this we can do:

3)
void f(enum E e)
{
     switch (e)
     {
         //used
         case A:
         case B:
          break;

         default:
          static_assert(_EnumCount(enum E2) == 20);
          break;
     };

}

Then when adding a new enumerator the programmer will have to review 
this code and update to 21 if it is not used, or handle it in a new case.

This is also useful in other scenarios. For instance:


enum E parse_enum_e(const char* s)
{
     if (strcmp(s, "A") == 0) return A;
     if (strcmp(s, "B") == 0) return B;
     if (strcmp(s, "C") == 0) return C;
     if (strcmp(s, "D") == 0) return D;
     if (strcmp(s, "E") == 0) return E;
     if (strcmp(s, "F") == 0) return F;
     static_assert(_Countof(enum E) == 6);

     return A;
}

If a new enumerator is added we need to include it.

--------

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


#394683

Frombart <bc@freeuk.com>
Date2025-10-23 17:46 +0100
Message-ID<10ddm5o$1su1m$1@dont-email.me>
In reply to#394682
On 23/10/2025 17:15, Thiago Adams wrote:
> On 10/23/2025 12:06 PM, David Brown wrote:

> One alternative is to use default for all the non used:
> 
> 2)
> void f(enum E e)
> {
>      switch (e)
>      {
>          //used
>          case A:
>          case B:
>           break;
> 
>          //NON USED (all others)
>          default:
>           break;
>      };
> }
> 
> 
> The problem with (2) is when we add a new enumerator and
> this new enumerations should be used,

How does the compiler know whether it should be used or not?

And if not, how do you stop the warning? (Of non-exhaustive checking, if 
there is one.)

> In C2Y the new keyword _Countof was introduced.
> It works returns the number of elements of array. IT IS FOR ARRAY ONLY.
> 
> I did an EXTENSION in my compiler where _Countof(enum E) also returns 
> the number of enumerators.
> 
> 
> enum E2 {A, B};
> static_assert(_Countof(enum E2) == 2);
> 
> (It also could be a new keyword.
> static_assert(_EnumCount(enum E2) == 2);)
> 
> Having this we can do:
> 
> 3)
> void f(enum E e)
> {
>      switch (e)
>      {
>          //used
>          case A:
>          case B:
>           break;
> 
>          default:
>           static_assert(_EnumCount(enum E2) == 20);
>           break;
>      };
> 
> }
> 
> Then when adding a new enumerator the programmer will have to review 
> this code and update to 21 if it is not used, or handle it in a new case.

It sounds limited. What if part of the set of enums is conditional? Then 
that '20' can vary depending on some macro value.

But having a hard-coded 20 is also problematical; do you have to 
painstakingly count maybe 200 enumerations? And then keep it maintained? 
What if you put in the wrong number?

What if you want to temporarily comment out some of those cases, or some 
of the enums?

There may also be muliple 'switch' statements working on the same set of 
enums, which may need to check a different subset.

Anyway, I don't see how this helps with reporting whether a enum that 
should be in a 'case' label is missing, or vice versa, when it should be 
checked.


> 
> This is also useful in other scenarios. For instance:
> 
> 
> enum E parse_enum_e(const char* s)
> {
>      if (strcmp(s, "A") == 0) return A;
>      if (strcmp(s, "B") == 0) return B;
>      if (strcmp(s, "C") == 0) return C;
>      if (strcmp(s, "D") == 0) return D;
>      if (strcmp(s, "E") == 0) return E;
>      if (strcmp(s, "F") == 0) return F;
>      static_assert(_Countof(enum E) == 6);
> 
>      return A;
> }
> 
> If a new enumerator is added we need to include it.

Another hard-coded value! An anti-pattern I think. Most of what I said 
above applies here. You have N enum values, you have N checking lines, 
and you have that static assert on N. But what happens if you leave out 
a checking line, or cidentally have the D line twice then check F?

What if you forget to update it to 7, or write it as 5 anyway?

It looks like a weak check that also adds more opportunities for error.

I think getting the number of values of an enum type has some uses: you 
can use to iterate over the values, when they start from zero and are 
consecutive.

Or it can iterate over arrays indexed by the enum. And it can be used to 
set the bounds of such arrays. These sound more useful and more reliable 
than your asserts!

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


#394684

FromThiago Adams <thiago.adams@gmail.com>
Date2025-10-23 14:12 -0300
Message-ID<10ddnlv$1uu0p$1@dont-email.me>
In reply to#394683
On 10/23/2025 1:46 PM, bart wrote:
> On 23/10/2025 17:15, Thiago Adams wrote:
>> On 10/23/2025 12:06 PM, David Brown wrote:
> 
>> One alternative is to use default for all the non used:
>>
>> 2)
>> void f(enum E e)
>> {
>>      switch (e)
>>      {
>>          //used
>>          case A:
>>          case B:
>>           break;
>>
>>          //NON USED (all others)
>>          default:
>>           break;
>>      };
>> }
>>
>>
>> The problem with (2) is when we add a new enumerator and
>> this new enumerations should be used,
> 
> How does the compiler know whether it should be used or not?
> 
> And if not, how do you stop the warning? (Of non-exhaustive checking, if 
> there is one.)
> 
>> In C2Y the new keyword _Countof was introduced.
>> It works returns the number of elements of array. IT IS FOR ARRAY ONLY.
>>
>> I did an EXTENSION in my compiler where _Countof(enum E) also returns 
>> the number of enumerators.
>>
>>
>> enum E2 {A, B};
>> static_assert(_Countof(enum E2) == 2);
>>
>> (It also could be a new keyword.
>> static_assert(_EnumCount(enum E2) == 2);)
>>
>> Having this we can do:
>>
>> 3)
>> void f(enum E e)
>> {
>>      switch (e)
>>      {
>>          //used
>>          case A:
>>          case B:
>>           break;
>>
>>          default:
>>           static_assert(_EnumCount(enum E2) == 20);
>>           break;
>>      };
>>
>> }
>>
>> Then when adding a new enumerator the programmer will have to review 
>> this code and update to 21 if it is not used, or handle it in a new case.
> 
> It sounds limited. What if part of the set of enums is conditional? Then 
> that '20' can vary depending on some macro value.
> 
> But having a hard-coded 20 is also problematical; do you have to 
> painstakingly count maybe 200 enumerations? And then keep it maintained? 
> What if you put in the wrong number?
> 
> What if you want to temporarily comment out some of those cases, or some 
> of the enums?
> 
> There may also be muliple 'switch' statements working on the same set of 
> enums, which may need to check a different subset.
> 
> Anyway, I don't see how this helps with reporting whether a enum that 
> should be in a 'case' label is missing, or vice versa, when it should be 
> checked.
> 
> 
>>
>> This is also useful in other scenarios. For instance:
>>
>>
>> enum E parse_enum_e(const char* s)
>> {
>>      if (strcmp(s, "A") == 0) return A;
>>      if (strcmp(s, "B") == 0) return B;
>>      if (strcmp(s, "C") == 0) return C;
>>      if (strcmp(s, "D") == 0) return D;
>>      if (strcmp(s, "E") == 0) return E;
>>      if (strcmp(s, "F") == 0) return F;
>>      static_assert(_Countof(enum E) == 6);
>>
>>      return A;
>> }
>>
>> If a new enumerator is added we need to include it.
> 
> Another hard-coded value! An anti-pattern I think. Most of what I said 
> above applies here. You have N enum values, you have N checking lines, 
> and you have that static assert on N. But what happens if you leave out 
> a checking line, or cidentally have the D line twice then check F?
> 
> What if you forget to update it to 7, or write it as 5 anyway?
> 
> It looks like a weak check that also adds more opportunities for error.
> 
> I think getting the number of values of an enum type has some uses: you 
> can use to iterate over the values, when they start from zero and are 
> consecutive.
> 
> Or it can iterate over arrays indexed by the enum. And it can be used to 
> set the bounds of such arrays. These sound more useful and more reliable 
> than your asserts!


I am trying to avoid having to answer each question (but if necessary I 
can do that)

Consider:

bool is_free(enum product product)
{
     switch (procuct)
     {
        case combo1:
        case combo2:
         return true;
        default:
        break;
    }
    static_assert(_countof(enum product) == 100);
    return false;
}

I have 100 products (or more), just few are free.
I have this function that return true is the product is free.

If I add a new product I need update this function. The new product can 
be free or not. default: assert(false) only catches the problem in runtime.
How to count? You can print _countof(enum product)



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


#394687

FromDavid Brown <david.brown@hesbynett.no>
Date2025-10-23 22:40 +0200
Message-ID<10de3sh$24ftv$1@dont-email.me>
In reply to#394682
On 23/10/2025 18:15, Thiago Adams wrote:
> On 10/23/2025 12:06 PM, David Brown wrote:
>> On 23/10/2025 13:03, Thiago Adams wrote:
>>> On 10/23/2025 4:12 AM, David Brown wrote:
>>>> On 22/10/2025 20:22, Kaz Kylheku wrote:
>>>>> On 2025-10-22, Thiago Adams <thiago.adams@gmail.com> wrote:
>>>>>> On 10/22/2025 8:44 AM, Richard Harnden wrote:
>>>>>> ....
>>>>>>>> Your program fragment is well defined.
>>>>>>>>
>>>>>>>> What the poster certainly tried to express was that in case you
>>>>>>>> haven't implemented a complete list of all possible cases and
>>>>>>>> also not provided a 'default' to catch all non-specified cases,
>>>>>>>> then you might get in troubles with your program, probably by
>>>>>>>> possible oversights, future extensions, new data, and whatnot.
>>>>>>>>
>>>>>>>> Personally I have the habit to always define a default branch,
>>>>>>>> and even if that default is impossible to reach you'll find an
>>>>>>>> error message (like "internal error with unexpected value...")
>>>>>>>> generated at that place.
>>>>>>>>
>>>>>>> Use an enum, and the compiler will warn you ...
>>>>>>>
>>>>>>> $ cat x.c
>>>>>>> #include <stdio.h>
>>>>>>>
>>>>>>> enum x {A, B, C};
>>>>>>>
>>>>>>> int main(void)
>>>>>>> {
>>>>>>>       enum x x = C;
>>>>>>>
>>>>>>>       switch (x)
>>>>>>>       {
>>>>>>>           case A:
>>>>>>>               printf("A\n");
>>>>>>>               break;
>>>>>>>
>>>>>>>           case B:
>>>>>>>               printf("B\n");
>>>>>>>               break;
>>>>>>>       }
>>>>>>>
>>>>>>>       return 0;
>>>>>>> }
>>>>>>>
>>>>>>> $ gcc -Wall x.c
>>>>>>> x.c: In function ‘main’:
>>>>>>> x.c:9:9: warning: enumeration value ‘C’ not handled in switch [- 
>>>>>>> Wswitch]
>>>>>>>       9 |         switch (x)
>>>>>>>         |         ^~~~~~
>>>>>>>
>>>>>>>
>>>>>>
>>>>>> The problem with this GCC approach is when there are many enumerators
>>>>>> but only a few are used.
>>>>>
>>>>> The problem with the C and GCC approach is that there is no
>>>>> one-size-fits all solution.
>>>>>
>>>>> Some switches are intended to be exhaustive, such that
>>>>> missing a case is a bug.
>>>>>
>>>>> Some are not.
>>>>>
>>>>> You need an "eswitch" for the exhaustively handled enumerations,  and
>>>>> switch for the others.
>>>>>
>>>>> GCC can turn on diagnostics over ranges of a file with pragma
>>>>> and there is also _Pragram, but it's all too clumsy.
>>>>>
>>>>
>>>> The gcc approach works fine in almost all situations - use "- 
>>>> Wswitch=error", and add a default case if your switch is not meant 
>>>> to handle all enumeration values.  If the default should do nothing, 
>>>> it's just "default: // Not all cases need handling".  If the default 
>>>> should never happen, "default: __builtin_unreachable();" or 
>>>> "default: __builtin_trap();" might be appropriate.
>>>>
>>>>
>>>>
>>>
>>> But then instead a compiler time error (like I suggest) you leave it 
>>> for runtime.
>>>
>>>
>>
>> As I said - use "-Wswitch=error".  That gives you a compile-time error 
>> - not merely a warning, as you had suggested.  But /if/ your switch is 
>> not meant to handle all cases, which was what Kaz was complaining 
>> about, then you add a default case.  I agree with you that a compile- 
>> time error is best when possible, which is why that was my suggestion.
>>
>>
>>
> 
> 
> I think my sample is not covered by any GCC flag
> 
> 
> I will copy past it again:
> --------
> The problem with this GCC approach is when there are many enumerators 
> but only a few are used.
> 
> For instance :
> 
> enum E {A, B, C /*, ...*/, Z};
> 
> 1)
> void f(enum E e)
> {
>      switch (e)
>      {
>          //used
>          case A:
>          case B:
>           break;
> 
>          //NON USED
>          case C:
>          ...
>          case Z:
>           break;
>      };
> }
> 
> The problem with (1) is when we have too many
> non used enumerators, it is impractical to have a lot of switch cases.
> 
> One alternative is to use default for all the non used:
> 
> 2)
> void f(enum E e)
> {
>      switch (e)
>      {
>          //used
>          case A:
>          case B:
>           break;
> 
>          //NON USED (all others)
>          default:
>           break;
>      };
> }
> 
> 
> The problem with (2) is when we add a new enumerator and
> this new enumerations should be used, but there is no warning and it 
> goes accidentally for default.
> 
> 
> Solution?
> 
> In C2Y the new keyword _Countof was introduced.
> It works returns the number of elements of array. IT IS FOR ARRAY ONLY.
> 
> I did an EXTENSION in my compiler where _Countof(enum E) also returns 
> the number of enumerators.
> 
> 
> enum E2 {A, B};
> static_assert(_Countof(enum E2) == 2);
> 
> (It also could be a new keyword.
> static_assert(_EnumCount(enum E2) == 2);)
> 
> Having this we can do:
> 
> 3)
> void f(enum E e)
> {
>      switch (e)
>      {
>          //used
>          case A:
>          case B:
>           break;
> 
>          default:
>           static_assert(_EnumCount(enum E2) == 20);
>           break;
>      };
> 
> }
> 
I appreciate that enumerations in C are missing a lot of potential 
compared to enumerations in some other languages (they could be real new 
types that do not implicitly convert with "int", they could have a 
"count" feature, they could have a feature for having the identifier 
names in a const array of strings, etc.).  But none of this is at all 
hard with today's enums :

enum E1 { A, B, C, D, last_E1 = D };

  void f(enum E1 e)
  {
       switch (e)
       {
           //used
           case A:
           case B:
            break;

           default:
            static_assert(last_E1 == D);
	   // or, according to preference
            static_assert(last_E1 == 4);
            break;
       };

  }

People do this regularly.  Personally, I usually prefer to have the 
"last", "max", or "count" indicator outside the enumeration :

	enum E1 { A, B, C, D };
	static const int count_of_E1 = D;

It is not standardised, so different people use different names, but 
it's perfectly doable.

And if you want to automate it a little more and negate the possibility 
of forgetting to change the "count_of_E1" line, because automation of 
these things is nice, then I think you could easily put together an 
XMacro style solution.

In a new, different language, I'd want better enumerations with more 
features, and this kind of thing could be handled better in the 
language.  But for C, I can't see this being worth the effort for an 
extension.


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


#394688

Fromscott@slp53.sl.home (Scott Lurndal)
Date2025-10-23 20:51 +0000
Message-ID<5zwKQ.658591$7Ika.341536@fx17.iad>
In reply to#394687
David Brown <david.brown@hesbynett.no> writes:
>On 23/10/2025 18:15, Thiago Adams wrote:
>> On 10/23/2025 12:06 PM, David Brown wrote:

>People do this regularly.  Personally, I usually prefer to have the 
>"last", "max", or "count" indicator outside the enumeration :
>
>	enum E1 { A, B, C, D };
>	static const int count_of_E1 = D;

D has the value '3', yet there are four elements in the
set.  Perhaps the name should be count_of_E1_minus_1?

How do you use count_of_E1 in this case?

Too bad 'sizeof' doesn't work on typed enums.

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


#394694

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2025-10-23 16:21 -0700
Message-ID<87bjlx2ohg.fsf@example.invalid>
In reply to#394688
scott@slp53.sl.home (Scott Lurndal) writes:
> David Brown <david.brown@hesbynett.no> writes:
>>On 23/10/2025 18:15, Thiago Adams wrote:
>>> On 10/23/2025 12:06 PM, David Brown wrote:
>
>>People do this regularly.  Personally, I usually prefer to have the 
>>"last", "max", or "count" indicator outside the enumeration :
>>
>>	enum E1 { A, B, C, D };
>>	static const int count_of_E1 = D;
>
> D has the value '3', yet there are four elements in the
> set.  Perhaps the name should be count_of_E1_minus_1?
>
> How do you use count_of_E1 in this case?
>
> Too bad 'sizeof' doesn't work on typed enums.

sizeof works just fine on typed enums.  It just doesn't tell you how
many members it has.

-- 
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */

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


#394700

FromDavid Brown <david.brown@hesbynett.no>
Date2025-10-24 08:59 +0200
Message-ID<10df844$2dph4$1@dont-email.me>
In reply to#394688
On 23/10/2025 22:51, Scott Lurndal wrote:
> David Brown <david.brown@hesbynett.no> writes:
>> On 23/10/2025 18:15, Thiago Adams wrote:
>>> On 10/23/2025 12:06 PM, David Brown wrote:
> 
>> People do this regularly.  Personally, I usually prefer to have the
>> "last", "max", or "count" indicator outside the enumeration :
>>
>> 	enum E1 { A, B, C, D };
>> 	static const int count_of_E1 = D;
> 
> D has the value '3', yet there are four elements in the
> set.  Perhaps the name should be count_of_E1_minus_1?
> 

Argh!  That's the danger of writing code directly in Usenet posts - it's 
easy to make the silliest of mistakes.  Clearly "D" is fine for "max" or 
"last", but a "count" would want "D + 1".

> How do you use count_of_E1 in this case?
> 
> Too bad 'sizeof' doesn't work on typed enums.

Sizeof does work on enumerated types :

	enum E : unsigned short { a, b, c, d, e, f };

	int x = sizeof(enum E);
	int y = sizeof(a);

"x" and "y" are both 2 on x86.  It would be a terrible mistake for it to 
be anything else here.

A better possibility is the _Lengthof operator in the future C2y 
standard.  It is for arrays, giving the number of elements (rather than 
the size).  I believe it could be practical to have it apply to 
enumeration types and give the count of the elements, though it is not 
clear what it should do if the enumeration constants are explicitly 
initialised in different ways.

(For those that don't obsessively read the latest standards, specifying 
the underlying type of an enumeration is a C23 addition.  But sizeof 
works without that too.)

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


#394709

Fromscott@slp53.sl.home (Scott Lurndal)
Date2025-10-24 15:51 +0000
Message-ID<6gNKQ.166393$D_Tb.132753@fx47.iad>
In reply to#394700
David Brown <david.brown@hesbynett.no> writes:
>On 23/10/2025 22:51, Scott Lurndal wrote:
>> David Brown <david.brown@hesbynett.no> writes:
>>> On 23/10/2025 18:15, Thiago Adams wrote:
>>>> On 10/23/2025 12:06 PM, David Brown wrote:
>> 
>>> People do this regularly.  Personally, I usually prefer to have the
>>> "last", "max", or "count" indicator outside the enumeration :
>>>
>>> 	enum E1 { A, B, C, D };
>>> 	static const int count_of_E1 = D;
>> 
>> D has the value '3', yet there are four elements in the
>> set.  Perhaps the name should be count_of_E1_minus_1?
>> 
>
>Argh!  That's the danger of writing code directly in Usenet posts - it's 
>easy to make the silliest of mistakes.  Clearly "D" is fine for "max" or 
>"last", but a "count" would want "D + 1".
>
>> How do you use count_of_E1 in this case?
>> 
>> Too bad 'sizeof' doesn't work on typed enums.
>
>Sizeof does work on enumerated types :

yes, I'm aware.  I should have added 'to determine the number of enums'.

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


#394689

FromtTh <tth@none.invalid>
Date2025-10-23 23:57 +0200
Message-ID<10de8bn$1hhn$1@news.gegeweb.eu>
In reply to#394687
On 10/23/25 22:40, David Brown wrote:
> Personally, I usually prefer to have the "last", "max", or "count" 
> indicator outside the enumeration :
> 
>      enum E1 { A, B, C, D };
>      static const int count_of_E1 = D;

    Why did you use 'static' here ?

-- 
**                                                            **
*                      tTh des Bourtoulots                     *
*                  http://maison.tth.netlib.re/                *
**                                                            **

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


#394701

FromDavid Brown <david.brown@hesbynett.no>
Date2025-10-24 09:22 +0200
Message-ID<10df9fk$2dph4$2@dont-email.me>
In reply to#394689
On 23/10/2025 23:57, tTh wrote:
> On 10/23/25 22:40, David Brown wrote:
>> Personally, I usually prefer to have the "last", "max", or "count" 
>> indicator outside the enumeration :
>>
>>      enum E1 { A, B, C, D };
>>      static const int count_of_E1 = D;
> 
>     Why did you use 'static' here ?
> 

The enumeration definition will either be in a C file, in which case the 
const should be "static" because it is local to the file, or it will be 
in a header, in which case making the small const "static" can be vastly 
more efficient in many ways.  I have been assuming that this definition 
is at file scope, as most type declarations are, rather than local to a 
function.  If it is local to a function, then decent compilers will give 
the same resulting code with or without the static.


But I see I have made a mistake here - I have mixed up with C++.  You 
can't use static_assert in C with a "const" like this for compile-time 
checking.  So in C, prior to C23, you actually need to write (with the 
+1 correction to my original example) :

       enum E1 { A, B, C, D };
       enum { count_of_E1 = D + 1 };

if you want to be able to write :

	_Static_assert(count_of_E1 == 4, "Checking enumeration");

With C23, you could have "constexpr int count_of_E1 = D;" and use it in 
a static assertion.  (constexpr declarations always have internal 
linkage, so no "static" is needed.)

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


#394695

Fromantispam@fricas.org (Waldek Hebisch)
Date2025-10-23 23:23 +0000
Message-ID<10deddt$1tjqm$1@paganini.bofh.team>
In reply to#394682
Thiago Adams <thiago.adams@gmail.com> wrote:
> On 10/23/2025 12:06 PM, David Brown wrote:
>> On 23/10/2025 13:03, Thiago Adams wrote:
>>> On 10/23/2025 4:12 AM, David Brown wrote:
>>>> On 22/10/2025 20:22, Kaz Kylheku wrote:
>>>>> On 2025-10-22, Thiago Adams <thiago.adams@gmail.com> wrote:
>>>>>> On 10/22/2025 8:44 AM, Richard Harnden wrote:
>>>>>> ....
>>>>>>>> Your program fragment is well defined.
>>>>>>>>
>>>>>>>> What the poster certainly tried to express was that in case you
>>>>>>>> haven't implemented a complete list of all possible cases and
>>>>>>>> also not provided a 'default' to catch all non-specified cases,
>>>>>>>> then you might get in troubles with your program, probably by
>>>>>>>> possible oversights, future extensions, new data, and whatnot.
>>>>>>>>
>>>>>>>> Personally I have the habit to always define a default branch,
>>>>>>>> and even if that default is impossible to reach you'll find an
>>>>>>>> error message (like "internal error with unexpected value...")
>>>>>>>> generated at that place.
>>>>>>>>
>>>>>>> Use an enum, and the compiler will warn you ...
>>>>>>>
>>>>>>> $ cat x.c
>>>>>>> #include <stdio.h>
>>>>>>>
>>>>>>> enum x {A, B, C};
>>>>>>>
>>>>>>> int main(void)
>>>>>>> {
>>>>>>>       enum x x = C;
>>>>>>>
>>>>>>>       switch (x)
>>>>>>>       {
>>>>>>>           case A:
>>>>>>>               printf("A\n");
>>>>>>>               break;
>>>>>>>
>>>>>>>           case B:
>>>>>>>               printf("B\n");
>>>>>>>               break;
>>>>>>>       }
>>>>>>>
>>>>>>>       return 0;
>>>>>>> }
>>>>>>>
>>>>>>> $ gcc -Wall x.c
>>>>>>> x.c: In function ‘main’:
>>>>>>> x.c:9:9: warning: enumeration value ‘C’ not handled in switch [- 
>>>>>>> Wswitch]
>>>>>>>       9 |         switch (x)
>>>>>>>         |         ^~~~~~
>>>>>>>
>>>>>>>
>>>>>>
>>>>>> The problem with this GCC approach is when there are many enumerators
>>>>>> but only a few are used.
>>>>>
>>>>> The problem with the C and GCC approach is that there is no
>>>>> one-size-fits all solution.
>>>>>
>>>>> Some switches are intended to be exhaustive, such that
>>>>> missing a case is a bug.
>>>>>
>>>>> Some are not.
>>>>>
>>>>> You need an "eswitch" for the exhaustively handled enumerations,  and
>>>>> switch for the others.
>>>>>
>>>>> GCC can turn on diagnostics over ranges of a file with pragma
>>>>> and there is also _Pragram, but it's all too clumsy.
>>>>>
>>>>
>>>> The gcc approach works fine in almost all situations - use "- 
>>>> Wswitch=error", and add a default case if your switch is not meant to 
>>>> handle all enumeration values.  If the default should do nothing, 
>>>> it's just "default: // Not all cases need handling".  If the default 
>>>> should never happen, "default: __builtin_unreachable();" or "default: 
>>>> __builtin_trap();" might be appropriate.
>>>>
>>>>
>>>>
>>>
>>> But then instead a compiler time error (like I suggest) you leave it 
>>> for runtime.
>>>
>>>
>> 
>> As I said - use "-Wswitch=error".  That gives you a compile-time error - 
>> not merely a warning, as you had suggested.  But /if/ your switch is not 
>> meant to handle all cases, which was what Kaz was complaining about, 
>> then you add a default case.  I agree with you that a compile-time error 
>> is best when possible, which is why that was my suggestion.
>> 
>> 
>> 
> 
> 
> I think my sample is not covered by any GCC flag
> 
> 
> I will copy past it again:
> --------
> The problem with this GCC approach is when there are many enumerators 
> but only a few are used.
> 
> For instance :
> 
> enum E {A, B, C /*, ...*/, Z};
> 
> 1)
> void f(enum E e)
> {
>     switch (e)
>     {
>         //used
>         case A:
>         case B:
>          break;
> 
>         //NON USED
>         case C:
>         ...
>         case Z:
>          break;
>     };
> }
> 
> The problem with (1) is when we have too many
> non used enumerators, it is impractical to have a lot of switch cases.
> 
> One alternative is to use default for all the non used:
> 
> 2)
> void f(enum E e)
> {
>     switch (e)
>     {
>         //used
>         case A:
>         case B:
>          break;
> 
>         //NON USED (all others)
>         default:
>          break;
>     };
> }
> 
> 
> The problem with (2) is when we add a new enumerator and
> this new enumerations should be used, but there is no warning and it 
> goes accidentally for default.
> 
> 
> Solution?
> 
> In C2Y the new keyword _Countof was introduced.
> It works returns the number of elements of array. IT IS FOR ARRAY ONLY.
> 
> I did an EXTENSION in my compiler where _Countof(enum E) also returns 
> the number of enumerators.
> 
> 
> enum E2 {A, B};
> static_assert(_Countof(enum E2) == 2);
> 
> (It also could be a new keyword.
> static_assert(_EnumCount(enum E2) == 2);)
> 
> Having this we can do:
> 
> 3)
> void f(enum E e)
> {
>     switch (e)
>     {
>         //used
>         case A:
>         case B:
>          break;
> 
>         default:
>          static_assert(_EnumCount(enum E2) == 20);
>          break;
>     };
> 
> }
> 
> Then when adding a new enumerator the programmer will have to review 
> this code and update to 21 if it is not used, or handle it in a new case.

This helps catch some switches that need attention, but will miss
situations where you need to change handling of some existing code,
more precisely, when you need to remove case from some switches and
add it to different ones.

In similar cases I usually depend on a different approach: I am
writing my types in a way that can be found by textual searches.
If there is any change to usage pattern I search for all uses
of given type (I may also wrap enum inside otherwise useless
struct to make sure that all uses need declaration).

Let me mention variation of technique proposed by David Brown.
Namely, for me reasonably typical usage pattern is when I
have a few ranges of enum, each with somewhat different usage.
For example, in a compiler I need codes for terminals and
nonterminals.  But there are places when I need to handle both,
while in other places only terminals (or maybe nonterminals)
make sense.  In such case one can define things like
'last_terminal' and 'last_sym_code' (and possibly 'first_nonterminal')
and test in appropriate places that codes are in expected
range.  In Pascal one can define new types as subranges of
bigger enum type, IIUC in C this is not possible.

> This is also useful in other scenarios. For instance:
> 
> 
> enum E parse_enum_e(const char* s)
> {
>     if (strcmp(s, "A") == 0) return A;
>     if (strcmp(s, "B") == 0) return B;
>     if (strcmp(s, "C") == 0) return C;
>     if (strcmp(s, "D") == 0) return D;
>     if (strcmp(s, "E") == 0) return E;
>     if (strcmp(s, "F") == 0) return F;
>     static_assert(_Countof(enum E) == 6);
> 
>     return A;
> }
> 
> If a new enumerator is added we need to include it.

In similar context I would probably use table of pairs + GNU
tyle macrology.  That is have "codes.h" with content like

M(A)
M(B)
...
M(F)

and in source file have

enum codes {
#define M(x) x,
#include "codes.h"
#undef M
};

struct {char * name; enum codes code;} name_tab[] = {
#define M(x) {#x, x},
#include "codes.h"
#undef M
};

Or possibly have an external program to generate needed C code.
The point is that when usage is consistent with a simple pattern
it is easier to make sure that code is correct "by construction"
than to depend on language features to catch errors in manually
maintained code.

-- 
                              Waldek Hebisch

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


#394703

FromDavid Brown <david.brown@hesbynett.no>
Date2025-10-24 09:53 +0200
Message-ID<10dfb9n$2eoat$1@dont-email.me>
In reply to#394695
On 24/10/2025 01:23, Waldek Hebisch wrote:
> Thiago Adams <thiago.adams@gmail.com> wrote:
>> On 10/23/2025 12:06 PM, David Brown wrote:
>>> On 23/10/2025 13:03, Thiago Adams wrote:

>>
>> Then when adding a new enumerator the programmer will have to review
>> this code and update to 21 if it is not used, or handle it in a new case.
> 
> This helps catch some switches that need attention, but will miss
> situations where you need to change handling of some existing code,
> more precisely, when you need to remove case from some switches and
> add it to different ones.
> 
> In similar cases I usually depend on a different approach: I am
> writing my types in a way that can be found by textual searches.
> If there is any change to usage pattern I search for all uses
> of given type (I may also wrap enum inside otherwise useless
> struct to make sure that all uses need declaration).

Since you mention "textual searches", one technique that can sometimes 
be helpful when you change a definition and want to be sure that you 
have changed or checked all uses is to change the names in the 
declaration.  While we have been using "enum E { A, B, C };" in these 
posts, real code typically has longer names - perhaps "enum run_states", 
or whatever, with either a prefix or a postfix to the enumeration type 
and the enumerators.  If you change that in your declaration, such as by 
adding an X somewhere, you are guaranteed compile-time errors until you 
have gone through all the uses in the code and added the X as you 
checked the use-cases and changed or added as appropriate.  Once 
everything is complete, a big search-and-replace can remove the X's.

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


Page 1 of 2  [1] 2  Next page →

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


csiph-web