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]
__typeof__
(GNU C providestypeof
) that allow you to query for type. See Referring to a Type with typeof – Cryosurgeryint a; __typeof__ a b;
makes botha
andb
of typeint
. However, last I checked,_Static_assert(__typeof__ a == __typeof__ b, "this failed");
doesn't seem to work at all. – Bleierif (sizeof(__typeof__ a) == sizeof(__typeof__ b))
would distinguish between theuintX_t
flavors. – Cryosurgery__typeof__
for that though; just do:if (sizeof(a) == sizeof(b))
, but that doesn't help me distinguish betweensigned
andunsigned
(ex:int16_t
anduint16_t
, both of which are 2 bytes),float
ordouble
anduint32_t
on an 8-bit machine (all 3 of these types are 4-bytes in this case), ordouble
anduint64_t
on a 64-bit machine (both these types are 8 bytes), etc etc. In other words,sizeof
appears to me to be ineffective and lack the necessary granulariy, unless someone knows a special way to use it I don't know. – Bleier__typeof__
where atypedef
would be allowed -- which limits the value. I'm not aware of anything else that would get you there -- but I'm not going to rule out someone smarter knowing something that I don't in that department. It's generally not something you run across in C with it being so strongly typed. – Cryosurgery__typeof__
. I could find no mention of it in the C11 working draft. I believe that link is merely explaining (in regards to__typeof__
working for ISO C programs) that compiling with certain flags (such as-ansi
) will disable thetypeof
keyword, whereas__typeof__
would not be disabled. See here: gcc.gnu.org/onlinedocs/gcc/Alternate-Keywords.html – Catalectictypeof
. So it won't be fully conforming. And yes, I did miss the alternate keyword implication. Thank you. – Cryosurgery__typeof__
is a gcc extension, but that's not a problem for me. I frequently rely on gcc extensions which are "non-conforming" to the C or C++ standards._Static_assert
, for example, is one of them. Eventually the standard comes around, 10 or 20 years later, and conforms to gcc. :) – Bleier