views:

316

answers:

4

Requirements:

  • Must be able to use C strings as well as C++ strings
  • Fast
  • No maps
  • No templates
  • No direct lookup, i.e. index might be out of bounds.
  • Index is not consecutive
  • Enums and strings contained in one header file
  • Only instantiate what you use.

This is what I have come up with so far:

- test.hh -

// Generic mapper
// 
// The idea here is to create a map between an integer and a string.
// By including it inside a class we prevent every module which
// includes this include file from creating their own instance.
//
struct Mapper_s
{
   int Idx;
   const char *pStr;
};

// Status
enum State_t
{
   Running = 1,
   Jumping = 6, 
   Singing = 12
};

struct State_s
{
   static const Mapper_s *GetpMap(void)
   {
       static Mapper_s Map[] = 
       {
            { Running,   "Running" },
            { Jumping,   "Jumping" },
            { Singing,   "Singing" },
            { 0,   0}
        };
        return Map;
    };
};

- test.cc -
// This is a generic function 
const char *MapEnum2Str(int Idx, const Mapper_s *pMap)
{
    int i;
    static const char UnknownStr[] = "Unknown";

    for (i = 0; pMap[i].pStr != 0; i++)
    {
        if (Idx == pMap[i].Idx)
        {
            return pMap[i].pStr;
        }
    }

    return UnknownStr;
}

int main()
{
   cout << "State: " << MapEnum2Str(State, State_s::GetpMap()) << endl;
   return 0;
}

Any suggestions on how to improve this ?

I feel that the header file looks slightly cluttered...

A: 
Patrick
I'm sorry but I forgot to mention that the indexes might not be in consecutive order i.e. the enums might have holes in them. This is why I need to do the manual search.
tormod
A: 

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

In this style enums and strings are not only contained in one file, they are also contained in one location. You can easily extend the factory to accept more "columns" in SOME_ENUM, in your case you might want the string not to be generated from name, but provided explicitely.

Suma
+1  A: 

Fastest execution: build a hash table. Since you know the indices ahead of time, you can even build a perfect hash table.

Easiest to program: use a massive switch statement and hope the compiler knows how to optimize for noncontiguous case integers. At least the strings will live in the .text segment of the executable itself so you don't need to instantiate anything:

// Status
enum State_t
{
   Running = 1,
   Jumping = 6, 
   Singing = 12
};

const char *StateToString(State_t state)
{
  switch(state)
  {
    case Running: return "Running";
    case Jumping: return "Jumping";
    case Singing: return "Singing";
    default: return "ERROR"; 
  }
}

You can hide all that inside macros (as Suma's link suggests) so it's not so much of a WTF.

Crashworks
+1  A: 

Here is what I settled on. Using this technique all you need to do is to include a header file. You will only instantiate what you use. You could also store a perfect hash table instead of just Idx & pStr. This approach does not work in C.

file: e2str.hh
struct Mapper_s
{
    int Idx;
    const char *pStr;
};

#define ENUM2STR_BEGIN(x) struct x { static const Mapper_s *GetpMap(void) {  static const Enum2StrMap_s Map[] = 
#define ENUM2STR_END      return Map; }; }

const char *MapEnum2Str(int Idx, const Mapper_s *pMap);

file: mapper.hh
#include "e2str.hh"

ENUM2STR_BEGIN(State_s) 
{
        { Running,   "Running" },
        { Singing,   "Singing" },
        { Jumping,   "Jumping" },
        { 0, 0}
};
ENUM2STR_END;

file: test.cc

#include "mapper.hh"
int main()
{
   cout << "State: " << MapEnum2Str(State, State_s::GetpMap()) << endl;
   return 0;
}
tormod