tormod
tormod

Reputation: 73

Simplest way of mapping an index to a string in C++

Requirements:

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...

Upvotes: 0

Views: 946

Answers (4)

tormod
tormod

Reputation: 73

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;
}

Upvotes: 1

Patrick
Patrick

Reputation: 8318

I can't do a direct mapping since one application might be feeding me values which are >out of range for my list of strings. A direct lookup into nothingness would be critical.

So add a count of objects to the array that your accessors check before returnng a value:

struct map {
const char *mapping[] = { "Running", "Jumping", "Singing" };
const int count = 3;
}

or if you want it automated

struct map {
map() { 
  for( count = 0; strlen( mapping[count] ); ++i )
}

const char *mapping[] = { "Running", "Jumping", "Singing", "" };
int count;
}

Upvotes: 0

Crashworks
Crashworks

Reputation: 41404

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.

Upvotes: 1

Suma
Suma

Reputation: 34403

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.

Upvotes: 0

Related Questions