4

I have an enum Eg.

enum {
APPLE,
MANGO,
BANANA
}

and a corresponding string array

char fruits[] = 
{
 "apple",
 "mango",
 "banana"
}

I need to retrieve the index of string, given I have the string. So given that the string is apple, I need to get 0 and so on. [ Enum is additionally there, might help the solution]

Is there an elegant way, to save [apple,0],[banana,1] that is short and simple, that I might use as a macro. I don't need lengthy things like a hashtable. Can Enum assist in the mapping?

shuttle87
  • 15,466
  • 11
  • 77
  • 106
Hooli
  • 711
  • 2
  • 13
  • 24

4 Answers4

10

You can do something like

entries.h

 ENTRY(APPLE, "apple"), 
 ENTRY(MANGO, "mango"),

In your file

#define ENTRY(a,b) b
const char *fruits [] = {
#include "entries.h"
} ; 

#undef ENTRY
#define ENTRY(a,b) a
enum fruit_t
{
#include "entries.h"
} ;
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
user3344003
  • 20,574
  • 3
  • 26
  • 62
  • 1
    This is a very interesting way of guaranteeing the index correlation required for [my solution](http://stackoverflow.com/a/23862767/119527) to work. It doesn't actually provide the means to go from `"apple"` to `0`, but I like it. – Jonathon Reinhart May 26 '14 at 04:41
  • 1
    These are the X Macros, if I'm not wrong. I could certainly use it but really doesn't help the cause. – Hooli May 26 '14 at 04:44
  • Wow. That's a creative use of headers and `#define` I've never seen before. – Baldrick May 26 '14 at 04:44
  • @IDK For my solution to work, the enum values *must* correspond to the indices of the strings in the `fruits` array. This answer provides a way to *guarantee* that. – Jonathon Reinhart May 26 '14 at 04:45
  • I agree that it does enforce your solution. – Hooli May 26 '14 at 04:46
  • +1 for the creativity! – CinCout May 26 '14 at 04:49
  • +1 I clicked the question intending to suggest this very thing. But see [my answer on X-Macros](http://stackoverflow.com/questions/6635851/real-world-use-of-x-macros/6636596#6636596) for a simpler style which doesn't need a separate file. – luser droog May 26 '14 at 05:09
  • +1, I have posted an alternative layout for this – M.M May 26 '14 at 05:27
  • @Baldrick - it's called the ["X macro" idiom](http://www.drdobbs.com/the-new-c-x-macros/184401387). It is actually an old trick, but not really widely known. – detly May 26 '14 at 05:34
  • @detly: Thanks for the info. I'll read up! It certainly violates every coding standard I've ever used. I love it! :) – Baldrick May 26 '14 at 05:39
  • At the link above relates, we used to have much more powerful Macro processors for assembly language and Bliss that made it simple to define data structures. For C, we had to come up with tricks like this to approach what we had in this other languages. – user3344003 May 26 '14 at 05:43
4

You can't really do a "mapping" with strings in C.

The most straightforward solution is a simple linear search:

typedef enum {
  INVALID = -1,

  APPLE = 0,
  MANGO,
  BANANA,

  NUM_FRUIT,
} fruit_t;

// NOTE: These indices must be kept in-sync with fruit_t!
const char* fruits[] = {
 "apple",
 "mango",
 "banana"
};

fruit_t lookup_fruit(const char* name) {
    int i;
    for (i=0; i<NUM_FRUIT; i++) {
        if (strcmp(name, fruits[i]) == 0)
            return i;
    }
    return INVALID;
}

void test(void) {
    fruit_t result = lookup_fruit("mango");
}
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
  • Thanks for the answer. Simple surely, I thought I could avoid the loop altogether but I guess I can't and probably there is no straightforward way to maintain a "mapping" as you said. – Hooli May 26 '14 at 04:45
  • @IDK Even something like Python where you can use a `dict` to maintain that mapping (and even generate a reverse-mapping), it's still implemented via a hash table. – Jonathon Reinhart May 26 '14 at 04:46
  • Oh that's insightful! – Hooli May 26 '14 at 04:47
  • If you've got a really large number of fruits to lookup at, I suggest searching for a C hash table library. That'll make your life easier. – Lie Ryan May 26 '14 at 04:48
  • Not really, I got a very small array of strings. Thanks for the tip, though! – Hooli May 26 '14 at 04:53
  • You can do a static_assert that `NUM_FRUIT == sizeof fruits / sizeof *fruits`, that will catch counting errors (but not ordering errors). – M.M May 26 '14 at 05:17
2

In C99 or C11, you can use designated initializers:

enum { APPLE, MANGO, BANANA = 7 };

char *fruits[] = 
{
    [BANANA] = "banana",
    [MANGO]  = "mango",
    [APPLE]  = "apple",
};

This works correctly even though the items are not listed in the same order, and even though there's a big gap between MANGO and BANANA.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

This is an alternative layout for user3344003's solution that does not require every .c file to provide the furniture; and has include guards:

// entries.h
#ifndef H_ENTRIES
    // other stuff for this header file that needs to be in header guards
#endif

// make sure to only include the enum once even if the file is multiply included
#if (!defined H_ENTRIES) || (defined C_ENTRIES)

#ifndef C_ENTRIES
    enum 
    {
#   define ENTRY(a,b) a
#endif

    ENTRY(APPLE, "apple"), 
    ENTRY(MANGO, "mango"),

#ifndef C_ENTRIES
    NUM_FRUITS
    };
    extern const char *fruits[NUM_FRUITS];   // if you also want array extern of course
#   undef ENTRY
#   define H_ENTRIES
#endif

// entries.c

#define C_ENTRIES
#define ENTRY(a,b) b

    const char *fruits[NUM_FRUITS] = {
#       include "entries.h"
    };

// other.c 

#include "entries.h"
M.M
  • 138,810
  • 21
  • 208
  • 365
  • Note, make `fruits` const in both places if you don't plan on having the table editable at runtime – M.M May 26 '14 at 05:29
  • I tend to use this for large tables , and use a static_assert for small tables. – M.M May 26 '14 at 05:42