Accessing enum strings by array notation? - c

If I were to compare these enum values in a switch statement. What is the purpose of numbering the strings?
If I store the enum in a header file and then import the header file. I know that I can say events event = ls; (I can store ls into events)
But can I say something like event[0] to access ls?
#define NUM_EVENTS 11
typedef enum events {
ls = 0,
rs = 1,
ws = 2,
lo = 3,
ro = 4,
lc = 5,
rc = 6,
gru = 7,
grl = 8,
gll = 9,
glu = 10
} events;

There's no such thing as enum strings. What you're talking about are identifiers, only used in the source code. An enum in C is represented as an integer type, typically int, but compilers are free to chose e.g. short or another one. An enum is even allowed to hold a value that doesn't correspond to one of the named members. Only these numbers go in the compiled programs. C compilers do not (have to) store any type information in the compiled executable.
So, if you need the enum identifiers as strings at runtime, you have to store them yourself. This typically looks like e.g.
typedef enum events {
ls,
rs,
ws,
lo,
ro,
lc,
rc,
gru,
grl,
gll,
glu
} events;
static const char *event_names[] = {
"ls",
"rs",
"ws",
"lo",
"ro",
"lc",
"rc",
"gru",
"grl",
"gll",
"glu"
};
void example(event ev)
{
// might want to check for a valid value here ...
// print name of event
puts(event_names[ev]);
}
As a side note, there's no need to explicitly number the members in your case -- the default numbering starts at 0 and increases by one for each member.
Further hints:
enum member identifiers are global, so it's a good practice to make them unique, e.g. by prepending the type name like this:
typedef enum events {
event_ls,
event_rs,
// ...
} events;
The above code violates the DRY (don't repeat yourself) principle: when changing the enum, you have to touch two different locations in the code which is a source of potential bugs. This is sometimes solved using X macros. There are opposing opinions whether this is good or bad style, just presenting it here for completeness:
#define EVENTS_LIST \
X(ls) \
X(rs) \
X(ws) \
X(lo) \
X(ro) \
X(lc) \
X(rc) \
X(gru) \
X(grl) \
X(gll) \
X(glu)
#define X(name) event_##name ,
typedef enum events {
EVENTS_LIST
} events;
#undef X
#define X(name) #name ,
static const char *event_names[] = {
EVENTS_LIST
};
#undef X
after preprocessing, this produces
typedef enum events {
event_ls , event_rs , event_ws , event_lo , event_ro , event_lc , event_rc , event_gru , event_grl , event_gll , event_glu ,
} events;
static const char *event_names[] = {
"ls" , "rs" , "ws" , "lo" , "ro" , "lc" , "rc" , "gru" , "grl" , "gll" , "glu" ,
};

You can cast from an integer to an enum, which is equivalent to indexing if the enum is a dense sequence starting at zero. In general enums are just a way to introduce symbolic names for a list of related constants. It is syntactically better than doing so using #define but there is very little functionality beyond such. If the numbers are not explicitly assigned, the compiler will manage assigning unique values to each member. But even finding the max value is not something offered by the language, much less mapping to names as strings or similar.

I think you are asking how to get the name of the event as a string from the event so you can do something like printf("%s\n", SOMEFUNCTION(event)); to print the name of the event.
There's is no such function.

If you really want to use event[0] you can do this:
events event[11];
event[0] = ls;
event[1] = rs;
event[2] = ws;
event[3] = lo;
event[4] = ro;
event[5] = lc;
event[6] = rc;
event[7] = gru;
event[8] = grl;
event[9] = gll;
event[10] = glu;
I will say though, I am sure there is a way around doing this ( since it is not elegant at all ), if you give more context :)
EDIT: After re-reading your question, I get the impression you want the STRING "ls" from your enum?
If this is so, then no you are not able to do this. Enumerated types are great for switch statements, but if you are trying to turn a variable name into a string through code without hardcoding event[0] = "ls", it isn't going to happen.

Related

Force member initialization in static const struct in c

