I've encountered this situation so many times...
enum Fruit {
Apple,
Banana,
Pear,
Tomato
};
Now I have Fruit f; // banana and I want to go from f to the string "Banana"; or I have string s = "Banana" and from that I want to go to Banana // enum value or int.
So far I've been doing this.. Assuming the enum is in Fruit.h:
// Fruit.cpp
const char *Fruits[] = {
"Apple",
"Banana",
"Pear",
"Tomato",
NULL
};
Obviously that's a messy solution. If a developer adds a new fruit to the header and doesn't add a new entry in Fruits[] (can't blame him, they have to be in two different files!) the application goes boom.
Is there a simple way to do what I want, where everything is in one file? Preprocessor hacks, alien magic, anything..
PS: This, contrary to reflection "for everything", would be really trivial to implement in compilers. Seeing how common a problem it is (at least for me) I really can't believe there is no reflective enum Fruit.. Not even in C++0x.
PS2: I'm using C++ but I tagged this question as C as well because C has the same problem. If your solution includes C++ only things, that's ok for me.
This one requires the fruits to be defined in an external file.
This would be the content of fruit.cpp:
#define FRUIT(name) name
enum Fruit {
#include "fruit-defs.h"
NUM_FRUITS
};
#undef FRUIT
#define FRUIT(name) #name
const char *Fruits [] = {
#include "fruit-defs.h"
NULL
};
#undef FRUIT
And this would be fruit-defs.h:
FRUIT(Banana),
FRUIT(Apple),
FRUIT(Pear),
FRUIT(Tomato),
It works as long as the values start in 0 and are consecutive...
Update: mix this solution with the one from Richard Pennington using C99 if you need non-consecutive values. Ie, something like:
// This would be in fruit-defs.h
FRUIT(Banana, 7)
...
// This one for the enum
#define FRUIT(name, number) name = number
....
// This one for the char *[]
#define FRUIT(name, number) [number] = #name
A c99 way that I've found helps reduce mistakes:
enum Fruit {
APPLE,
BANANA
};
const char* Fruits[] = {
[APPLE] = "APPLE",
[BANANA] = "BANANA"
};
You can add enums, even in the middle, and not break old definitions. You can still get NULL strings for values you forget, of course.
One trick I've done in the past is to add an extra enum and then do a compile time assert (such as Boost's) to make sure the two are kept in sync:
enum Fruit {
APPLE,
BANANA,
// MUST BE LAST ENUM
LAST_FRUIT
};
const char *FruitNames[] =
{
"Apple",
"Banana",
};
BOOST_STATIC_ASSERT((sizeof(FruitNames) / sizeof(*FruitNames)) == LAST_FRUIT);
This will at least prevent someone from forgetting to add to both the enum and the name array and will let them know as soon as they try to compile.
One comment on the macro solution - you don't need a separate file for the enumerators. Just use another macro:
#define FRUITs \
FRUIT(Banana), \
FRUIT(Apple), \
FRUIT(Pear), \
FRUIT(Tomato)
(I would probably leave the commas out, though, and incorporate them into the FRUIT macro as needed.)
There is also Better Enums, which is a head-only library (file) that requires C++11 and is licensed under the BSD software license. Official description:
Reflective compile-time enums for C+: Better Enums is a single, lightweight header file that makes your compiler generate reflective enum types.
Here is the code example from the official website:
#include <enum.h>
BETTER_ENUM(Channel, int, Red = 1, Green, Blue)
Channel c = Channel::_from_string("Red");
const char *s = c._to_string();
size_t n = Channel::_size();
for (Channel c : Channel::_values()) {
run_some_function(c);
}
switch (c) {
case Channel::Red: // ...
case Channel::Green: // ...
case Channel::Blue: // ...
}
Channel c = Channel::_from_integral(3);
constexpr Channel c =
Channel::_from_string("Blue");
It looks very promising, though I haven't tested it yet. In addition there are plenty of (custom) reflection libraries for C++. I hope something similar to Better Enums will be part of the Standard Template Library (STL) (or at least Boost), sooner or later.
What if you did something like this?
enum Fruit {
Apple,
Banana,
NumFruits
};
const char *Fruits[NumFruits] = {
"Apple",
"Banana",
};
Then if you add a new entry to the Fruit enum, your compiler should complain that there are insufficient entries in the initializer of the array, so you would be forced to add an entry to the array.
So it protects you from having the array be the wrong size, but it doesn't help you ensure that the strings are correct.
Take a look at Metaresc library https://github.com/alexanderchuranov/Metaresc
It provides interface for types declaration that will also generate meta-data for the type. Based on meta-data you can easily serialize/deserialize objects of any complexity. Out of the box you can serialize/deserialize XML, JSON, XDR, Lisp-like notation, C-init notation.
Here is a simple example:
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include "metaresc.h"
TYPEDEF_ENUM (fruit_t,
Apple,
Banana,
Pear,
Tomato,
);
int main (int argc, char * argv[])
{
mr_td_t * tdp = mr_get_td_by_name ("fruit_t");
if (tdp)
{
int i;
for (i = 0; i < tdp->fields_size / sizeof (tdp->fields[0]); ++i)
printf ("[%" SCNd64 "] = %s\n", tdp->fields[i].fdp->param.enum_value, tdp->fields[i].fdp->name.str);
}
return (EXIT_SUCCESS);
}
This program will output
$ ./enum
[0] = Apple
[1] = Banana
[2] = Pear
[3] = Tomato
Library works fine for latest gcc and clang.
Could make a class structure for it:
class Fruit {
int value; char const * name ;
protected:
Fruit( int v, char const * n ) : value(v), name(n) {}
public:
int asInt() const { return value ; }
char const * cstr() { return name ; }
} ;
#define MAKE_FRUIT_ELEMENT( x, v ) class x : public Fruit { x() : Fruit( v, #x ) {} }
// Then somewhere:
MAKE_FRUIT_ELEMENT(Apple, 1);
MAKE_FRUIT_ELEMENT(Banana, 2);
MAKE_FRUIT_ELEMENT(Pear, 3);
Then you can have a function that takes a Fruit, and it will even be more type safe.
void foo( Fruit f ) {
std::cout << f.cstr() << std::endl;
switch (f.asInt()) { /* do whatever * } ;
}
The sizeof this is 2x bigger than just an enum. But more than likely that doesn't matter.
As the other people answering the question have shown, there isn't really a clean ("D.R.Y.") way to do this using the C preprocessor alone. The problem is that you need to define an array of size of your enum containing strings corresponding to each enum value, and the C preprocessor isn't smart enough to be able to do that. What I do is to create a text file something like this:
%status ok
%meaning
The routine completed its work successfully.
%
%status eof_reading_content
%meaning
The routine encountered the end of the input before it expected
to.
%
Here %'s mark delimiters.
Then a Perl script, the working part of which looks like this,
sub get_statuses
{
my ($base_name, $prefix) = #_;
my #statuses;
my $status_txt_file = "$base_name.txt";
my $status_text = file_slurp ($status_txt_file);
while ($status_text =~
m/
\%status\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\n
\%meaning\s*(.*?)\s*\n\%\s*\n
/gxs) {
my ($code, $meaning) = ($1, $2);
$code = $prefix."_$code";
$meaning =~ s/\s+/ /g;
push #statuses, [$code, $meaning];
}
return #statuses;
}
reads this file and writes a header file:
typedef enum kinopiko_status {
kinopiko_status_ok,
kinopiko_status_eof_reading_content,
and a C file:
/* Generated by ./kinopiko-status.pl at 2009-11-09 23:45. */
#include "kinopiko-status.h"
const char * kinopiko_status_strings[26] = {
"The routine completed its work successfully.",
"The routine encountered the end of the input before it expected to. ",
using the input file at the top. It also calculates the number 26 here by counting the input lines. (There are twenty-six possible statuses in fact.)
Then the construction of the status string file is automated using make.
I don't like macro solutions, in general, though I admit it's kind of difficult there to avoid them.
Personally I opted for a custom class to wrap my enums in. The goal was to offer a bit more that traditional enums (like iteration).
Under the cover, I use a std::map to map the enum to its std::string counterpart. Then I can use this to both iterate over the enum and "pretty print" my enum or initialize it from a string read in a file.
The problem, of course, is the definition, since I have to first declare the enum and then map it... but that's the price you pay for using them.
Also, I then use not a real enum, but a const_iterator pointing to the map (under the covers) to represent the enum value (with end representing an invalid value).
Related
I'm trying to reproduce (*) something similar to Python fstring, or at least its format function (and while at it, I'd like to implement something like its "Mini-language").
(*) N.B.: please note that I am aware of the existence of the standard lib's format library, as well as the existence of the {fmt} library; but,
a: neither the g++ (11.2.1) nor the clang++ (12.0.1) that I have on my machine can compile code including <format>, and
b: I don't want to use the excellent {fmt} lib, because I'm precisely trying to do my own thing/thingy.
I'm going to use a string in input to my format object, and any number of additional arguments, like that:
// First, some vars
std::string stef{"Stéphane"};
std::string cpp{"C++"};
int ilu3t{3000};
// Then the big deal
std::string my_fstring = badabwe::format(
"My name is {stef}, and I love {cpp} {ilu3t} !",
cpp,
stef,
ilu3t
);
// Obviously, only the 1st parameter is positional!
// my_fstring should now be:
// My name is Stephane, and I love C++ 3000 !
That's one of the first problem, I have to solve. I think this process is called reflection (please let me know if it's the case).
Next I need to handle a variable number of arguments; the 1st parameter is the only positional and mandatory one (I'm still trying to find a way to iterate over a parameter pack), but its a subject for another question.
A function is not aware of name of parameters passed it. The parameter doen't even have to have a name:
void foo(int x); // name of the argument is x
foo(42); // 42 has no name
As suggested in a comment, if you want some mapping between strings (values to be replaced) and strings (their names) then you can use a map. To avoid the caller to spell out this mapping you can use a macro (usually to be avoided, but for now its the only way to get the name of a variable as a string):
#include <iostream>
#include <string>
#include <unordered_map>
using token_t = std::unordered_map<std::string,std::string>;
std::string format(const std::string& tokenized,const token_t& token) {
return "test";
}
#define tokenize(token) { #token , to_string(token) }
using std::to_string;
std::string to_string(const std::string& str) { return str; }
int main() {
std::string stef{"Stéphane"};
std::string cpp{"C++"};
int ilu3t{3000};
std::string my_fstring = format(
"My name is {stef}, and I love {cpp} {ilu3t} !",
{
tokenize(cpp),
tokenize(stef),
tokenize(ilu3t)
}
);
}
I assumed that you can use std::to_string, though there is no std::to_string(const std::string&) hence I added a custom implementation.
I am currently attempting to rebuild and update a Project written in Rust (more specifically it's an SKSE64 plugin for Skyrim: https://github.com/lukasaldersley/sse-mod-skyrim-search-se forked from qbx2)
The last problem I'm facing is the library now requires a struct to be exported from our library for version checking.
I have attempted many probably stupid ways to implement this, but I can't get it to work.
The c++ code is as follows:
struct SKSEPluginVersionData
{
enum
{
kVersion = 1,
};
enum
{
// set this if you are using a (potential at this time of writing) post-AE version of the Address Library
kVersionIndependent_AddressLibraryPostAE = 1 << 0,
// set this if you exclusively use signature matching to find your addresses and have NO HARDCODED ADDRESSES
kVersionIndependent_Signatures = 1 << 1,
};
UInt32 dataVersion; // set to kVersion
UInt32 pluginVersion; // version number of your plugin
char name[256]; // null-terminated ASCII plugin name
char author[256]; // null-terminated ASCII plugin author name (can be empty)
char supportEmail[256]; // null-terminated ASCII support email address (can be empty)
// version compatibility
UInt32 versionIndependence; // set to one of the kVersionIndependent_ enums or zero
UInt32 compatibleVersions[16]; // zero-terminated list of RUNTIME_VERSION_ defines your plugin is compatible with
UInt32 seVersionRequired; // minimum version of the script extender required, compared against PACKED_SKSE_VERSION
// you probably should just set this to 0 unless you know what you are doing
};
#define RUNTIME_VERSION_1_6_318 0x010613E0
extern "C" {
__declspec(dllexport) SKSEPluginVersionData SKSEPlugin_Version =
{
SKSEPluginVersionData::kVersion,
1,
"Skyrim Search",
"qbx2",
"",
0, // not version independent
{ RUNTIME_VERSION_1_6_318, 0 }, // RUNTIME_VERSION_1_6_318 is
0, // works with any version of the script extender. you probably do not need to put anything here
};
};
What I've come up with so far in Rust is:
enum KVersionenum {
KVersion=1,
}
#[repr(C)]
pub struct SKSEPluginVersionData {
dataVersion: u32,
pluginVersion: u32,
name: [char;256],
author: [char;256],
supportEmail: [char;256],
versionIndependence: u32,
compatibleVersions: [u32;16],
seVersionRequired: u32,
}
//0x010613E0 is RUNTIME_VERSION_1_6_318
//how can I do this OUTSIDE of a method and how can I make it public to the dll? is that even possible?
let SKSEPlugin_Version = SKSEPluginVersionData {
dataVersion: KVersionenum::KVersion as u32,
pluginVersion: 1,
name: "Skyrim Search\0", //this doesn't work, how can I fill this char array?
author: "qbx2 / lukasaldersley\0", //same here
supportEmail: "something#something.something\0", //and here
versionIndependence: 0,
compatibleVersions: [0x010613E0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], //I'm sure this is a horrible way of doing this
seVersionRequired: 0,
};
When I tried to use the let thingy outside of a function the compiler complained about expecting 'item' but my google-fu isn't good enough to find any useful information there because I just kept finding information about items in the videogame Rust.
For the car array/string problem I have come across that std:ffi stuff and am completely lost in it's documentation but as far as I can tell it will only ever deal with pointers, which is not what I need.
The two questions now are how to I fill these char arrays (I cannot just pass a pointer) and how do I create an instance of this struct as a global variable (or however Rust calls it) that I can export since the let name = something {...} doesn't work.
As far as I can tell exporting to the dll for a function would look like this, but I assume it wouldn't work the same way for that struct.
#[no_mangle]
pub extern "C" fn SKSEPlugin_Query(skse: *const SKSEInterface, info: *mut PluginInfo) -> bool {...}
Is it even possible to do this?
Could someone help me here or at least point me in the right direction?
Please note I am an absolute beginner to Rust and apparently falsely assumed just adding a struct wouldn't be so complicated.
First, a char in Rust is a 32-bit value while its 8-bit in C++ (that's not strictly true but Rust doesn't support architectures where it isn't). So the
name, author, and supportEmail fields should be u8 arrays.
You can export a global value by using #[no_mangle] on a public static variable:
#[no_mangle]
pub static SKSEPlugin_Version: SKSEPluginVersionData = SKSEPluginVersionData {
...
};
See: Can a Rust constant/static be exposed to C?
You can initialize a u8 array from a literal using a byte string and dereferencing it: *b"...". Unfortunately, there's no shorthand like in C++ for zero-padding the undetermined part of the array, so you'd be left with:
name: *b"Skyrim Search\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
author: *b"qbx2 / lukasaldersley\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
supportEmail: *b"something#something.something\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
compatibleVersions: [0x010613E0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
Which honestly, is not nice at all. You can clean this up with some functions that pad the array for you, however, initializing static variables requires const functions, which are still relatively immature in Rust so we can't use things like for loops or traits to help us out:
const fn zero_pad_u8<const N: usize, const M: usize>(arr: &[u8; N]) -> [u8; M] {
let mut m = [0; M];
let mut i = 0;
while i < N {
m[i] = arr[i];
i += 1;
}
m
}
const fn zero_pad_u32<const N: usize, const M: usize>(arr: &[u32; N]) -> [u32; M] {
let mut m = [0; M];
let mut i = 0;
while i < N {
m[i] = arr[i];
i += 1;
}
m
}
...
name: zero_pad_u8(b"Skyrim Search"),
author: zero_pad_u8(b"qbx2 / lukasaldersley"),
supportEmail: zero_pad_u8(b"something#something.something"),
compatibleVersions: zero_pad_u32(&[0x010613E0]),
Still not that nice, but at least its manageable. There may be a crate available that can do this for you.
Lastly, you don't have to use the same field naming convention as used in C++ since its just the order and type that matters, so I'd recommend using snake_case, but if you do want to keep the same names for consistency, you can put the #[allow(non_snake_case)] attribute on SKSEPluginVersionData to suppress the compiler warnings.
I would also recommend making a constant for that magic value instead of just a comment:
const RUNTIME_VERSION_1_6_318: u32 = 0x010613E0;
See the full thing on the playground.
So I had this query wherein say I have an enum and a struct that look like these,
enum fields {
field_1,
field_2
};
struct my_struct {
int field_1;
int field_2;
};
My specific need is, given the enum with the names of the structure members (field_1, field_2 etc) I should be able to generate a macro which can set the structure member to a given value.
#define my_macro (__a, __field) \
__a.__field = 1;
So is there a way to call my_macro like this:
struct my_struct b;
/* extract members of enum as string literals */
my_macro(b, /*field name from the enum */);
Few other posts detailing usage of boost macros helps me extract the enum members as strings ( How to convert an enum type variable to a string?). Issue is with passing it in the appropriate manner to the macro.
It should work the way it is. Macros are processed before compilation, while the code is still code, and they result in code generation.
Your macro #define my_macro(__a, __field) __a.__field = 1; will cause any entry like my_macro(x, y) to be transformed into x.y = 1;, literally, before its handed to the compiler.
If you do something like my_macro(1+1, "test"), it will generate the code 1+1."test" = 1; and will create a compile error. Thats how simple macros are.
Thats why macro parameters are often enclosed by () to make sure it will work the way it was intented, if you don't do that things like this can happen:
#define div_by_100(a) a / 100
printf("%d\n", div_by_100(1000)); // will print 10
printf("%d\n", div_by_100(500 + 500)); // will print 505
This happens because order of operations is resolved at compile time, after the macros have been resolved.
Notice that, because macros are resolved before compilation, they are not a runtime solution. If this enum value is not explict in the code, macros won't help you at all. You will have to write a routing function that will assign a value to each member of this class/struct depending on what enum value was given. Example:
void route(struct farm* s, enum animals e)
{
switch (e)
{
case cow:
s->cow = 1;
break;
case duck:
s->duck = 1;
break;
case horse:
s->horse = 1;
break;
case goat:
s->goat = 1;
break;
}
}
It depends on the way your mechanism of turning an enum to a string works. Your macro should work as long as that mechanism is still a macro that gets replaced by the preprocessor. That's the issue. Your macro should otherwise work properly.
I was wondering if someone out there could give me a pointer to reducing duplication when coding.
im required to call a function a number of times to populate a structure, for example:
typedef struct {
uint16_t u16_a;
bool b_org;
char* c_c;
uint16_t u16_d;
} TEntry;
I need to populate each value of these with a function call, although the return values vary, the same function is used for all.
Would a macro be sufficient to create a template in some way, so that the return type would be dependent on the specific parameter ("string")
for example:
Trrelevant::Trrelevant()
{
TPoint* u_apoint = Insufficient::FindValue("A");
if (u_bpoint != NULL) {
int a = u_apoint;
}
TPoint* p_apoint = Insufficient::FindValue("borg");
if (p_bpoint != NULL) {
bool b = p_bpoint;
}
TPoint* p_cpoint = Insufficient::FindValue("C");
if (etc != NULL) {
char* c = etc;
}
TEct* etc = Insufficient::FindValue("ETC");
if (etc != ETC) {
etc = etc;
}
TEntry entry = {a,
b,
c,
etc};
}
this code is not compiled or accurate, im just trying to illustrate. Im weak in C++ and new to macros, but would anyone know a way to have a macro solve this?
Thank you for your time
You could do something like this, although I don't know what it really buys you.
#define QuickFindValue(NAME, TYPE, FUNCTION) \
TYPE *NAME##Value = Insufficient::FindValue(#NAME); \
if (NAME##Value == NULL) { FUNCTION; }
You would use it like so:
QuickFindValue(C, TPoint, {
char *c = CValue;
// Do stuff..
});
Recently I had the same kind of issue, I'm not sure what kind of source you use for your inputs.
Personnaly, I used XML as input.
Then I have A Builder class that parses the XML call a factory funciton to build every struct in the c++ using the data from the parser.
I don't think that MACRO or templtes would be of any help (or it would be a bad solution).
Note that an external resource (like xml) is nice if ever you want to change without recompiling.
Best
Is there a way to display the name of an enum's value?
say we have:
enum fuits{
APPLE,
MANGO,
ORANGE,
};
main(){
enum fruits xFruit = MANGO;
...
printf("%s",_PRINT_ENUM_STRING(xFruit));
...
}
using the preprocessor
#define _PRINT_ENUM_STRING(x) #x
won't work as we need to get the value of the variable 'x' and then convert It to string.
Is this at all possible in c/C++?
You could use the preprocessor to do this, I believe this technique is called X-Macros:
/* fruits.def */
X(APPLE)
X(MANGO)
X(ORANGE)
/* file.c */
enum fruits {
#define X(a) a,
#include "fruits.def"
#undef X
};
const char *fruit_name[] = {
#define X(a) #a,
#include "fruits.def"
#undef X
};
Note that the last entry includes a trailing comma, which is allowed in C99 (but not in C89). If that is a problem you can add sentinal values. It is also possible to make the macro more complicated by giving multiple arguments for custom names or enum values, etc:
X(APPLE, Apple, 2)
#define X(a,b,c) a = c, /* in enum */
#define X(a,b,c) [c] = #b, /* in name array */
Limitations: You cannot have negative constants and your array is sizeof (char *) * largest_constant. But you could work around both by using an extra lookup table:
int map[] = {
#define X(a,b,c) c,
#include "fruits.def"
#undef X
};
This doesn't work of course. What does work is generating an extra set of enum constants as keys for the names:
enum fruits {
#define X(a,b,c) a ## _KEY,
#include "fruits.def"
#undef X
#define X(a,b,c) a = c,
#include "fruits.def"
#undef X
};
Now you can find the name of X(PINEAPPLE, Pineapple, -40) by using fruit_name[PINEAPPLE_KEY].
People noted that they didn't like the extra include file. You don't need this extra file, you also use a #define. This may be more appropriate for small lists:
#define FRUIT_LIST X(APPLE) X(ORANGE)
And replace #include "fruits.def with FRUIT_LIST in the previous examples.
You can use a mapping in this case.
char *a[10] = { "APPLE","MANGO","ORANGE"};
printf("%s",a[xFruit]);
Yes the preprocessor won't work unless you provide the exact enum -value.
Also check this question for more insights.
I've used preprocessor programming successfully to get a macro of this kind:
DEFINE_ENUM(Fruits, (Apple)(Mango)(Orange));
It does a tad more than just printing the names, but it could easily be simplified to 2 switches if necessary.
It's based on Boost.Preprocessor facilities (notably BOOST_PP_SEQ_FOREACH) which is a must have for preprocessor programming, and I find it much more elegant than the X facility and its file reinclusion system.
public enum LDGoalProgressUpdateState
{
[Description("Yet To Start")]
YetToStart = 1,
[Description("In Progress")]
InProgress = 2,
[Description("Completed")]
Completed = 3
}
var values = (ENUMList[])Enum.GetValues(typeof(ENUMList));
var query = from name in values
select new EnumData//EnumData is a Modal or Entity
{
ID = (short)name,
Name = GetEnumDescription(name)//Description of Particular Enum Name
};
return query.ToList();
region HelperMethods
public static string GetEnumDescription(Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
return attributes[0].Description;
else
return value.ToString();
}
#endregion