2017-02-22 4 views
1

Я хочу перегрузить оператор битовой смены для uint32x4_t, определенного в системах ARM, в arm_neon.h.Перегрузка оператора битовой смены в C++

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; 
} 

Этот код produ КЕС ошибка:

‘uint32x4_t operator>>(const uint32x4_t&, int)’ must have an argument of class or enumerated type uint32x4_t operator>>(uint32x4_t const & vec, const int v) {

Есть обходной путь для перегрузки operator>> для «родных» типов, таких как uint32x4_t?

Edit: Я адаптировал предложенные методы обхода, но ошибка по-прежнему остается той же :(

+0

Вам необходимо привести оператора в объем; 'using namespace A :: B;' - простое исправление. В стороне, что 'constant_immediate', что' simdShift' ожидает, должно быть _constant expression_; параметр 'const int v' недостаточен. – ildjarn

+0

@ildjarn Но я не могу передать constexpr в функцию, могу ли я? – Hymir

+2

@Hymir: Не как 'int', no. Но вы можете сделать его шаблоном функции и вместо этого передать экземпляр 'std :: integral_constant ', который будет работать. Вы можете использовать переменные шаблоны или UDL, чтобы сделать целостность экземпляров integ_constant разборчивыми. – ildjarn

ответ

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>; 
} 

Nb Я положил operator>> в глобальное пространство имен; если вы хотите поместить его в другое пространство имен, вам нужно будет довести оператора до области действия до его использования.

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; 
+0

Это выглядит потрясающе, но uint32x4_t - это родной тип, предоставляемый arm_neon.h. Таким образом, я не могу просто добавить оператор >> в качестве члена, могу ли я? – Hymir

+0

@Hymir Не как член, нет. Но вы можете определить оператор глобально для существующего типа. Если вы хотите, чтобы он был членом, вам придется сначала обернуть 'uint32x4_t' в свой собственный тип, а затем добавить к нему оператор. –

+0

@RemyLebeau Если я использовать его как это: Пространство имен {namspace Б { шаблон uint32x4_t shiftRight (uint32x4_t VEC) { возврата vshrq_n_u32 (VEC, N); }} } uint32x4_t оператор >> (uint32x4_t Const & VEC, Const INT v) { возвращают :: Б :: shiftRight (VEC); } и использовать его следующим образом: uint32x4_t vec >> 3; У меня такая же ошибка: «uint32x4_t operator >> (const uint32x4_t &, int)» должен иметь аргумент класса или нумерованного типа uint32x4_t operator >> (uint32x4_t const & vec, const int v) {" – Hymir

2

«uint32x4_t является родным тип, предоставленный arm_neon.h. "(из другого комментария).

Проблема, с которой вы сталкиваетесь изначально, состоит в том, что C++ использует что-то, называемое Argument-Dependent Lookup. Для A::B::uint32x4 C++ рассмотрит A::B::operator>>(uint32x4, int). То есть C++ будет выглядеть в пространствах имен соответствующих аргументов.

Ваша проблема в том, что uint32x4 находится в глобальном пространстве имен, и все же вы помещаете свой operator>> в другое пространство имен. Это просто неправильно. Поместите его в правильное пространство имен.

Обратите внимание, что пространства имен являются одним из двух механизмов, предоставляемых во избежание конфликтов имен. Перегрузка - это другой механизм. Пространства имен работают для всех типов имен: переменных, типов и функций. Перегрузка работает только для функций. Но в этом случае этого достаточно, поскольку операторы являются подмножеством функций. Вы не столкнетесь с именем; ваши operator>> перегрузки с другими operator>>.

2

Для вызова функции с постоянным немедленным (часто встречается в SIMD-intrinsics), я обычно использую функцию шаблона с параметром целочисленного шаблона.В следующем примере используется 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; 
} 
+0

Именно так я и реализовал это! Но я думал, что оператор >> будет гораздо лучше читать. – Hymir

Смежные вопросы