Related
I found the code below in a project and I'm having problems understanding why GCC doesn't complaint since dlc is called before it has been defined.
#define CAN_SET_DLC(dlc) (CANCDMOB |= (dlc))
typedef struct
{
uint8_t mob_n;
uint8_t handle;
long id;
long id_msk;
uint8_t dlc;
uint8_t data_pt[8];
uint8_t status;
} can_msg;
¿The variable type of dlc should also be defined?:
define CAN_SET_DLC(uint8_t dlc) (CANCDMOB |= (dlc))
The preprocessor has no knowledge of variables or any language construct. It is just a token processor.
In #define a(b) ( b + 1 ) this means that anytime the preprocessor encounters a(foo) in the source text, it will replace it with ( foo + 1 ). Then the compiler will check whether it is correct C.
This is the normal way to define macro with arguments: https://gcc.gnu.org/onlinedocs/cpp/Macro-Arguments.html#Macro-Arguments
Macros just replace text basically so you have to take care about variable type.
No, macros only substitute text (more precisely tokens) and it is done before actual C code compilation. The preprocessor does not know anything about C language.
The preprocessed source code then is compiled by the C compiler.
#define CAN_SET_DLC(dlc) ((DLC) |= CANCDMOB)
#define CANCMOB 0x34
int foo(uint8_t z)
{
CAN_SET_DLC(z);
return z;
}
will result in
int foo(uint8_t z)
{
((z) |= 0x34);
return z;
}
I need to write a C macro that checks to ensure all parameters passed to it are unsigned and of the same integer type. Ex: all input params are uint8_t, or all uint16_t, or all uint32_t, or all uint64_t.
Here is how this type of checking can be done in C++: Use static_assert to check types passed to macro
Does something similar exist in C, even if only by way of a gcc extension?
Note that static asserts are available in gcc via _Static_assert. (See my answer here: Static assert in C).
This fails to work:
int a = 1;
int b = 2;
_Static_assert(__typeof__ a == __typeof__ b, "types don't match");
Error:
main.c: In function ‘main’:
main.c:23:20: error: expected expression before ‘__typeof__’
_Static_assert(__typeof__ a == __typeof__ b, "types don't match");
UPDATE:
Here's precisely how to do what I want in C++ (using a function template, static_assert, and the <type_traits> header file). I needed to learn this anyway, for comparison purposes, so I just did. Run this code for yourself here: https://onlinegdb.com/r1k-L3HSL.
#include <stdint.h>
#include <stdio.h>
#include <type_traits> // std::is_same()
// Templates: https://www.tutorialspoint.com/cplusplus/cpp_templates.htm
// Goal: test the inputs to a "C macro" (Templated function in this case in C++) to ensure
// they are 1) all the same type, and 2) an unsigned integer type
// 1. This template forces all input parameters to be of the *exact same type*, even
// though that type isn't fixed to one type! This is because all 4 inputs to test_func()
// are of type `T`.
template <typename T>
void test_func(T a, T b, T c, T d)
{
printf("test_func: a = %u; b = %u; c = %u; d = %u\n", a, b, c, d);
// 2. The 2nd half of the check:
// check to see if the type being passed in is uint8_t OR uint16_t OR uint32_t OR uint64_t!
static_assert(std::is_same<decltype(a), uint8_t>::value ||
std::is_same<decltype(a), uint16_t>::value ||
std::is_same<decltype(a), uint32_t>::value ||
std::is_same<decltype(a), uint64_t>::value,
"This code expects the type to be an unsigned integer type\n"
"only (uint8_t, uint16_t, uint32_t, or uint64_t).");
// EVEN BETTER, DO THIS FOR THE static_assert INSTEAD!
// IE: USE THE TEMPLATE TYPE `T` DIRECTLY!
static_assert(std::is_same<T, uint8_t>::value ||
std::is_same<T, uint16_t>::value ||
std::is_same<T, uint32_t>::value ||
std::is_same<T, uint64_t>::value,
"This code expects the type to be an unsigned integer type\n"
"only (uint8_t, uint16_t, uint32_t, or uint64_t).");
}
int main()
{
printf("Begin\n");
// TEST A: This FAILS the static assert since they aren't unsigned
int i1 = 10;
test_func(i1, i1, i1, i1);
// TEST B: This FAILS to find a valid function from the template since
// they aren't all the same type
uint8_t i2 = 11;
uint8_t i3 = 12;
uint32_t i4 = 13;
uint32_t i5 = 14;
test_func(i2, i3, i4, i5);
// TEST C: this works!
uint16_t i6 = 15;
uint16_t i7 = 16;
uint16_t i8 = 17;
uint16_t i9 = 18;
test_func(i6, i7, i8, i9);
return 0;
}
With just TEST A uncommented, you get this failure in the static assert since the inputs aren't unsigned:
main.cpp: In instantiation of ‘void test_func(T, T, T, T) [with T = int]’:
<span class="error_line" onclick="ide.gotoLine('main.cpp',46)">main.cpp:46:29</span>: required from here
main.cpp:32:5: error: static assertion failed: This code expects the type to be an unsigned integer type
only (uint8_t, uint16_t, uint32_t, or uint64_t).
static_assert(std::is_same<decltype(a), uint8_t>::value ||
^~~~~~~~~~~~~
with just TEST B uncommented, you get this failure to find a valid function from the template since the template expects all inputs to be the same type T:
main.cpp: In function ‘int main()’:
main.cpp:54:29: error: no matching function for call to ‘test_func(uint8_t&, uint8_t&, uint32_t&, uint32_t&)’
test_func(i2, i3, i4, i5);
^
main.cpp:26:6: note: candidate: template void test_func(T, T, T, T)
void test_func(T a, T b, T c, T d)
^~~~~~~~~
main.cpp:26:6: note: template argument deduction/substitution failed:
main.cpp:54:29: note: deduced conflicting types for parameter ‘T’ (‘unsigned char’ and ‘unsigned int’)
test_func(i2, i3, i4, i5);
^
And with just TEST C uncommented, it passes and looks like this!
Begin
test_func: a = 15; b = 16; c = 17; d = 18
References:
http://www.cplusplus.com/reference/type_traits/is_same/
https://en.cppreference.com/w/cpp/types/is_same
https://en.cppreference.com/w/cpp/language/decltype
How do I restrict a template class to certain built-in types?
Related:
Use static_assert to check types passed to macro [my own answer]
Static assert in C [my own answer]
If the most important aspect here is that you want it to fail to compile if a and b are different types, you can make use of C11's _Generic along with GCC's __typeof__ extension to manage this.
A generic example:
#include <stdio.h>
#define TYPE_ASSERT(X,Y) _Generic ((Y), \
__typeof__(X): _Generic ((X), \
__typeof__(Y): (void)NULL \
) \
)
int main(void)
{
int a = 1;
int b = 2;
TYPE_ASSERT(a,b);
printf("a = %d, b = %d\n", a, b);
}
Now if we try to compile this code, it will compile fine and everybody is happy.
If we change the type of b to unsigned int, however, it will fail to compile.
This works because _Generic selection uses the type of a controlling expression ((Y) in this case) to select a rule to follow and insert code corresponding to the rule. In this case, we only provided a rule for __typeof__(X), thus if (X) is not a compatible type for (Y), there is no suitable rule to select and therefore cannot compile. To handle arrays, which have a controlling expression that will decay to a pointer, I added another _Generic that goes the other way ensuring they must both be compatible with one another rather than accepting one-way compatibility. And since--as far as I particularly cared--we only wanted to make sure it would fail to compile on a mismatch, rather than execute something particular upon a match, I gave the corresponding rule the task of doing nothing: (void)NULL
There is a corner case where this technique stumbles: _Generic does not handle Variably Modifiable types since it is handled at compile time. So if you attempt to do this with a Variable Length Array, it will fail to compile.
To handle your specific use-case for fixed-width unsigned types, we can modify the nested _Generic to handle that rather than handling the pecularities of an array:
#define TYPE_ASSERT(X,Y) _Generic ((Y), \
__typeof__(X): _Generic ((Y), \
uint8_t: (void)NULL, \
uint16_t: (void)NULL, \
uint32_t: (void)NULL, \
uint64_t: (void)NULL \
) \
)
Example GCC error when passing non-compatible types:
main.c: In function 'main':
main.c:7:34: error: '_Generic' selector of type 'signed char' is not compatible with any association
7 | __typeof__(X): _Generic ((Y), \
| ^
It is worth mentioning that __typeof__, being a GCC extension, will not be a solution that is portable to all compilers. It does seem to work with Clang, though, so that's another major compiler supporting it.
What you want is doable in standard C11, no extensions or GCC required.
We'll build up to the final answer, so all can follow.
According to the C11 standard [6.7.10], static_assert-declaration: _Static_assert( constant-expression , string-literal ) is a Declaration. Thus if we are going to use a macro, we had best provide a scope for a declaration, to keep things tidy. Typically of the usual form:
#define MY_AMAZING_MACRO() do {_Static_assert(...some magic...);} while(0)
Next, so that our _Static_assert within the macro at least repeats via stdio the actual issue if the assert fails, well use familiar stringification setup:
#define STATIC_ASSERT_H(x) _Static_assert(x, #x)
#define STATIC_ASSERT(x) STATIC_ASSERT_H(x)
Next, we'll use C11's Generic selection feature to declare a macro that returns a constant 1 if the object is of the type we're looking for, and zero otherwise:
#define OBJ_IS_OF_TYPE(Type, Obj) _Generic(Obj, Type: 1, default: 0)
Next we''l make a macro to test if all four of your inputs are of the same type:
#define ALL_OBJS_ARE_OF_TYPE(Type, Obj_0, Obj_1, Obj_2, Obj_3) \
(OBJ_IS_OF_TYPE(Type, Obj_0) && \
OBJ_IS_OF_TYPE(Type, Obj_1) && \
OBJ_IS_OF_TYPE(Type, Obj_2) && \
OBJ_IS_OF_TYPE(Type, Obj_3))
Next, using the above, well make a macro to test if all four of your inputs are further one of the four types:
#define IS_ACCEPTABLE(Type_0, Type_1, Type_2, Type_3, Obj_0, Obj_1, Obj_2, Obj_3) \
(ALL_OBJS_ARE_OF_TYPE(Type_0, Obj_0, Obj_1, Obj_2, Obj_3) || \
ALL_OBJS_ARE_OF_TYPE(Type_1, Obj_0, Obj_1, Obj_2, Obj_3) || \
ALL_OBJS_ARE_OF_TYPE(Type_2, Obj_0, Obj_1, Obj_2, Obj_3) || \
ALL_OBJS_ARE_OF_TYPE(Type_3, Obj_0, Obj_1, Obj_2, Obj_3))
And FINALLY, putting it all together:
#define TEST_FUNC(a,b,c,d) \
do \
{ \
STATIC_ASSERT(IS_ACCEPTABLE(uint8_t, uint16_t, uint32_t, uint64_t, \
a, b, c, d)); \
} while(0)
Of course, you could separate the above into more distinct, individual STATIC_ASSERTs, as you wish, if you want more verbose error output if any of the _Static_asserts fail.
uint32_t fail_count = 0;
...
if(is_failed)
if(fail_count < UINT32_MAX - 1 )
++fail_count;
It works fine, but this code is fragile. Tomorrow, I may change the type of fail_count from uint32_t to int32_t and I forget to update UINT32_MAX.
Is there any way to assert fail_count is a uint32_t at the function where I have written my ifs?
P.S. 1- I know it is easy in C++ but I'm looking for a C way.
P.S. 2- I prefer to use two asserts than relying on the compiler warnings. Checking the number size via sizeof should work but is there any way to distinguish if type is unsigned?
As of C11, you can use a generic selection macro to produce a result based on the type of an expression. You can use the result in a static assertion:
#define IS_UINT32(N) _Generic((N), \
uint32_t: 1, \
default: 0 \
)
int main(void) {
uint32_t fail_count = 0;
_Static_assert(IS_UINT32(fail_count), "wrong type for fail_count");
}
You could of course use the result in a regular assert(), but _Static_assert will fail at compile time.
A better approach could be dispatching the comparison based on type, again using generic selection:
#include <limits.h>
#include <stdint.h>
#define UNDER_LIMIT(N) ((N) < _Generic((N), \
int32_t: INT32_MAX, \
uint32_t: UINT32_MAX \
) -1)
int main(void) {
int32_t fail_count = 0;
if (UNDER_LIMIT(fail_count)) {
++fail_count;
}
}
As you mentioned GCC, you can use a compiler extension to accomplish this in case you are not using C11:
First write a macro that emulates the C++ is_same. And then call it with the types you want to compare.
A minimal example for your particular case:
#include<assert.h>
#define is_same(a, b) \
static_assert(__builtin_types_compatible_p(typeof(a), typeof(b)), #a " is not unsigned int")
int main()
{
int fail_count = 0;
is_same(fail_count, unsigned int);
}
The compiler asserts:
<source>: In function 'main':
<source>:4:3: error: static assertion failed: "fail_count is not unsigned int"
static_assert(__builtin_types_compatible_p(typeof(a), typeof(b)), #a " is not unsigned int")
^~~~~~~~~~~~~
<source>:9:5: note: in expansion of macro 'is_same'
is_same(fail_count, unsigned int);
^~~~~~~
See Demo
What about a low-tech solution that works even with K&R C and any compiler past and present?
Place the right comment in the right place:
/*
* If this type is changed, don't forget to change the macro in
* if (fail_count < UINT32_MAX - 1) below (or file foobar.c)
*/
uint32_t fail_count = 0;
With a proper encapsulation this should refer to exactly one place in the code.
Don't tell me you increment the fail count in many places. And if you do, what
about a
#define FAIL_COUNT_MAX UINT32_MAX
right next to the declaration? That's more proper and clean code anyway.
No need for all the assertion magic and rocket sciencery :-)
What does ## do in C?
Example:
typedef struct
{
unsigned int bit0:1;
unsigned int bit1:1;
unsigned int bit2:1;
unsigned int bit3:1;
unsigned int bit4:1;
unsigned int bit5:1;
unsigned int bit6:1;
unsigned int bit7:1;
} _io_reg;
#define REGISTER_BIT(rg,bt) ((volatile _io_reg*)&rg)->bit##bt
(I know what it all does besides the ## part.)
It is string concatenation, as part of the preprocessor macro.
(In this context, "string" refers to a preprocessor token of course, or a "string of source code", and not a C-string.)
It's called the pasting operator; it concatenates the text in bt with the text bit. So for example, if your macro invocation was
REGISTER_BIT(x, 4)
It would expand to
((volatile _io_reg*)&x)->bit4
Without it, you couldn't put a macro argument directly beside text in the macro body, because then the text would touch the argument name and become part of the same token, and it'd become a different name.
The operator ## concatenates two arguments leaving no blank spaces between them:
#define glue(a,b) a ## b
glue(c,out) << "test";
That is the token pasting operator.
That's part of the macro definition.
It allows you to concatenate strings inside the macro.
In your case, you can use bt from 7 to 0 like this:
REGISTER_BIT(myreg, 0)
and it will be expanded as:
((volatile _io_reg*)&myreg)->bit0
Without this, you'd have to define the bit part of the macro as one of the macro's arguments:
#define REGISTER_BIT(rg,bt) ((volatile _io_reg*)&rg)->bt
where the usage would be:
REGISTER_BIT(myreg, bit0)
which is more cumbersome.
This also allows you to build new names.
Assume you have these macros:
#define AAA_POS 1
#define AAA_MASK (1 << AAA_POS)
#define BBB_POS 2
#define BBB_MASK (1 << BBB_POS)
and you want a macro that extracts AAA from a bit vector. You can write it like this:
#define EXTRACT(bv, field) ((bv & field##_MASK) >> field##_POS)
and then you use it like this:
EXTRACT(my_bitvector, AAA)
It's not a C construct, it's a preprocessor feature. In this case it's meant to evaluate the bt variable and concatenate it with the bit prefix. Without the hashes you would have bitbt there, which obviously would not work.
Here's an example from ffmpeg, a macro that registers both audio and video filters:
#define REGISTER_FILTER(X, x, y) \
{ \
extern AVFilter ff_##y##_##x; \
if (CONFIG_##X##_FILTER) \
avfilter_register(&ff_##y##_##x); \
}
and usage can be:
REGISTER_FILTER(AECHO,aecho,af);
REGISTER_FILTER(VFLIP,vflip,vf);
In C (not C++/C#) how do I check if a variable is of a certain type?
For example, something like this:
double doubleVar;
if( typeof(doubleVar) == double ) {
printf("doubleVar is of type double!");
}
Or more general: How do I compare two types so that compare(double1,double2) will evaluate to true, and compare(int,double) will evaluate to false. Also I'd like to compare structs of different composition as well.
Basically, I have a function that operates on variables of type "struct a" and "struct b". I want to do one thing with the "struct a" variables and the other with the "struct b" variables. Since C doesn't support overloading and the void pointer losses its type information I need to check for type. BTW, what would be the sense in having a typeof operator, if you can't compare types?
The sizeof method seems to be a practical workaround solution for me. Thanks for your help. I still find it a bit strange since the types are known at compile time, but if I imagine the processes in the machine I can see, why the information is not stored in terms of types, but rather in terms of byte size. Size is the only thing really relevant besides addresses.
Getting the type of a variable is, as of now, possible in C11 with the _Generic generic selection. It works at compile-time.
The syntax is a bit like that for switch. Here's a sample (from this answer):
#define typename(x) _Generic((x), \
_Bool: "_Bool", unsigned char: "unsigned char", \
char: "char", signed char: "signed char", \
short int: "short int", unsigned short int: "unsigned short int", \
int: "int", unsigned int: "unsigned int", \
long int: "long int", unsigned long int: "unsigned long int", \
long long int: "long long int", unsigned long long int: "unsigned long long int", \
float: "float", double: "double", \
long double: "long double", char *: "pointer to char", \
void *: "pointer to void", int *: "pointer to int", \
default: "other")
To actually use it for compile-time manual type checking, you can define an enum with all of the types you expect, something like this:
enum t_typename {
TYPENAME_BOOL,
TYPENAME_UNSIGNED_CHAR,
TYPENAME_CHAR,
TYPENAME_SIGNED_CHAR,
TYPENAME_SHORT_INT,
TYPENAME_UNSIGNED_CHORT_INT,
TYPENAME_INT,
/* ... */
TYPENAME_POINTER_TO_INT,
TYPENAME_OTHER
};
And then use _Generic to match types to this enum:
#define typename(x) _Generic((x), \
_Bool: TYPENAME_BOOL, unsigned char: TYPENAME_UNSIGNED_CHAR, \
char: TYPENAME_CHAR, signed char: TYPENAME_SIGNED_CHAR, \
short int: TYPENAME_SHORT_INT, unsigned short int: TYPENAME_UNSIGNED_SHORT_INT, \
int: TYPENAME_INT, \
/* ... */ \
int *: TYPENAME_POINTER_TO_INT, \
default: TYPENAME_OTHER)
C does not support this form of type introspection. What you are asking is not possible in C (at least without compiler-specific extensions; it would be possible in C++, however).
In general, with C you're expected to know the types of your variable. Since every function has concrete types for its parameters (except for varargs, I suppose), you don't need to check in the function body. The only remaining case I can see is in a macro body, and, well, C macros aren't really all that powerful.
Further, note that C does not retain any type information into runtime. This means that, even if, hypothetically, there was a type comparison extension, it would only work properly when the types are known at compile time (ie, it wouldn't work to test whether two void * point to the same type of data).
As for typeof: First, typeof is a GCC extension. It is not a standard part of C. It's typically used to write macros that only evaluate their arguments once, eg (from the GCC manual):
#define max(a,b) \
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
_a > _b ? _a : _b; })
The typeof keyword lets the macro define a local temporary to save the values of its arguments, allowing them to be evaluated only once.
In short, C does not support overloading; you'll just have to make a func_a(struct a *) and func_b(struct b *), and call the correct one. Alternately, you could make your own introspection system:
struct my_header {
int type;
};
#define TYPE_A 0
#define TYPE_B 1
struct a {
struct my_header header;
/* ... */
};
struct b {
struct my_header header;
/* ... */
};
void func_a(struct a *p);
void func_b(struct b *p);
void func_switch(struct my_header *head);
#define func(p) func_switch( &(p)->header )
void func_switch(struct my_header *head) {
switch (head->type) {
case TYPE_A: func_a((struct a *)head); break;
case TYPE_B: func_b((struct b *)head); break;
default: assert( ("UNREACHABLE", 0) );
}
}
You must, of course, remember to initialize the header properly when creating these objects.
As other people have already said this isn't supported in the C language. You could however check the size of a variable using the sizeof() function. This may help you determine if two variables can store the same type of data.
Before you do that, read the comments below.
Gnu GCC has a builtin function for comparing types __builtin_types_compatible_p.
https://gcc.gnu.org/onlinedocs/gcc-3.4.5/gcc/Other-Builtins.html
This built-in function returns 1 if the unqualified versions of the
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 is equivalent to const int.
Used in your example:
double doubleVar;
if(__builtin_types_compatible_p(typeof(doubleVar), double)) {
printf("doubleVar is of type double!");
}
As others have mentioned, you can't extract the type of a variable at runtime. However, you could construct your own "object" and store the type along with it. Then you would be able to check it at runtime:
typedef struct {
int type; // or this could be an enumeration
union {
double d;
int i;
} u;
} CheesyObject;
Then set the type as needed in the code:
CheesyObject o;
o.type = 1; // or better as some define, enum value...
o.u.d = 3.14159;
As another answer mentioned, you can now do this in C11 with _Generic.
For example, here's a macro that will check if some input is compatible with another type:
#include <stdbool.h>
#define isCompatible(x, type) _Generic(x, type: true, default: false)
You can use the macro like so:
double doubleVar;
if (isCompatible(doubleVar, double)) {
printf("doubleVar is of type double!\n"); // prints
}
int intVar;
if (isCompatible(intVar, double)) {
printf("intVar is compatible with double too!\n"); // doesn't print
}
This can also be used on other types, including structs. E.g.
struct A {
int x;
int y;
};
struct B {
double a;
double b;
};
int main(void)
{
struct A AVar = {4, 2};
struct B BVar = {4.2, 5.6};
if (isCompatible(AVar, struct A)) {
printf("Works on user-defined types!\n"); // prints
}
if (isCompatible(BVar, struct A)) {
printf("And can differentiate between them too!\n"); // doesn't print
}
return 0;
}
And on typedefs.
typedef char* string;
string greeting = "Hello world!";
if (isCompatible(greeting, string)) {
printf("Can check typedefs.\n");
}
However, it doesn't always give you the answer you expect. For instance, it can't distinguish between an array and a pointer.
int intArray[] = {4, -9, 42, 3};
if (isCompatible(intArray, int*)) {
printf("Treats arrays like pointers.\n");
}
// The code below doesn't print, even though you'd think it would
if (isCompatible(intArray, int[4])) {
printf("But at least this works.\n");
}
Answer borrowed from here: http://www.robertgamble.net/2012/01/c11-generic-selections.html
From linux/typecheck.h:
/*
* Check at compile time that something is of a particular type.
* Always evaluates to 1 so you may use it easily in comparisons.
*/
#define typecheck(type,x) \
({ type __dummy; \
typeof(x) __dummy2; \
(void)(&__dummy == &__dummy2); \
1; \
})
Here you can find explanation which statements from standard and which GNU extensions above code uses.
(Maybe a bit not in scope of the question, since question is not about failure on type mismatch, but anyway, leaving it here).
This is crazily stupid, but if you use the code:
fprintf("%x", variable)
and you use the -Wall flag while compiling, then gcc will kick out a warning of that it expects an argument of 'unsigned int' while the argument is of type '____'. (If this warning doesn't appear, then your variable is of type 'unsigned int'.)
Best of luck!
Edit: As was brought up below, this only applies to compile time. Very helpful when trying to figure out why your pointers aren't behaving, but not very useful if needed during run time.
As of C2x, typeof is now a part of the language's standard. This allows the creation of a macro that compares the types of two values:
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#define sametypeof(A,B) _Generic(A, typeof(B): true, default: false)
int main() {
if (sametypeof(1, 2)) {
printf("1 and 2 have the same type.\n");
} else {
printf("1 and 2 don't have the same type.\n");
}
}
(This compiles with the latest experimental version of GCC 13, using the -std=c2x flag)
If you want to compare between two types, you can use the following workaround:
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#define sametype(A,B) _Generic(*((A*)0), B: true, default: false)
int main() {
if (sametype(void*, nullptr_t)) {
printf("void* and nullptr_t are the same type.\n");
} else {
printf("void* and nullptr_t are not the same type.\n");
}
}
Although *((A*)0) is not valid code at runtime, the compiler will still be able to deduce its type as A, so it will work in _Generic, as the code itself will not run and will be discarded. (as far as I remember, this trick has worked in every C11 compliant compiler I've used, including Clang and the Tiny C Compiler)
(you also cannot just do (A)0 because 0 cannot be cast to a struct)
C is statically typed language. You can't declare a function which operate on type A or type B, and you can't declare variable which hold type A or type B. Every variable has an explicitly declared and unchangeable type, and you supposed to use this knowledge.
And when you want to know if void * points to memory representation of float or integer - you have to store this information somewhere else. The language is specifically designed not to care if char * points to something stored as int or char.
One possible way is to have your variables names prepend your variable definitions with the type information.
Ex:
All integers will have i_
All floats will have f_
etc..
The variable name can be got out by the #<variable_name>,
This
There is a built-in function in GCC.
Built-in Function: int __builtin_types_compatible_p (type1, type2)
You can use the built-in function __builtin_types_compatible_p to determine whether two types are the same.
i've searched a solution to solve the issue of controlling data type for while , and i thought that maybe my founding could add up well with the initial demand #con-f-use, even if it's no exactly the same issue.
An other way around to control the datatype could be done using an union with predefined type. In my case, i had a defined structure in which i was originally using a void* to allow divers data type to be passed :
originally:
//[main]:
uint32_t vtest3= 100000;
int32_t vtest2= 100000;
struct entity list[] = {
{ TYPE_INT32, s_int32_t, .label="tension", &vtest3},
{ TYPE_INT32, s_int32_t, .label="tension", &vtest3}
};
//[file.h]:
struct entity {
enum entity_type type;
uint32_t dimension;
char* label;
void* ptr_data;
uint32_t offset;
};
enum entity_type {
TYPE_NONE = 0,
TYPE_INT8 = 1,
TYPE_INT16 = 2,
TYPE_INT32 = 3,
TYPE_INT64 = 4,
TYPE_UINT8 = 5,
TYPE_UINT16 = 6,
TYPE_UINT32 = 7,
TYPE_UINT64 = 8,
TYPE_FLOAT32 = 9
};
The issue with this method is that it accept all type of variable in an uncontrolled way. There is no easy method to control the data type referenced by the void* pointer, Excepted maybe thought the use of a macro and _Generic as described before in this thread.
If the programmer decided to pass a type different from the list of type accepted ,there while be no error thrown at compile time.
. They other way around is by replacing the void* by an union , this way the structure while only accept specific data type defined inside the union list . If the programmer decide to pass a pointer with an type which is not already defined inside the ptr_data union{...} , it will throw an error.
//[file.h]:
enum entity_type {
TYPE_NONE = 0,
TYPE_INT8 = 1,
TYPE_INT16 = 2,
TYPE_INT32 = 3,
TYPE_INT64 = 4,
TYPE_UINT8 = 5,
TYPE_UINT16 = 6,
TYPE_UINT32 = 7,
TYPE_UINT64 = 8,
TYPE_FLOAT32 = 9
};
struct entity {
enum entity_type type;
uint32_t dimension;
char* label;
union {
uint8_t *uint8;
uint16_t *uint16;
uint32_t *uint32;
uint32_t *uint;
int16_t *int16;
int32_t *int32;
int64_t *int64;
float *f;
} ptr_data;
uint32_t offset;
};
[main:]
uint32_t vtest3= 100000;
int32_t vtest2= 100000;
struct entity list[] = {
{ TYPE_INT32, s_int32_t, .label="a", .ptr_data = {.uint16=&vtest1}
},
{ TYPE_INT32, s_int32_t, .label="b", .ptr_data = {.int32=&vtest2}
};
This method make use of the union to control implicitly the data type of the variable inserted by the programmer in the structure. If not correct the compiler while throw an error at compile time.
Obviously this code example is far from perfect and cannot be used directly but i tried to explain in a way as clear as possible the logic and the the idea that i proposed ;)