SO Stinks
SO Stinks

Reputation: 3406

Request for a simple C code example that shows how a generic or untyped (via void *) array can be used

I'm struggling to create a generic (or untyped) array in C (I'm aware that C++ makes this easier). In a nutshell I want to allocate an array to hold an array of a specific known type (at runtime). In the real implementation it depends on user input.

I've been trying to use a enum/struct scenario following the advice found in several Google hits but I'm afraid that my inexperience with void pointers and lack of a concrete example are preventing me from getting a working piece of code. (Normally I would just buy a book but I'm in a country where I don't speak the language and English programming books are non-existent.)

The problem boils down to the following simplification: I have an image (only 1D) were the pixel values may be int, float, or double. I do have a function that will tell me the type. All I want to do is store the pixels in an array of the appropriate type. (In practice these images are very large and my motivation is to save memory and to prevent writing blocks of code for each type.)

I've been trying something like the following but maybe it's not the best anyway (code snippet for a possible data structure) :

enum type {
    typeint, typefloat, typedouble
};

struct genericarray {
    enum type type;
    void *storage;
};

Somehow I want to store those pixels in instances of generciarray. All my attempts so far have turned into "warning: dereferencing ‘void *’ pointer" parties, which I admit I am not understanding.

I'm be eternally grateful for any help and in particular for some simplified example how to get a working generic array. The example could just use for-loops to init. I can figure out how to load my actual images.

Upvotes: 3

Views: 608

Answers (5)

James Eichele
James Eichele

Reputation: 119164

I would encapsulate the whole thing as follows:

#include "stdlib.h"
#include "stdio.h"

// structure
enum type { typeint, typefloat, typedouble };

struct genericarray
{
    enum type type;
    void ** storage;
};

typedef struct genericarray genericarray;

// allocate
void allocate(long numItems, enum type varType, genericarray * array)
{
    (*array).type = varType;
    switch (varType)
    {
        case typeint:
            (*array).storage = malloc(numItems*sizeof(int));
            break;
        case typefloat:
            (*array).storage = malloc(numItems*sizeof(float));
            break;
        case typedouble:
            (*array).storage = malloc(numItems*sizeof(double));
    }
}

// release
void release(genericarray array)
{
    free(array.storage);
}

// usage
int main(int argCount, char ** argList)
{
    genericarray image_1;
    genericarray image_2;

    int iv;
    float fv;

    allocate(10, typeint, &image_1);
    allocate(10, typefloat, &image_2);

    ((int *)(image_1.storage))[5] = 42;
    iv = ((int *)(image_1.storage))[5];
    printf("image_1[5] = %d\n", iv);

    ((float *)(image_2.storage))[5] = 3.14159;
    fv = ((float *)(image_2.storage))[5];
    printf("image_2[5] = %f\n", fv);

    release(image_2);
    release(image_1);

    return 0;
}

Upvotes: 4

nothrow
nothrow

Reputation: 16178

enum type {
    typeint, typefloat, typedouble
}

struct genericarray {
    enum type type;
    union { int i; float f; double d; } storage;
};

is what you need (imho.)

EDIT: to use void* as a container for int/float/double, you must allocate it: (array is genericarray of your old type - with void* storage)

array.storage = malloc(sizeof(int));

and dereference as:

(*((int*)array.storage))

replace float for the case of type == typefloat, etc.

and you must also free it

free(array.storage);

Upvotes: 2

epatel
epatel

Reputation: 46041

You don't actually specify the lines that gives the error but I imagine they might look a bit like

struct genericarray ga;
float fValue;

fValue = ga.storage[idx];

What you could do is add a cast

fValue = ((float*)ga.storage)[idx];

I'd recommend creating some macros or functions for setting and getting values

#define GET_STORAGE(_type, _src, _idx) \
  ((_type*)_src.storage)[_idx]
#define SET_STORAGE(_type, dst, _idx, _src) \
  ((_type*)_dst.storage)[_idx] = _src 

fValue = GET_STORAGE(float, ga, 3);

SET_STORAGE( float, ga, 3, sin(ang) );

I see 'Chris Young' using switch statements in his answers and that is a good thing for is these access functions.

Upvotes: 2

TJ Seabrooks
TJ Seabrooks

Reputation: 20723

You want to define a struct that has a an item that is a union of the three types you will need to use and then dynamically allocate a buffer of that type.

Upvotes: 0

Chris Young
Chris Young

Reputation: 15767

That's because you can't dereference a void pointer without first casting it to something else. This is because the C implementation needs to know what type of object the dereferenced pointer is.

In addition to the union suggested already, you can use the method you allude to, but you would have to conditionally convert (implicitly or explicitly with a cast) if you want to dereference storage:

int *iptr;
double *dptr; 

switch (x.type)
{
    case typedouble:
        dptr = x.storage; // implcit conversion example
        // reference your "image" as dptr[i] now
        break;
    case typeint:
        iptr = (int *)x.storage; // explicit conversion, actually unnecessary
        // reference your "image" as iptr[i] now
        break;
 }

Upvotes: 2

Related Questions