I am using a structs for storing different configurations and I would like to force the initialization of all members. I mean not let the compiler do it (to zero), but the progammer explizitly.
A compiler error would be best but a warning would be fine as well. I am using Segger clang for arm.
Any tips about how to achieve this?
Since I would like to use designated initializers, -Wmissing-field-initializers does not work in this case. See: Docs
typedef struct{
int option_1,
int option_2,
....
} config_t;
// this is fine
const config_t config_1 = {
.option_1 = 10,
.option_2 = 20,
};
// this should generate a warning
const config_t config_1 = {
.option_2 = 20,
};
There's no particularly elegant way to do this programmatically. Best bet is to use a static initializer tool. (There's for example a MISRA-C:2012 rule that can be checked for, requiring that all elements of a struct must be initialized explicitly.)
With pure standard C, well...
Since this struct won't have any padding, I suppose you could cook up something fairly ugly like this:
#define CONFIG_INIT_LIST1 10, 20
#define CONFIG_INIT_LIST2 10
_Static_assert(sizeof (int[]){CONFIG_INIT_LIST1} == sizeof (config_t),
"CONFIG_INIT_LIST1 wrong number of initializers.");
_Static_assert(sizeof (int[]){CONFIG_INIT_LIST2} == sizeof (config_t),
"CONFIG_INIT_LIST2 wrong number of initializers.");
// this is fine
const config_t config_1 = {
CONFIG_INIT_LIST1
};
const config_t config_2 = {
CONFIG_INIT_LIST2
};
This gives the compiler error
error: static assertion failed: "CONFIG_INIT_LIST2 wrong number of initializers."

Dynamic method of calling structures in C

