J
Joel C. Salomon
I'm attempting to use C11's _Generic selection expressions to enforce a
kind of type consistency, and it's failing in an odd way.
[The _Generic keyword is described in <http://bit.ly/c11-n1404+> and
<http://bit.ly/c11-n1441+> by Intel's Clark Nelson, whom I have CC'd on
this message. Clark, can I ask you to respond on <comp.lang.c>?]
Motivation: In the revised IEEE 754 (2008, aka IEC 60559:2011) there is
the function totalOrder(x, y) which is meaningful for each
floating-point type, but meaningless for mixed types. I was wondering
whether this was a candidate for _Generic selection: not simply for
convenience, but for type safety as well.
Consider a simpler case, the functions funcf() & funcd which I want to
wrap in a macro func(x, y) so that
* func(1.0f, 2.0f) calls funcf(),
* func(1.0, 2.0) calls funcd(), and
* func(1.0f, 2.0) & func(1.0, 2.0f) are invalid.
My first effort looked like this:
#include <stdio.h>
void funcf(float x, float y) {
printf("floats %f, %f\n", x, y);
}
void funcd(double x, double y) {
printf("doubles %f, %f\n", x, y);
}
#define func(x, y) _Generic((x), \
float: _Generic((y), float: funcf), \
double: _Generic((y), double: funcd) ) (x, y)
int main(void) {
func(1.0f, 2.0f); // should work, but doesn't
// func(1.0f, 2.0); // shouldn't work
// func(1.0, 2.0f); // shouldn't work
func(1.0, 2.0); // should work, but doesn't
}
I used LLVM Clang version 3.0, which complained about the lines which
I'd intended to have work:
gen.c:12:13: error: controlling expression type 'float' not compatible
with any generic association type
func(1.0f, 2.0f);
~~~~~~~~~~~^~~~~
gen.c:10:20: note: expanded from:
double: _Generic((y), double: funcd) ) (x, y)
^
[And a similar error for the `func(1.0, 2.0)` line.]
First question: It seems that all "legs" of the selection expression
are evaluated; is that correct behavior?
Second question: Assuming that is correct, what might be the best way
to accomplish what I'm trying to? The best method I've come up with is
this:
#define func(x, y) _Generic((x), \
float: _Generic((y), float: funcf, double: 0 ), \
double: _Generic((y), double: funcd, float: 0 ) ) (x, y)
int main(void) {
func(1.0f, 2.0f);
func(1.0f, 2.0);
func(1.0, 2.0f);
func(1.0, 2.0);
}
This "works"; the correct forms compile and the incorrect ones cause
compile errors -- but they obscure the issue:
gen.c:13:2: error: called object type 'int' is not a function or
function pointer
func(1.0f, 2.0);
^~~~~~~~~~~~~~~
gen.c:10:53: note: expanded from:
double: _Generic((y), double: funcd, float: 0 ) ) (x, y)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^
Can this be improved, while working within what the Standard provides?
kind of type consistency, and it's failing in an odd way.
[The _Generic keyword is described in <http://bit.ly/c11-n1404+> and
<http://bit.ly/c11-n1441+> by Intel's Clark Nelson, whom I have CC'd on
this message. Clark, can I ask you to respond on <comp.lang.c>?]
Motivation: In the revised IEEE 754 (2008, aka IEC 60559:2011) there is
the function totalOrder(x, y) which is meaningful for each
floating-point type, but meaningless for mixed types. I was wondering
whether this was a candidate for _Generic selection: not simply for
convenience, but for type safety as well.
Consider a simpler case, the functions funcf() & funcd which I want to
wrap in a macro func(x, y) so that
* func(1.0f, 2.0f) calls funcf(),
* func(1.0, 2.0) calls funcd(), and
* func(1.0f, 2.0) & func(1.0, 2.0f) are invalid.
My first effort looked like this:
#include <stdio.h>
void funcf(float x, float y) {
printf("floats %f, %f\n", x, y);
}
void funcd(double x, double y) {
printf("doubles %f, %f\n", x, y);
}
#define func(x, y) _Generic((x), \
float: _Generic((y), float: funcf), \
double: _Generic((y), double: funcd) ) (x, y)
int main(void) {
func(1.0f, 2.0f); // should work, but doesn't
// func(1.0f, 2.0); // shouldn't work
// func(1.0, 2.0f); // shouldn't work
func(1.0, 2.0); // should work, but doesn't
}
I used LLVM Clang version 3.0, which complained about the lines which
I'd intended to have work:
gen.c:12:13: error: controlling expression type 'float' not compatible
with any generic association type
func(1.0f, 2.0f);
~~~~~~~~~~~^~~~~
gen.c:10:20: note: expanded from:
double: _Generic((y), double: funcd) ) (x, y)
^
[And a similar error for the `func(1.0, 2.0)` line.]
First question: It seems that all "legs" of the selection expression
are evaluated; is that correct behavior?
Second question: Assuming that is correct, what might be the best way
to accomplish what I'm trying to? The best method I've come up with is
this:
#define func(x, y) _Generic((x), \
float: _Generic((y), float: funcf, double: 0 ), \
double: _Generic((y), double: funcd, float: 0 ) ) (x, y)
int main(void) {
func(1.0f, 2.0f);
func(1.0f, 2.0);
func(1.0, 2.0f);
func(1.0, 2.0);
}
This "works"; the correct forms compile and the incorrect ones cause
compile errors -- but they obscure the issue:
gen.c:13:2: error: called object type 'int' is not a function or
function pointer
func(1.0f, 2.0);
^~~~~~~~~~~~~~~
gen.c:10:53: note: expanded from:
double: _Generic((y), double: funcd, float: 0 ) ) (x, y)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^
Can this be improved, while working within what the Standard provides?