Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
Question:
I want to create a reusable function, because in my code much line that use same code structure
The code using for example if { if { `Only here's the different` } }. Of course the pattern not same as this, this using as an example.
I've been code using framework such as Laravel, there's a directive called as SLOT
Is there any way I can inject code in the middle of for loop? Or anything same as SLOT inside C programming
Sample code:
void functionname() {
for (int i=0; i < total_count; i++) {
SELECT THE ITEM (i)
if (a == b) return;
if (c) {
CODE INJECT HERE
}
}
}
Forget to mention before, a, b, c and so on from the coding above is getting from ITEM (i)
You should use a callback. i.e. you should send a function pointer (i.e. the address of the function you want to execute) and use that pointer to execute that function inside your loop.
In the example below, p is a pointer to a function taking a const char * for a parameter and returning an int.
int (*p)(const char *s) ;
NB: all functions passed as parameter, to be used as callback must have the same prototype (which is why such functions are often declared taking a generic pointer parameter void * to accept whatever you've got to send to the function).
So with your example and with functions taking void * as a parameter and returning void *, and with param defining a parameter that you want to feed to your function, this gives us the following code:
void functionname(void *(*func)(void *)) {
for (int i=0; i < total_count; i++) {
SELECT THE ITEM (i)
if (a == b) return;
if (c) {
func(¶m);
}
}
}
you can call your function wiht whatever function respecting the prototype... For instance:
void *my_custom_function(void *param) {
...
}
...
functionname(my_custom_function);
...
As suggested in the comment by KamilCik, use function pointers:
void functionname(void *fx)(void)) {
for (int i=0; i < total_count; i++) {
SELECT THE ITEM (i)
if (a == b) return;
if (c) {
//CODE INJECT HERE
fx();
}
}
}
And use it like
void foo(void) { puts("foo() called"); }
void bar(void) { puts("bar() called"); }
int main(void) {
functionname(foo);
functionname(bar);
}
For a concrete example:
#include <stdio.h>
int a = 1;
int b = 2;
typedef void (*selector)(int, int *);
typedef void (*injector)(void);
void select1(int x, int *c) { printf("%s: %d\n", __func__, *c = x); }
void select2(int x, int *c) { printf("%s: %d\n", __func__, *c = x); }
void inject1(void) { printf("%s\n", __func__); }
void inject2(void) { printf("%s\n", __func__); }
void
functionname(size_t total_count, selector SELECT_THE_ITEM,
injector CODE_INJECT_HERE )
{
for (size_t i=0; i < total_count; i++) {
int c;
SELECT_THE_ITEM (i, &c);
if (a == b) return;
if (c) {
CODE_INJECT_HERE();
}
}
}
int
main(void)
{
functionname(2, select1, inject1);
functionname(3, select2, inject2);
}
You can do what you ask by defining your "CODE INJECT HERE" as the body of a function, and passing a pointer to that function:
void functionname(void (*inject)(void)) {
for (int i=0; i < total_count; i++) {
SELECT THE ITEM (i)
if (a == b) return;
if (c) {
inject();
}
}
}
void do_something(void) {
CODE INJECT HERE
}
void do_something_else(void) {
OTHER CODE INJECT HERE
}
int main(void) {
functionname(do_something));
functionname(do_something_else));
}
Do note, however, that this is not simple code injection in the same sense as a macro would provide. In particular, the executions of do_something() and do_something_else() will not see the local variables of main() or of functionname(), and the do_* functions can return only from themselves, not from a caller further up the chain. The former can be mitigated to some extent by passing parameters to the do_* functions (which they must be prepared to accept).
Another alternative would be to use a macro instead of a function to provide the common framework. It would look something like this:
#define frame_it(x) do { \
for (int i=0; i < total_count; i++) { \
SELECT THE ITEM (i) \
if (a == b) return; \
if (c) { \
x \
} \
} \
} while (0)
int main(void) {
frame_it(
CODE INJECT HERE
);
frame_it(
OTHER CODE INJECT HERE
);
}
That keeps the CODE INJECT HERE code in the function using it, which might be advantageous if in fact each such piece of code is used in only one place. It also allows both that code and the framing code to access the local variables of the function in which they appear, and to return from that function if desired.
However, macro programming has earned a mostly-deserved reputation for being error prone and difficult to read and debug. Your particular need may be one that is well served by this approach, but do not choose this direction lightly.
Function pointers are great for this. You can typedef the function signatures you'd like to support. Example:
/* A signature for condition checking functions, taking a "void*" argument
and returning true or false */
typedef bool(*cond_check_t)(void*);
/* A signature for functions to execute if a condition is met. This takes a
"void*" argument but you decide what you need */
typedef void(*exec_t)(void*);
You can package these two in a struct to form a nice pair:
typedef struct {
cond_check_t checker;
exec_t executor;
} check_exec_t;
And with that, another struct to keep a bunch of these condition and executor pairs:
typedef struct {
size_t size;
size_t capacity;
check_exec_t *conditionals;
} cond_pack_t;
You then create support functions for adding checkers and executors and a function to processes one of these packaged checkers and executors.
cond_pack_t* cond_pack_create(size_t capacity) {
cond_pack_t* cp = malloc(sizeof(*cp));
if(cp) {
cp->conditionals = malloc(sizeof(*cp->conditionals) * capacity);
if(cp->conditionals) {
cp->size = 0;
cp->capacity = capacity;
} else {
free(cp);
cp = NULL;
}
}
return cp;
}
void cond_pack_destroy(cond_pack_t *cp) {
free(cp->conditionals);
free(cp);
}
bool cond_pack_add(cond_pack_t *cp, cond_check_t checker, exec_t executor) {
if(cp->size == cp->capacity) return false;
cp->conditionals[cp->size].checker = checker;
cp->conditionals[cp->size].executor = executor;
++cp->size;
return true;
}
void cond_pack_process(cond_pack_t *cp) {
for(size_t i = 0; i < cp->size; ++i) {
if(cp->conditionals[i].checker(NULL)) { /* execute checker */
cp->conditionals[i].executor(NULL); /* execute executor */
}
}
}
With that, a usage example could look like this
//---
bool some_check(void *foo) {
return true;
}
void some_executor(void *foo) {
printf("some_executor\n");
}
bool some_other_check(void *foo) {
return false;
}
void some_other_executor(void *foo) {
printf("some_other_executor\n");
}
int main() {
cond_pack_t *cp = cond_pack_create(10);
if(cp) {
cond_pack_add(cp, some_check, some_executor);
cond_pack_add(cp, some_other_check, some_other_executor);
cond_pack_process(cp); /* run all the checkers / executors */
cond_pack_destroy(cp);
}
}
Demo
I am browsing through telegram-cli source code to make some changes. However, I am stuck at a syntax that I am not able to understand.
#define user_cmp(a,b) (tgl_get_peer_id ((a)->id) - tgl_get_peer_id ((b)->id))
DEFINE_TREE(user, struct tgl_user *,user_cmp,0)
static void notify_status (struct tgl_user *U, void *ex) {
struct tgl_state *TLS = ex;
if (TLS->callback.user_status_update) {
TLS->callback.user_status_update (TLS, U);
}
}
static void status_notify (struct tgl_state *TLS, void *arg) {
tree_act_ex_user (TLS->online_updates, notify_status, TLS);
tree_clear_user (TLS->online_updates);
TLS->online_updates = NULL;
TLS->timer_methods->free (TLS->online_updates_timer);
TLS->online_updates_timer = NULL;
}
The tree_act_ex_user function is not defined anywhere and IDE is navigating to second line i.e. DEFINE_TREE(user, struct tgl_user *,user_cmp,0). Please explain or point to a reference what construct is this?
what construct is this?
A macro expansion that defines a function. A reduced example:
#define DEFINE_TREE(NAME, some_arg, some_arg2, yet_another_arg) \
static void tree_act_ex_##NAME() { \
do_something(); \
}
DEFINE_TREE(some_string, arg1, arg2, arg3)
// expands to:
// static void tree_act_ex_some_string() { do_something(); }
int main() {
tree_act_ex_some_string();
}
int main()
{
enum Days{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};
Days TheDay;
int j = 0;
printf("Please enter the day of the week (0 to 6)\n");
scanf("%d",&j);
TheDay = Days(j);
//how to PRINT THE VALUES stored in TheDay
printf("%s",TheDay); // isnt working
return 0;
}
Enumerations in C are numbers that have convenient names inside your code. They are not strings, and the names assigned to them in the source code are not compiled into your program, and so they are not accessible at runtime.
The only way to get what you want is to write a function yourself that translates the enumeration value into a string. E.g. (assuming here that you move the declaration of enum Days outside of main):
const char* getDayName(enum Days day)
{
switch (day)
{
case Sunday: return "Sunday";
case Monday: return "Monday";
/* etc... */
}
}
/* Then, later in main: */
printf("%s", getDayName(TheDay));
Alternatively, you could use an array as a map, e.g.
const char* dayNames[] = {"Sunday", "Monday", "Tuesday", /* ... etc ... */ };
/* ... */
printf("%s", dayNames[TheDay]);
But here you would probably want to assign Sunday = 0 in the enumeration to be safe... I'm not sure if the C standard requires compilers to begin enumerations from 0, although most do (I'm sure someone will comment to confirm or deny this).
I use something like this:
in a file "EnumToString.h":
#undef DECL_ENUM_ELEMENT
#undef DECL_ENUM_ELEMENT_VAL
#undef DECL_ENUM_ELEMENT_STR
#undef DECL_ENUM_ELEMENT_VAL_STR
#undef BEGIN_ENUM
#undef END_ENUM
#ifndef GENERATE_ENUM_STRINGS
#define DECL_ENUM_ELEMENT( element ) element,
#define DECL_ENUM_ELEMENT_VAL( element, value ) element = value,
#define DECL_ENUM_ELEMENT_STR( element, descr ) DECL_ENUM_ELEMENT( element )
#define DECL_ENUM_ELEMENT_VAL_STR( element, value, descr ) DECL_ENUM_ELEMENT_VAL( element, value )
#define BEGIN_ENUM( ENUM_NAME ) typedef enum tag##ENUM_NAME
#define END_ENUM( ENUM_NAME ) ENUM_NAME; \
const char* GetString##ENUM_NAME(enum tag##ENUM_NAME index);
#else
#define BEGIN_ENUM( ENUM_NAME) const char * GetString##ENUM_NAME( enum tag##ENUM_NAME index ) {\
switch( index ) {
#define DECL_ENUM_ELEMENT( element ) case element: return #element; break;
#define DECL_ENUM_ELEMENT_VAL( element, value ) DECL_ENUM_ELEMENT( element )
#define DECL_ENUM_ELEMENT_STR( element, descr ) case element: return descr; break;
#define DECL_ENUM_ELEMENT_VAL_STR( element, value, descr ) DECL_ENUM_ELEMENT_STR( element, descr )
#define END_ENUM( ENUM_NAME ) default: return "Unknown value"; } } ;
#endif
then in any header file you make the enum declaration, day enum.h
#include "EnumToString.h"
BEGIN_ENUM(Days)
{
DECL_ENUM_ELEMENT(Sunday) //will render "Sunday"
DECL_ENUM_ELEMENT(Monday) //will render "Monday"
DECL_ENUM_ELEMENT_STR(Tuesday, "Tuesday string") //will render "Tuesday string"
DECL_ENUM_ELEMENT(Wednesday) //will render "Wednesday"
DECL_ENUM_ELEMENT_VAL_STR(Thursday, 500, "Thursday string") // will render "Thursday string" and the enum will have 500 as value
/* ... and so on */
}
END_ENUM(MyEnum)
then in a file called EnumToString.c:
#include "enum.h"
#define GENERATE_ENUM_STRINGS // Start string generation
#include "enum.h"
#undef GENERATE_ENUM_STRINGS // Stop string generation
then in main.c:
int main(int argc, char* argv[])
{
Days TheDay = Monday;
printf( "%d - %s\n", TheDay, GetStringDay(TheDay) ); //will print "1 - Monday"
TheDay = Thursday;
printf( "%d - %s\n", TheDay, GetStringDay(TheDay) ); //will print "500 - Thursday string"
return 0;
}
this will generate "automatically" the strings for any enums declared this way and included in "EnumToString.c"
The way I usually do this is by storing the string representations in a separate array in the same order, then indexing the array with the enum value:
const char *DayNames[] = { "Sunday", "Monday", "Tuesday", /* etc */ };
printf("%s", DayNames[Sunday]); // prints "Sunday"
enums in C don't really work the way you're expecting them to. You can think of them kind of like glorified constants (with a few additional benefits relating to being a collection of such constants), and the text you've written in for "Sunday" really gets resolved to a number during compilation, the text is ultimately discarded.
In short: to do what you really want you'll need to keep an array of the strings or create a function to map from the enum's value to the text you'd like to print.
Enumerations in C are basically syntactical sugar for named lists of automatically-sequenced integer values. That is, when you have this code:
int main()
{
enum Days{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};
Days TheDay = Monday;
}
Your compiler actually spits out this:
int main()
{
int TheDay = 1; // Monday is the second enumeration, hence 1. Sunday would be 0.
}
Therefore, outputting a C enumeration as a string is not an operation that makes sense to the compiler. If you want to have human-readable strings for these, you will need to define functions to convert from enumerations to strings.
Here's a cleaner way to do it with macros:
#include <stdio.h>
#include <stdlib.h>
#define DOW(X, S) \
X(Sunday) S X(Monday) S X(Tuesday) S X(Wednesday) S X(Thursday) S X(Friday) S X(Saturday)
#define COMMA ,
/* declare the enum */
#define DOW_ENUM(DOW) DOW
enum dow {
DOW(DOW_ENUM, COMMA)
};
/* create an array of strings with the enum names... */
#define DOW_ARR(DOW ) [DOW] = #DOW
const char * const dow_str[] = {
DOW(DOW_ARR, COMMA)
};
/* ...or create a switchy function. */
static const char * dowstr(int i)
{
#define DOW_CASE(D) case D: return #D
switch(i) {
DOW(DOW_CASE, ;);
default: return NULL;
}
}
int main(void)
{
for(int i = 0; i < 7; i++)
printf("[%d] = «%s»\n", i, dow_str[i]);
printf("\n");
for(int i = 0; i < 7; i++)
printf("[%d] = «%s»\n", i, dowstr(i));
return 0;
}
I'm not sure that this is totally portable b/w preprocessors, but it works with gcc.
This is c99 btw, so use c99 strict if you plug it into (the online compiler) ideone.
I know I am late to the party, but how about this?
const char* dayNames[] = { [Sunday] = "Sunday", [Monday] = "Monday", /*and so on*/ };
printf("%s", dayNames[Sunday]); // prints "Sunday"
This way, you do not have to manually keep the enum and the char* array in sync. If you are like me, chances are that you will later change the enum, and the char* array will print invalid strings.
This may not be a feature universally supported. But afaik, most of the mordern day C compilers support this designated initialier style.
You can read more about designated initializers here.
I like this to have enum in the dayNames.
To reduce typing, we can do the following:
#define EP(x) [x] = #x /* ENUM PRINT */
const char* dayNames[] = { EP(Sunday), EP(Monday)};
The question is you want write the name just one times.
I have an ider like this:
#define __ENUM(situation,num) \
int situation = num; const char * __##situation##_name = #situation;
const struct {
__ENUM(get_other_string, -203);//using a __ENUM Mirco make it ease to write,
__ENUM(get_negative_to_unsigned, -204);
__ENUM(overflow,-205);
//The following two line showing the expanding for __ENUM
int get_no_num = -201; const char * __get_no_num_name = "get_no_num";
int get_float_to_int = -202; const char * get_float_to_int_name = "float_to_int_name";
}eRevJson;
#undef __ENUM
struct sIntCharPtr { int value; const char * p_name; };
//This function transform it to string.
inline const char * enumRevJsonGetString(int num) {
sIntCharPtr * ptr = (sIntCharPtr *)(&eRevJson);
for (int i = 0;i < sizeof(eRevJson) / sizeof(sIntCharPtr);i++) {
if (ptr[i].value == num) {
return ptr[i].p_name;
}
}
return "bad_enum_value";
}
it uses a struct to insert enum, so that a printer to string could follows each enum value define.
int main(int argc, char *argv[]) {
int enum_test = eRevJson.get_other_string;
printf("error is %s, number is %d\n", enumRevJsonGetString(enum_test), enum_test);
>error is get_other_string, number is -203
The difference to enum is builder can not report error if the numbers are repeated.
if you don't like write number, __LINE__ could replace it:
#define ____LINE__ __LINE__
#define __ENUM(situation) \
int situation = (____LINE__ - __BASELINE -2); const char * __##situation##_name = #situation;
constexpr int __BASELINE = __LINE__;
constexpr struct {
__ENUM(Sunday);
__ENUM(Monday);
__ENUM(Tuesday);
__ENUM(Wednesday);
__ENUM(Thursday);
__ENUM(Friday);
__ENUM(Saturday);
}eDays;
#undef __ENUM
inline const char * enumDaysGetString(int num) {
sIntCharPtr * ptr = (sIntCharPtr *)(&eDays);
for (int i = 0;i < sizeof(eDays) / sizeof(sIntCharPtr);i++) {
if (ptr[i].value == num) {
return ptr[i].p_name;
}
}
return "bad_enum_value";
}
int main(int argc, char *argv[]) {
int d = eDays.Wednesday;
printf("day %s, number is %d\n", enumDaysGetString(d), d);
d = 1;
printf("day %s, number is %d\n", enumDaysGetString(d), d);
}
>day Wednesday, number is 3 >day Monday, number is 1
There is another solution: Create your own dynamic enumeration class. Means you have a struct and some function to create a new enumeration, which stores the elements in a struct and each element has a string for the name. You also need some type to store a individual elements, functions to compare them and so on.
Here is an example:
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Enumeration_element_T
{
size_t index;
struct Enumeration_T *parent;
char *name;
};
struct Enumeration_T
{
size_t len;
struct Enumeration_element_T elements[];
};
void enumeration_delete(struct Enumeration_T *self)
{
if(self)
{
while(self->len--)
{
free(self->elements[self->len].name);
}
free(self);
}
}
struct Enumeration_T *enumeration_create(size_t len,...)
{
//We do not check for size_t overflows, but we should.
struct Enumeration_T *self=malloc(sizeof(self)+sizeof(self->elements[0])*len);
if(!self)
{
return NULL;
}
self->len=0;
va_list l;
va_start(l,len);
for(size_t i=0;i<len;i++)
{
const char *name=va_arg(l,const char *);
self->elements[i].name=malloc(strlen(name)+1);
if(!self->elements[i].name)
{
enumeration_delete(self);
return NULL;
}
strcpy(self->elements[i].name,name);
self->len++;
}
return self;
}
bool enumeration_isEqual(struct Enumeration_element_T *a,struct Enumeration_element_T *b)
{
return a->parent==b->parent && a->index==b->index;
}
bool enumeration_isName(struct Enumeration_element_T *a, const char *name)
{
return !strcmp(a->name,name);
}
const char *enumeration_getName(struct Enumeration_element_T *a)
{
return a->name;
}
struct Enumeration_element_T *enumeration_getFromName(struct Enumeration_T *self, const char *name)
{
for(size_t i=0;i<self->len;i++)
{
if(enumeration_isName(&self->elements[i],name))
{
return &self->elements[i];
}
}
return NULL;
}
struct Enumeration_element_T *enumeration_get(struct Enumeration_T *self, size_t index)
{
return &self->elements[index];
}
size_t enumeration_getCount(struct Enumeration_T *self)
{
return self->len;
}
bool enumeration_isInRange(struct Enumeration_T *self, size_t index)
{
return index<self->len;
}
int main(void)
{
struct Enumeration_T *weekdays=enumeration_create(7,"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday");
if(!weekdays)
{
return 1;
}
printf("Please enter the day of the week (0 to 6)\n");
size_t j = 0;
if(scanf("%zu",&j)!=1)
{
enumeration_delete(weekdays);
return 1;
}
// j=j%enumeration_getCount(weekdays); //alternative way to make sure j is in range
if(!enumeration_isInRange(weekdays,j))
{
enumeration_delete(weekdays);
return 1;
}
struct Enumeration_element_T *day=enumeration_get(weekdays,j);
printf("%s\n",enumeration_getName(day));
enumeration_delete(weekdays);
return 0;
}
The functions of enumeration should be in their own translation unit, but i combined them here to make it simpler.
The advantage is that this solution is flexible, follows the DRY principle, you can store information along with each element, you can create new enumerations during runtime and you can add new elements during runtime.
The disadvantage is that this is complex, needs dynamic memory allocation, can't be used in switch-case, needs more memory and is slower. The question is if you should not use a higher level language in cases where you need this.
Using a Macro and stringize operator(#) we can achieve this....
#include <stdio.h>
typedef enum
{
MON=0,
TUE
}week;
int main()
{
#define printt(data) printf("%s",#data);
printt(MON);
return 0;
}
i'm new to this but a switch statement will defenitely work
#include <stdio.h>
enum mycolor;
int main(int argc, const char * argv[])
{
enum Days{Sunday=1,Monday=2,Tuesday=3,Wednesday=4,Thursday=5,Friday=6,Saturday=7};
enum Days TheDay;
printf("Please enter the day of the week (0 to 6)\n");
scanf("%d",&TheDay);
switch (TheDay)
{
case Sunday:
printf("the selected day is sunday");
break;
case Monday:
printf("the selected day is monday");
break;
case Tuesday:
printf("the selected day is Tuesday");
break;
case Wednesday:
printf("the selected day is Wednesday");
break;
case Thursday:
printf("the selected day is thursday");
break;
case Friday:
printf("the selected day is friday");
break;
case Saturday:
printf("the selected day is Saturaday");
break;
default:
break;
}
return 0;
}
TheDay maps back to some integer type. So:
printf("%s", TheDay);
Attempts to parse TheDay as a string, and will either print out garbage or crash.
printf is not typesafe and trusts you to pass the right value to it. To print out the name of the value, you'd need to create some method for mapping the enum value to a string - either a lookup table, giant switch statement, etc.
I want to prevent invalid value enum assignment. I know if i even assign value that is not in enum it will work. Example:
enum example_enum
{
ENUM_VAL0,
ENUM_VAL1,
ENUM_VAL2,
ENUM_VAL3
};
void example_function(void)
{
enum example_enum the_enum = ENUM_VAL3; // correct
the_enum = 41; // will work
the_enum = 0xBADA55; // also will work
bar(the_enum); // this function assumes that input parameter is correct
}
Is there easy, efficient way to check if assignment to enum is correct? I could test value by function
void foo(enum example_enum the_enum)
{
if (!is_enum(the_enum))
return;
// do something with valid enum
}
I could resolve this in following way:
static int e_values[] = { ENUM_VAL0, ENUM_VAL1, ENUM_VAL2, ENUM_VAL3 };
int is_enum(int input)
{
for (int i=0;i<4;i++)
if (e_values[i] == input)
return 1;
return 0;
}
For me, my solution is inefficient, how can i write this if i have more enums and more values in enums?
As long as the enum is continuous one can do something like this:
static int e_values[] = { ENUM_VAL0, ENUM_VAL1, ENUM_VAL2, ENUM_VAL3, ENUM_VAL_COUNT };
int is_enum(int input) { return 0 <= input && input < ENUM_VAL_COUNT; }
Another alternative is to not validate the enum value beforehand, but error out once the code detects an invalid value:
switch(input) {
case ENUM_VAL0: ... break;
case ENUM_VAL1: ... break;
...
default:
assert(0 && "broken enum");
break;
}
But there is no way to enforce that the enum value doesn't go out of the range at all in C. The best you can do if you want to secure the enum against fiddling is to hide the value away in a struct and then have functions to manipulate the struct. The function and struct implementation can be hidden away from the user via a forward declaration in the .h file and the implementation in the .c file:
struct example_t {
enum example_enum value;
}
void example_set_val0(example_t* v) { v->value = ENUM_VAL0; }
There is no way of warning about assigning integers that fit into the enum.
Enumerators in C are synonyms for integer types. Assuming the type chosen for enum example_enum is int, then your code is identical to:
void example_function(void)
{
int the_enum = ENUM_VAL3; // correct
the_enum = 12345; // will work
bar(the_enum); // this function assumes that input parameter is correct
}
void foo(int the_enum)
{
if (!is_enum(the_enum))
return;
// do something with valid enum
}
You could use structures, but even that can be circumvented:
struct example_enum_struct e = { 12345 };
e.value = 23456;
Basically if you want to restrict a type to specific values, you will need to perform checks.
If anyone is interested in this topic, here I have some solution which works.
typed_enums.h
#ifndef TYPED_ENUMS_H
#define TYPED_ENUMS_H
#define TYPED_ENUM(name_) \
typedef struct { int v; } name_
#define TYPED_ENUM_VALUE(name_, value_) (name_) { value_ }
#define GET_TYPED_ENUM_VALUE(en_) (en_.v)
#define TYPED_ENUM_EQ(a_, b_) (GET_TYPED_ENUM_VALUE(a_) == GET_TYPED_ENUM_VALUE(b_))
#endif
usb_class.h
#ifndef USB_CLASS_H
#define USB_CLASS_H
#include "typed_enums.h"
TYPED_ENUM(UsbClass);
#define USB_CLASS_BILLBOARD TYPED_ENUM_VALUE(UsbClass, 0x11)
#define USB_CLASS_TYPE_C_BRIDGE TYPED_ENUM_VALUE(UsbClass, 0x12)
#define USB_CLASS_DIAGNOSTIC_DEVICE TYPED_ENUM_VALUE(UsbClass, 0xDC)
#define USB_CLASS_WIRELESS_CONTROLLER TYPED_ENUM_VALUE(UsbClass, 0xE0)
#endif
usb_class_example.c
#include "typed_enums.h"
#include "usb_class.h"
#include <stdio.h>
int main(int argc, char ** argv)
{
UsbClass usbClass = USB_CLASS_WIRELESS_CONTROLLER;
usbClass = 12345; // tadam!!!! throws error
usbClass = USB_CLASS_VIDEO;
if (TYPED_ENUM_EQ(usbClass, USB_CLASS_VIDEO)) {
printf("usbClass = USB_CLASS_VIDEO\n");
}
printf("usb class value: %02X\n", GET_TYPED_ENUM_VALUE(usbClass));
return 0;
}
Pros:
enum value assignment works like struct assignment
enum for pointers also works
enum value can't be changed
Cons:
can't be used in switch
can't be directly compared
can't directly return enum number value
Note: sorry for abusing preprocessor here
int main()
{
enum Days{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};
Days TheDay;
int j = 0;
printf("Please enter the day of the week (0 to 6)\n");
scanf("%d",&j);
TheDay = Days(j);
//how to PRINT THE VALUES stored in TheDay
printf("%s",TheDay); // isnt working
return 0;
}
Enumerations in C are numbers that have convenient names inside your code. They are not strings, and the names assigned to them in the source code are not compiled into your program, and so they are not accessible at runtime.
The only way to get what you want is to write a function yourself that translates the enumeration value into a string. E.g. (assuming here that you move the declaration of enum Days outside of main):
const char* getDayName(enum Days day)
{
switch (day)
{
case Sunday: return "Sunday";
case Monday: return "Monday";
/* etc... */
}
}
/* Then, later in main: */
printf("%s", getDayName(TheDay));
Alternatively, you could use an array as a map, e.g.
const char* dayNames[] = {"Sunday", "Monday", "Tuesday", /* ... etc ... */ };
/* ... */
printf("%s", dayNames[TheDay]);
But here you would probably want to assign Sunday = 0 in the enumeration to be safe... I'm not sure if the C standard requires compilers to begin enumerations from 0, although most do (I'm sure someone will comment to confirm or deny this).
I use something like this:
in a file "EnumToString.h":
#undef DECL_ENUM_ELEMENT
#undef DECL_ENUM_ELEMENT_VAL
#undef DECL_ENUM_ELEMENT_STR
#undef DECL_ENUM_ELEMENT_VAL_STR
#undef BEGIN_ENUM
#undef END_ENUM
#ifndef GENERATE_ENUM_STRINGS
#define DECL_ENUM_ELEMENT( element ) element,
#define DECL_ENUM_ELEMENT_VAL( element, value ) element = value,
#define DECL_ENUM_ELEMENT_STR( element, descr ) DECL_ENUM_ELEMENT( element )
#define DECL_ENUM_ELEMENT_VAL_STR( element, value, descr ) DECL_ENUM_ELEMENT_VAL( element, value )
#define BEGIN_ENUM( ENUM_NAME ) typedef enum tag##ENUM_NAME
#define END_ENUM( ENUM_NAME ) ENUM_NAME; \
const char* GetString##ENUM_NAME(enum tag##ENUM_NAME index);
#else
#define BEGIN_ENUM( ENUM_NAME) const char * GetString##ENUM_NAME( enum tag##ENUM_NAME index ) {\
switch( index ) {
#define DECL_ENUM_ELEMENT( element ) case element: return #element; break;
#define DECL_ENUM_ELEMENT_VAL( element, value ) DECL_ENUM_ELEMENT( element )
#define DECL_ENUM_ELEMENT_STR( element, descr ) case element: return descr; break;
#define DECL_ENUM_ELEMENT_VAL_STR( element, value, descr ) DECL_ENUM_ELEMENT_STR( element, descr )
#define END_ENUM( ENUM_NAME ) default: return "Unknown value"; } } ;
#endif
then in any header file you make the enum declaration, day enum.h
#include "EnumToString.h"
BEGIN_ENUM(Days)
{
DECL_ENUM_ELEMENT(Sunday) //will render "Sunday"
DECL_ENUM_ELEMENT(Monday) //will render "Monday"
DECL_ENUM_ELEMENT_STR(Tuesday, "Tuesday string") //will render "Tuesday string"
DECL_ENUM_ELEMENT(Wednesday) //will render "Wednesday"
DECL_ENUM_ELEMENT_VAL_STR(Thursday, 500, "Thursday string") // will render "Thursday string" and the enum will have 500 as value
/* ... and so on */
}
END_ENUM(MyEnum)
then in a file called EnumToString.c:
#include "enum.h"
#define GENERATE_ENUM_STRINGS // Start string generation
#include "enum.h"
#undef GENERATE_ENUM_STRINGS // Stop string generation
then in main.c:
int main(int argc, char* argv[])
{
Days TheDay = Monday;
printf( "%d - %s\n", TheDay, GetStringDay(TheDay) ); //will print "1 - Monday"
TheDay = Thursday;
printf( "%d - %s\n", TheDay, GetStringDay(TheDay) ); //will print "500 - Thursday string"
return 0;
}
this will generate "automatically" the strings for any enums declared this way and included in "EnumToString.c"
The way I usually do this is by storing the string representations in a separate array in the same order, then indexing the array with the enum value:
const char *DayNames[] = { "Sunday", "Monday", "Tuesday", /* etc */ };
printf("%s", DayNames[Sunday]); // prints "Sunday"
enums in C don't really work the way you're expecting them to. You can think of them kind of like glorified constants (with a few additional benefits relating to being a collection of such constants), and the text you've written in for "Sunday" really gets resolved to a number during compilation, the text is ultimately discarded.
In short: to do what you really want you'll need to keep an array of the strings or create a function to map from the enum's value to the text you'd like to print.
Enumerations in C are basically syntactical sugar for named lists of automatically-sequenced integer values. That is, when you have this code:
int main()
{
enum Days{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};
Days TheDay = Monday;
}
Your compiler actually spits out this:
int main()
{
int TheDay = 1; // Monday is the second enumeration, hence 1. Sunday would be 0.
}
Therefore, outputting a C enumeration as a string is not an operation that makes sense to the compiler. If you want to have human-readable strings for these, you will need to define functions to convert from enumerations to strings.
Here's a cleaner way to do it with macros:
#include <stdio.h>
#include <stdlib.h>
#define DOW(X, S) \
X(Sunday) S X(Monday) S X(Tuesday) S X(Wednesday) S X(Thursday) S X(Friday) S X(Saturday)
#define COMMA ,
/* declare the enum */
#define DOW_ENUM(DOW) DOW
enum dow {
DOW(DOW_ENUM, COMMA)
};
/* create an array of strings with the enum names... */
#define DOW_ARR(DOW ) [DOW] = #DOW
const char * const dow_str[] = {
DOW(DOW_ARR, COMMA)
};
/* ...or create a switchy function. */
static const char * dowstr(int i)
{
#define DOW_CASE(D) case D: return #D
switch(i) {
DOW(DOW_CASE, ;);
default: return NULL;
}
}
int main(void)
{
for(int i = 0; i < 7; i++)
printf("[%d] = «%s»\n", i, dow_str[i]);
printf("\n");
for(int i = 0; i < 7; i++)
printf("[%d] = «%s»\n", i, dowstr(i));
return 0;
}
I'm not sure that this is totally portable b/w preprocessors, but it works with gcc.
This is c99 btw, so use c99 strict if you plug it into (the online compiler) ideone.
I know I am late to the party, but how about this?
const char* dayNames[] = { [Sunday] = "Sunday", [Monday] = "Monday", /*and so on*/ };
printf("%s", dayNames[Sunday]); // prints "Sunday"
This way, you do not have to manually keep the enum and the char* array in sync. If you are like me, chances are that you will later change the enum, and the char* array will print invalid strings.
This may not be a feature universally supported. But afaik, most of the mordern day C compilers support this designated initialier style.
You can read more about designated initializers here.
I like this to have enum in the dayNames.
To reduce typing, we can do the following:
#define EP(x) [x] = #x /* ENUM PRINT */
const char* dayNames[] = { EP(Sunday), EP(Monday)};
The question is you want write the name just one times.
I have an ider like this:
#define __ENUM(situation,num) \
int situation = num; const char * __##situation##_name = #situation;
const struct {
__ENUM(get_other_string, -203);//using a __ENUM Mirco make it ease to write,
__ENUM(get_negative_to_unsigned, -204);
__ENUM(overflow,-205);
//The following two line showing the expanding for __ENUM
int get_no_num = -201; const char * __get_no_num_name = "get_no_num";
int get_float_to_int = -202; const char * get_float_to_int_name = "float_to_int_name";
}eRevJson;
#undef __ENUM
struct sIntCharPtr { int value; const char * p_name; };
//This function transform it to string.
inline const char * enumRevJsonGetString(int num) {
sIntCharPtr * ptr = (sIntCharPtr *)(&eRevJson);
for (int i = 0;i < sizeof(eRevJson) / sizeof(sIntCharPtr);i++) {
if (ptr[i].value == num) {
return ptr[i].p_name;
}
}
return "bad_enum_value";
}
it uses a struct to insert enum, so that a printer to string could follows each enum value define.
int main(int argc, char *argv[]) {
int enum_test = eRevJson.get_other_string;
printf("error is %s, number is %d\n", enumRevJsonGetString(enum_test), enum_test);
>error is get_other_string, number is -203
The difference to enum is builder can not report error if the numbers are repeated.
if you don't like write number, __LINE__ could replace it:
#define ____LINE__ __LINE__
#define __ENUM(situation) \
int situation = (____LINE__ - __BASELINE -2); const char * __##situation##_name = #situation;
constexpr int __BASELINE = __LINE__;
constexpr struct {
__ENUM(Sunday);
__ENUM(Monday);
__ENUM(Tuesday);
__ENUM(Wednesday);
__ENUM(Thursday);
__ENUM(Friday);
__ENUM(Saturday);
}eDays;
#undef __ENUM
inline const char * enumDaysGetString(int num) {
sIntCharPtr * ptr = (sIntCharPtr *)(&eDays);
for (int i = 0;i < sizeof(eDays) / sizeof(sIntCharPtr);i++) {
if (ptr[i].value == num) {
return ptr[i].p_name;
}
}
return "bad_enum_value";
}
int main(int argc, char *argv[]) {
int d = eDays.Wednesday;
printf("day %s, number is %d\n", enumDaysGetString(d), d);
d = 1;
printf("day %s, number is %d\n", enumDaysGetString(d), d);
}
>day Wednesday, number is 3 >day Monday, number is 1
There is another solution: Create your own dynamic enumeration class. Means you have a struct and some function to create a new enumeration, which stores the elements in a struct and each element has a string for the name. You also need some type to store a individual elements, functions to compare them and so on.
Here is an example:
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Enumeration_element_T
{
size_t index;
struct Enumeration_T *parent;
char *name;
};
struct Enumeration_T
{
size_t len;
struct Enumeration_element_T elements[];
};
void enumeration_delete(struct Enumeration_T *self)
{
if(self)
{
while(self->len--)
{
free(self->elements[self->len].name);
}
free(self);
}
}
struct Enumeration_T *enumeration_create(size_t len,...)
{
//We do not check for size_t overflows, but we should.
struct Enumeration_T *self=malloc(sizeof(self)+sizeof(self->elements[0])*len);
if(!self)
{
return NULL;
}
self->len=0;
va_list l;
va_start(l,len);
for(size_t i=0;i<len;i++)
{
const char *name=va_arg(l,const char *);
self->elements[i].name=malloc(strlen(name)+1);
if(!self->elements[i].name)
{
enumeration_delete(self);
return NULL;
}
strcpy(self->elements[i].name,name);
self->len++;
}
return self;
}
bool enumeration_isEqual(struct Enumeration_element_T *a,struct Enumeration_element_T *b)
{
return a->parent==b->parent && a->index==b->index;
}
bool enumeration_isName(struct Enumeration_element_T *a, const char *name)
{
return !strcmp(a->name,name);
}
const char *enumeration_getName(struct Enumeration_element_T *a)
{
return a->name;
}
struct Enumeration_element_T *enumeration_getFromName(struct Enumeration_T *self, const char *name)
{
for(size_t i=0;i<self->len;i++)
{
if(enumeration_isName(&self->elements[i],name))
{
return &self->elements[i];
}
}
return NULL;
}
struct Enumeration_element_T *enumeration_get(struct Enumeration_T *self, size_t index)
{
return &self->elements[index];
}
size_t enumeration_getCount(struct Enumeration_T *self)
{
return self->len;
}
bool enumeration_isInRange(struct Enumeration_T *self, size_t index)
{
return index<self->len;
}
int main(void)
{
struct Enumeration_T *weekdays=enumeration_create(7,"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday");
if(!weekdays)
{
return 1;
}
printf("Please enter the day of the week (0 to 6)\n");
size_t j = 0;
if(scanf("%zu",&j)!=1)
{
enumeration_delete(weekdays);
return 1;
}
// j=j%enumeration_getCount(weekdays); //alternative way to make sure j is in range
if(!enumeration_isInRange(weekdays,j))
{
enumeration_delete(weekdays);
return 1;
}
struct Enumeration_element_T *day=enumeration_get(weekdays,j);
printf("%s\n",enumeration_getName(day));
enumeration_delete(weekdays);
return 0;
}
The functions of enumeration should be in their own translation unit, but i combined them here to make it simpler.
The advantage is that this solution is flexible, follows the DRY principle, you can store information along with each element, you can create new enumerations during runtime and you can add new elements during runtime.
The disadvantage is that this is complex, needs dynamic memory allocation, can't be used in switch-case, needs more memory and is slower. The question is if you should not use a higher level language in cases where you need this.
Using a Macro and stringize operator(#) we can achieve this....
#include <stdio.h>
typedef enum
{
MON=0,
TUE
}week;
int main()
{
#define printt(data) printf("%s",#data);
printt(MON);
return 0;
}
i'm new to this but a switch statement will defenitely work
#include <stdio.h>
enum mycolor;
int main(int argc, const char * argv[])
{
enum Days{Sunday=1,Monday=2,Tuesday=3,Wednesday=4,Thursday=5,Friday=6,Saturday=7};
enum Days TheDay;
printf("Please enter the day of the week (0 to 6)\n");
scanf("%d",&TheDay);
switch (TheDay)
{
case Sunday:
printf("the selected day is sunday");
break;
case Monday:
printf("the selected day is monday");
break;
case Tuesday:
printf("the selected day is Tuesday");
break;
case Wednesday:
printf("the selected day is Wednesday");
break;
case Thursday:
printf("the selected day is thursday");
break;
case Friday:
printf("the selected day is friday");
break;
case Saturday:
printf("the selected day is Saturaday");
break;
default:
break;
}
return 0;
}
TheDay maps back to some integer type. So:
printf("%s", TheDay);
Attempts to parse TheDay as a string, and will either print out garbage or crash.
printf is not typesafe and trusts you to pass the right value to it. To print out the name of the value, you'd need to create some method for mapping the enum value to a string - either a lookup table, giant switch statement, etc.