Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic functions taking unknown type parameters in C

I'm trying to make some functions, taking unknown type parameters, to generically apply functions.

Let's take for example a function that could apply close(int fd) for each element of an array:

void for_each(void *array[], void (*func)(void *))
{
    for (size_t i = 0; array[i] != NULL; ++i)
        func(array[i]);
}

If I want to use this function with close(int fd), I have to make a wrapping function like this:

void close_fd(void *fd)
{
    close(*(int *)fd);
}

I would like to use this for_each function with strings, floats and everything else.

  • Isn't there something that could achieve it, without the wrapping part ?
  • I know that C++ have a lot of way to do it, lambdas, templates etc., but is there a good way in C ? A compromise ?
like image 932
BiasInput Avatar asked Nov 08 '22 07:11

BiasInput


1 Answers

Well, it is clear that you want to call several times a function (the same function) for different data. But those data are passed in an array, with by definition is a storage of several data items of the same type. So finally there's no special construct to do that, just do it in plain C:

typedef .... MY_TYPE;

/* define a pointer to represent the type of callback pointer you are using */
typedef void (*callback_ptr)(MY_TYPE param);

/* this is the real callback function you are going to use */
void my_func1(MY_TYPE param){
    /* process a single MY_TYPE */
}

void my_funcN(MY_TYPE param) {
    /* another process function */
}

/* function to apply the callback to an array of MY_TYPE */
void for_each( MY_TYPE array[], size_t array_sz, callback_ptr f )
{
    int i;
    for (i = 0; i < array_sz; i++)
        f(array[i]);
}

....
MY_TYPE my_array[100] = { ... };
size_t my_array_sz = sizeof my_array / sizeof my_array[0];

for_each(my_array, my_array_sz, my_func1); /* process all by my_func1 */
for_each(my_array, my_array_sz, my_funcN); /* process all by my_funcN */

Try to avoid using void * when it's not strictly necessary. Probably in an early design phase, you don't know the actual type of data it is going to process, but it's clear that once you write the actual call statement, you have to put parameters and use the return value, so you are sticking then to actual types.... and that gives you the needed hint on how you have to declare the callback pointer, and the process functions. Putting the actual types in the function call makes the compiler to check for correctness, and that's a help for you to avoid commiting mistakes.

generic functions

Generic functions are not supported in C, but if you want some kind of arrangement, that allows you to write functions in which some data type is generic, you can simulate that with macros (you have to write very carefully the macros to work, but very good approximations can be achieved)

You can define a different function for each type, with a cpp macro, as in:

func_def.h

#define FUNC(TYPE)                        \
        void my_func_##TYPE( TYPE param ) \
        {                                 \
          /* body of function */          \
        }

and then include in file scope, the following functions:

program.c

FUNC(int)
FUNC(double)

and that will expand into:

        void my_func_int( int param )     \
        {                                 \
          /* body of function */          \
        }
    
        void my_func_double( double param ) \
        {                                   \
            /* body of function */          \
        }

And you'll get type checking in parameters anyway. As you see, you have to use the type in the function name, as C doesn't support overloading of functions (functions with same name and different argument lists) In this case, both, the my_func_* callbacks, and the for_each_* functions will have to be defined, as for the compiler to fully do type checking.

like image 107
Luis Colorado Avatar answered Nov 15 '22 04:11

Luis Colorado