Optimizing tagged unions in C - c

I'm using tagged unions in a personal project. As an example, this is the type of code I use:
struct data_unit {
enum {
d_A, d_B, d_C
} dtype;
union {
char a;
int b;
double c;
} data;
};
void print_data(data_unit du) {
switch (du.dtype) {
case d_A: {
printf("%c", du.data.a);
break;
}
case d_B: {
printf("%d", du.data.b);
break;
}
case d_C: {
printf("%lf", du.data.c);
break;
}
default:
break;
}
return;
};
Is there more efficient way to implement print_data, one without manually checking each case of dtype? If C allowed lists of types, I would just iterate through it using the enum, but that isn't an available utility. I need a general way to do this, as I plan on adding a lot of data types to the union.
Is this possible to do? If not, what would a better strategy be for me in this context?

You could make use of the fact that a pointer to a struct-object may be treated as a pointer to its first member. Hence, when exchanging dtype and data, you could access all members in some generic way:
struct data_unit {
union {
char a;
int b;
double c;
} data;
enum {
d_A, d_B, d_C
} dtype;
};
typedef void (print_data_unit) (struct data_unit *du);
void print_data_unit_char(struct data_unit *du) {
printf("%c\n", *((char*)(du)));
}
void print_data_unit_double(struct data_unit *du) {
printf("%lf\n", *((double*)(du)));
}
void print_data_unit_int(struct data_unit *du) {
printf("%d\n", *((int*)(du)));
}
static print_data_unit* functions[3] = { print_data_unit_char, print_data_unit_int, print_data_unit_double };
void print_data(struct data_unit du) {
functions[du.dtype](&du);
}
int main() {
struct data_unit du;
du.dtype = 0;
du.data.a = 'c';
print_data(du);
du.dtype = 1;
du.data.b = 100;
print_data(du);
du.dtype = 2;
du.data.c = 200.55;
print_data(du);
}

Related

C: Reading property of struct pointer causes property to change?

I have a program that stores two different structs (tri & quad) in an array of a special struct defined below:
struct inst_ptr
{
void* p;
unsigned type;
};
The structs tri & quad have a superbase property
The following code displays how the structs are defined:
struct inst
{
char name[50];
int id;
};
struct tri
{
struct inst superbase;
};
struct quad
{
struct inst superbase;
};
Function for creating new instances (all located outside of main):
struct inst_ptr insts[100]; //instance container
int cinsts=0; //instance count
int inst_new(int type)
{
switch(type)
{
case TYPE_TRI:
{
struct tri i;
insts[cinsts].p=&i;
}
case TYPE_QUAD:
{
struct quad i;
insts[cinsts].p=&i;
}
}
insts[cinsts].type=type;
cinsts++;
return cinsts-1; //return index of instance in array
}
In my main function I create a sample tri, writing and reading it's values:
struct tri* t1=(struct tri*)insts[inst_new(TYPE_TRI)].p;
strcpy(t1->superbase.name, "tri1");
t1->superbase.id=70;
printf("id: %d\n", t1->superbase.id);
printf("id: %d\n", t1->superbase.id); <- problem occurs here and onward
Printing the id the first time works, printing 70 in the output. However, the second time it prints a long random integer.
Output:
id: 69
id: 1973802146
id: 1973802146
As you shown above, after the second read it keeps printing 1973802146. I'm not really sure what I'm doing wrong but I have a feeling I'm missing an extremely basic concept relating to pointers and structs.
You are saving the address of local variables in the array. That's bad because those variables doesn't exist once the function returns. You should use dynamic allocation instead.
Besides that you switch need break statements. And perhaps also a default case.
Like:
int inst_new(int type)
{
switch(type)
{
case TYPE_TRI:
{
struct tri *p = malloc(sizeof *p);
if (p == NULL) exit(1);
insts[cinsts].p=p;
break;
}
case TYPE_QUAD:
{
struct quad *p = malloc(sizeof *p);
if (p == NULL) exit(1);
insts[cinsts].p=p;
break;
}
default:
{
// Unknown type.. add error handling here
exit(1);
}
}
insts[cinsts].type=type;
cinsts++;
return cinsts-1; //return index of instance in array
}
As an alternative you can do:
int inst_new(int type)
{
insts[cinsts].p = NULL
switch(type)
{
case TYPE_TRI:
{
insts[cinsts].p=malloc(sizeof(struct tri));
break;
}
case TYPE_QUAD:
{
insts[cinsts].p=malloc(sizeof(struct quad));
break;
}
}
if (insts[cinsts].p == NULL) exit(1); // malloc failed
insts[cinsts].type=type;
cinsts++;
return cinsts-1; //return index of instance in array
}

Variabe type for variable inside function

