How to make generic function using void * in c?

后端 未结 7 2287
广开言路
广开言路 2020-12-13 07:48

I have an incr function to increment the value by 1 I want to make it generic,because I don\'t want to make different functions for the same functi

相关标签:
7条回答
  • 2020-12-13 08:22

    You should cast your pointer to concrete type before dereferencing it. So you should also add code to pass what is the type of pointer variable.

    0 讨论(0)
  • 2020-12-13 08:24

    You can't do exactly what you're asking - operators like increment need to work with a specific type. So, you could do something like this:

    enum type { 
        TYPE_CHAR,
        TYPE_INT,
        TYPE_FLOAT
    };
    
    void incr(enum type t, void *vp)
    {
        switch (t) {
            case TYPE_CHAR:
            (*(char *)vp)++;
            break;
    
            case TYPE_INT:
            (*(int *)vp)++;
            break;
    
            case TYPE_FLOAT:
            (*(float *)vp)++;
            break;
        }
    }
    

    Then you'd call it like:

    int i=5;
    float f=5.6f;
    char c='a';
    
    incr(TYPE_INT, &i);
    incr(TYPE_FLOAT, &f);
    incr(TYPE_CHAR, &c);
    

    Of course, this doesn't really give you anything over just defining separate incr_int(), incr_float() and incr_char() functions - this isn't the purpose of void *.

    The purpose of void * is realised when the algorithm you're writing doesn't care about the real type of the objects. A good example is the standard sorting function qsort(), which is declared as:

    void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));
    

    This can be used to sort arrays of any type of object - the caller just needs to supply a comparison function that can compare two objects.

    Both your swap() and sort() functions fall into this category. swap() is even easier - the algorithm doesn't need to know anything other than the size of the objects to swap them:

    void swap(void *a, void *b, size_t size)
    {
        unsigned char *ap = a;
        unsigned char *bp = b;
        size_t i;
    
        for (i = 0; i < size; i++) {
            unsigned char tmp = ap[i];
    
            ap[i] = bp[i];
            bp[i] = tmp;
        }
    }
    

    Now given any array you can swap two items in that array:

    int ai[];
    double ad[];
    
    swap(&ai[x], &ai[y], sizeof(int));
    swap(&di[x], &di[y], sizeof(double));
    
    0 讨论(0)
  • 2020-12-13 08:26

    You can implement the first as a macro:

    #define incr(x) (++(x))
    

    Of course, this can have unpleasant side effects if you're not careful. It's about the only method C provides for applying the same operation to any of a variety of types though. In particular, since the macro is implemented using text substitution, by the time the compiler sees it, you just have the literal code ++whatever;, and it can apply ++ properly for the type of item you've provided. With a pointer to void, you don't know much (if anything) about the actual type, so you can't do much direct manipulation on that data).

    void * is normally used when the function in question doesn't really need to know the exact type of the data involved. In some cases (e.g., qsort) it uses a callback function to avoid having to know any details of the data.

    Since it does both sort and swap, let's look at qsort in a little more detail. Its signature is:

    void qsort(void *base, size_t nmemb, size_t size,
               int(*cmp)(void const *, void const *));
    

    So, the first is the void * you asked about -- a pointer to the data to be sorted. The second tells qsort the number of elements in the array. The third, the size of each element in the array. The last is a pointer to a function that can compare individual items, so qsort doesn't need to know how to do that. For example, somewhere inside qsort will be some code something like:

    // if (base[j] < base[i]) ...
    if (cmp((char *)base+i, (char *)base+j) == -1)
    

    Likewise, to swap two items, it'll normally have a local array for temporary storage. It'll then copy bytes from array[i] to its temp, then from array[j] to array[i] and finally from temp to array[j]:

    char temp[size];
    
    memcpy(temp, (char *)base+i, size);              // temp = base[i]
    memcpy((char *)base+i, (char *)base+j, size);    // base[i] = base[j]
    memcpy((char *)base+j, temp, size);              // base[j] = temp
    
    0 讨论(0)
  • 2020-12-13 08:27

    Example for using "Generic" swap.

    This code swaps two blocks of memory.

    void memswap_arr(void* p1, void* p2, size_t size)
    {      
          size_t         i;
          char* pc1= (char*)p1;
          char* pc2= (char*)p2;
          char  ch;
    
          for (i= 0; i<size; ++i) {
            ch=     pc1[i];
            pc1[i]= pc2[i];
            pc2[i]= ch;
          }
    }
    

    And you call it like this:

    int main() {
         int i1,i2;
         double d1,d2;
         i1= 10; i2= 20;
         d1= 1.12; d2= 2.23;
         memswap_arr(&i1,&i2,sizeof(int));     //I use memswap_arr to swap two integers
         printf("i1==%d i2==%d \n",i1,i2);     //I use the SAME function to swap two doubles
         memswap_arr(&d1,&d2,sizeof(double));      
         printf("d1==%f d2==%f \n",d1,d2);
         return 0;
    }
    

    I think that this should give you an idea of how to use one function for different data types.

    0 讨论(0)
  • 2020-12-13 08:29

    Sorry if this may come off as a non-answer to the broad question "How to make generic function using void * in c?".. but the problems you seem to have (incrementing a variable of an arbitrary type, and swapping 2 variables of unknown types) can be much easier done with macros than functions and pointers to void.

    Incrementing's simple enough:

    #define increment(x) ((x)++)
    

    For swapping, I'd do something like this:

    #define swap(x, y)                  \
    ({                                  \
            typeof(x) tmp = (x);        \
            (x) = (y);                  \
            (y) = tmp;                  \
    })
    

    ...which works for ints, doubles and char pointers (strings), based on my testing.

    Whilst the incrementing macro should be pretty safe, the swap macro relies on the typeof() operator, which is a GCC/clang extension, NOT part of standard C (tho if you only really ever compile with gcc or clang, this shouldn't be too much of a problem).

    I know that kind of dodged the original question; but hopefully it still solves your original problems.

    0 讨论(0)
  • 2020-12-13 08:30

    You can use the type-generic facilities (C11 standard). If you intend to use more advanced math functions (more advanced than the ++ operator), you can go to <tgmath.h>, which is type-generic definitions of the functions in <math.h> and <complex.h>.

    You can also use the _Generic keyword to define a type-generic function as a macro. Below an example:

    #include <stdio.h>
    
    #define add1(x) _Generic((x), int: ++(x), float: ++(x), char: ++(x), default: ++(x))
    
    int main(){
      int i = 0;
      float f = 0;
      char c = 0;
    
      add1(i);
      add1(f);
      add1(c);
    
      printf("i = %d\tf = %g\tc = %d", i, f, c);
    }
    

    You can find more information on the language standard and more soffisticated examples in this post from Rob's programming blog.

    As for the * void, swap and sort questions, better refer to Jerry Coffin's answer.

    0 讨论(0)
提交回复
热议问题