如何从指针类型和指针到指针类型中删除__unaligned说明符



在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 *

这是一个带有内联解释的可能解决方案。有两个三个警告:

  1. 这不是一个内置运算符,所以int **foo = aligned_cast<int **>(&bar)是 out 的(&bar的结果是一个右值(。
  2. 如果指针本身被标记为__unaligned,这将不起作用(不过,我什至不确定这是否是一回事(。
  3. 它可能会删除指针本身的 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 上的代码。

最新更新