I have a project which involves writing a C program for some software used by my company. I want it to be as efficient as possible, but the way the software references the signals I'm working with is a little wonky. I'm working with 4 sets of 96 signals; these signals are grouped into 32 groups with 3 members each. Rather than generic functions to work with these signals (the generic functions exist, but there's no documentation on how they work), the auto-generated header file has defined a group of macros (I think) for each of these groups.
Each of the groups are defined as follows...
typedef struct {
//struct members
} AB_A_Group_A_Network
Each of those structures have a group of macros(?) defined like this...
void AB_A_Group_ZF_Network_Init(AB_A_Group_A_Network *pAbc)
{
double values[6]
...
pAbc->Member_1 = values[3] //array positions vary
pAbc->Member_2 = values[4]
pAbc->Member_3 = values[5]
}
The software's tech support suggested I do the following, but I was hoping there'd be a better way to do it. Of course this is a long method, but I can write it with a python script no problem, if need be. These 250+ lines of code will run every second in my application for each data set.
AB_A_Group_A_Network GroupA;
AB_A_Group_B_Network GroupB;
//...and so on
AB_A_Group_ZF_Network GroupZF;
AB_A_Group_A_Network_Init(&GroupA);
AB_A_Group_B_Network_Init(&GroupB);
//...and so on
AB_A_Group_ZF_Network_Init(&GroupZF);
CD_Array_Set(0,GroupA.Member_1); //a custom array function meant to interface with the software
CD_Array_Set(1,GroupA.Member_2);
//...and so on
CD_Array_Set(95,GroupZF.Member_96);
//...Repeat 3 times for 4 sets of data (Data sets A-D), with checks to see if that data exists
I thought of doing something like this, but I'm not sure you can use char arrays in this way. I'll add the disclaimer don't have much experience with C, so this might look/sound really stupid. This isn't working code, just a stream of consciousness. I'm also not sure if doing it in this way, if possible, would just end up losing efficiency. I'm completely open to other methods.
char groupName[70] = "AB_*_Group_*_Network";
char dataset = 'A';
char group = 'A';
char member = '1';
char groupInit[70]
//write a loop to increment dataset alphabetically
//check if dataset exists
groupName[3] = dataset;
//write a sub-loop to increment group alphabetically (each set has 32 groups)
groupName[27] = group;
//use groupName in place of struct name (not sure how or if this is possible with my current methods)
groupInit = strncat(groupName, "_Init", 5);
//use groupInit in place of _init macro name
//write a sub-loop to increment member numerically (each group has 3 members)
char member[10] = "Member_*";
member[7] = member;
CD_Array_Set(i,groupName.member);
Is the long method really the best way of doing this? Any advice you can offer is appreciated!
There's no way to refer to variables and type names dynamically from strings at runtime. But you can use token pasting in a macro to avoid all the repeated code.
#define INIT_GROUP(dataset, group) \
AB_ ## dataset ## _Group_ ## group ## _Network Group ## group; \
AB_ ## dataset ## _Group_ ## group ## _Network_Init(&Group ## group); \
CD_Array_Set(0,Group ## group .Member_1); \
CD_Array_Set(1,Group ## group .Member_2); \
...
CD_Array_Set(95,Group ## group .Member_96);
Unfortunately, there are no loops in the preprocessor, so you have to write all 96 CD_Array_Set lines in the macro.
With this macro, you can then write:
INIT_GROUP(A, A)
INIT_GROUP(A, B)
...
INIT_GROUP(A, ZF)
Auto generated C code
It is possible to do such things in C but it requires ugly Macros.
First thing i would look at is
An old Stackoverflow question: https://stackoverflow.com/a/12591965/8964221
And the mentioned gist in it: https://gist.github.com/epatel/3786323
BUT!
I would think about the time it would consume to have it bug free and working.
It won`t be that easy to transform it to ur needs.

Best way to extract all enums from C project

I'm writing my own enums-based backtrace. Basically each function call will append it's error to a pre-defined stack which will eventually include all errors across all function calls.
I want to create an efficient way to parse it. In the end I got int backtrace[] with enum values from all over my codebase, and I'd like to relate each enum value to it's name.
This should of course be done post-failure (not at runtime), so what I'm missing is a way, maybe as a part of the compilation step, to dump all my enums and there values to a file. Then i can write a script that parse the errors.
Is there an easy (cross-platform) static tool that does that?
Not sure how you log the errors, but how about:
typedef enum
{
E_SUCCESS = 0,
E_FAIL,
...
} error_t;
typedef struct
{
error_t code;
char * error_name;
} backtrace_entry_t;
backtrace_entry_t backtrace[..];
#define FUNC_EXIT(error_code__) \
do { \
backtrace[func_id].code = (error_code__); \
backtrace[func_id].error_name = #error_code__; \
} while (0)
Then, when you call FUNC_EXIT(E_SUCCESS);, you'll get for the backtrace for the function to be: {.code = 0, .error_name = "E_SUCCESS"}
The problem is, you can't get the right name if you call FUNC_EXIT(var);, where var is some local variable.
Another option, although still not an automagical one:
typedef enum
{
E_SUCCESS = 0,
E_FAIL,
...
NOF_ERROR_CODES
} error_t;
#define MAP_E_CODE_TO_NAME(error_code__) [error_code__] = #error_code__
const char * error_to_name_mapping[NOF_ERROR_CODES] = {
MAP_E_CODE_TO_NAME(E_SUCCESS),
MAP_E_CODE_TO_NAME(E_FAIL),
...
};
Will give a const char * array with {"E_SUCCESS", "E_FAIL", ...}, which you can use like this:
printf("The name of error code %d is '%s'", error, error_to_name_mapping[error]);
The problem here is that you have to have positive value to errors with increasing values.

Declare struct and struct-size related arrays

I'm working in a condition like this:
typedef struct __type_x{
sub_type_a a;
sub_type_b b;
sub_type_c c;
}type_x_t;
uint32_t type_x_uids[] = {
1, //a's_uid
2, //b's_uid
3, //c's_uid
};
uint32_t type_x_uid_another[] = {
3, //a's uid in another API system
1, //b's uid in another API system
2, //c's uid in another API system
};
uint32_t type_x_uids_one_more[] = {
1, //a's_uid in yet another system
3, //b's_uid in yet another system
2, //c's_uid in yet another system
};
The problem is: If I need to add one more data(like d) into struct type_x_t, I need to add its uids into all other three arrays.
It is hard to maintain the code.
So I'm wondering if it is possible to maintain all these data from four places into one table?
I can put three uint32 arrays into one 2D array
uint32_t uid[system_id][value];
but I also have to maintain two pieces of code instead of one.
I'm wondering if there is anyway I can move forward? Therefore I can maintain one table to manage all these data. Such like
{#data_type, #data_name, #uid_1, #uid2, #uid 3},
Trying to use Macro to solve the problem, but I cannot access the data in certain position by __VA_ARGS__.
First of all, if you have placed the code adjacently in one single file, you are actually maintaining one place and not several. What you have is not necessarily bad. Anyway...
I will assume there's a reason why type_x_t is a struct and not an array. Then clearly the best design is to add the "uid" for "a" to the sub_type_a struct, or alternatively make a new struct containing both, since those data belong together.
If that's not an option, you could go with the array and then put that inside the type_x_t struct.
If that's not an option either, consider some completely different program design.
With all program design options exhausted, then - and only then - you could consider macros. What you are fishing for is so-called "X macros", which is the last resort and not really recommended, since they make the code much harder to read. The purpose of X macros is to centralize code maintenance of data to one place. It goes like this:
#include <stdint.h>
#define TYPE_X_LIST \
X(a,1,3,1) \
X(b,2,1,3) \
X(c,3,2,2)
typedef struct
{
#define X(name, dummmy1, dummy2, dummy3) sub_type_##name name;
TYPE_X_LIST
#undef X
} type_x_t;
uint32_t type_x_uids[] = {
#define X(dummy1, id, dummy2, dummy3) id,
TYPE_X_LIST
#undef X
};
uint32_t type_x_uid_another[] = {
#define X(dummy1, dummy2, id, dummy3) id,
TYPE_X_LIST
#undef X
};
uint32_t type_x_uids_one_more[] = {
#define X(dummy1, dummy2, dummy3, id) id,
TYPE_X_LIST
#undef X
};
Now you only have to change the "TYPE_X_LIST" when you need to change the data. If you want to keep track of how many data sets you have, you can add an enum to count them:
typedef enum
{
#define X(name, ...) something_##name,
TYPE_X_LIST
#undef X
TYPE_X_N // the number of data sets
} type_x_size_t;
Then TYPE_X_N could be used to set array sizes etc.

Could I use preprocessor to make this one clearer?

I was writing a little source file function for my Pic32 and I got stucked on one thing.
It's basically an utility that should store incomming char data into buffer and then, if '\r' is recieved, it compares the buffer against list of commands (in array names), and if match is found, the index of the item is returned.
This part is from header:
#define NAMECNT 6
static const char names[NAMESCNT][10] = { // 6commands, max 10 char each
"korr", // 1
"adc", // 2
"fft", // 3
"data", // 4
"pr", // 5
"prsc"}; // 6
/* functions */
extern int comm(char cdata);
At the main file, there is one big switch:
switch( comm(recieved_ch) ){
case 1: foo1(); break;
case 2: foo2(); break;
...
}
Now, for the better clarity, I wanted to use instead of 1, 2, ... the original names (like case KORR: case ADC:) so I wrote deffinitions for each one of them
#define KORR 1
#define ADC 2
But I don't like that solution, because I want to use this source file in more projects and there is gonna be different list of commands for each. Is there any way how to do this?
Best thing would be to create the array names in preprocessor, but I doubt that's even possible. I was thinking about using enum type (which would have same items as list of commands names), but I am not sure how would that go.
You can use X-macros to build an enum and fill the array, then you can use the enum values in the switch:
#define VARS \
X(korr) \
X(adc) \
X(fft) \
X(data) \
X(pr) \
X(prsc)
static const char names[][10] = { // 6commands, max 10 char each
#define X(name) #name,
VARS
#undef X
};
enum evars {
#define X(name) name,
VARS
#undef X
};
extern int comm(char cdata);
int main(void)
{
char x = 1;
switch (comm(x)) {
case korr:
printf("korr");
break;
case adc:
printf("adc");
break;
/* ... and so on */
}
return 0;
}
The expansion of X is:
static const char names[][10] = {
"korr", "adc", "fft", "data", "pr", "prsc",
};
enum evars {
korr, adc, fft, data, pr, prsc,
};
Edit: As pointed out by #5gon12eder, you don't need to hardcode 6 in the first dimension of the array (you can leave it unspecified).
The preprocessor could make things clearer here, I think, using the concatentation operator ##, but it'll not yield a performance advantage. A switch statement could be optimized by the compiler, but that's implementation-dependent.
Instead of the "one big switch," use an array of function pointers. Something like
func_ptrs[comm(received_ch) - 1]();
will call the corresponding function, where foo1 is at index 0, foo2 at 1, etc. To add a command, simply append a command name to the command list and a function pointer func_ptrs.
After all, you kill two birds with one stone: you create an easy way to add commands and improve performance.
Besides, a linear search through an array of strings is pretty inefficient. A hash table would yield a performance advantage.

Resources