【发布时间】:2020-03-10 04:38:03
【问题描述】:
我需要编写一个 C 宏来检查以确保传递给它的所有参数都是unsigned 并且是相同的整数类型。例如:所有输入参数均为uint8_t,或全部为uint16_t,或全部为uint32_t,或全部为uint64_t。
以下是在 C++ 中如何进行此类检查:Use static_assert to check types passed to macro
C 中是否存在类似的东西,即使只是通过 gcc 扩展?
请注意,静态断言可通过_Static_assert 在 gcc 中使用。 (在这里查看我的答案:Static assert in C)。
这不起作用:
int a = 1;
int b = 2;
_Static_assert(__typeof__ a == __typeof__ b, "types don't match");
错误:
main.c: In function ‘main’:
main.c:23:20: error: expected expression before ‘__typeof__’
_Static_assert(__typeof__ a == __typeof__ b, "types don't match");
更新:
这正是我想要在 C++ 中做的事情(使用 函数模板、static_assert 和 <type_traits> 头文件)。为了比较的目的,我无论如何都需要学习这个,所以我就这样做了。在此处为自己运行此代码: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;
}
如果 TEST A 未注释,您会在静态断言中遇到此故障,因为输入不是无符号的:
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 ||
^~~~~~~~~~~~~
如果只取消注释 TEST B,您将无法从模板中找到有效函数,因为模板要求所有输入都是同一类型 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);
^
只要 TEST C 未注释,它就会通过,看起来像这样!
Begin
test_func: a = 15; b = 16; c = 17; d = 18
参考资料:
- 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?
相关:
- Use static_assert to check types passed to macro[我自己的回答]
- Static assert in C[我自己的回答]
【问题讨论】:
-
C 提供
__typeof__(GNU C 提供typeof)允许您查询类型。见Referring to a Type with typeof -
是的,我想过这个问题,但没有办法使用它来比较类型——只能复制它们以进行实例化。例如:
int a; __typeof__ a b;生成a和b类型为int。但是,我上次检查时,_Static_assert(__typeof__ a == __typeof__ b, "this failed");似乎根本不起作用。 -
我在想的是
if (sizeof(__typeof__ a) == sizeof(__typeof__ b))会区分uintX_t的口味。 -
确实如此,但您甚至不需要
__typeof__。只做:if (sizeof(a) == sizeof(b)),但这并不能帮助我区分signed和unsigned(例如:int16_t和uint16_t,它们都是2 个字节)、float或double和uint32_t在 8 位机器上(在这种情况下这三种类型都是 4 字节),或double和uint64_t在 64 位机器上(这两种类型都是 8 字节)等等。换句话说,sizeof在我看来无效并且缺乏必要的粒度,除非有人知道我不知道的特殊使用方式。 -
这是一个难题,因为您只能使用
__typeof__,而typedef将被允许——这限制了该值。我不知道还有什么能让你到达那里的——但我不会排除某个更聪明的人知道我在那个部门不知道的事情。它通常不是你在 C 中遇到的,因为它是强类型的。
标签: c gcc types macros typechecking