I'm having different different types of structs, which are going to be passed to a function which performs the same tasks on them.
int menu_parameter_arrow_print(game_setting_identifier* identifier, controller_direction direction, uint8_t position)
{
if((position > setting->alternatives_number) || position < 0)
{
#ifdef OLED_PRINT_DEBUG_ENABLE
OLED_debug_print("Out of bounds");
#endif
return RETURN_VALUE_FAILURE;
}
else
{
switch ((int)*identifier)
{
case ((int) GAME_SETTING_ANALOG):
game_setting_analog* setting = (game_setting_analog*)&identifier;
case ((int) GAME_SETTING_TOGGLE):
game_setting_toggle* setting = (game_setting_toggle*)&identifier;
case ((int) GAME_SETTING_VALUE):
game_setting_value* setting = (game_setting_value*)&identifier;
}
This function gives a conflicting type-error
The operations performed on the structs are the same, but the structs contains different types of members:
struct game_setting_analog
{
//Identifier for the game-setting type:
game_setting_identifier identifier;
//Alternatives:
char* alternatives[4];
};
typedef struct game_setting_value game_setting_value;
struct game_setting_value
{
game_setting_identifier identifier;
uint8_t* alternatives[6];
uint8_t alternatives_number;
};
typedef struct game_setting_toggle game_setting_toggle;
struct game_setting_toggle
{
//Identifier for the game-setting type:
game_setting_identifier identifier;
toggle_state* alternatives[2];
};
typedef struct game_setting_difficulty game_setting_difficulty;
struct game_setting_difficulty
{
game_setting_identifier identifier;
char* alternatives[3];
};
Actions will be performed on the 'alternatives'-member of the structs, even though these members are of different types.
Is there a solution to doing this without having to use one if-statement for each identifier?
Edit: With a modification to the switch-case, I'm able to get the initialization compiled. The variables inside the switch-scope is however not visible to the rest of the function
int menu_print_parameter_line(game_setting_identifier* identifier, controller* C, uint8_t position)
{
uint8_t next_position = position;
controller_direction previous_direction = C->joystick.generalDirection;
if ((identifier == NULL) || (C == NULL) || (position == NULL))
{
return -1;
}
switch((int) identifier)
{
case ((int) GAME_SETTING_ANALOG):
{
game_setting_analog* setting = (game_setting_analog*)identifier;
uint8_t alternatives_number = 4;
}
break;
case ((int) GAME_SETTING_TOGGLE):
{
game_setting_toggle* setting = (game_setting_toggle*)identifier;
uint8_t alternatives_number = 2;
}
break;
case ((int) GAME_SETTING_VALUE):
{
game_setting_value* setting = (game_setting_value*)identifier;
uint8_t alternatives_number = setting->alternatives_number;
}
break;
default:
{
return -1;
}
break;
}
#ifdef MENU_PARAMETER_ASSIGNMENT_DEBUG
OLED_debug_print("before switch-case");
#endif
switch (previous_direction)
{
case LEFT:
next_position -= 1;
if(next_position <= 0)
{
next_position = alternatives_number;
}
I personally don't like the inheritance model that depends on the first member of the structure, like the BSD socket library is using. Basically you are just trying to implement std::variant from c++ in C.
Is there a solution to doing this without having to use one if-statement for each identifier?
The object-oriented concept of interface works very nice and I believe is applicable in this case. It takes some C discipline to write it, but it works like a charm and you could be looking for it here.
I copied your definitions from which I removed typedefs because I don't like them:
struct game_setting_analog {
char* alternatives[4];
};
struct game_setting_value {
uint8_t* alternatives[6];
uint8_t alternatives_number;
};
struct game_setting_toggle {
toggle_state* alternatives[2];
};
struct game_setting_difficulty {
char* alternatives[3];
};
Let's first implement the interface abstraction with a function pointer that allows to get the alternatives number:
// forward definition
struct game_setting_s;
// the virtual table for game_settings
struct game_setting_vtable_s {
uint8_t (*get_alternatives_number)(struct game_setting_s *t);
// TODO: add other members, constructor, copy constructor, destructor, etc.
};
// represents any game_setting
// exposes a public interface to access and manipulate a game_setting
struct game_setting_s {
// the vtable is const, so it can save RAM
const struct game_setting_vtable_s *v;
// this is a pointer to private settings data
void *data;
};
// accessor for less (or more ;) typing
static inline
uint8_t game_setting_get_alternatives_number(struct game_setting_s *t) {
// alternative you could pass t->data to the function, I pass it all
// so that functions can modify the t->data member
// and also so that advanced functions usages can use like container_of macros
return t->v.get_alternatives_number(t);
}
Then you need to provide the virtual tables for each of the types. The definitions can be in separate types, so you can have a separate .c/.h file pair for each of the type, just exposing public interface.
// game_setting_analog --------------------
static
uint8_t game_setting_analog_get_altenatives_number(struct game_setting_s *t)
{
return 4;
}
const struct game_setting_vtable_s game_setting_analog_vtable = {
.get_alternatives_number = game_setting_analog_get_altenatives_number,
};
// game_setting_toggle --------------------
static
uint8_t game_setting_toggle_get_altenatives_number(struct game_setting_s *t) {
struct game_setting_toggle *data = t->data;
return data->alternatives_number;
}
const struct game_toggle_vtable_s game_setting_toggle_vtable = {
.get_alternatives_number = game_setting_toggle_get_altenatives_number,
};
// and so on...
Then your function takes just the interface and is very clear without any switch case:
int some_function_that_needs_to_know_which_setting_is_passed(struct game_setting_s *s) {
int number_of_alternatives = game_setting_get_alternatives_number(s);
}
Remember to construct the interface object properly and watch who owns the memory of the object. Let's construct a toggle and call out function:
struct game_settting_toggle memory;
// your function to initialize the toggle
game_setting_toggle_intialize(&memory);
// the interface is constructed with the proper vtable
// and a pointer to proper memory region with the data
struct game_setting_s any_setting = {
.vtable = game_setting_toggle_vtable,
.data = &memory,
};
// the initailize function could be in interface too
// so you would just call game_setting_initialize(&any_setting);
// with usage of dynamic allocation, you can just ex.
// struct game_setting_s *any_setting = game_setting_new_toggle();
// and write proper object-oriented factories
// finally call our function.
some_function_that_needs_to_know_which_setting_is_passed(&any_setting);
Case labels do not provide scopes for variables. All three setting variables within the switch have different types which are the conflicts the compiler. Use brackets to define scopes:
switch ((int)*identifier)
{
case ((int) GAME_SETTING_ANALOG):
{
game_setting_analog* setting = (game_setting_analog*)&identifier;
}
case ((int) GAME_SETTING_TOGGLE):
{
game_setting_toggle* setting = (game_setting_toggle*)&identifier;
}
case ((int) GAME_SETTING_VALUE):
{
game_setting_value* setting = (game_setting_value*)&identifier;
}
}
Also, you're not breaking in the cases, so the code in all three cases are run if ((int)*identifier == (int) GAME_SETTING_ANALOG)

Return a union in C, but make it look nice

So, I have a union:
typedef union {
int intVal;
char charVal;
bool boolVal;
} myUnion_t;
And I have a function, foo, (in the same file as the latter union) which will return myUnion_t.
I obviously could do:
myUnion_t foo(int n){
myUnion_t rtn;
if(n == 0){
rtn.intVal = 1;
} else if(n == 1){
rtn.charVal = 'b';
} else {
rtn.boolVal = false;
}
return rtn;
}
But this is rather messy; my CDO doesn't like it. Is there a nicer way to do this, something like:
myUnion_t foo(int n){
if(n == 1){
return 1;
} else if(n == 2){
return 'b';
} else {
return false;
}
}
EDIT: Okay, unions are inherently messy. Thanks for your help, I'll just do it the normal way :)
Although you cannot return a value of a union member in place of a union itself, you could use compound literals of C99 to avoid declaring the union at the top and setting its fields outside initializer:
typedef union object_t {
int intVal;
char charVal;
_Bool boolVal;
} object_t;
object_t foo(char ch){
switch(ch) {
case 'a': return (object_t) { .intVal = 4 };
case 'b': return (object_t) { .charVal = 'b' };
default: return (object_t) { .boolVal = true };
}
}
The reason you need to use compound literal is that the type by itself is insufficient to identify the member of a union that you would like to be assigned.

Refactoring: Very similar switch cases

I have several struct declared which contain different data. I also have an enum that corresponds to those structures. There are several places in my code where I need to access information about the structures and I'm doing it via the enum. This results in few switch statements that return this information.
I've enclosed those switch statements in their own functions in order to re-use wherever possible. This resulted in three functions that look very similar.
Example psuedo-code:
#include <stdio.h>
typedef struct
{
int varA;
char varB;
} A;
typedef struct
{
int varA;
int varB;
int varC;
} B;
typedef struct
{
int varA;
short varB;
} C;
typedef enum { structA, structB, structC } STRUCT_ENUM;
int returnSize(STRUCT_ENUM structType)
{
int retVal = 0;
switch(structType)
{
case structA:
retVal = sizeof(A);
break;
case structB:
retVal = sizeof(B);
break;
case structC:
retVal = sizeof(C);
break;
default:
break;
}
return retVal;
}
void printStructName(STRUCT_ENUM structType)
{
switch(structType)
{
case structA:
printf("Struct: A\r\n");
break;
case structB:
printf("Struct: B\r\n");
break;
case structC:
printf("Struct: C\r\n");
break;
default:
break;
}
}
void createDataString(STRUCT_ENUM structType, char* output, unsigned char* input)
{
switch(structType)
{
case structA:
{
A a = *(A*)input;
sprintf(output, "data: %d, %d", a.varA, a.varB);
break;
}
case structB:
{
B b = *(B*)input;
sprintf(output, "data: %d, %d, %d", b.varA, b.varB, b.varC);
break;
}
case structC:
{
C c = *(C*)input;
sprintf(output, "data: %d, %d", c.varA, c.varB);
break;
}
default:
break;
}
}
int main(void) {
char foobar[50];
printf("Return size: %d\r\n", returnSize(structA));
printStructName(structB);
C c = { 10, 20 };
createDataString(structC, foobar, (unsigned char*) &c);
printf("Data string: %s\r\n", foobar);
return 0;
}
Those free functions basically contain the same switch with different code placed in the cases. With this setup, adding new struct and enum value results in three places in the code that needs changing.
The question is: is there a way to refactor this into something more maintainable? Additional constraint is that the code is written in C.
EDIT: online example: http://ideone.com/xhXmXu
You can always use static arrays and use STRUCT_ENUM as the index. Given the nature of your functions, I don't really know if you would consider it more maintainable, but it's an alternative I usually prefer, examples for names and sizes:
typedef enum { structA, structB, structC, STRUCT_ENUM_MAX } STRUCT_ENUM;
char *struct_name[STRUCT_ENUM_MAX] = {[structA] = "Struct A", [structB] = "Struct B", [structC] = "Struct C"};
size_t struct_size[STRUCT_ENUM_MAX] = {[structA] = sizeof(A), [structB] = sizeof(B), [structC] = sizeof(C)};
for printing content you can keep a similar array of functions receiving a void * that will print the value of this argument.
Edit:
Added designated initializers as per Jen Gustedt's comment.
You can make it into a single function and a single switch, with an additional parameter. Like so
int enumInfo(STRUCT_ENUM structType, int type) // 1 = returnSize 2 = printStructName
{
int retVal = 0;
switch(structType)
{
case structA:
If ( type == 1 ) { retVal = sizeof(A); }
else { printf("Struct: A"); }
break;
case structB:
If ( type == 1 ) { retVal = sizeof(B); }
else { printf("Struct: B"); }
break;
case structC:
If ( type == 1 ) { retVal = sizeof(C); }
else { printf("Struct: C"); }
break;
default:
break;
}
return retVal;
}

How can I do it smarter?

I must read and extract some values from string.
These values are coded like this:
k="11,3,1" v="140.3"
I have defined the codes and created struct with all field as well as a temp one where I store k and v. In fillFields proc I transfer values from temp struct to the right one (with the valid types).
It works but I have many fields and fillFields would need to have many if-conditions. Maybe someone could give me any hint how to write it smarter.
The simplified code now:
#define ASK "11,3,1"
#define BID "11,2,1"
#define CLOSE "3,1,1"
typedef struct tic {
float ask;
float bid;
float close;
}tic, *ticP;
typedef struct pElem {
char * k;
char * v;
}pElem, *pElemP;
void fillFields(ticP t, pElemP p)
{
if (strcmp( ASK, p->k)==0)
{
printf ("ASK %s\n", p->v);
t->ask = atof(p->v);
}
if (strcmp( BID, p->k)==0)
{
printf ("BID %s\n", p->v);
t->bid = atof(p->v);
}
if (strcmp( CLOSE, p->k)==0)
{
printf("CLOSE >>>%s<<<\n", p->v) ;
t->close = atof (p->v);
}
}
Rather than save the text value in pElem, save the converted values.
This creates an extra step in parsing k="11,3,1" v="140.3", to convert text to an enumerated type, but it's paid once. The fillFields() calls then run simpler. Assuming you have more ticP variables, it's a win.
typedef enum pElem_type {
pElem_None, pElem_ASK, pElem_BID, pElem_CLOSE, pElem_N
} pElem_type;
typedef struct pElem {
pElem_type type;
float value;
} pElem;
void fillFields(ticP t, const pElem *p) {
switch (p->type) {
case pElem_ASK:
printf("ASK %f\n", p->value);
t->ask = p->value;
break;
case pElem_BID:
printf("BID %f\n", p->value);
t->bid = p->value;
break;
case pElem_CLOSE:
printf("Close %f\n", p->value);
t->close = p->value;
break;
default:
printf("Error\n");
}
}
// Further simplifications possible
typedef struct tic {
float field[pElem_N];
}tic, *ticP;
static const char *FieldName[pElem_N] = {
"None", "ASK", "BID", "Close"
};
void fillFields(ticP t, const pElem *p) {
if (p->type < pElem_N) {
printf("%s %f\n", FieldName[p->type], p->value);
t->field[p->type] = p->value;
}
}

Resources