[Cfp-interest] C1x's _Generic and totalorder()
Joel C. Salomon
joelcsalomon at gmail.com
Tue Jan 17 12:31:27 PST 2012
On 01/17/2012 01:58 PM, Fred J. Tydeman wrote in thread "type-generic math":
> On Tue, 17 Jan 2012 10:13:37 -0800 Jim Thomas wrote:
>> and the total order functions (F.10.12)
>
> Why are they excluded?
Because they're more than a little difficult to implement.
We'd last discussed this subject in July, and I've only recently had
LLVM's clang 3.0 compiler (which implements _Generic) to play with, so
I'll recap. (I've also been discussing this on <comp.lang.c>, in thread
"Nested _Generic selections".)
Without loss of generality, 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.
The naïve implementation of the macro would look like this:
void funcf(float x, float y) {/* ... */}
void funcd(double x, double 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
}
The trouble is that clang considers all the legs of the
generic-selection; e.g., when expanding the first (correct!) line, it
fails, complaining:
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)
(I cannot tell, by reading the Standard, n1404, or n1441, whether this
is correct behavior on the part of clang.)
My next effort:
#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)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^
Shao Miller suggested something like this (edited for style):
#define STATIC_ASSERT_EXPR(expr, name) \
(sizeof(struct{char name[(expr) ? 1 : -1];}))
#define IS_FLOAT(x) _Generic((x), float: 1, default: 0)
#define BOTH_FLOATS(x, y) (IS_FLOAT(x) && IS_FLOAT(y))
#define IS_DOUBLE(x) _Generic((x), double: 1, default: 0)
#define BOTH_DOUBLES(x, y) (IS_DOUBLE(x) && IS_DOUBLE(y))
#define TYPES_MATCH(x, y) \
(BOTH_FLOATS((x), (y)) || BOTH_DOUBLES((x), (y)))
#define func(x, y) ( \
(void) STATIC_ASSERT_EXPR( \
TYPES_MATCH((x), (y)), \
types_of_arguments_to_func_must_match \
), \
_Generic((x), \
float: funcf, \
double: funcd \
)((x), (y)) \
)
The error message produced by this code is still obscure, but at least
contains the correct information:
gen.c:34:2: error: 'types_of_arguments_to_func_must_match' declared as
an array with a negative size
func(1.0f, 2.0);
^~~~~~~~~~~~~~~
gen.c:22:9: note: expanded from:
(void) STATIC_ASSERT_EXPR( \
^
gen.c:10:27: note: expanded from:
(sizeof(struct{char name[(expr) ? 1 : -1];}))
(Suggestions to use C11's _Static_assert are no help because it isn't
usable in an expression.)
So, given the more-than-awkward implementation, and the less-than-ideal
error messages, is it useful to mandate that the total order functions
be wrapped by such a macro?
--Joel
More information about the Cfp-interest
mailing list