From: thiago.adams@gmail.com   
      
   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.   
      
   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.   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|