Look-up table of C macros - c

I'm making look-up tables between 2 drivers. I use an enum starting at index 0, which is passed to a look-up table to return an integer. This works fine.
// In library
#define LIB_FEATURE_1 0x10
#define LIB_FEATURE_2 0x22
#define LIB_FEATURE_3 0x44
#define LIB_FEATURE_4 0x42
// In my costum driver
enum features_s {
my_feature_a = 0,
my_feature_b = 1,
my_feature_c = 2
};
const int feature_tbl[] = {
LIB_FEATURE_2, // Maps my_feature_a to LIB_FEATURE_2
0xFF, // Maps my_feature_b to an error code because this
// feature is not present in library
LIB_FEATURE_4 // Maps my_feature_c to LIB_FEATURE_4
};
// In app
int value = feature_tbl[my_feature_a];
The library contains some more complicated macros (setting registers in an embedded system) :
// In library
#define LIB_FEATURE_1 do {
//does black magic
} while(0)
#define LIB_FEATURE_2 do {
//does white magic
} while(0)
#define LIB_FEATURE_3 do {
//does yellow magic
} while(0)
#define LIB_FEATURE_4 do {
//does purple magic
} while(0)
// In my costum driver
enum features_s {
my_feature_a = 0,
my_feature_b = 1,
my_feature_c = 2
};
/*
* something missing here. I want this mapping :
* my_feature_a executes LIB_FEATURE_2();
* my_feature_b executes nothing
* my_feature_c executes LIB_FEATURE_4();
*/
// In app
SOME_KIND_OF_LOOK_UP_TABLE[my_feature_a]();
Is it possible to create a const table, or a macro that takes an index as argument and executes the right feature?
I also tried macro concatenation, but it doesn't seem to work.
I tried using a const table, and a macro concatenations

Runtime version:
//function prototype
typedef void (*my_lib_feature_function)();
//implement feature functions
//we need an address, therefore real functions instead of macros
static void my_lib_feature_function_1() { /* feature magic 1 */ }
static void my_lib_feature_function_2() { /* feature magic 2 */ }
static void my_lib_feature_function_3() { /* feature magic 3 */ }
static void my_lib_feature_function_4() { /* feature magic 4 */ }
//feature table (lookup table)
//organize as you see fit, extend or shorten it, but
//take care of the right indices (enum values)
static const my_lib_feature_function function_table[] = {
my_lib_feature_function_2, //index 0 -> my_feature_a
NULL, //index 1 -> my_feature_b
my_lib_feature_function_4 //index 2 -> my_feature_c
};
//invoker
//param feature is the index to the lookup table
static void invoke_my_lib_feature(enum features_s feature)
{
if (function_table[feature] != NULL) //if entry exists
function_table[feature](); //execute it
}
//In app
/*either*/ //invoke_my_lib_feature(my_feature_a);
/*and/or*/ //invoke_my_lib_feature(my_feature_b);
/*and/or*/ //invoke_my_lib_feature(my_feature_c);
Compiletime version:
//pp util (concatenation)
#define CAT(A,B) CAT_(A,B)
#define CAT_(A,B) A##B
//instead of enum (unfortunately, we can't use enum values here)
#define MY_FEATURE_A 0
#define MY_FEATURE_B 1
#define MY_FEATURE_C 2
//feature table
#define LIB_FEATURE_ENTRY_0 LIB_FEATURE_2 //-> MY_FEATURE_A
#define LIB_FEATURE_ENTRY_1 //empty //-> MY_FEATURE_B
#define LIB_FEATURE_ENTRY_2 LIB_FEATURE_4 //-> MY_FEATURE_C
//invoker
#define INVOKE_LIB_FEATURE(IDX) CAT(LIB_FEATURE_ENTRY_,IDX)
//In App
/*either*/ //INVOKE_LIB_FEATURE(MY_FEATURE_A); //expands to text after LIB_FEATURE_2
/*and/or*/ //INVOKE_LIB_FEATURE(MY_FEATURE_B); //empty
/*and/or*/ //INVOKE_LIB_FEATURE(MY_FEATURE_C); //expands to text after LIB_FEATURE_4

Related

C struct initialization number of array elements initialized at compile time

