12

I want to write generic function (for example, function that get array in type of void** and doing something about this array) such then this function will get the type of the element (in the example, it's will be the type of any element in the array) as a argument.

Can I do it in c?


For example:

I want to write a function that get array (in type of void**) and initialize this array in some random way.

By "in some random way" I mean, for example a function that gets as arguments: array (in type void**) , the type of any element in the array, index (in type of int) and initialize this cell.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
AskMath
  • 415
  • 1
  • 4
  • 11
  • I want to write generic function in c , for example, function that get array (in type void**) and doing something about this array. And I want to know if can I pass the type of any element in this array (for example) to the function.. – AskMath May 21 '18 at 06:49
  • Macros can (sort of) do that. C does not have stuff like templates in C++ – Support Ukraine May 21 '18 at 06:53
  • 1
    To some limited extent, you can use `_Generic` of C11 to write generic functions, but you'd pretty much have to know beforehand *all* the types that you want to genericize – Antti Haapala -- Слава Україні May 21 '18 at 06:55

4 Answers4

16

This is only possible if you have a standard C compiler, in which case you can use the _Generic keyword for this purpose. You have to write a different function per supported type.

#include <stdio.h>

#define func(x) _Generic((x), int: func_int, char: func_char)(x);

void func_int (int x)
{
  printf("%s\t%d\n", __func__, x);
}

void func_char (char x)
{
  printf("%s\t%c\n", __func__, x);
}


int main(void)
{
  int i = 5;
  char c = 'A';

  func(i);
  func(c);
}

Output:

func_int        5
func_char       A
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 2
    You should add [a reference or two](http://en.cppreference.com/w/c/language/generic); it's C11-only. (Also, "a standard C compiler" is basically meaningless unless you specify _which_ standard.) – Nic May 21 '18 at 14:29
  • 1
    Nah, unless the OP mentions another C version explicitly, we can assume that they are using standard C. – Lundin May 21 '18 at 14:30
  • 2
    @Lundin Again, which standard? It's entirely possible someone reading this answer is working on a C99 compiler -- say, because they're at a school which hasn't moved on to an actual, modern compiler, which is disturbingly plausible -- and if it's not at least specified, it'll just waste their time. – Nic May 22 '18 at 15:58
  • @NicHartley ISO 9899. They only have one active standard. See https://stackoverflow.com/tags/c/info – Lundin May 23 '18 at 08:33
  • 1
    @Lundin But "active standard" and "used standard" _are not the same_. Like I said, it's entirely possible for someone to be limited to an older version of C. You've wasted more time debating this than you would have spent changing "standard C" to "C11", which is simple, unambiguous, and easier to figure out for people new to C. I'm done debating this with you; if you can't figure out that making something easier to read for no additional work on your part is a good thing, then I'll let you figure that out on your own. – Nic May 23 '18 at 17:27
  • @NicHartley What is it that you can't understand from: "Unless the question explicitly mentions which version of the C standard that is used, it is assumed that the current version is used. That is, whichever version of ISO 9899 that ISO currently lists as active. Please have this in mind when answering or commenting on questions tagged [tag:c]" – Lundin May 24 '18 at 06:32
8

You don't pass the "type". C has no built-in way to encode and decode type information at runtime. The function operating on the objects must know the type statically. If you are absolutely intent on working with pointers to void, you have to delegate to a function that knows the type information. That can be done with a callback. For instance, the standard library function qsort accepts a callback for comparing the objects' values:

void qsort( void *ptr, size_t count, size_t size,
            int (*comp)(const void *, const void *) );

The calling code supplies the callback, and inside said callback it will cast back to the static type it needs to compare. That's how one usually works with pointers to void, one defines the set of operations it needs to do on the type in an abstract form, and then asks the calling code to supply an implementation for those operations.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
5

Here's an example for for some macro trickery.

func.h

#ifndef FUNC_H
#define FUNC_H

#define add(a, b, typename) functionAdd##typename(a,b)

/* function declarations */
#define declared(typename) \
typename functionAdd##typename(typename, typename)

declared(int);
declared(float);

#endif

func.c

#include "func.h"

/* function code */
#define functionAdd(a, b, typename) \
typename functionAdd##typename(typename a, typename b){ \
    return a+b; \
}

/* function bodies (definitions) */
functionAdd(a, b, int)
functionAdd(a, b, float)

main.c

#include <stdio.h>
#include "func.h"

int main()
{
    int x1 = add(1, 2, int);
    float x2 = add(3.0, 4.0, float);
    printf("%d %f\n", x1, x2);  
    return 0;
}
Stan
  • 787
  • 5
  • 16
3

Another solution could be to define an enumeration to represent a type like this:

#include "stdio.h"

typedef enum {
    TYPE_INT,
    TYPE_CHAR,
    TYPE_STRING
} type_id;

int print(type_id type, void *data) {
    switch (type) {
    case TYPE_INT:
        // Do something with data as int
        printf("%d\n", * (int *)data);
        break;
    case TYPE_CHAR:
        // Do something with data as char
        printf("%c\n", * (char *)data);
        break;
    case TYPE_STRING:
        // Do something with data as string
        printf("%s\n", (char *)data);
        break;
    }
}

int main() {
    int a = 5;
    char b = 'a';
    char *c = "string";

    print(TYPE_INT, &a);
    print(TYPE_CHAR, &b);
    print(TYPE_STRING, c);

    return 0;
}

I like the suggestion by Lundin more as it provides type safety.

PoVa
  • 995
  • 9
  • 24