Error fetching dynamic typing value

1

I'm having trouble collecting the values in a dynamic typing in C.

Values entered as char , const char * , int , void * , and long int work perfectly when placed in printf (prints the value without passing the union field), but when I use data of type float and double the value returned is always 0 , only works if I pass the complete path of the variable of union .

var.c:

#include <var.h>

const char *__var_type_2_string(var v){
    switch (v.type){
        case CHAR:
            return "Char";
        case INTEGER:
            return "Int";
        case L_INTEGER:
            return "Long Int";
        case FLOAT:
            return "Float";
        case DOUBLE:
            return "Double";
        case STRING:
            return "String";
        case POINTER:
            return "Pointer";
    }

    return "Undefined";
}

var.h:

#ifndef  __VAR_H__
#define  __VAR_H__

#define typeof(var) _Generic( (var),\
char: CHAR,\
int: INTEGER,\
long int: L_INTEGER,\
char *: STRING,\
const char *: STRING,\
void *: POINTER,\
float: FLOAT,\
double: DOUBLE,\
default: 0)

#define set_wtype(var, out) _Generic( (var),\
char: (out.data.c = var),\
int: (out.data.i = var),\
long int: (out.data.l = var),\
char *: (out.data.s = var),\
const char *: (out.data.s = var),\
void *: (out.data.p = var),\
default: 0)

#define set_wtypef(var, out) _Generic( (var),\
float: (out.data.f = var),\
double: (out.data.d = var),\
default: 0)

enum VAR_TYPE{
    CHAR        = 1,
    INTEGER     = 2,
    L_INTEGER   = 3,
    FLOAT       = 4,
    DOUBLE      = 5,
    STRING      = 6,
    POINTER     = 7
};

typedef struct __attribute__((__packed__)) _var{
    union _data{
        char c;
        int i;
        long int l;
        float f;
        double d;
        const char *s;
        void *p;
    } data;
    enum VAR_TYPE type;
}var;

const char *__var_type_2_string(var v);

#define var_set(var, dta)\
    var.type = typeof(dta);\
    set_wtype(dta, var)
#define var_setf(var, dta)\
    var.type = typeof(dta);\
    set_wtypef(dta, var)
//#define var_get(var) var.data
#define var_type(var) var.type
#define var_typeS(var) __var_type_2_string(var)

#endif  /*VAR_H*/

main.c:

#include <var.h>
#include <stdio.h>

int main(int argc, const char **argv){
    /*Array*/

    var vec[5];
    var_set(vec[0], 37);
    var_set(vec[1], 6625L);
    var_set(vec[2], "Text");
    var_set(vec[3], (char)'A');
    var_setf(vec[4], 13.33f);

    for(int x=0; x<5; x++){
        puts(var_typeS(vec[x]));
    }

    puts("\nResultados: ");
    printf("vec[0]= %i\n", vec[0]);
    printf("vec[1]= %li\n", vec[1]);
    printf("vec[2]= %s\n", vec[2]);
    printf("vec[3]= %c\n", vec[3]);
    printf("vec[4]= %f\n", vec[4]);
    printf("vec[4]= %f\n", vec[4].data.f);

    return 0;
}

Is there any way to print the value of vec[4] without passing vec[4].data.f to printf ?

Exit:

Int
Long Int
String
Char
Float

Resultados: 
vec[0]= 37
vec[1]= 6625
vec[2]= Text
vec[3]= A
vec[4]= 0.000000
vec[4]= 13.330000

compilado em gcc-v6.3

    
asked by anonymous 23.02.2017 / 22:14

1 answer

0

Calling printf in this way, passing directly to struct var as argument ( vec[0] , vec[1] , etc) is not a good practice because it can lead to undefined behavior .

In addition, by compiling the code as it is currently with the flag -Wall you can observe a series of warnings .

So I would say that a more appropriate solution is to create a __var_value_2_string() function, similar to __var_type_2_string , but to convert all values (instead of types ) to char * .

A possible implementation of this function is as follows:

void __var_value_2_string(var v, char **out)
{
    char *s = NULL;
    char *default_str = "undefined";

    switch (v.type) {
        case CHAR:
            s = malloc(2 * sizeof(char));
            s[0] = v.data.c;
            s[1] = '
#define var_valueS(var, out) __var_value_2_string(var, out)
'; break; case INTEGER: s = malloc(21 * sizeof(char)); snprintf(s, 21, "%i", v.data.i); break; case L_INTEGER: s = malloc(21 * sizeof(char)); snprintf(s, 21, "%li", v.data.l); break; case FLOAT: s = malloc(21 * sizeof(char)); snprintf(s, 21, "%f", v.data.f); break; case DOUBLE: s = malloc(21 * sizeof(char)); snprintf(s, 21, "%lf", v.data.d); break; case STRING: s = malloc((strlen(v.data.s) + 1) * sizeof(char)); strcpy(s, v.data.s); break; case POINTER: s = malloc(19 * sizeof(char)); snprintf(s, 19, "0x%p", v.data.p); break; default: s = malloc((strlen(default_str) + 1) * sizeof(char)); strcpy(s, default_str); } (*out) = s; }

Then, we could add a new var.h to typedef :

char *buffers[5];
for (int i = 0; i < 5; i++)
    var_valueS(vec[i], &buffers[i]);

puts("\nResultados: ");
for (int i = 0; i < 5; i++)
    printf("vec[%d] = %s\n", i, buffers[i]);

And finally, in main() , the results could be displayed by doing:

void __var_value_2_string(var v, char **out)
{
    char *s = NULL;
    char *default_str = "undefined";

    switch (v.type) {
        case CHAR:
            s = malloc(2 * sizeof(char));
            s[0] = v.data.c;
            s[1] = '
#define var_valueS(var, out) __var_value_2_string(var, out)
'; break; case INTEGER: s = malloc(21 * sizeof(char)); snprintf(s, 21, "%i", v.data.i); break; case L_INTEGER: s = malloc(21 * sizeof(char)); snprintf(s, 21, "%li", v.data.l); break; case FLOAT: s = malloc(21 * sizeof(char)); snprintf(s, 21, "%f", v.data.f); break; case DOUBLE: s = malloc(21 * sizeof(char)); snprintf(s, 21, "%lf", v.data.d); break; case STRING: s = malloc((strlen(v.data.s) + 1) * sizeof(char)); strcpy(s, v.data.s); break; case POINTER: s = malloc(19 * sizeof(char)); snprintf(s, 19, "0x%p", v.data.p); break; default: s = malloc((strlen(default_str) + 1) * sizeof(char)); strcpy(s, default_str); } (*out) = s; }

To conclude, it is important to realize that buffers are being allocated via malloc() and that eventually the memory associated with those buffers should be freed through the function free() .

    
25.02.2017 / 02:11