[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