Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easy way to use variables of enum types as string in C?

Here's what I am trying to do:

typedef enum { ONE, TWO, THREE } Numbers;

I am trying to write a function that would do a switch case similar to the following:

char num_str[10];
int process_numbers_str(Numbers num) {
  switch(num) {
    case ONE:
    case TWO:
    case THREE:
    {
      strcpy(num_str, num); //some way to get the symbolic constant name in here?
    } break;
    default:
      return 0; //no match
  return 1;
}

Instead of defining at every case, is there a way to set it using the enum variable like I am trying to do above?

like image 988
zxcv Avatar asked Sep 29 '08 02:09

zxcv


People also ask

How do I convert an enum to a string?

There are two ways to convert an Enum to String in Java, first by using the name() method of Enum which is an implicit method and available to all Enum, and second by using toString() method.

What is the method to convert an enum type to a string type?

We can convert an enum to string by calling the ToString() method of an Enum.

Can we use enum as string?

You can't. I think you have FOUR options here. All four offer a solution but with a slightly different approach... Option One: use the built-in name() on an enum.


2 Answers

The technique from Making something both a C identifier and a string? can be used here.

As usual with such preprocessor stuff, writing and understanding the preprocessor part can be hard, and includes passing macros to other macros and involves using # and ## operators, but using it is real easy. I find this style very useful for long enums, where maintaining the same list twice can be really troublesome.

Factory code - typed only once, usually hidden in the header:

enumFactory.h:

// expansion macro for enum value definition #define ENUM_VALUE(name,assign) name assign,  // expansion macro for enum to string conversion #define ENUM_CASE(name,assign) case name: return #name;  // expansion macro for string to enum conversion #define ENUM_STRCMP(name,assign) if (!strcmp(str,#name)) return name;  /// declare the access function and define enum values #define DECLARE_ENUM(EnumType,ENUM_DEF) \   enum EnumType { \     ENUM_DEF(ENUM_VALUE) \   }; \   const char *GetString(EnumType dummy); \   EnumType Get##EnumType##Value(const char *string); \  /// define the access function names #define DEFINE_ENUM(EnumType,ENUM_DEF) \   const char *GetString(EnumType value) \   { \     switch(value) \     { \       ENUM_DEF(ENUM_CASE) \       default: return ""; /* handle input error */ \     } \   } \   EnumType Get##EnumType##Value(const char *str) \   { \     ENUM_DEF(ENUM_STRCMP) \     return (EnumType)0; /* handle input error */ \   } \ 

Factory used

someEnum.h:

#include "enumFactory.h" #define SOME_ENUM(XX) \     XX(FirstValue,) \     XX(SecondValue,) \     XX(SomeOtherValue,=50) \     XX(OneMoreValue,=100) \  DECLARE_ENUM(SomeEnum,SOME_ENUM) 

someEnum.cpp:

#include "someEnum.h" DEFINE_ENUM(SomeEnum,SOME_ENUM) 

The technique can be easily extended so that XX macros accepts more arguments, and you can also have prepared more macros to substitute for XX for different needs, similar to the three I have provided in this sample.

Comparison to X-Macros using #include / #define / #undef

While this is similar to X-Macros others have mentioned, I think this solution is more elegant in that it does not require #undefing anything, which allows you to hide more of the complicated stuff is in the factory the header file - the header file is something you are not touching at all when you need to define a new enum, therefore new enum definition is a lot shorter and cleaner.

like image 141
Suma Avatar answered Sep 28 '22 03:09

Suma


// Define your enumeration like this (in say numbers.h);
ENUM_BEGIN( Numbers )
    ENUM(ONE),
    ENUM(TWO),
    ENUM(FOUR)
ENUM_END( Numbers )

// The macros are defined in a more fundamental .h file (say defs.h);
#define ENUM_BEGIN(typ) enum typ {
#define ENUM(nam) nam
#define ENUM_END(typ) };

// Now in one and only one .c file, redefine the ENUM macros and reinclude
//  the numbers.h file to build a string table
#undef ENUM_BEGIN
#undef ENUM
#undef ENUM_END
#define ENUM_BEGIN(typ) const char * typ ## _name_table [] = {
#define ENUM(nam) #nam
#define ENUM_END(typ) };
#undef NUMBERS_H_INCLUDED   // whatever you need to do to enable reinclusion
#include "numbers.h"

// Now you can do exactly what you want to do, with no retyping, and for any
//  number of enumerated types defined with the ENUM macro family
//  Your code follows;
char num_str[10];
int process_numbers_str(Numbers num) {
  switch(num) {
    case ONE:
    case TWO:
    case THREE:
    {
      strcpy(num_str, Numbers_name_table[num]); // eg TWO -> "TWO"
    } break;
    default:
      return 0; //no match
  return 1;
}

// Sweet no ? After being frustrated by this for years, I finally came up
//  with this solution for my most recent project and plan to reuse the idea
//  forever
like image 23
Bill Forster Avatar answered Sep 28 '22 03:09

Bill Forster