Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make generic function using void * in c?

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 functionality.

Suppose I want to increment int,float,char by 1

void incr(void *vp)
{
        (*vp)++;
}

But the problem I know is Dereferencing a void pointer is undefined behaviour. Sometimes It may give error :Invalid use of void expression.

My main funciton is :

int main()
{

int i=5;
float f=5.6f;
char c='a';

incr(&i);
incr(&f);
incr(&c);

return 0;
}

The problem is how to solve this ? Is there a way to solve it in Conly

or

will I have to define incr() for each datatypes ? if yes, then what's the use of void *

Same problem with the swap() and sort() .I want to swap and sort all kinds of data types with same function.

like image 208
Omkant Avatar asked Nov 20 '12 08:11

Omkant


People also ask

What does void * mean in C?

void* is a "pointer to anything". void ** is another level of indirection - "pointer to pointer to anything". Basically, you pass that in when you want to allow the function to return a pointer of any type.

What are generic functions in C?

A generic function is a function that is declared with type parameters. When called, actual types are used instead of the type parameters.

Can you do generics in C?

Unlike C++ and Java, C doesn't support generics. How to create a linked list in C that can be used for any data type? In C, we can use a void pointer and a function pointer to implement the same functionality.


3 Answers

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
like image 109
Jerry Coffin Avatar answered Oct 14 '22 15:10

Jerry Coffin


Using void * will not give you polymorphic behavior, which is what I think you're looking for. void * simply allows you to bypass the type-checking of heap variables. To achieve actual polymorphic behavior, you will have to pass in the type information as another variable and check for it in your incr function, then casting the pointer to the desired type OR by passing in any operations on your data as function pointers (others have mentioned qsort as an example). C does not have automatic polymorphism built in to the language, so it would be on you to simulate it. Behind the scenes, languages that build in polymorphism are doing something just like this behind the scenes.

To elaborate, void * is a pointer to a generic block of memory, which could be anything: an int, float, string, etc. The length of the block of memory isn't even stored in the pointer, let alone the type of the data. Remember that internally, all data are bits and bytes, and types are really just markers for how the logical data are physically encoded, because intrinsically, bits and bytes are typeless. In C, this information is not stored with variables, so you have to provide it to the compiler yourself, so that it knows whether to apply operations to treat the bit sequences as 2's complement integers, IEEE 754 double-precision floating point, ASCII character data, functions, etc.; these are all specific standards of formats and operations for different types of data. When you cast a void * to a pointer to a specific type, you as the programmer are asserting that the data pointed to actually is of the type you're casting it to. Otherwise, you're probably in for weird behavior.

So what is void * good for? It's good for dealing with blocks of data without regards to type. This is necessary for things like memory allocation, copying, file operations, and passing pointers-to-functions. In almost all cases though, a C programmer abstracts from this low-level representation as much as possible by structuring their data with types, which have built-in operations; or using structs, with operations on these structs defined by the programmer as functions.

You may want to check out the Wikipedia explanation for more info.

like image 25
acjay Avatar answered Oct 14 '22 13:10

acjay


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));
like image 23
caf Avatar answered Oct 14 '22 15:10

caf