2015-06-20

Type-generic functions (overloading) in C11

As per Michael Stoffregen suggestion in the comment, I've tried to move arguments outside and it worked (even with gcc 4.9.2)! So I can finally say C(11) has Fortran-like function “overloading” — type generic functions.

I have often wondered why C hasn't any kind of function overloading. The typical answer is that we don't want to live with name mangling in C — the hell of the C++ — that would break a lot of things which must stay as they are. I agree… except that you don't need to implement it the C++ way when you have a bright example like Fortran.

Now it seems like C11 has something that could give us function overloading (and other tricks)… maybe in C11 you shouldn't say “overloaded function”, but “type-generic function”, I suppose. Anyway… I will use them interchangeably.

I hoped for something similar to Fortran… Instead, we have _Generic: C11 introduced this macro (sort of) which allows to select a function1 according to a type.

I have found it unsatisfactory2, unless you consider it more like a new tool for powerful tricks rather than a way towards type-generic functions. Also, it seems they had just a single usage scenario in mind: tgmath.h. I daresay it's hard to think to use this _Generic in a generic scenario…

What I have seen so far…

  • No way of overloading operators. Maybe there are many who think that such a possibility can help in making code uglier, less clear and so on — but this is, and always has been and always will be, a programmers' sin: let “good” programmers choose when it is ok to use such a feature and when it is not… (Macros, after all, are “dangerous”: you shall use them sparingly and you shall handle them with care…)
  • It's easy to overload functions according to a single argument; tricks to cope with non-single argument functions may exist and be cool, but… (e.g. see this stack overflow answer or this question; also, take a look at this idea about how generic pow could be done…)
  • It seems it does not work with all types — maybe just for “simple”, primitive types and “aliases”?

My test (simple and basic). (Modified according to Stoffregen's suggestion — he was right!)

#include <stdio.h>
typedef struct {
double x;
double y;
} vec2d;
#define sum(X, Y) _Generic((X), \
int: sum2l((X), (Y)), \
long: sum2l((X), (Y)), \
double: sum2d((X), (Y)), \
float: sum2d((X), (Y)))
// this won't work - because it's wrong
#define sumxWrong(X, Y) _Generic((X), \
int: sum2l((X), (Y)), \
long: sum2l((X), (Y)), \
double: sum2d((X), (Y)), \
float: sum2d((X), (Y)), \
vec2d: sum2v((X), (Y)))
// this works (thanks to Michael Stoffregen who suggested the right way)
#define sumx(X, Y) _Generic((X), \
int: sum2l, \
long: sum2l, \
double: sum2d, \
float: sum2d, \
vec2d: sum2v)((X), (Y))
long sum2l(long x, long y)
{
return x + y/2;
}
double sum2d(double x, double y)
{
return x + y/2.0;
}
vec2d sum2v(vec2d a, vec2d b)
{
vec2d r = {a.x + b.x, (a.y + b.y)/2.0};
return r;
}
int main(void)
{
printf("%ld %lf\n", sum(5, 3), sum(5.0, 3));
vec2d a = {1.0, 2.5};
vec2d b = {3.0, 3.5};
// calling explicitly the right function: no overloading
vec2d r = sum2v(a, b);
printf("%lf %lf\n", r.x, r.y);
// let's try with "genericity"
r = sumx(a, b);
printf("%lf %lf\n", r.x, r.y);
/* the following does not work in both compiler: it seems both would choose sum2l, instead of sum2v.
part of clang error output:
generic.c:57:9: error: passing 'vec2d' to parameter of incompatible type 'long';
r = sumx(a, b);
^~~~~~~~~~
part of gcc error output:
generic.c:57:9: note: in expansion of macro ‘sumx’
r = sumx(a, b);
^
generic.c:24:6: note: expected ‘long int’ but argument is of type ‘vec2d’
*/
//r = sumxWrong(a, b); // it was sumx, which now works fine ^_^
/*
the following does not work neither.
In both cases I would expect it would choose sum2l; but...
gcc:
generic.c:80:15: note: in expansion of macro ‘sumx’
int res = sumx(1, 2);
^
generic.c:34:7: note: expected ‘vec2d’ but argument is of type ‘int’
vec2d sum2v(vec2d a, vec2d b)
^
clang:
generic.c:89:15: error: passing 'int' to parameter of incompatible type 'vec2d';
int res = sumx(1, 2);
^~~~~~~~~~
*/
//int res = sumxWrong(1, 2);
return 0;
}
view raw generic.c hosted with ❤ by GitHub

