用c ++

时间:2017-02-22 08:01:13

标签: c++ arm operator-overloading c++14 simd

我想为uint32x4_t中ARM系统上定义的arm_neon.h重载bitshift运算符。

struct uint32x4_t {
   uint32_t val[4];
};

这应该通过调用SIMD函数来完成,该函数期望值移位并立即保持恒定:

uint32x4_t simdShift(uint32x4_t, constant_immediate);

shift.h

#ifndef SHIFT_H
#define SHIFT_H

namespace A {
   namespace B {
      /*uint32x4_t simdLoad(uint32_t*) {
         ...
      }*/

      template<int N>
      uint32x4_t shiftRight(uint32x4_t vec) {
        return vshrq_n_u32(vec,N);
      }
   }
}
uint32x4_t operator>>(uint32x4_t const & vec, const int v) {
  return A::B::shiftRight<v>(vec);
}
#endif

的main.cpp

#include "shift.h"

int main() {
   uint32_t* data = new uint32_t[4];
   data[0] = 1;
   data[1] = 2;
   data[2] = 3;
   data[3] = 4;
   uint32x4_t reg;// = simdLoad(data);
   reg = reg>>3;
   return 0;
}

此代码产生错误:

  

'uint32x4_t operator&gt;&gt;(const uint32x4_t&amp;,int)'必须有一个参数   类或枚举类型uint32x4_t运算符&gt;&gt;(uint32x4_t const&amp;   vec,const int v){

是否有针对operator>>等“原生”类型重载uint32x4_t的解决方法?

编辑:我调整了建议的解决方法,但错误仍然保持不变:(

4 个答案:

答案 0 :(得分:3)

对ErmIg的回答逐步改进:

template<int N>
constexpr std::integral_constant<int, N> i_{};

template<int N>
uint32x4_t operator >>(uint32x4_t value, std::integral_constant<int, N>) noexcept {
    return _mm_slli_si128(value, N);
}

int main() {
    std::uint32_t data[4] = {1, 2, 3, 4};
    uint32x4_t reg;// = simdLoad(&data);
    reg = reg >> i_<3>;
}

N.b。我已将operator>>放在全局命名空间中;如果你想把它放在一个不同的命名空间中,你需要在使用它之前将操作符带到范围内。

答案 1 :(得分:2)

&#34; uint32x4_t是本机类型,由arm_neon.h提供。&#34; (来自另一条评论)。

您最初面临的问题是C ++使用了一种名为Argument-Dependent Lookup的东西。对于A::B::uint32x4,C ++会考虑A::B::operator>>(uint32x4, int)。也就是说,C ++将查看相应参数的名称空间。

您的问题是uint32x4位于全局命名空间中,但您将operator>>放在另一个命名空间中。这是错的。把它放在正确的命名空间中。

请注意,命名空间是为避免名称冲突而提供的两种机制之一。重载是另一种机制。命名空间适用于所有类型的名称:变量,类型和函数。重载仅适用于函数。但在这种情况下,这是足够的,因为运算符是函数的子集。你不会得到名字冲突;您的operator>>重载与其他operator>>重叠。

答案 2 :(得分:2)

对于使用常量立即调用函数(它经常在SIMD内在函数中遇到)我通常使用带有整数模板参数的模板函数。以下示例使用SSE2,但对于NEON,它将类似:

template<int shift> __m128i Shift(__m128i value)
{
    return _mm_slli_si128(value, shift);
}

int main()
{
    __m128i a = _mm_set1_epi8(3);
    __m128i b = Shift<2>(a);
    return 0;
}

不幸的是,我不知道它是如何为C ++运算符制作的。当然,我们可以使用模板参数创建一个运算符,但使用它非常不方便:

template<int shift> __m128i operator >> (__m128i value, int shift_)
{
    return _mm_slli_si128(value, shift);
}

int main()
{
    __m128i a = _mm_set1_epi8(3);
    __m128i b = operator >> <2>(a, 2);
    return 0;
}

受@ildjarn启发的变种:

template<int N> struct Imm {};

#define IMM(N) Imm<N>()

template<int shift> __m128i operator >> (__m128i value, Imm<shift>)
{
    return _mm_slli_si128(value, shift);
}

int main()
{
    __m128i a = _mm_set1_epi8(3);
    __m128i b = a >> IMM(2);
    return 0;
}

答案 3 :(得分:1)

一种解决方案是将operator>>A::B命名空间移动到全局命名空间。如果所有其他符号都是不同的名称空间,那么您只需要对它们进行限定。例如,如果simdShift位于A::B,您仍然可以拥有这样的全局operator>>

uint32x4_t operator>>(uint32x4_t const & vec, const int v) {
    return A::B::simdShift(vec, v);
}

但我想让operator>>成为uint32x4_t的成员更合适:

struct uint32x4_t {
    uint32_t val[4];
    uint32x4_t operator>>(const int v) const;
};

namespace A { namespace B {
/// TODO: Put declaration/definition of simdShift here
}} // namespace A { namespace B {

uint32x4_t uint32x4_t::operator>>(const int v) const {
    return A::B::simdShift(*this, v);
}

或者,作为评论中建议的ildjarn,将A::B命名空间中的符号拉入您使用它们的上下文中:

using namespace A::B;