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; | |
} |
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 |
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.
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:
ReplyDelete/* 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))
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.
DeleteMoreover, 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.
At last I tried it: you were right (of course).
Delete