Taste of Fortran

Let's compare how Fortran intends type-generic functions. Of course Fortran is not C and C is not Fortran… But good ideas can always be borrowed.

program generic_example
use my_sum_module
implicit none
print *, my_sum(1.0, 3.0) ! 1 + 1.5
print *, my_sum(1, 5) ! 1 + 2
print *, my_sum(2, 3.0) ! 2 + 1.5
print *, my_sum(1.5, 7) ! 1.5 + 3.5
! 1 + 1, (1 + 2)/2 (1.5)
print *, my_sum(something(1,1), something(1,2))
! the following halts compilations, since
! There is no specific function for the generic 'my_sum'
!print *, my_sum(something(1,1), 5.0)
end program generic_example
view raw generic.f90 hosted with ❤ by GitHub
module my_sum_module
implicit none
! interfaces are generated from the function themselves
interface my_sum
module procedure my_sum_r, my_sum_i, my_sum_v, &
my_sum_m1, my_sum_m2
end interface
type, public :: something
real :: x, y
end type something
contains
function my_sum_r(x, y)
implicit none
real, intent(in) :: x, y
real :: my_sum_r
my_sum_r = x + y/2.0
end function my_sum_r
function my_sum_m1(x, y)
implicit none
integer, intent(in) :: x
real, intent(in) :: y
real :: my_sum_m1
my_sum_m1 = real(x) + y/2.0
end function my_sum_m1
function my_sum_m2(x, y)
implicit none
integer, intent(in) :: y
real, intent(in) :: x
real :: my_sum_m2
my_sum_m2 = x + real(y)/2.0
end function my_sum_m2
function my_sum_i(x, y)
implicit none
integer, intent(in) :: x, y
integer :: my_sum_i
my_sum_i = x + y/2
end function my_sum_i
function my_sum_v(x, y)
implicit none
type(something), intent(in) :: x, y
type(something) :: my_sum_v
my_sum_v%x = x%x + y%x
my_sum_v%y = (x%y + y%y)/2.0
end function my_sum_v
end module my_sum_module

You can also use interface operator(+) (say) if you want to “overload” +, or interface assignment(=) to overload the assignment operator.

Right now I have no idea how this could be done keeping the C syntax and “taste”.

At this point, before Stoffregen-suggested fix, I had written:

Anyway, with C11 and _Generic, in my opinion C has lost a chance to have generic (!) type-generic functions done quite well. Therefore I'll wait the next revision of the standard before yelling “at last!”

Maybe they are not perfect yet, but this judgement must be made less harsh.


  1. Since it works like a macro, we can produce any string, even so that the code becomes syntactically broken. As it is for preprocessor's macros.

  2. I have tested this new feature with gcc 4.9.2 (just compiled) and clang 3.0-6.2 (package in Debian Wheezy).

3 comments:

  1. I know your post is years old, but I'm playing around with generic C programing and this was one of the top(ish) google results so I felt compelled to comment. In the C example, I think you're doing it wrong. Change it to this and rock and roll:

    /* this should work.. the (X,Y) are moved outside of the generic () block */
    #define sumx(X, Y) _Generic((X), \
    int: sum2l, \
    long: sum2l, \
    double: sum2d, \
    float: sum2d, \
    vec2d: sum2v ) ((X), (Y))

    ReplyDelete
    Replies
    1. That's interesting. Anyway it forces a different syntax in two cases: don't forget "sum" works just fine — I wonder why I must write it differently when I don't use a primitive type! It's odd.
      Moreover, now I have a more recent gcc version, maybe I should also try it again, maybe it was a specific issue of that version. I will update the post accordingly.
      Thanks for the feedback.

      Delete
    2. At last I tried it: you were right (of course).

      Delete