13

I have been thinking about ways to validate types in C macros and so far the best way that I have come up with is this:

#define ASSERT_PTYPE(TYPE, VALUE) (0 && (*(int (*)(TYPE*))0)(VALUE))

This obviously expects a type name and a pointer to that type. A similar ASSERT_TYPE macro can be made as well. This seems to work quite well with GCC. It even gives a very helpful error message in the case that the types do not match. The problems are that I am not completely certain that this is valid C or the best way for that matter.

As I understand it the standard says that you can cast a function pointer, but the result of calling the cast function pointer is undefined. In this case it is impossible for the function to be called at runtime. Is that good enough or does the standard mean that you cannot even write code that cannot be called that calls the cast function?

a3f
  • 8,517
  • 1
  • 41
  • 46
ltc
  • 3,313
  • 1
  • 28
  • 26
  • Nice question, here's a possible C++ counterpart http://stackoverflow.com/questions/5237163/how-does-the-following-code-work – JoeSlav Mar 18 '11 at 09:01
  • The question is... what cases in C exist where the programmer does not already know the type passed to that macro? What's the code good for? Also, none of this solves any issues related to the many implicit type conversions in the C language. As far as C is concerned, signed and unsigned are compatible, for example. – Lundin Mar 18 '11 at 12:43

1 Answers1

12

With C99 and compound literals you can do something like

#define ASSERT_TYPE(TYPE, VALUE) ((TYPE){ 0 } = (VALUE))

This ensures that VALUE is assignment compatible to TYPE. The expression returns an rvalue because of the assignment.

Compound literals work in function scope as well as in file scope and any decent compiler should optimize the extra object that is created out of the way.

Addition: TYPE in that macro can be any valid type name, e.g pointer double*, struct or union struct toto, besides arrays. Array type such as double[4] wouldn't work because of the assignment. Use pointer to array double(*)[4] instead, e.g as in

double A[4];
(*ASSERT_TYPE(double(*)[4], &A))

where the second line again is a lvalue of type double[4] that is compile time checked for that property.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • Shouldn't it read `(TYPE*){ 0 }` because he wants to assert that `VALUE` has type _pointer_ to `TYPE` (the macro's name is `ASSERT_PTYPE` not `ASSERT_TYPE`)? – Curd Mar 18 '11 at 09:48
  • @Curd, no, the proposed solution works for any type, not only pointer types. I renamed to `ASSERT_TYPE` to make that clearer. To use it for a pointer type you'd just to have such a pointer type as a first argument, e.g something like `double*` or whatever. – Jens Gustedt Mar 18 '11 at 12:27
  • This is exactly where I was trying to get. The only negative here is that the error message a little less to the point then the one that you get from the fake function call. Also, in he usage example I think that you mean the macro returns an rvalue not an lvalue right? – ltc Mar 18 '11 at 20:34
  • @ltc, no I mean an lvalue. `ASSERT_TYPE` returns an rvalue, but which is a pointer to an array. Dereferencing it gives you the original array `A`. You could e.g again take a reference of it, which you couldn't if it were an rvalue. – Jens Gustedt Mar 18 '11 at 22:11