40

I need to implement some methods that do stuff with different kinds of number arrays. Usually, I'd use generics for that job, but as C doesn't provide them, I'm now trying to emulate them using macros.

Here's an example of what I'm trying to do:

#ifndef TYPE
#define TYPE int
#endif

TYPE get_minimum_##TYPE (TYPE * nums, int len){
    TYPE min = nums[0];

    for (int i = 1; i < len; i++) {
        if (nums[i] < min) {
            min = nums[i];
        }
    }

    return min;
}

However, this won't compile. The clang error message:

error: expected ';' after top level declarator

Is there any way to do this in C? Or do I need implement this for every type by hand?

a3f
  • 8,517
  • 1
  • 41
  • 46
fb55
  • 1,197
  • 1
  • 11
  • 16
  • 1
    If you want to read a little bit more about "templates"/"generics" in C, [you can read this question](http://stackoverflow.com/questions/10950828/simulation-of-templates-in-c). There are some interesting answers :) – Jehan May 13 '13 at 13:33

3 Answers3

58

You can do something like this in a header file:

//
// generic.h
//

#define TOKENPASTE(x, y) x ## y

#define GET_MINIMUM(T) TOKENPASTE(get_minimum_, T)

TYPE GET_MINIMUM (TYPE) (TYPE * nums, size_t len){
    TYPE min = nums[0];

    for (size_t i = 1; i < len; i++) {
        if (nums[i] < min) {
            min = nums[i];
        }
    }

    return min;
}

and then #include it in a source file for each required type, e.g.:

//
// generic.c
//

#define TYPE int
#include "generic.h"
#undef TYPE

#define TYPE float
#include "generic.h"
#undef TYPE

You can test this by running it through the preprocessor:

$ gcc -E generic.c 

int get_minimum_int (int * nums, size_t len){
    int min = nums[0];

    for (size_t i = 1; i < len; i++) {
        if (nums[i] < min) {
            min = nums[i];
        }
    }

    return min;
}

float get_minimum_float (float * nums, size_t len){
    float min = nums[0];

    for (size_t i = 1; i < len; i++) {
        if (nums[i] < min) {
            min = nums[i];
        }
    }

    return min;
}
Groshh
  • 140
  • 1
  • 11
Paul R
  • 208,748
  • 37
  • 389
  • 560
41

Actually, the best you can do is to define a macro that will generate the function for the given type.

#define define_get_minimum(T) \
T get_minimum_##T(T* nums, int len){ \
    T min = nums[0]; \
    for (int i = 1; i < len; i++) { \
        if (nums[i] < min) { \
            min = nums[i]; \
        } \
    } \
    return min; \
}

Then, you can call that macro to define the specializations you need (with C++ template, a similar thing is done automagically by the compiler).

define_get_minimum(int)
define_get_minimum(double)
define_get_minimum(float)

Another thing that a C++ compiler does automagically is deduce the overloaded function you need. You can't have that in C, so you will have to tell you are using the it specialization. You can simulate a template-like syntax for your function with the following macro (the C++ <> are just replaced by ()):

#define get_minimum(T) get_minimum_##T

Then, you should be able to call it the following way:

int main()
{
    // Define arr as char* array...
    // Do stuff...
    int res = get_minimum(int)(arr, 3);
}

I did not test this code, but it should work.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Jehan
  • 746
  • 5
  • 13
  • I like the style of the final call, but I was trying to archive this without large macros. – fb55 May 14 '13 at 09:47
  • @fb55 Well, generic and not large are not that compatible in C :) – Jehan May 14 '13 at 16:24
  • 2
    That's what I liked about @PaulR's answer: The macro was pretty short, and was reusable at other places :) – fb55 May 14 '13 at 16:27
  • 1
    @fb55 The problem is that is depends on a compile-time parameter. With his version, you can not use the `float` and the `int` version of the function in the same compile unit :/ – Jehan May 14 '13 at 16:29
  • @Jehan I was trying to include the file twice, with different parameters. But that doesn't seem to work, see http://stackoverflow.com/questions/16542158 – fb55 May 14 '13 at 16:30
  • 3
    I've updated my answer now to show how to generate multiple functions for all the required types in one translation unit. – Paul R May 14 '13 at 18:00
  • Thx for this solution :) As additional information I googled for myself: The "##" is a concatenation operator (see e.g https://www.cprogramming.com/reference/preprocessor/token-pasting-operator.html) – shaman-apprentice Nov 19 '20 at 19:50
3

You can also use function pointers (Array of function pointers), other than a switch statement, and pass the argument of the switch as the index to the array.

truth_seeker
  • 345
  • 4
  • 14