typedef struct
{
struct item_t items[MAX_ITEMS];
int num_items;
} item_list_t;`
items_list_t item_list =
{
.items =
{
#ifdef ITEM1
{
/* item1 initialized here */
},
#endif
#ifdef ITEM2
{
/* item2 initialized here */
},
#endif
#ifdef ITEM3
{
/* item3 initialized here */
},
#endif
}
/* Find out how many items were initialized */
.num_items = sizeof(items) / sizeof(struct item_t); //doesn't work
}
I am trying to find a way to populate the .num_items field with the number of items in the array that were initialized at compile time. I know why the code example doesn't work, im wondering if there is there any way of accomplishing this?
A not portable hack would be to use the __COUNTER__ preprocessor extension. It's not a standard C feature, but should be supported in recent gcc, clang and msvc compilers.
The preprocessor increments the value of the counter after each evaluation, so you have to force evaluation using something like:
// in case __COUNTER__ was previously used in a different header
const static int initial_counter = __COUNTER__;
#ifdef ITEM1
const static int item1_defined = __COUNTER__; // +1
#endif
#ifdef ITEM2
const static int item2_defined = __COUNTER__; // +1
#endif
#ifdef ITEM3
const static int item3_defined = __COUNTER__; // +1
#endif
// must subtract 1 because this line also evaluates __COUNTER__
const static int total_item_count = (__COUNTER__) - initial_counter - 1;
And then:
items_list_t item_list =
{
.items = { ... },
.num_items = total_item_count
}
A more portable preprocessor alternative is to use Boost which should be more portable (but it's written for C++ so you might have to port some of the details to C yourself), or use a code generator (recommended).
Involving a code generator into your build scripts will generate actual source code you can step in and debug if needed. Preprocessor hacks can easily get out of hand.

Calling OpenSSL's PEM_write_PUBKEY || PEM_write_PrivateKey API makes the program to exit abruptly with message "no OPENSSL_Applink"

I am trying to use EVP_* APIs from OpenSSL, but I have encountered a weird strange behavior when trying to dump the Public/Private key from the EVP_PKEY struct.
Issue:: After populating the EVP_PKEY struct, on calling PEM_write_PUBKEY API (see TRIAL1), the program exits. The same happens on calling of PEM_write_PrivateKey API (see TRIAL2).
Output: I am left with a temp.pem 0 bytes file and a message on cmd prompt saying OPENSSL_Uplink(5D8C7000,08): no OPENSSL_Applink
#define TRIAL1
void InitOpenSSLLib(void)
{
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
}
int main(int argc, char** argv)
{
EVP_PKEY_CTX* ctx = NULL;
EVP_PKEY* pKeyPair = EVP_PKEY_new();
BIO *mem = BIO_new(BIO_s_mem());
FILE* fp = fopen("temp.pem", "wb");
InitOpenSSLLib();
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, 0);
EVP_PKEY_keygen_init(ctx);
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048);
EVP_PKEY_keygen(ctx, &pKeyPair);
// Succeeds till here... all of the above called APIs return value greater than 0
#ifdef TRIAL1
// Program exits even before printing any error
if (PEM_write_PUBKEY(fp, pKeyPair) <= 0)
printf("PEM_write_PUBKEY failed\n");
#elif TRIAL2
// same behavior with this API too
PEM_write_PrivateKey(fp, pKeyPair, NULL, NULL, 0, 0, NULL);
#endif
// Tried this too.... but the control never reaches here
fflush(fp);
// Tried this too... most of the mem struct members are NULL.
EVP_PKEY_print_private(mem, pKeyPair, 0, 0);
//.... Cleanup codes
fclose(fp);
BIO_free_all(mem);
return 0;
}
Any pointers? Am I doing anything wrong here? Is there any other way to dump private/public key or the pair PEM format to a file?
I am using VC++ 2015. Also, on hitting Ctrl+F5, the prompt shows the message:: OPENSSL_Uplink(5D8C7000,08): no OPENSSL_Applink
Answering my own question for future devs
The key question here is the error (read, message) thrown by OpenSSL, i.e., no OPENSSL_Applink.
As documented here,
As per 0.9.8 the above limitation is eliminated for .DLLs. OpenSSL
.DLLs compiled with some specific run-time option [we insist on the
default /MD] can be deployed with application compiled with different
option or even different compiler. But there is a catch! Instead of
re-compiling OpenSSL toolkit, as you would have to with prior
versions, you have to compile small C snippet with compiler and/or
options of your choice. The snippet gets installed as
/include/openssl/applink.c and should be either added to
your application project or simply #include-d in one [and only one] of
your application source files. Failure to link this shim module into
your application manifests itself as fatal "no OPENSSL_Applink"
run-time error. An explicit reminder is due that in this situation
[mixing compiler options] it is as important to add CRYPTO_malloc_init
prior first call to OpenSSL.
you should check your compiling options for /MD (assuming you know the Code Generation options under VC++ Project Properties).
I did the same, but still my issue didn't get resolved. The answer to this is the second paragraph to the link given [https://www.openssl.org/docs/faq.html#PROG2], where it instructs us to include <install-root>/include/openssl/applink.c
Where to get this applink.c file if not found in openssl's
dir?
In my case, I installed OpenSSL using exe binary and I couldn't find this specific applink.c file anywhere in my <install-root>, so I started looking and found it here on GitHub.
All you have to do is, include this file as source file in your project or better save it in openssl install-root dir, so that you can include it from the same location for other projects too.
// applink.c from https://github.com/openssl/openssl/blob/master/ms/applink.c
/*
* Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#define APPLINK_STDIN 1
#define APPLINK_STDOUT 2
#define APPLINK_STDERR 3
#define APPLINK_FPRINTF 4
#define APPLINK_FGETS 5
#define APPLINK_FREAD 6
#define APPLINK_FWRITE 7
#define APPLINK_FSETMOD 8
#define APPLINK_FEOF 9
#define APPLINK_FCLOSE 10 /* should not be used */
#define APPLINK_FOPEN 11 /* solely for completeness */
#define APPLINK_FSEEK 12
#define APPLINK_FTELL 13
#define APPLINK_FFLUSH 14
#define APPLINK_FERROR 15
#define APPLINK_CLEARERR 16
#define APPLINK_FILENO 17 /* to be used with below */
#define APPLINK_OPEN 18 /* formally can't be used, as flags can vary */
#define APPLINK_READ 19
#define APPLINK_WRITE 20
#define APPLINK_LSEEK 21
#define APPLINK_CLOSE 22
#define APPLINK_MAX 22 /* always same as last macro */
#ifndef APPMACROS_ONLY
# include <stdio.h>
# include <io.h>
# include <fcntl.h>
static void *app_stdin(void)
{
return stdin;
}
static void *app_stdout(void)
{
return stdout;
}
static void *app_stderr(void)
{
return stderr;
}
static int app_feof(FILE *fp)
{
return feof(fp);
}
static int app_ferror(FILE *fp)
{
return ferror(fp);
}
static void app_clearerr(FILE *fp)
{
clearerr(fp);
}
static int app_fileno(FILE *fp)
{
return _fileno(fp);
}
static int app_fsetmod(FILE *fp, char mod)
{
return _setmode(_fileno(fp), mod == 'b' ? _O_BINARY : _O_TEXT);
}
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport)
void **
# if defined(__BORLANDC__)
/*
* __stdcall appears to be the only way to get the name
* decoration right with Borland C. Otherwise it works
* purely incidentally, as we pass no parameters.
*/
__stdcall
# else
__cdecl
# endif
OPENSSL_Applink(void)
{
static int once = 1;
static void *OPENSSL_ApplinkTable[APPLINK_MAX + 1] =
{ (void *)APPLINK_MAX };
if (once) {
OPENSSL_ApplinkTable[APPLINK_STDIN] = app_stdin;
OPENSSL_ApplinkTable[APPLINK_STDOUT] = app_stdout;
OPENSSL_ApplinkTable[APPLINK_STDERR] = app_stderr;
OPENSSL_ApplinkTable[APPLINK_FPRINTF] = fprintf;
OPENSSL_ApplinkTable[APPLINK_FGETS] = fgets;
OPENSSL_ApplinkTable[APPLINK_FREAD] = fread;
OPENSSL_ApplinkTable[APPLINK_FWRITE] = fwrite;
OPENSSL_ApplinkTable[APPLINK_FSETMOD] = app_fsetmod;
OPENSSL_ApplinkTable[APPLINK_FEOF] = app_feof;
OPENSSL_ApplinkTable[APPLINK_FCLOSE] = fclose;
OPENSSL_ApplinkTable[APPLINK_FOPEN] = fopen;
OPENSSL_ApplinkTable[APPLINK_FSEEK] = fseek;
OPENSSL_ApplinkTable[APPLINK_FTELL] = ftell;
OPENSSL_ApplinkTable[APPLINK_FFLUSH] = fflush;
OPENSSL_ApplinkTable[APPLINK_FERROR] = app_ferror;
OPENSSL_ApplinkTable[APPLINK_CLEARERR] = app_clearerr;
OPENSSL_ApplinkTable[APPLINK_FILENO] = app_fileno;
OPENSSL_ApplinkTable[APPLINK_OPEN] = _open;
OPENSSL_ApplinkTable[APPLINK_READ] = _read;
OPENSSL_ApplinkTable[APPLINK_WRITE] = _write;
OPENSSL_ApplinkTable[APPLINK_LSEEK] = _lseek;
OPENSSL_ApplinkTable[APPLINK_CLOSE] = _close;
once = 0;
}
return OPENSSL_ApplinkTable;
}
#ifdef __cplusplus
}
#endif
#endif
That's it, this will eliminate the strange behavior of program exit.

C initializer element is not constant - pointers in struct

Code snippets appear below
/// System Parameters for the gateway
typedef struct {
bool mobile_gateway; // true = GPS receiver used, else false
uint16_t gw_ordinal_number; // Starting at 1, the ordinal gateway number
} CONFIG_SYSTEM_PARMS;
// Data types for the system parameters
typedef enum {
CFGPARM_TYPE_BOOL,
CFGPARM_TYPE_UINT,
CFGPARM_TYPE_INT,
CFGPARM_TYPE_DOUBLE,
CFGPARM_NUM_TYPES
} CONFIG_PARM_DATA_TYPES;
// These mnemonic names and data types correspond to the system parameters
#define SYS_PARM_MOBILE_GATEWAY_MNEM "MOBILEGW"
#define SYS_PARM_GATEWAY_NUMBER_MNEM "GWNUM"
#define SYS_PARM_MOBILE_GATEWAY_TYPE CFGPARM_TYPE_BOOL
#define SYS_PARM_GATEWAY_NUMBER_TYPE CFGPARM_TYPE_UINT
typedef struct {
char FILLER; // TODO - Define User Parms later...
} CONFIG_USER_PARMS;
static const uint16_t TOTAL_CONFIG_PARM_BYTESIZE = sizeof(CONFIG_SYSTEM_PARMS) + sizeof(CONFIG_USER_PARMS);
typedef struct {
const char *sysparm_mnemonic;
CONFIG_PARM_DATA_TYPES sysparm_data_type;
void *sysparm_data_ptr;
} CONFIG_PARM_LOOKUP_TABLE;
// ****************************************
static LT_LOGGER_DATA gateway_log_data;
static CONFIG_SYSTEM_PARMS *config_sys_parms_ptr = &gateway_log_data.config_system_parms;
// **************************************************
// Lookup table for the System Parms
//
// CAA: This is a stinking mess - There is no easy way to define &gateway_log_data.config_system_parms
// outside of the following initialization, so the result is more verbose than it should be
static const CONFIG_PARM_LOOKUP_TABLE config_parm_lookup_table[] = {
{ SYS_PARM_MOBILE_GATEWAY_MNEM, SYS_PARM_MOBILE_GATEWAY_TYPE, &config_sys_parms_ptr->mobile_gateway},
{ SYS_PARM_GATEWAY_NUMBER_MNEM, SYS_PARM_GATEWAY_NUMBER_TYPE, &gateway_log_data.config_system_parms.gw_ordinal_number},
// FILLER to mark end of list
{ "", 0, NULL}
};
The question is this:
Why, am I getting the error : initializer element is not constant (near initialization for 'config_parm_lookup_table[0].sysparm_data_ptr')
when the initializer for config_parm_lookup_table[1].sysparm_data_ptr is effectively using the same construct?
Obviously, I have a work-around, but it seems verbose.

C (Preprocessor): How to concatenate/append substitution string

I define my exceptions on the command line:
-DEXCEPTION_1=\"first\" -DEXCEPTION_2=\"second\" -DEXCEPTION_3=\"third\"
which I check against a string:
except = 0;
#ifdef EXCEPTION_1
if (! strcmp(EXCEPTION_1, mystring))
{ except = 1;
}
#endif
#ifdef EXCEPTION_2
if (! strcmp(EXCEPTION_2, mystring))
{ except = 1;
}
#endif
#ifdef EXCEPTION_3
if (! strcmp(EXCEPTION_3, mystring))
{ except = 1;
}
#endif
if (except == 1)
{ // do something
}
else
{ // do something else
}
Needless to say, while this works, it is also quite ugly, inflexible and causes redundancy in my code.
Is there a way to append a string to a preprocessor macro variable?
I would like to get something like this (the problem of course is that #append does not exist):
#ifdef EXCEPTION_1 #append EXCEPTIONS if (! strcmp(EXCEPTION_1, mystring)) { except = 1; }
#ifdef EXCEPTION_2 #append EXCEPTIONS if (! strcmp(EXCEPTION_2, mystring)) { except = 1; }
#ifdef EXCEPTION_3 #append EXCEPTIONS if (! strcmp(EXCEPTION_3, mystring)) { except = 1; }
Then I could just use EXCEPTIONS in the code and it would work with all possible permutations of exceptions.
In other words I want to append a string to a macro variable - is it possible?
You can have chains of defines, but it won't look much better:
#ifdef EXCEPTION_1
#define EXCEPTIONS1 if (! strcmp(EXCEPTION_1, mystring)) { except = 1; }
#else
#define EXCEPTIONS1
#endif
#ifdef EXCEPTION_2
#define EXCEPTIONS2 EXCEPTIONS1 if (! strcmp(EXCEPTION_2, mystring)) { except = 1; }
#else
#define EXCEPTIONS2 EXCEPTIONS1
#endif
// etc
Again, not much better.
And you really shouldn't define macros with open if's. It allows weird interactions like if(cond) EXCEPTIONS1 else cout<<"error"; -- that won't do what you expect because EXCEPTIONS1 is a plain if and will gobble up the else branch.
The typical way of writing macros with code blocks is to wrap the whole thing in a do{...}while(0) (note no ending ;).
Checkout the token stringification and concatenation section here
It might help some. In general, it would seem easier to use your command line macro to populate a table and have a macro that simply expands to a loop that checks the successive entries of that table to set the except flag.
For instance, save the following as silly.c and compile with cc -DEX1=\"hello\"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct Except {
const char* key;
int flag;
};
struct Except table[] = {
#if defined(EX1)
{EX1,1},
#endif
{NULL,0}
};
#define CHECKEX(mys,rv) { rv = 0;for(int i=0;table[i].key!=0;i++) \
if (strcmp(table[i].key,(mys))==0) \
rv = 1; \
}
int main()
{
int rv;
CHECKEX("hello",rv);
if (rv)
printf("Got an hello\n");
else printf("Got nothing\n");
return 0;
}
Simply add more "blocks" to the table as needed. Just a suggestion of course.
You can convert a macro argument to a string:
#define STR(x) #x
STR(hello) --> "hello"
And you can concatenate string literals by just writing them next to each other:
"123" "abc" --> "123abc"
Or create a macro:
#define CONCAT(a, b) a b
CONCAT("hello", " world!") --> "hello world!"
You can use ## to concatenate string to macros. Use an index and use the conditions only to set the index. Then at the end you can concatenate the index with your macro EXCEPTION. like,
define a macro like #define append(name) name ## counter
this will in turn give you name as name1 if counter is 1
Now you can define different append macros to come with the result in a single variable name

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?
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.
// 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
There's no built-in solution. The easiest way is with an array of char* where the enum's int value indexes to a string containing the descriptive name of that enum. If you have a sparse enum (one that doesn't start at 0 or has gaps in the numbering) where some of the int mappings are high enough to make an array-based mapping impractical then you could use a hash table instead.
There is definitely a way to do this -- use X() macros. These macros use the C preprocessor to construct enums, arrays and code blocks from a list of source data. You only need to add new items to the #define containing the X() macro. The switch statement would expand automatically.
Your example can be written as follows:
// Source data -- Enum, String
#define X_NUMBERS \
X(ONE, "one") \
X(TWO, "two") \
X(THREE, "three")
...
// Use preprocessor to create the Enum
typedef enum {
#define X(Enum, String) Enum,
X_NUMBERS
#undef X
} Numbers;
...
// Use Preprocessor to expand data into switch statement cases
switch(num)
{
#define X(Enum, String) \
case Enum: strcpy(num_str, String); break;
X_NUMBERS
#undef X
default: return 0; break;
}
return 1;
There are more efficient ways (i.e. using X Macros to create an string array and enum index), but this is the simplest demo.
I know you have a couple good solid answers, but do you know about the # operator in the C preprocessor?
It lets you do this:
#define MACROSTR(k) #k
typedef enum {
kZero,
kOne,
kTwo,
kThree
} kConst;
static char *kConstStr[] = {
MACROSTR(kZero),
MACROSTR(kOne),
MACROSTR(kTwo),
MACROSTR(kThree)
};
static void kConstPrinter(kConst k)
{
printf("%s", kConstStr[k]);
}
C or C++ does not provide this functionality, although I've needed it often.
The following code works, although it's best suited for non-sparse enums.
typedef enum { ONE, TWO, THREE } Numbers;
char *strNumbers[] = {"one","two","three"};
printf ("Value for TWO is %s\n",strNumbers[TWO]);
By non-sparse, I mean not of the form
typedef enum { ONE, FOUR_THOUSAND = 4000 } Numbers;
since that has huge gaps in it.
The advantage of this method is that it put the definitions of the enums and strings near each other; having a switch statement in a function spearates them. This means you're less likely to change one without the other.
KISS. You will be doing all sorts of other switch/case things with your enums so why should printing be different? Forgetting a case in your print routine isn't a huge deal when you consider there are about 100 other places you can forget a case. Just compile -Wall, which will warn of non-exhaustive case matches. Don't use "default" because that will make the switch exhaustive and you wont get warnings. Instead, let the switch exit and deal with the default case like so...
const char *myenum_str(myenum e)
{
switch(e) {
case ONE: return "one";
case TWO: return "two";
}
return "invalid";
}
Try Converting C++ enums to strings. The comments have improvements that solve the problem when enum items have arbitrary values.
The use of boost::preprocessor makes possible an elegant solution like the following:
Step 1: include the header file:
#include "EnumUtilities.h"
Step 2: declare the enumeration object with the following syntax:
MakeEnum( TestData,
(x)
(y)
(z)
);
Step 3: use your data:
Getting the number of elements:
td::cout << "Number of Elements: " << TestDataCount << std::endl;
Getting the associated string:
std::cout << "Value of " << TestData2String(x) << " is " << x << std::endl;
std::cout << "Value of " << TestData2String(y) << " is " << y << std::endl;
std::cout << "Value of " << TestData2String(z) << " is " << z << std::endl;
Getting the enum value from the associated string:
std::cout << "Value of x is " << TestData2Enum("x") << std::endl;
std::cout << "Value of y is " << TestData2Enum("y") << std::endl;
std::cout << "Value of z is " << TestData2Enum("z") << std::endl;
This looks clean and compact, with no extra files to include.
The code I wrote within EnumUtilities.h is the following:
#include <boost/preprocessor/seq/for_each.hpp>
#include <string>
#define REALLY_MAKE_STRING(x) #x
#define MAKE_STRING(x) REALLY_MAKE_STRING(x)
#define MACRO1(r, data, elem) elem,
#define MACRO1_STRING(r, data, elem) case elem: return REALLY_MAKE_STRING(elem);
#define MACRO1_ENUM(r, data, elem) if (REALLY_MAKE_STRING(elem) == eStrEl) return elem;
#define MakeEnum(eName, SEQ) \
enum eName { BOOST_PP_SEQ_FOR_EACH(MACRO1, , SEQ) \
last_##eName##_enum}; \
const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
static std::string eName##2String(const enum eName eel) \
{ \
switch (eel) \
{ \
BOOST_PP_SEQ_FOR_EACH(MACRO1_STRING, , SEQ) \
default: return "Unknown enumerator value."; \
}; \
}; \
static enum eName eName##2Enum(const std::string eStrEl) \
{ \
BOOST_PP_SEQ_FOR_EACH(MACRO1_ENUM, , SEQ) \
return (enum eName)0; \
};
There are some limitation, i.e. the ones of boost::preprocessor. In this case, the list of constants cannot be larger than 64 elements.
Following the same logic, you could also think to create sparse enum:
#define EnumName(Tuple) BOOST_PP_TUPLE_ELEM(2, 0, Tuple)
#define EnumValue(Tuple) BOOST_PP_TUPLE_ELEM(2, 1, Tuple)
#define MACRO2(r, data, elem) EnumName(elem) EnumValue(elem),
#define MACRO2_STRING(r, data, elem) case EnumName(elem): return BOOST_PP_STRINGIZE(EnumName(elem));
#define MakeEnumEx(eName, SEQ) \
enum eName { \
BOOST_PP_SEQ_FOR_EACH(MACRO2, _, SEQ) \
last_##eName##_enum }; \
const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
static std::string eName##2String(const enum eName eel) \
{ \
switch (eel) \
{ \
BOOST_PP_SEQ_FOR_EACH(MACRO2_STRING, _, SEQ) \
default: return "Unknown enumerator value."; \
}; \
};
In this case, the syntax is:
MakeEnumEx(TestEnum,
((x,))
((y,=1000))
((z,))
);
Usage is similar as above (minus the eName##2Enum function, that you could try to extrapolate from the previous syntax).
I tested it on mac and linux, but be aware that boost::preprocessor may not be fully portable.
By merging some of the techniques over here I came up with the simplest form:
#define MACROSTR(k) #k
#define X_NUMBERS \
X(kZero ) \
X(kOne ) \
X(kTwo ) \
X(kThree ) \
X(kFour ) \
X(kMax )
enum {
#define X(Enum) Enum,
X_NUMBERS
#undef X
} kConst;
static char *kConstStr[] = {
#define X(String) MACROSTR(String),
X_NUMBERS
#undef X
};
int main(void)
{
int k;
printf("Hello World!\n\n");
for (k = 0; k < kMax; k++)
{
printf("%s\n", kConstStr[k]);
}
return 0;
}
If you are using gcc, it's possible to use:
const char * enum_to_string_map[]={ [enum1]='string1', [enum2]='string2'};
Then just call for instance
enum_to_string_map[enum1]
Check out the ideas at Mu Dynamics Research Labs - Blog Archive. I found this earlier this year - I forget the exact context where I came across it - and have adapted it into this code. We can debate the merits of adding an E at the front; it is applicable to the specific problem addressed, but not part of a general solution. I stashed this away in my 'vignettes' folder - where I keep interesting scraps of code in case I want them later. I'm embarrassed to say that I didn't keep a note of where this idea came from at the time.
Header: paste1.h
/*
#(#)File: $RCSfile: paste1.h,v $
#(#)Version: $Revision: 1.1 $
#(#)Last changed: $Date: 2008/05/17 21:38:05 $
#(#)Purpose: Automated Token Pasting
*/
#ifndef JLSS_ID_PASTE_H
#define JLSS_ID_PASTE_H
/*
* Common case when someone just includes this file. In this case,
* they just get the various E* tokens as good old enums.
*/
#if !defined(ETYPE)
#define ETYPE(val, desc) E##val,
#define ETYPE_ENUM
enum {
#endif /* ETYPE */
ETYPE(PERM, "Operation not permitted")
ETYPE(NOENT, "No such file or directory")
ETYPE(SRCH, "No such process")
ETYPE(INTR, "Interrupted system call")
ETYPE(IO, "I/O error")
ETYPE(NXIO, "No such device or address")
ETYPE(2BIG, "Arg list too long")
/*
* Close up the enum block in the common case of someone including
* this file.
*/
#if defined(ETYPE_ENUM)
#undef ETYPE_ENUM
#undef ETYPE
ETYPE_MAX
};
#endif /* ETYPE_ENUM */
#endif /* JLSS_ID_PASTE_H */
Example source:
/*
#(#)File: $RCSfile: paste1.c,v $
#(#)Version: $Revision: 1.2 $
#(#)Last changed: $Date: 2008/06/24 01:03:38 $
#(#)Purpose: Automated Token Pasting
*/
#include "paste1.h"
static const char *sys_errlist_internal[] = {
#undef JLSS_ID_PASTE_H
#define ETYPE(val, desc) desc,
#include "paste1.h"
0
#undef ETYPE
};
static const char *xerror(int err)
{
if (err >= ETYPE_MAX || err <= 0)
return "Unknown error";
return sys_errlist_internal[err];
}
static const char*errlist_mnemonics[] = {
#undef JLSS_ID_PASTE_H
#define ETYPE(val, desc) [E ## val] = "E" #val,
#include "paste1.h"
#undef ETYPE
};
#include <stdio.h>
int main(void)
{
int i;
for (i = 0; i < ETYPE_MAX; i++)
{
printf("%d: %-6s: %s\n", i, errlist_mnemonics[i], xerror(i));
}
return(0);
}
Not necessarily the world's cleanest use of the C pre-processor - but it does prevent writing the material out multiple times.
Making something both a C identifier and a string
#define stringify( name ) # name
enum MyEnum {
ENUMVAL1
};
...stuff...
stringify(EnumName::ENUMVAL1); // Returns MyEnum::ENUMVAL1
Further discussion on this method
Preprocessor directive tricks for newcomers
If the enum index is 0-based, you can put the names in an array of char*, and index them with the enum value.
I have created a simple templated class streamable_enum that uses stream operators << and >> and is based on the std::map<Enum, std::string>:
#ifndef STREAMABLE_ENUM_HPP
#define STREAMABLE_ENUM_HPP
#include <iostream>
#include <string>
#include <map>
template <typename E>
class streamable_enum
{
public:
typedef typename std::map<E, std::string> tostr_map_t;
typedef typename std::map<std::string, E> fromstr_map_t;
streamable_enum()
{}
streamable_enum(E val) :
Val_(val)
{}
operator E() {
return Val_;
}
bool operator==(const streamable_enum<E>& e) {
return this->Val_ == e.Val_;
}
bool operator==(const E& e) {
return this->Val_ == e;
}
static const tostr_map_t& to_string_map() {
static tostr_map_t to_str_(get_enum_strings<E>());
return to_str_;
}
static const fromstr_map_t& from_string_map() {
static fromstr_map_t from_str_(reverse_map(to_string_map()));
return from_str_;
}
private:
E Val_;
static fromstr_map_t reverse_map(const tostr_map_t& eToS) {
fromstr_map_t sToE;
for (auto pr : eToS) {
sToE.emplace(pr.second, pr.first);
}
return sToE;
}
};
template <typename E>
streamable_enum<E> stream_enum(E e) {
return streamable_enum<E>(e);
}
template <typename E>
typename streamable_enum<E>::tostr_map_t get_enum_strings() {
// \todo throw an appropriate exception or display compile error/warning
return {};
}
template <typename E>
std::ostream& operator<<(std::ostream& os, streamable_enum<E> e) {
auto& mp = streamable_enum<E>::to_string_map();
auto res = mp.find(e);
if (res != mp.end()) {
os << res->second;
} else {
os.setstate(std::ios_base::failbit);
}
return os;
}
template <typename E>
std::istream& operator>>(std::istream& is, streamable_enum<E>& e) {
std::string str;
is >> str;
if (str.empty()) {
is.setstate(std::ios_base::failbit);
}
auto& mp = streamable_enum<E>::from_string_map();
auto res = mp.find(str);
if (res != mp.end()) {
e = res->second;
} else {
is.setstate(std::ios_base::failbit);
}
return is;
}
#endif
Usage:
#include "streamable_enum.hpp"
using std::cout;
using std::cin;
using std::endl;
enum Animal {
CAT,
DOG,
TIGER,
RABBIT
};
template <>
streamable_enum<Animal>::tostr_map_t get_enum_strings<Animal>() {
return {
{ CAT, "Cat"},
{ DOG, "Dog" },
{ TIGER, "Tiger" },
{ RABBIT, "Rabbit" }
};
}
int main(int argc, char* argv []) {
cout << "What animal do you want to buy? Our offering:" << endl;
for (auto pr : streamable_enum<Animal>::to_string_map()) { // Use from_string_map() and pr.first instead
cout << " " << pr.second << endl; // to have them sorted in alphabetical order
}
streamable_enum<Animal> anim;
cin >> anim;
if (!cin) {
cout << "We don't have such animal here." << endl;
} else if (anim == Animal::TIGER) {
cout << stream_enum(Animal::TIGER) << " was a joke..." << endl;
} else {
cout << "Here you are!" << endl;
}
return 0;
}
Here is a solution using macros with the following features:
only write each value of the enum once, so there are no double lists to maintain
don't keep the enum values in a separate file that is later #included, so I can write it wherever I want
don't replace the enum itself, I still want to have the enum type defined, but in addition to it I want to be able to map every enum name to the corresponding string (to not affect legacy code)
the searching should be fast, so preferably no switch-case, for those huge enums
https://stackoverflow.com/a/20134475/1812866
I thought that a solution like Boost.Fusion one for adapting structs and classes would be nice, they even had it at some point, to use enums as a fusion sequence.
So I made just some small macros to generate the code to print the enums. This is not perfect and has nothing to see with Boost.Fusion generated boilerplate code, but can be used like the Boost Fusion macros. I want to really do generate the types needed by Boost.Fusion to integrate in this infrastructure which allows to print names of struct members, but this will happen later, for now this is just macros :
#ifndef SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
#define SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
#include <swissarmyknife/detail/config.hpp>
#include <string>
#include <ostream>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#define SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C( \
R, unused, ENUMERATION_ENTRY) \
case ENUMERATION_ENTRY: \
return BOOST_PP_STRINGIZE(ENUMERATION_ENTRY); \
break;
/**
* \brief Adapts ENUM to reflectable types.
*
* \param ENUM_TYPE To be adapted
* \param ENUMERATION_SEQ Sequence of enum states
*/
#define SWISSARMYKNIFE_ADAPT_ENUM(ENUM_TYPE, ENUMERATION_SEQ) \
inline std::string to_string(const ENUM_TYPE& enum_value) { \
switch (enum_value) { \
BOOST_PP_SEQ_FOR_EACH( \
SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C, \
unused, ENUMERATION_SEQ) \
default: \
return BOOST_PP_STRINGIZE(ENUM_TYPE); \
} \
} \
\
inline std::ostream& operator<<(std::ostream& os, const ENUM_TYPE& value) { \
os << to_string(value); \
return os; \
}
#endif
The old answer below is pretty bad, please don't use that. :)
Old answer:
I've been searching a way which solves this problem without changing too much the enums declaration syntax. I came to a solution which uses the preprocessor to retrieve a string from a stringified enum declaration.
I'm able to define non-sparse enums like this :
SMART_ENUM(State,
enum State {
RUNNING,
SLEEPING,
FAULT,
UNKNOWN
})
And I can interact with them in different ways:
// With a stringstream
std::stringstream ss;
ss << State::FAULT;
std::string myEnumStr = ss.str();
//Directly to stdout
std::cout << State::FAULT << std::endl;
//to a string
std::string myStr = State::to_string(State::FAULT);
//from a string
State::State myEnumVal = State::from_string(State::FAULT);
Based on the following definitions :
#define SMART_ENUM(enumTypeArg, ...) \
namespace enumTypeArg { \
__VA_ARGS__; \
std::ostream& operator<<(std::ostream& os, const enumTypeArg& val) { \
os << swissarmyknife::enums::to_string(#__VA_ARGS__, val); \
return os; \
} \
\
std::string to_string(const enumTypeArg& val) { \
return swissarmyknife::enums::to_string(#__VA_ARGS__, val); \
} \
\
enumTypeArg from_string(const std::string &str) { \
return swissarmyknife::enums::from_string<enumTypeArg>(#__VA_ARGS__, str); \
} \
} \
namespace swissarmyknife { namespace enums {
static inline std::string to_string(const std::string completeEnumDeclaration, size_t enumVal) throw (std::runtime_error) {
size_t begin = completeEnumDeclaration.find_first_of('{');
size_t end = completeEnumDeclaration.find_last_of('}');
const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );
size_t count = 0;
size_t found = 0;
do {
found = identifiers.find_first_of(",}", found+1);
if (enumVal == count) {
std::string identifiersSubset = identifiers.substr(0, found);
size_t beginId = identifiersSubset.find_last_of("{,");
identifiersSubset = identifiersSubset.substr(beginId+1);
boost::algorithm::trim(identifiersSubset);
return identifiersSubset;
}
++count;
} while (found != std::string::npos);
throw std::runtime_error("The enum declaration provided doesn't contains this state.");
}
template <typename EnumType>
static inline EnumType from_string(const std::string completeEnumDeclaration, const std::string &enumStr) throw (std::runtime_error) {
size_t begin = completeEnumDeclaration.find_first_of('{');
size_t end = completeEnumDeclaration.find_last_of('}');
const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );
size_t count = 0;
size_t found = 0;
do {
found = identifiers.find_first_of(",}", found+1);
std::string identifiersSubset = identifiers.substr(0, found);
size_t beginId = identifiersSubset.find_last_of("{,");
identifiersSubset = identifiersSubset.substr(beginId+1);
boost::algorithm::trim(identifiersSubset);
if (identifiersSubset == enumStr) {
return static_cast<EnumType>(count);
}
++count;
} while (found != std::string::npos);
throw std::runtime_error("No valid enum value for the provided string");
}
}}
When I'll need support for sparse enum and when I'll have more time I'll improve the to_string and from_string implementations with boost::xpressive, but this will costs in compilation time because of the important templating performed and the executable generated is likely to be really bigger. But this has the advantage that it will be more readable and maintanable than this ugly manual string manipulation code. :D
Otherwise I always used boost::bimap to perform such mappings between enums value and string, but it has to be maintained manually.
Because I prefer not to use macros for all the usual reasons, I used a more limited macro solution that has the advantage of keeping the enum declaration macro free. Disadvantages include having to copy paste the macro defintion for each enum, and having to explicitly add a macro invocation when adding values to the enum.
std::ostream& operator<<(std::ostream& os, provenance_wrapper::CaptureState cs)
{
#define HANDLE(x) case x: os << #x; break;
switch (cs) {
HANDLE(CaptureState::UNUSED)
HANDLE(CaptureState::ACTIVE)
HANDLE(CaptureState::CLOSED)
}
return os;
#undef HANDLE
}
There's a way simpler and imo sorta clearer approach
that was missing on this thread:
#define ENUM_PUSH(ENUM) ENUM,
#define STRING_PUSH(STR) #STR,
#define FETCH_MSG(X) \
X(string1) \
X(string2) \
static const char * msgStr[] = {
FETCH_MSG(STRING_PUSH)
};
enum msg {
FETCH_MSG(ENUM_PUSH)
};
static enum msg message;
void iterate(void) {
switch (message) {
case string1:
// do your thing here
break;
case string2:
break;
}
}
The only downside is that the last cell will be postceded by a comma,
though it appears to be acceptable by C/C++ compilers.

Resources