Making a dynamic array that accepts any type in C

后端 未结 5 1453
闹比i
闹比i 2021-01-07 18:34

I\'m trying to find a way to make a struct to hold a dynamic array that can work with any data type (Including user defined data types), so far this is what I came up with.<

5条回答
  •  北海茫月
    2021-01-07 18:52

    Expanding this answer regarding a polymorphism solution, we can as well make it include pointer types or user-defined types. The major advantage with this method is to get rid of the "data type" enum and with it all the run-time checking switch statements.

    variant.h

    #ifndef VARIANT_H
    #define VARIANT_H
    
    #include 
    #include 
    
    typedef void print_data_t (const void* data);
    typedef void print_type_t (void);
    
    typedef struct 
    {
      void* data;
      print_data_t* print_data;
      print_type_t* print_type;
    } variant_t;
    
    void print_data_char    (const void* data);
    void print_data_short   (const void* data);
    void print_data_int     (const void* data);
    void print_data_ptr     (const void* data);
    void print_data_nothing (const void* data);
    
    void print_type_char        (void);
    void print_type_short       (void);
    void print_type_int         (void);
    void print_type_int_p       (void);
    void print_type_void_p      (void);
    void print_type_void_f_void (void);
    
    void print_data (const variant_t* var);
    void print_type (const variant_t* var);
    
    #define variant_init(var) {                \
      .data = &var,                            \
                                               \
      .print_data = _Generic((var),            \
        char:  print_data_char,                \
        short: print_data_short,               \
        int:   print_data_int,                 \
        int*:  print_data_ptr,                 \
        void*: print_data_ptr,                 \
        void(*)(void): print_data_nothing),    \
                                               \
      .print_type = _Generic((var),            \
        char:  print_type_char,                \
        short: print_type_short,               \
        int:   print_type_int,                 \
        int*:  print_type_int_p,               \
        void*: print_type_void_p,              \
        void(*)(void): print_type_void_f_void) \
    }
    
    
    #endif /* VARIANT_H */
    

    variant.c

    #include "variant.h"
    
    void print_data_char    (const void* data) { printf("%c",  *(const char*)  data); }
    void print_data_short   (const void* data) { printf("%hd", *(const short*) data); }
    void print_data_int     (const void* data) { printf("%d",  *(const int*)   data); }
    void print_data_ptr     (const void* data) { printf("%p",  data); }
    void print_data_nothing (const void* data) {}
    
    void print_type_char        (void) { printf("char");          }
    void print_type_short       (void) { printf("short");         }
    void print_type_int         (void) { printf("int");           }
    void print_type_int_p       (void) { printf("int*");          }
    void print_type_void_p      (void) { printf("void*");         }
    void print_type_void_f_void (void) { printf("void(*)(void)"); }
    
    
    void print_data (const variant_t* var)
    {
      var->print_data(var->data);
    }
    
    void print_type (const variant_t* var)
    {
      var->print_type();
    }
    

    main.c

    #include 
    #include "variant.h"
    
    int main (void) 
    {
      char c = 'A';
      short s = 3;
      int i = 5;
      int* iptr = &i;
      void* vptr= NULL;
      void (*fptr)(void) = NULL;
    
      variant_t var[] =
      {
        variant_init(c),
        variant_init(s),
        variant_init(i),
        variant_init(iptr),
        variant_init(vptr),
        variant_init(fptr)
      };
    
      for(size_t i=0; i

    Output:

    Type: char      Data: A
    Type: short     Data: 3
    Type: int       Data: 5
    Type: int*      Data: 000000000022FD98
    Type: void*     Data: 000000000022FDA0
    Type: void(*)(void)     Data:
    

    Disadvantages with _Generic for this purpose is that it blocks us from using private encapsulation, since it has to be used as a macro in order to pass on type information.

    On the other hand, the "variant" in this case has to be maintained for all new types one comes up with, so it isn't all that practical or generic.

    Still these tricks are good to know for various similar purposes.

提交回复
热议问题