How to check `typeof` for void value at compile time?

let's say that I want to have a C macro that works on any type. I use the GCC compiler (> = 4.6) and can use the GNU99 macros.

//code... any_type_t *retVal = function_that_runs_very_long_time(a, b, &&c, **d, &e, *f); //other code... 

using a macro for TIMER might look like this, for example

 //code... any_type_t *retVal = TIMER( function_that_runs_very_long_time(a, b, &&c, **d, &e, *f), "TIMING FOR VALUE <%d, %d>", a, b ); //other code... 

Thus, TIMER should return the value of the given function and the duration of its run. There is a problem with functions that have a void return type.

I can obviously have two macros, such as TIMER_TYPE and TIMER_VOID, but I want to use one function from one to time with any return value.

Thank you for your suggestions.


Edited example of this macro TIMER

 #define TIMER(expr, fmt_msg, ...) \ ({ \ struct timeval before, after; \ uint64_t time_span; \ int time_span_sec, time_span_usec; \ gettimeofday(&before, NULL); \ typeof(expr) _timer_expr__ = (expr); \ // <- static if? gettimeofday(&after, NULL); \ time_span = (after.tv_sec * 1000000 + after.tv_usec) \ - (before.tv_sec * 1000000 + before.tv_usec); \ time_span_sec = time_span / 1000000; \ time_span_usec = time_span % 1000000; \ TRACE(fmt_msg "\n%s : %d.%d seconds", \ #expr, time_span_sec, time_span_usec, ...); \ _timer_expr__; \ }) 
+8
c types c-preprocessor void compile-time
source share
3 answers

What an interesting question, really!

After several experiments, I found a solution that uses the __builtin_types_compatible_p and __builtin_choose_expr properties of the GCC.

__builtin_types_compatible_p

GCC Guidelines:

Built-in Function: int __builtin_types_compatible_p (type1, type2)

You can use the __builtin_types_compatible_p built-in function to determine if the two types are the same.

This built-in function returns 1 if unqualified versions of types type1 and type2 (which are types, not expressions) are compatible, 0 otherwise. The result of this built-in function can be used in integer constant expressions.

This built-in function ignores top-level qualifiers (e.g. const , volatile ). For example, int equivalent to const int .

So here is how we can test void ness.

 #define __type_is_void(expr) __builtin_types_compatible_p(typeof(expr), void) 

__builtin_choose_expr

Built-in Function: type __builtin_choose_expr (const_exp, exp1, exp2)

You can use the __builtin_choose_expr built-in function to evaluate the code based on the value of a constant expression. This built-in function returns exp1 if const_exp , which is an integer constant expression, is nonzero. Otherwise, it returns exp2 .

? : this built-in function similar to the operator ? : ? : in C, except that the returned expression has its own type, which is not changed by the rules of promotion. In addition, the built-in function does not evaluate an expression that is not selected . For example, if const_exp is true, exp2 not evaluated, even if it has side effects.

If exp1 returned, the return type is the same as exp1 . Similarly, if exp2 returned, its return type will be the same as exp2 .

So __builtin_choose_expr intrinsic is something like a “static switch” evaluated at compile time.

Training

I am not inserting your TIMER macro here, but I assume that it can break it into two versions: one for void expr and one for the rest. Here are just the butts that evaluate the expression and give the result of the same type.

 #define __DO(expr) \ ({ typeof(expr) __ret; __ret = (expr); __ret; }) #define __DO_VOID(expr) \ (void) (expr) 

Naive decision

Now we can statically switch between the two implementations, depending on the actual type of expression. But in fact, the naive solution does not work, see below.

 #define DO(expr) \ __builtin_choose_expr(__type_is_void(expr), \ __DO_VOID(expr), \ __DO(expr)) # won't work 

Attempting to compile this code that passes the void expression gives the following error:

 test.c:28:9: error: variable or field '__ret' declared void test.c:28:9: error: void value not ignored as it ought to be 

Although __DO_VOID selected, __DO generates errors. This behavior is described in the manual:

... an unused expression ( exp1 or exp2 depending on the value of const_exp ) can still generate syntax errors. This may change in future versions.

Working solution

The trick is to replace the original void expr with some non-empty value in order to be able to compile the __DO case (which in any case is dead code when expr invalid).

 #define __expr_or_zero(expr) __builtin_choose_expr(__type_is_void(expr), 0, (expr)) #define DO(expr) \ __builtin_choose_expr(__type_is_void(expr), \ __DO_VOID(expr), \ __DO(__expr_or_zero(expr))) # works fine! 

What is it! Here is the complete source code on Ideone: http://ideone.com/EFy4pE

+10
source share

can you accept the answer "this is really impossible"?

not the part about returning from a macro. but the part is about conditional testing expr for the return type.

you ask for something like the following:

let's say instead of some magic check called "is_expr_type_void (expr)", instead you just pass 1 or 0 during the call to indicate is_void or! is_void in the following version of your macro:

 #define TIMER(is_void, expr, fmt_msg, ...) \ ({ \ struct timeval before, after; \ uint64_t time_span; \ int time_span_sec, time_span_usec; \ gettimeofday(&before, NULL); \ if (is_void) \ (expr) \ else \ typeof(expr) _timer_expr__ = (expr); \ // <- static if? gettimeofday(&after, NULL); \ time_span = (after.tv_sec * 1000000 + after.tv_usec) \ - (before.tv_sec * 1000000 + before.tv_usec); \ time_span_sec = time_span / 1000000; \ time_span_usec = time_span % 1000000; \ TRACE(fmt_msg "\n%s : %d.%d seconds", \ #expr, time_span_sec, time_span_usec, ...); \ if (!is_void) \ _timer_expr__; \ }) 

it just can't work. the preprocessor will create code for this if-else conditional in all cases, both for void and non-void function calls. and both parties would compile a fine for non-empty functions. but the compiler always chokes on the "else" part of the conditional expression when TIMER is called with the void function ... even though the code will never be called.

(now, if there was a really smart compiler that could identify that it would be dead code and dead-strip it before marking it as a compile-time error, you would be lucky! but I don't think gcc 4.6 is smart .. .)

this leaves you with what would be the preferred #if (is_void) conditional option inside #define. but this is simply not allowed. since this answer indicates an attempt to answer a similar question about conditional preprocessing, the preprocessor is not Turing .

so ... despite your desire to have one macro, I think your easiest answer is to create one for void functions and one for functions with return values.

+2
source share

If you really need to return from the macro, use the built-in function.

0
source share

All Articles