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

时间:2018-06-22 18:46:41

标签: c++ templates pointers winapi visual-c++

在面向x86-64的Windows上,我们有__unaligned说明符。 1 2 这很麻烦。我要:1)对其进行编译时测试; 2)在编译时从类型中删除说明符; 3)当我在函数调用的结果中找到它时,将其丢弃。抛弃它恰好需要const_cast,就像运气一样,所以任何解决方案都可能不会轻易保持const-ness。

另外,鉴于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是安腾时代之前一直存在的怪异现象。它隐藏在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 个答案:

答案 0 :(得分:0)

这是内联说明的可能解决方案。有两个三个警告:

  1. 这不是内置运算符,因此int **foo = aligned_cast<int **>(&bar)无效(&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;
}

这里是the code的Godbolt。