Is it possible to implement static_if in C99?
#define STATIC_IF(COND, ...) \
if (COND) MACRO1(__VA_ARGS__); \
else MACRO2(__VA_ARGS__);
How can I properly implement STATIC_IF(…) in here? Depending on COND the arguments either should be passed to MACRO1 or MACRO2, but the arguments for both macros look differently. COND is statically testable, something like sizeof (…) > 42.
#if COND then #define STATIC_IF MACRO1 … wouldn't work for my use case.
I cannot use compiler specific solutions.
In your specific case (if I understand your comments correctly), yes, you can do this.
You can't pass sizeof to anything in the preprocessor because the preprocessor runs before type information is available. Luckily for you, you don't need sizeof to count the number of arguments in a statically-written list (X-Y alert!), so this is no obstacle.
Here's one possible implementation using the Order macro library:
#include <stdio.h>
#include <order/interpreter.h>
void oneArg(int a) {
printf("one arg: %d\n", a);
}
void twoArgs(int a, int b) {
printf("two args: %d %d\n", a, b);
}
void threeArgs(int a, int b, int c) {
printf("three args: %d %d %d\n", a, b, c);
}
#define ORDER_PP_DEF_8function_list \
ORDER_PP_CONST(("unused") \
(oneArg) \
(twoArgs) \
(threeArgs))
#define SelectFunction(...) ORDER_PP ( \
8seq_at(8tuple_size(8((__VA_ARGS__))), 8function_list) \
)
#define Overloaded(...) SelectFunction(__VA_ARGS__)(__VA_ARGS__)
int main(void) {
Overloaded(42);
Overloaded(42, 47);
Overloaded(42, 47, 64);
return 0;
}
(This simple case indexes a list by the number of arguments - probably not exactly what you want to do, but enough to get the idea. Order does provide a full range of complex, nonevaluating control structures - if, cond, match, etc. - for more complex decision-making.)
Order is pretty heavyweight: I assume you can do something similar with the much lighter and more realistically-portable P99 (not familiar with it). Order works very well with GCC and adequately well with Clang (Clang will choke on deep recursion or long loops); it is standard, but not all compilers are.
This is not possible, because a condition like sizeof(something)>42 is not static for the preprocessor. The preprocessor is purely textual (in principle, except for arithmetic). It does not know about C or types.
Notice that expression of the condition in #if is severely constrained.
However, you could use build tricks. For instance, you might have a standalone program like
// generate-sizeof.c
#include <stdio.h>
#include "foo-header.h"
int main(int argc, char**argv) {
const char* headername = NULL;
if (argc<2)
{ fprintf(stderr, "%s: missing header name\n", argv[0]);
exit(EXIT_FAILURE); };
headername = argv[1];
FILE *fh = fopen(headername, "w");
if (!fh) { perror(headername); exit(EXIT_FAILURE); };
fprintf(fp, "// generated file %s\n", headername);
fprintf(fp, "#define SIZEOF_charptr %d\n", (int) sizeof(char*));
fprintf(fp, "#define SIZEOF_Foo %d\n", (int) sizeof(Foo));
fclose (fp);
}
then have a rule like
generated-sizes.h : generate-sizeof foo-header.h
./generate-sizeof generated-sizes.h
in your Makefile etc etc...
So your build machinery will generate the appropriate headers.
Things become much tricker if you want to cross-compile!
Then you might have an #include "generated-sizes.h" in your header, and later code
#if SIZEOF_Foo > 42
#error cannot have such big Foo
#endif
I don't think so, not in the sense you mean.
But: I would just go ahead, and trust that an optimizing compiler notices that the condition is always true (or false) and does the right thing, i.e. optimizes out the test.
You might need to force some optimization to provoke the compiler into doing this.
If you can remove the restriction of having to stick to C99, there is a better solution to this problem built-in to the language since C11:
#include <stdio.h>
void f1(float x, double y, float * z) {
printf("inside f1\n");
}
void f2(int x, _Bool * y) {
printf("inside f2\n");
}
#define STATIC_IF(COND, ...) _Generic(&(int[(!!(COND))+1]){ 0 }, \
int(*)[2]: f1, \
int(*)[1]: f2) \
(__VA_ARGS__)
int main(void) {
float fl;
_Bool b;
STATIC_IF(sizeof(double) > 4, 0.0f, 1.0, &fl);
STATIC_IF(sizeof(double) > 128, 16, &b);
}
The _Generic operator performs a compile-time selection based on a type. Since it selects based on a type, it's also the only language-level expression that can accept conflicting types of "argument", since its very purpose is to resolve a value of the right type based on inputs.
This means you can easily use it to choose between your two functions with incompatible signatures, because it will completely ignore the type of the one that isn't chosen by matching the input; the arguments (applied to whichever function _Generic returns) will only be checked against the successful match.
Although _Generic is designed to dispatch on types, not values, any integer constant expression can be "turned into" a type by using it as the size of an array. So in the above macro we create an anonymous array (n.b. this is not a VLA), of count either 2 (for true) or 1 (for false) and dispatch against the type of the pointer to that array in order to resolve which of the two incompatible functions to use.
This will certainly reduce to nothing at runtime, since not only is the condition static, but the alternative "execution path" wouldn't even type check and thus can't have code generated for it in the first place.
Related
This questions is about my homework.
This topic is need to use like:
#define GENERIC_MAX(type)\
type type##_max(type x, type y)\
{\
return x > y ? x : y;\
}
The content of the question is to make this code run normally:
#include <stdio.h>
GenerateShowValueFunc(double)
GenerateShowValueFunc(int)
int main()
{
double i = 5.2;
int j = 3;
showValue_double(i);
showValue_int(j);
}
The result of the operation is like this:
i=5.2000
j=3
And this code is my current progress, but there are have problems:
#include <stdio.h>
#define printname(n) printf(#n);
#define GenerateShowValueFunc(type)\
type showValue_##type(type x)\
{\
printname(x);\
printf("=%d\n", x);\
return 0;\
}
GenerateShowValueFunc(double)
GenerateShowValueFunc(int)
int main()
{
double i = 5.2;
int j = 3;
showValue_double(i);
showValue_int(j);
}
I don’t know how to make the output change with the type, and I don’t know how to display the name of the variable. OAO
This original task description:
Please refer to ShowValue.c below:
#include <stdio.h>
GenerateShowValueFunc(double)
GenerateShowValueFunc(int)
int main()
{
double i = 5.2;
int j = 3;
showValue_double(i);
showValue_int(j);
}
Through [GenerateShowValueFunc(double)] and [GenerateShowValueFunc(int)] these two lines macro call, can help us to generated as [showValue_double( double )] and [showValue_int( int )] function, And in main() function called. The execution result of this program is as follows:
i=5.2000
j=3
Please insert the code that defines GenerateShowValueFunc macro into the appropriate place in the ShowValue.c program, so that this program can compile and run smoothly.
A quick & dirty solution would be:
type showValue_##type(type x)\
{\
const char* double_fmt = "=%f\n";\
const char* int_fmt = "=%d\n";\
printname(x);\
printf(type##_fmt, x);\
return 0;\
}
The compiler will optimize out the variable that isn't used, so it won't affect performance. But it might yield warnings "variable not used". You can add null statements like (void)double_fmt; to silence it.
Anyway, this is all very brittle and bug-prone, it was never recommended practice to write macros like these. And it is not how you do generic programming in modern C. You can teach your teacher how, by showing them the following example:
#include <stdio.h>
void double_show (double d)
{
printf("%f\n", d);
}
void int_show (int i)
{
printf("%d\n", i);
}
#define show(x) _Generic((x),\
double: double_show, \
int: int_show) (x) // the x here is the parameter passed to the function
int main()
{
double i = 5.2;
int j = 3;
show(i);
show(j);
}
This uses the modern C11/C17 standard _Generic keyword, which can check for types at compile-time. The macro picks the appropriate function to call and it is type safe. The caller doesn't need to worry which "show" function to call nor that they pass the correct type.
Without changing the shown C-code (i.e. only doing macros), which I consider a requirement, the following code has the required output:
#include <stdio.h>
#define showValue_double(input) \
showValueFunc_double(#input"=%.4f\n" , input)
#define showValue_int(input) \
showValueFunc_int(#input"=%d\n" , input)
#define GenerateShowValueFunc(type) \
void showValueFunc_##type(const char format[], type input)\
{\
printf(format, input); \
}
/* ... macro magic above; */
/* unchangeable code below ... */
GenerateShowValueFunc(double)
GenerateShowValueFunc(int)
int main()
{
double i = 5.2;
int j = 3;
showValue_double(i);
showValue_int(j);
}
Output:
i=5.2000
j=3
Note that I created something of a lookup-table for type-specific format specifiers. I.e. for each type to be supported you need to add a macro #define showValue_ .... This is also needed to get the name of the variable into the output.
This uses the fact that two "strings" are concatenated by C compilers, i.e. "A""B" is the same as "AB". Where "A" is the result of #input.
The rest, i.e. the required function definition is very similar to the teacher-provided example, using the ## operator.
Note, this is if the variable name has to correctly be mentioned in the output.
With out the i = things would be easier and would more elegantly use the generated functions WITHOUT having the called showValue_double(i); be explicit macros. I.e. the functions generated are 1:1 what is called from main(). I think that might be what is really asked. Let me know if you want that version.
I know the following is a very trivial example, but how would I convert the following into a single function call that uses the preprocessor ## 'glue' operator?
void print_string(char *s)
{
printf("%s\n", s);
}
void print_num(int n)
{
printf("%d\n", n);
}
int main(void)
{
print_string("Hello");
print_num(5);
}
The only thing I can thing of (which doesn't really simplify anything) is:
#define PRINT(type) print_ ## type
PRINT(string)("Hello");
PRINT(num)(4);
Or, is there a better way to use that?
You can make the identification to be the first function argument:
#define PRINT(type, value) print_ ## type(value)
PRINT(string, "Hello");
PRINT(num, 4);
But I see no value in that over just writing printf, as someone will have to learn to write num in case of int, he might as well learn to use %d anyway:
printf("%s\n", "Hello");
printf("%d\n", 4);
Type dispatch is not possible in pre-processor - it's not aware of types. In C11 there's _Generic that allows compiler to choose different function depending on type:
#define PRINT(value) _Generic((value), \
char *: print_string, \
int: print_int)(value)
PRINT("Hello");
PRINT(4);
By overloading the macro on each argument and applying such _Generic macro on each argument it's possible to build a replacement for C++ std::cout. So a little self-promotion: that's a topic I explored in yio library that allows just to do:
yprint("Hello ", 4, "\n");
Is there any way that I can discover the type of a variable automatically in C, either through some mechanism within the program itself, or--more likely--through a pre-compilation script that uses the compiler's passes up to the point where it has parsed the variables and assigned them their types? I'm looking for general suggestions about this. Below is more background about what I need and why.
I would like to change the semantics of the OpenMP reduction clause. At this point, it seems easiest simply to replace the clause in the source code (through a script) with a call to a function, and then I can define the function to implement the reduction semantics I want. For instance, my script would convert this
#pragma omp parallel for reduction(+:x)
into this:
my_reduction(PLUS, &x, sizeof(x));
#pragma omp parallel for
where, earlier, I have (say)
enum reduction_op {PLUS, MINUS, TIMES, AND,
OR, BIT_AND, BIT_OR, BIT_XOR, /* ... */};
And my_reduction has signature
void my_reduction(enum reduction_op op, void * var, size_t size);
Among other things, my_reduction would have to apply the addition operation to the reduction variable as the programmer had originally intended. But my function cannot know how to do this correctly. In particular, although it knows the kind of operation (PLUS), the location of the original variable (var), and the size of the variable's type, it does not know the variable's type itself. In particular, it does not know whether var has an integral or floating-point type. From a low-level POV, the addition operation for those two classes of types is completely different.
If only the nonstandard operator typeof, which GCC supports, would work the way sizeof works--returning some sort of type variable--I could solve this problem easily. But typeof is not really like sizeof: it can only be used, apparently, in l-value declarations.
Now, the compiler obviously does know the type of x before it finishes generating the executable code. This leads me to wonder whether I can somehow leverage GCC's parser, just to get x's type and pass it to my script, and then run GCC again, all the way, to compile my altered source code. It would then be simple enough to declare
enum var_type { INT8, UINT8, INT16, UINT16, /* ,..., */ FLOAT, DOUBLE};
void my_reduction(enum reduction_op op, void * var, enum var_type vtype);
And my_reduction can cast appropriately before dereferencing and applying the operator.
As you can see, I am trying to create a kind of "dispatching" mechanism in C. Why not just use C++ overloading? Because my project constrains me to work with legacy source code written in C. I can alter the code automatically with a script, but I cannot rewrite it into a different language.
Thanks!
C11 _Generic
Not a direct solution, but it does allow you to achieve the desired result if you are patient to code all types as in:
#include <assert.h>
#include <string.h>
#define typename(x) _Generic((x), \
int: "int", \
float: "float", \
default: "other")
int main(void) {
int i;
float f;
void* v;
assert(strcmp(typename(i), "int") == 0);
assert(strcmp(typename(f), "float") == 0);
assert(strcmp(typename(v), "other") == 0);
}
Compile and run with:
gcc -std=c11 a.c
./a.out
A good starting point with tons of types can be found in this answer.
Tested in Ubuntu 17.10, GCC 7.2.0. GCC only added support in 4.9.
You can use sizeof function to determine type , let the variable of unknown type be var.
then
if(sizeof(var)==sizeof(char))
printf("char");
else if(sizeof(var)==sizeof(int))
printf("int");
else if(sizeof(var)==sizeof(double))
printf("double");
Thou it will led to complications when two or more primary types might have same size .
C doesn't really have a way to perform this at pre-compile time, unless you write a flood of macros. I would not recommend the flood of macros approach, it would basically go like this:
void int_reduction (enum reduction_op op, void * var, size_t size);
#define reduction(type,op,var,size) type##_reduction(op, var, size)
...
reduction(int, PLUS, &x, sizeof(x)); // function call
Note that this is very bad practice and should only be used as last resort when maintaining poorly written legacy code, if even then. There is no type safety or other such guarantees with this approach.
A safer approach is to explicitly call int_reduction() from the caller, or to call a generic function which decides the type in runtime:
void reduction (enum type, enum reduction_op op, void * var, size_t size)
{
switch(type)
{
case INT_TYPE:
int_reduction(op, var, size);
break;
...
}
}
If int_reduction is inlined and various other optimizations are done, this runtime evaluation isn't necessarily that much slower than the obfuscated macros, but it is far safer.
GCC provides the typeof extension. It is not standard, but common enough (several other compilers, e.g. clang/llvm, have it).
You could perhaps consider customizing GCC by extending it with MELT (a domain specific language to extend GCC) to fit your purposes.
You could also consider customizing GCC with a plugin or a MELT extension for your needs. However, this requires understanding some of GCC internal representations (Gimple, Tree) which are complex (so will take you days of work at least).
But types are a compile-only thing in C. They are not reified.
In general it is not possible to identify what kind of data is in a given byte or sequence of bytes. For example, the 0 byte could be an empty string or the integer 0. the bit pattern for 99 could be that number, or the letter 'c'.
The following is a bit of hackery to turn an arbitrary sequence of bytes into a printable value. It works in most cases (but not for numbers that could also be characters). It is for the lcc compiler under Windows 7, with 32-bit ints, longs and 64-bit doubles.
char* OclAnyToString(void* x)
{ char* ss = (char*) x;
int ind = 0;
int* ix = (int*) x;
long* lx = (long*) x;
double* dx = (double*) x;
char* sbufi = (char*) calloc(21, sizeof(char));
char* sbufl = (char*) calloc(21, sizeof(char));
char* sbufd = (char*) calloc(21, sizeof(char));
if (ss[0] == '\0')
{ sprintf(sbufi, "%d", *ix);
sprintf(sbufd, "%f", *dx);
if (strcmp(sbufi,"0") == 0 &&
strcmp(sbufd,"0.000000") == 0)
{ return "0"; }
else if (strcmp(sbufd,"0.000000") != 0)
{ return sbufd; }
else
{ return sbufi; }
}
while (isprint(ss[ind]) && 0 < ss[ind] && ss[ind] < 128 && ind < 1024)
{ /* printf("%d\n", ss[ind]); */
ind++;
}
if (ss[ind] == '\0')
{ return (char*) x; }
sprintf(sbufi, "%d", *ix);
sprintf(sbufl, "%ld", *lx);
sprintf(sbufd, "%f", *dx);
if (strcmp(sbufd,"0.000000") != 0)
{ free(sbufi);
free(sbufl);
return sbufd;
}
if (strcmp(sbufi,sbufl) == 0)
{ free(sbufd);
free(sbufl);
return sbufi;
}
else
{ free(sbufd);
free(sbufi);
return sbufl;
}
}
I have a function that I need to macro'ize. The function contains temp variables and I can't remember if there are any rules about use of temporary variables in macro substitutions.
long fooAlloc(struct foo *f, long size)
{
long i1, i2;
double *data[7];
/* do something */
return 42;
}
MACRO Form:
#define ALLOC_FOO(f, size) \
{\
long i1, i2;\
double *data[7];\
\
/* do something */ \
}
Is this ok? (i.e. no nasty side effect - other than the usual ones : not "type safe" etc). BTW, I know "macros are evil" - I simply have to use it in this case - not much choice.
There are only two conditions under which it works in any "reasonable" way.
The macro doesn't have a return statement. You can use the do while trick.
#define macro(x) do { int y = x; func(&y); } while (0)
You only target GCC.
#define min(x,y) ({ int _x = (x), _y = (y); _x < _y ? _x : _y; })
It would help if you explain why you have to use a macro (does your office have "macro mondays" or something?). Otherwise we can't really help.
C macros are only (relatively simple) textual substitutions.
So the question you are maybe asking is: can I create blocks (also called compound statements) in a function like in the example below?
void foo(void)
{
int a = 42;
{
int b = 42;
{
int c = 42;
}
}
}
and the answer is yes.
Now as #DietrichEpp mentioned it in his answer, if the macro is a compound statement like in your example, it is a good practice to enclose the macro statements with do { ... } while (0) rather than just { ... }. The link below explains what situation the do { ... } while (0) in a macro tries to prevent:
http://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html
Also when you write a function-like macro always ask yourself if you have a real advantage of doing so because most often writing a function instead is better.
First, I strongly recommend inline functions. There are very few things macros can do and they can't, and they're much more likely to do what you expect.
One pitfall of macros, which I didn't see in other answers, is shadowing of variable names.
Suppose you defined:
#define A(x) { int temp = x*2; printf("%d\n", temp); }
And someone used it this way:
int temp = 3;
A(temp);
After preprocessing, the code is:
int temp = 3;
{ int temp = temp*2; printf("%d\n", temp); }
This doesn't work, because the internal temp shadows the external.
The common solution is to call the variable __temp, assuming nobody will define a variable using this name (which is a strange assumption, given that you just did it).
This is mostly OK, except that macros are usually enclosed with do { ... } while(0) (take a look at this question for explanations):
#define ALLOC_FOO(f, size) \
do { \
long i1, i2;\
double *data[7];\
/* do something */ \
} while(0)
Also, as far as your original fooAlloc function returns long you have to change your macro to store the result somehow else. Or, if you use GCC, you can try compound statement extension:
#define ALLOC_FOO(f, size) \
({ \
long i1, i2;\
double *data[7];\
/* do something */ \
result; \
})
Finally you should care of possible side effects of expanding macro argument. The usual pattern is defining a temporary variable for each argument inside a block and using them instead:
#define ALLOC_FOO(f, size) \
({ \
typeof(f) _f = (f);\
typeof(size) _size = (size);\
long i1, i2;\
double *data[7];\
/* do something */ \
result; \
})
Eldar's answer shows you most of the pitfalls of macro programming and some useful (but non standard) gcc extension.
If you want to stick to the standard, a combination of macros (for genericity) and inline functions (for the local variables) can be useful.
inline
long fooAlloc(void *f, size_t size)
{
size_t i1, i2;
double *data[7];
/* do something */
return 42;
}
#define ALLOC_FOO(T) fooAlloc(malloc(sizeof(T)), sizeof(T))
In such a case using sizeof only evaluates the expression for the type at compile time and not for its value, so this wouldn't evaluate F twice.
BTW, "sizes" should usually be typed with size_t and not with long or similar.
Edit: As to Jonathan's question about inline functions, I've written up something about the inline model of C99, here.
Yes it should work as you use a block structure and the temp variables are declared in the inner scope of this block.
Note the last \ after the } is redundant.
A not perfect solution: (does not work with recursive macros, for example multiple loops inside each other)
#define JOIN_(X,Y) X##Y
#define JOIN(X,Y) JOIN_(X,Y)
#define TMP JOIN(tmp,__LINE__)
#define switch(x,y) int TMP = x; x=y;y=TMP
int main(){
int x = 5,y=6;
switch(x,y);
switch(x,y);
}
will become after running the preprocessor:
int main(){
int x=5,y=6;
int tmp9 = x; x=y; y=tmp9;
int tmp10 = x; x=y; y=tmp10;
}
They can. They often shouldn't.
Why does this function need to be a macro? Could you inline it instead?
If you're using c++ use inline, or use -o3 with gcc it will inline all functions for you.
I still don't understand why you need to macroize this function.
Is it possible to typecheck arguments to a #define macro? For example:
typedef enum
{
REG16_A,
REG16_B,
REG16_C
}REG16;
#define read_16(reg16) read_register_16u(reg16); \
assert(typeof(reg16)==typeof(REG16));
The above code doesn't seem to work. What am I doing wrong?
BTW, I am using gcc, and I can guarantee that I will always be using gcc in this project. The code does not need to be portable.
gcc supports typeof
e.g. a typesafe min macro taken from the linux kernel
#define min(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })
but it doesn't allow you to compare two types. Note though the pointer comparison which Will generate a warning - you can do a typecheck like this (also from the linux kernel)
#define typecheck(type,x) \
({ type __dummy; \
typeof(x) __dummy2; \
(void)(&__dummy == &__dummy2); \
1; \
})
Presumably you could do something similar - i.e. compare pointers to the arguments.
The typechecking in C is a bit loose for integer-related types; but you can trick the compiler by using the fact that most pointer types are incompatible.
So
#define CHECK_TYPE(var,type) { __typeof(var) *__tmp; __tmp = (type *)NULL; }
This will give a warning, "assignment from incompatible pointer type" if the types aren't the same. For example
typedef enum { A1,B1,C1 } my_enum_t;
int main (int argc, char *argv) {
my_enum_t x;
int y;
CHECK_TYPE(x,my_enum_t); // passes silently
CHECK_TYPE(y,my_enum_t); // assignment from incompatible pointer type
}
I'm sure that there's some way to get a compiler error for this.
This is an old question, But I believe I have a general answer that according to Compiler Explorer apears to work on MSVC, gcc and clang.
#define CHECK_TYPE(type,var) { typedef void (*type_t)(type); type_t tmp = (type_t)0; if(0) tmp(var);}
In each case the compiler generates a useful error message if the type is incompatible. This is because it imposes the same type checking rules used for function parameters.
It can even be used multiple times within the same scope without issue. This part surprises me somewhat. (I thought I would have to utilize "__LINE__" to get this behavior)
Below is the complete test I ran, commented out lines all generate errors.
#include <stdio.h>
#define CHECK_TYPE(type,var) { typedef void (*type_t)(type); type_t tmp = (type_t)0; if(0) tmp(var);}
typedef struct test_struct
{
char data;
} test_t;
typedef struct test2_struct
{
char data;
} test2_t;
typedef enum states
{
STATE0,
STATE1
} states_t;
int main(int argc, char ** argv)
{
test_t * var = NULL;
int i;
states_t s;
float f;
CHECK_TYPE(void *, var); //will pass for any pointer type
CHECK_TYPE(test_t *, var);
//CHECK_TYPE(int, var);
//CHECK_TYPE(int *, var);
//CHECK_TYPE(test2_t, var);
//CHECK_TYPE(test2_t *, var);
//CHECK_TYPE(states_t, var);
CHECK_TYPE(int, i);
//CHECK_TYPE(void *, i);
CHECK_TYPE(int, s); //int can be implicitly used instead of enum
//CHECK_TYPE(void *, s);
CHECK_TYPE(float, s); //MSVC warning only, gcc and clang allow promotion
//CHECK_TYPE(float *, s);
CHECK_TYPE(float, f);
//CHECK_TYPE(states_t, f);
printf("hello world\r\n");
}
In each case the compiler with -O1 and above did remove all traces of the macro in the resulting code.
With -O0 MSVC left the call to the function at zero in place, but it was rapped in an unconditional jump which means this shouldn't be a concern. gcc and clang with -O0 both remove everything except for the stack initialization of the tmp variable to zero.
No, macros can't provide you any typechecking. But, after all, why macro? You can write a static inline function which (probably) will be inlined by the compiler - and here you will have type checking.
static inline void read_16(REG16 reg16) {
read_register_16u(reg16);
}
Building upon Zachary Vander Klippe's answer, we might even go a step further (in a portable way, even though that wasn't a requirement) and additionally make sure that the size of the passed-in type matches the size of the passed-in variable using the "negative array length" trick that was commonly used for implementing static assertions in C (prior to C11, of course, which does provide the new _Static_assert keyword).
As an added benefit, let's throw in some const compatibility.
#define CHECK_TYPE(type,var) \
do {\
typedef void (*type_t) (const type);\
type_t tmp = (type_t)(NULL);\
typedef char sizes[((sizeof (type) == sizeof (var)) * 2) - 1];\
if (0) {\
const sizes tmp2;\
(void) tmp2;\
tmp (var);\
}\
} while (0)
Referencing the new typedef as a variable named tmp2 (and, additionally, referencing this variable, too) is just a method to make sure that we don't generate more warnings than necessary, c.f., -Wunused-local-typedefs and the like. We could have used __attribute__ ((unused)) instead, but that is non-portable.
This will work around the integer promotion "issue" in the original example.
Example in the same spirit, failing statements are commented out:
#include <stdio.h>
#include <stdlib.h>
#define CHECK_TYPE(type,var) \
do {\
typedef void (*type_t) (const type);\
type_t tmp = (type_t)(NULL);\
typedef char sizes[((sizeof (type) == sizeof (var)) * 2) - 1];\
if (0) {\
const sizes tmp2;\
(void) tmp2;\
tmp (var);\
}\
} while (0)
int main (int argc, char **argv) {
long long int ll;
char c;
//CHECK_TYPE(char, ll);
//CHECK_TYPE(long long int, c);
printf("hello world\n");
return EXIT_SUCCESS);
}
Naturally, even that approach isn't able to catch all issues. For instance, checking signedness is difficult and often relies on tricks assuming that a specific complement variant (e.g., two's complement) is being used, so cannot be done generically. Even less so if the type can be a structure.
To continue the idea of ulidtko, take an inline function and have it return something:
inline
bool isREG16(REG16 x) {
return true;
}
With such as thing you can do compile time assertions:
typedef char testit[sizeof(isREG16(yourVariable))];
No. Macros in C are inherently type-unsafe and trying to check for types in C is fraught with problems.
First, macros are expanded by textual substitution in a phase of compilation where no type information is available. For that reason, it is utterly impossible for the compiler to check the type of the arguments when it does macro expansion.
Secondly, when you try to perform the check in the expanded code, like the assert in the question, your check is deferred to runtime and will also trigger on seemingly harmless constructs like
a = read_16(REG16_A);
because the enumerators (REG16_A, REG16_B and REG16_C) are of type int and not of type REG16.
If you want type safety, your best bet is to use a function. If your compiler supports it, you can declare the function inline, so the compiler knows you want to avoid the function-call overhead wherever possible.