An elegant way to access union members

Is there any way to cancel the #define statement?

In the following example

#define ZERO 0
#define ONE 1
#define TWO 2
#define THREE 3

is it possible to get TWO from integer value 2?

This example comes from C code, but if necessary, I can use C ++ code. My goal is to be able to factor some false switch loops in this form:

switch(num)
{
   case ZERO:
      return std::to_string(foo.V_ZERO);
   case ONE:
      return std::to_string(foo.V_ONE);
   case TWO:
      return std::to_string(foo.V_TWO);
   case THREE:
      return std::to_string(foo.V_THREE);
}

where foo is an instance of such a structure:

struct Foo
{
   union Val
   { 
      int V_ZERO;
      int V_ONE;
      double V_TWO; // nonsense: just to say that types are not the same
      int V_THREE;
   };
};

My limitations are as follows:

  • I cannot remove the functionality provided by #define, that is, I can write something equivalent, for example. an enumeration, but I cannot lose a card between ZERO and 0, ONE and 1, etc.
  • existing code is written in C and I cannot rewrite it in C ++. However, I can write additional C ++ code.

, , , .

EDIT: std:: to_string , , , .

+4
5

, xmacros. , (, ), :

xmacro:

#define XLIST(xmacro) \
   xmacro(ZERO, 0) \
   xmacro(ONE, 1) \
   xmacro(TWO, 2) \
   xmacro(THREE, 3) \

, :

// create an enum containing all items
#define xmacro(name, value) name,
enum Item
{
    XLIST(xmacro)
};
#undef xmacro

// get the number of items
#define xmacro(name, value) +1
const int NumberOfItems = 0 XLIST(xmacro);
#undef xmacro

// enum -> value
int itemToValue(enum Item item)
{
    switch (item)
    {
        // create a mapping from enum to x
#define xmacro(name, value) case name: return value;
        XLIST(xmacro)
#undef xmacro
    }

    return -1;
}

// get enum name
const char * getItemName(enum Item item)
{
    switch (item)
    {
        // create a mapping from enum to x
#define xmacro(name, value) case name: return #name;
        XLIST(xmacro)
#undef xmacro
    }

    return NULL;
}

- :

enum Item
{
    ZERO,
    ONE, 
    TWO, 
    THREE,
};

const int NumberOfItems = 0 +1 +1 +1 +1; // == 4

int itemToValue(enum Item item)
{
    switch (item)
    {
        case ZERO: return 0; 
        case ONE: return 1; 
        case TWO: return 2; 
        case THREE: return 3;   
    }

    return -1;
}

const char * getItemName(enum Item item)
{
    switch (item)
    {
        case ZERO: return "ZERO"; 
        case ONE: return "ONE"; 
        case TWO: return "TWO"; 
        case THREE: return "THREE";

    }

    return NULL;
}

, , .. - , -:

// get struct value by item type
double getValueByName(enum Item item, struct Foo values)
{
    switch (item)
    {
        // create a mapping from enum to x
#define xmacro(name, value) case name: return values.V_##name;
        XLIST(xmacro)
#undef xmacro
    }

    return -1;
}
+3

, .

#define . . , TWO ? 2 , .

const int two_define = TWO;
const int two_literal = 2;

2 , , - TWO 2. const int two_define = 2;, .

: , , int, double s? , double.

+1

- " X", . , . , .

. enum . :

#include <stdio.h>

typedef enum
{
  ZERO,
  ONE,
  TWO,
  THREE,
  SUPPORTED_NUMBERS
} number_t;

const char* STR_NUMBER [] =
{
  "ZERO",
  "ONE",
  "TWO",
  "THREE",
};

_Static_assert((sizeof STR_NUMBER / sizeof *STR_NUMBER) == SUPPORTED_NUMBERS,
               "Error: enum does not correspond to look-up table.");

int main (void)
{
  for(number_t i=0; i<SUPPORTED_NUMBERS; i++)
  {
    printf("%d %s\n", i, STR_NUMBER[i]);
  }
}
+1

:

#define?

2?

, :

#define ZERO 0
#define ONE 1
#define TWO 2
#define THREE 3

typedef struct
{
    int number;
    const char *name;
} name_map_t;


#define MAP_NAME_STR(id) { id, #id },


static const name_map_t name_map_table[] =
{
    MAP_NAME_STR( ZERO  )
    MAP_NAME_STR( ONE   )
    MAP_NAME_STR( TWO   )
    MAP_NAME_STR( THREE )
};

name_map_table, .

0

, , , :

, , , switch, ..

, include, , :

enum(INITIAL_STATE, 0, "This is the initial state")
enum(FLAG_READ, 1, "We have read the flag symbol")
...

, :

struct enum MyEnum {
#define enum(val,ix, string) val,
#include "myenumdef.i"
#undef enum
};

/* then, later in the same file ... */

char *MyEnumStrings[] = {
#define enum(val,ix, string) string,
#include "myenumdef.i"
#undef enum
};

/* .... */

char *MyEnumNames[] = {
#define enum(val, ix, string) #val, /* the string equivalent of enum names */
#include "myenumdef.i"
#undef enum
};

/* and more complex forms... like */

struct myEnumDesc {
    int   e_val;
    int   e_ix;
    char *e_name;
    char *e_desc;
} enum_table[] = {
#define enum(val,ix,string) val, ix, #val, string,
#include "myenumdef.i"
#undef enum
}; /* enum_table */


/* ... even, when I want to switch on them */
    switch(val) {
#define enum(val,ix,string) case VAL_##val: return string"("#val"="#ix")";
#include "myenumdef.i"
#undef enum
    default: return "invalid val";
    } /* switch */

C, , C.

, ( ), enum ( , ), . , .i, , , .

C-only , , .

0

Source: https://habr.com/ru/post/1694038/


All Articles