如何从指针类型和指针到指针类型中删除__unaligned说明符
How to remove the __unaligned specifier from pointer types and pointer-to-pointer types
在Windows上,当面向x86-64时,我们有__unaligned
说明符。12 这很麻烦。我想:1(编译时测试;2(在编译时从类型中删除说明符;3(当我在函数调用的结果中找到它时,将其丢弃。把它扔掉碰巧需要const_cast
,因为运气好的话,所以任何解决方案都可能不会轻易保持const
性。
此外,鉴于 Windows Shell API 中 ITEMIDLIST 类型(P[C][U]IDLIST_ABSOLUTE、P[C][U]IDLIST_RELATIVE...(的指针驱动性质,最好让上述方法使用指向指针的指针。
如何实现这一目标?
下面是一个方便的代码示例:
int main(int, char**)
{
//test that aligned_cast works for non-const pointers
using AType = int __unaligned *;
int a_data = 1;
AType a = &a_data;
auto aa = aligned_cast<int *>(a);
//check that it works for const pointers
using BType = const int __unaligned *;
const int b_data = 2;
BType b = &b_data;
auto *bb = aligned_cast<const int *>(b);
//int *bbb = aligned_cast<int *>(b); <--won't compile, good
//check that remove_aligned really is doing its job
using CType = remove_aligned<AType>::type;
static_assert (is_aligned<CType>::value, "");
//auto aaa = aligned_cast<int **>(&a); <-- &a cannot be passed as a reference
return 0;
}
1(__unaligned
是Itanium时代之前挥之不去的一个奇怪的怪癖。它偷偷溜进了Windows API,所以我们(在Windows上(被它困住了。我们大多不使用ITEMIDLIST
,当我们这样做时,我们通常会小心#define STRICT_TYPE_ITEMIDS
和踩踏。问题是reinterpret_cast
这些类型非常烦人(然后const_cast
去掉__unaligned
说明符,如果需要的话(。
2(编辑:如果你不相信__unaligned
在面向x86-64时不再适用,请在Godbolt上玩弄我的答案,看看它是否可以在没有强制转换的情况下编译。具体而言,更改测试工具中的以下行会导致编译失败:
59 //auto aa = aligned_cast<int *>(a);
60 int *aa = a; //nope, a is int __unaligned *
这是一个带有内联解释的可能解决方案。有两个三个警告:
- 这不是一个内置运算符,所以
int **foo = aligned_cast<int **>(&bar)
是 out 的(&bar
的结果是一个右值(。 - 如果指针本身被标记为__unaligned,这将不起作用(不过,我什至不确定这是否是一回事(。
- 它可能会删除指针本身的 cv 限定(
int __unaligned * const
变成int *
(。我还没有对此进行测试,因为我已经为此工作了九个多小时并且很累。
无论如何
#include <utility>
//remove_all_pointers simply wipes out any pointers so we can get to the raw type (plus its cv-qualifiers)
template <class T>
struct remove_all_pointers { using type = T; };
template <class T>
struct remove_all_pointers<T *> { using type = remove_all_pointers<T>; };
template <class T>
using remove_all_pointers_t = typename remove_all_pointers<T>::type;
//this selector works like is_const, only it catches __unaligned instead
template<class T>
struct is_aligned_helper : std::true_type {};
template<class T>
struct is_aligned_helper<__unaligned T> : std::false_type {};
//wipe all pointers before we check for __unaligned
template<class T , class = std::enable_if<std::is_pointer_v<T>>>
struct is_aligned : is_aligned_helper<remove_all_pointers_t<T>> {};
template<class T>
using is_aligned_v = typename is_aligned<T>::value;
template <class T>
struct remove_aligned_helper { using type = T; };
template <class T>
struct remove_aligned_helper<__unaligned T> { using type = T; };
template <class T>
struct remove_aligned
{
//remove the pointer, remove the __unaligned specifier, then put the pointer back
using type = std::add_pointer_t<
typename remove_aligned_helper<
std::remove_pointer_t<T>>::type>;
};
//we can specialize for pointers to pointers, too
template <class T>
struct remove_aligned<T **> {
using type = std::add_pointer_t<typename remove_aligned<T *>::type>;
};
template <class T>
using remove_aligned_t = typename remove_aligned<T>::type;
template <class To, class From, class = std::enable_if<std::is_pointer<From>() && !is_aligned<From>()>>
constexpr To aligned_cast(From &value) noexcept
{
return reinterpret_cast<To>(const_cast<remove_aligned_t<From>>(value));
}
int main(int, char**)
{
//check that it works at all
using AType = int __unaligned *;
int a_data = 1;
AType a = &a_data;
int * aa = aligned_cast<int *>(a);
(void)aa;
//check that it works for const pointers
using BType = const int __unaligned *;
const int b_data = 2;
BType b = &b_data;
const int *bb = aligned_cast<const int *>(b);
(void)bb;
//int *bbb = aligned_cast<int *>(b); //<--won't compile, good
//check that remove_aligned really is doing its job
using CType = remove_aligned<AType>::type;
static_assert (is_aligned<CType>::value, "");
//check that T** works
AType *d = &a;
int ** aaa = aligned_cast<int **>(d); //<-- &a cannot be passed as a reference
(void)aaa;
}
这是 Godbolt 上的代码。
相关文章:
- C++中的双指针类型转换
- C++默认情况下,指针类型数组的元素是否保证初始化为 nullptr?
- 将类指针类型转换为键时出错
- 错误:表达式必须具有算术、无作用域枚举或带有运算符重载的指针类型
- C++在一个映射中存储不同的指针类型(并处理销毁)
- 指针类型类成员的动态强制转换的恒定性是什么?
- 我正在尝试将表的地址传递给要在另一个函数中使用的指针,但得到不兼容的指针类型
- 在将派生类指针类型转换为派生类指针后,从基类指针调用派生类函数
- 如何使用静态多态性在 int 和指针类型之间进行转换?
- STL 函数和函数类型与函数指针类型
- 如何调用指针类型的方法(禁用多态性)?
- 为什么新表达式可以正确生成指针类型,即使它应该返回 void*?
- 对于非常量指针类型的参数,未调用具有常量指针模板类型参数的功能
- 是否允许调用方对我的 Builder 类使用任何指针类型(包括智能指针)?
- OPENCL 警告:不兼容的指针类型将'float __global[16]'传递给类型为 '__global float4 的参数 *
- 专门用于"direct"函数类型(与函数指针类型相对)
- 指向成员的指针类型和模板
- 返回对常量结构(指针类型)成员的引用:明显的左值到右值转换
- 在C++17中,为什么类模板和函数模板的指针类型推导明显不一致
- 为什么允许将整型、枚举和指向成员的指针类型reinterpret_cast到自身?