home bbs files messages ]

Forums before death by AOL, social media and spammers... "We can't have nice things"

   comp.lang.c      Meh, in C you gotta define EVERYTHING      243,242 messages   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]

   Message 241,442 of 243,242   
   Waldek Hebisch to Thiago Adams   
   Re: bugprone-switch-missing-default-case   
   23 Oct 25 23:23:43   
   
   From: antispam@fricas.org   
      
   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  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    
   >>>>>>>   
   >>>>>>> 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;   
      
   [continued in next message]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]


(c) 1994,  bbs@darkrealms.ca