4

I am trying to declare a generic variable type in C (I can't us C++), and I have in my mind the following options.

Option1

typedef struct 
{
     void *value;
     ElementType_e type;
} Data_t;

Option 2

typedef struct {
    ElementType_e type;
    union {
        int a; 
        float b; 
        char c;
    } my_union;
} my_struct;

where ElementType_e is an enum that holds all the possible type of variables (e.g. int, char, unsigned int, etc..). I am kinda leaning toward option 1, because I don't believe casting will add extra computational time, compared to switch, right?

I am just wondering which type is more useful? I know option 1 will require casting every-time being used/accessed. is there any possible issues that could happen with casting ( especially with running/compiling the code on different platform, e.g 32 bits and 16 bits micro)

While option2 require a switch () to do any operation (e.g. addition, ...).

The following link explained that Option 2 is better ( from readability point of view), but i mainly concern about the code size and computational cost. Generic data type in C [ void * ]

Community
  • 1
  • 1
ras red2004
  • 169
  • 1
  • 1
  • 10

3 Answers3

4

is there any possible issues that could happen with casting

No, as you do not want cast, as there is no need to cast when assigning from/to a void-pointer (in C).

I am just wondering which type is more useful?

Both do, so it depends, as

  • 1 is for the lazy (as it's few typing, and few different variables' names to remember).
  • 2 is for the cautious (as it's type-save, as opposed to option 1, where the "real" type info is lost, so you can even assign a variable's address of a type not in ElementType_e).

Referring a comment:

Regarding performance I expect no major difference between both approaches (if implemented sanely), as both options need condtional statments on assigning to/from (exception here are pointer variables, which for option 1 go without conditonal statements for assignment).

Community
  • 1
  • 1
alk
  • 69,737
  • 10
  • 105
  • 255
  • you need to type-cast when doing arithmetic operation on void pointer, right? you assuming that the compiler(/run-time) knows what type of object the pointer was set to, and does the operation, which is not true. when you say the second option is for cautions, which it add more computation time? – ras red2004 Jul 10 '15 at 19:25
  • @rasred2004: "*you assuming that the compiler(/run-time) knows what type of object the pointer*" No, I don't. I assumed that the `void`-pointer's value is assigned to an instance of a pointer to the type describe by `ElementType_e` before any operation would be applied. – alk Jul 10 '15 at 19:29
  • can you explain more? by " I assumed that the void-pointer's value is assigned to an instance of a pointer to the type describe by ElementType_e", you already type_cast it to the right variable type? or you mean you can do the following (*a_ptr++) + (*b_ptr++), a_ptr and b_ptr are void pointers. Can you provide an example? BTW: I like idea of no difference between both approaches (from computational cost). – ras red2004 Jul 10 '15 at 20:22
  • @rasred2004: You cannot do this with `void*`'s as per you comment, no. Option 1 as well as option 2 needs some kind of condtional statements to assign to/from the "general"-variable. Whether you use the one or the other option can be considered an implemenation detail to the overall-problem. Just implement both approaches for some types and you see where you end up and then understand what I mean ... ;-) It's a nice exercise in any case, you'll lose nothing. – alk Jul 10 '15 at 20:29
  • And again: I would need no casting to do this. `int a = 1, b, *pi; void * pv = &a; pi = pv; b = *pi;` This code assigns the value of `a` to `b`. – alk Jul 10 '15 at 20:35
2

I'd recommend using a union. In fact, I've used one myself in a similar situation:

union sockaddr_u {
    struct sockaddr_storage ss;
    struct sockaddr_in sin;
    struct sockaddr_in6 sin6;
};

I use this union in socket code where I could be working with either IPv4 or IPv6 addresses. In this particular case, the "type" field is actually the first field in each of the inner structs (ss.ss_family, sin.sin_family, and sin6.sin6_family).

dbush
  • 205,898
  • 23
  • 218
  • 273
  • that's good example, did you you compare the results from using union with the other option? – ras red2004 Jul 10 '15 at 19:28
  • I hadn't considered using the other option when I made this union. I prefer this way because I don't need to worry about extra memory allocation, and because I can easily pass a pointer to this union to any function that expects a `struct sockaddr` or related structure. It also takes advantage of the type system to validate that you're passing around what you expect, which you don't get with a `void *`. – dbush Jul 10 '15 at 19:37
1

I think the problem is not well posed, since there are infinite possible data types definable by the programmer. Consider for example the following sequence:

typedef char S0_t;
typedef struct { S0_t x; } S1_t;
typedef struct { S1_t x; } S2_t;
typedef struct { S2_t x; } S3_t;

It's pretty clear that it's possible to follow indefinitely in order to define as many new types as we want.
So, there is not a straight manner to handle this possibilities.

On the other hand, as pointers are of more flexible nature, you can take the decision of defining a generic type concerned only with pointer types.
Thus, the types used in your project will have to be only pointers.
In this way, probably something very simple like the following definition could work:

 typedef void* generic_t;
pablo1977
  • 4,281
  • 1
  • 15
  • 41