2017-02-22 3 views
1

Ich möchte den Bitshift-Operator für eine uint32x4_t über ARM-Systeme in arm_neon.h definiert überladen.Überladen Bitshift-Operator in C++

struct uint32x4_t { 
    uint32_t val[4]; 
}; 

Dies sollte mit einem Aufruf eine SIMD-Funktion erfolgen, die der Wert zu verschieben und einem konstanten sofort erwartet:

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

Dieser Code produ ces der Fehler:

‘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) {

Gibt es eine Abhilfe wie uint32x4_toperator>> für "native" Typen zu überlasten?

Edit: Ich die vorgeschlagenen Abhilfen angepasst, aber der Fehler bleibt immer noch die gleiche :(

+0

Sie müssen den Bediener in den Geltungsbereich bringen; 'mit Namespace A :: B;' ist eine einfache Lösung. Nebenbei bemerkt, muss das 'constant_immediate', das' simdShift' erwartet, ein _konstanter Ausdruck_ sein; ein 'const int v' Parameter ist nicht ausreichend. – ildjarn

+0

@ildjarn Aber ich kann einen conetexpr in eine Funktion nicht übergeben kann ich? – Hymir

+2

@Hymir: Nicht als ein 'int', nein. Aber Sie können es zu einer Funktionsvorlage machen und statt dessen eine Instanz von 'std :: integral_constant ' übergeben, was funktionieren wird. Sie können variable Vorlagen oder eine UDL verwenden, um das Konstruieren von integral_constant-Instanzen lesbar zu machen. – ildjarn

Antwort

3

Eine inkrementelle Verbesserung auf ErmIg Antwort:

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 Ich habe operator>> in den globalen Namespace gestellt; Wenn Sie es in einen anderen Namespace einfügen möchten, müssen Sie den Operator in den Bereich bringen, bevor Sie ihn verwenden.

1

Eine Lösung, die die operator>> vom A::B Namespace in dem globalen Namensraum zu bewegen wäre, wenn alle anderen Symbole verschiedene Namensräume sind. dann brauchen Sie nur, sie zu qualifizieren wenn zum Beispiel simdShift in A::B ist immer noch Sie globale operator>> wie diese haben können.

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

Aber ich denke, dass es besser geeignet waren, um operator>> Uhr zurücl uint32x4_t statt:

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

oder alternativ als ildjarn in einem Kommentar vorgeschlagen, ziehen Symbole aus dem A::B Namespace in den Kontext, in dem man sie durch Schreiben verwenden:

using namespace A::B; 
+0

Das sieht toll aus, aber uint32x4_t ist ein nativer Typ, der von arm_neon.h bereitgestellt wird. So kann ich nicht einfach den Operator >> als Mitglied hinzufügen, kann ich? – Hymir

+0

@Hymir Nicht als Mitglied, nein. Sie können den Operator jedoch global für einen vorhandenen Typ definieren. Wenn Sie es als Mitglied wünschen, müssten Sie zuerst "uint32x4_t" in Ihren eigenen Typ einbinden und dann den Operator hinzufügen. –

+0

@RemyLebeau Wenn ich es wie folgt verwenden: Namensraum A {namspace B { template 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 (VEC); } und verwenden Sie es wie folgt: uint32x4_t vec >> 3; habe ich den gleichen Fehler: "uint32x4_t Operator >> (const uint32x4_t &, int)‘ muss ein Argument der Klasse oder Aufzählungstyp uint32x4_t Operator >> (uint32x4_t const & Vec, const int v) {" – Hymir

2

„uint32x4_t ist gebürtiger type, bereitgestellt von arm_neon.h. "(aus einem anderen Kommentar).

Das Problem, mit dem Sie anfänglich konfrontiert sind, ist, dass C++ etwas namens Argument-abhängige Lookup verwendet. Für A::B::uint32x4 würde C++ A::B::operator>>(uint32x4, int) berücksichtigen. Das heißt, C++ sucht in den Namespaces der jeweiligen Argumente.

Ihr Problem ist, dass uint32x4 im globalen Namespace ist, und Sie setzen Ihre operator>> in einem anderen Namespace. Das ist einfach falsch. Setzen Sie es in den richtigen Namespace.

Beachten Sie, dass Namespaces einer der beiden bereitgestellten Mechanismen sind, um Namenskonflikte zu vermeiden. Überladen ist der andere Mechanismus. Namespaces funktionieren für alle Arten von Namen: Variablen, Typen und Funktionen. Überladen funktioniert nur für Funktionen. Aber in diesem Fall ist das ausreichend, da Operatoren eine Teilmenge von Funktionen sind. Sie werden keinen Namenskonflikt bekommen; Ihre operator>> überlastet mit anderen operator>>.

2

Für den Aufruf der Funktion mit Konstante sofort (es trifft oft in SIMD intrinsics) Ich verwende in der Regel Template-Funktion mit ganzzahligen Template-Parameter.Das folgende Beispiel wird SSE2 verwendet, aber für NEON wird es ähnlich sein:

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

Leider weiß ich nicht, wie es für C++ Operator vorgenommen werden kann. Natürlich können wir einen Operator mit Template-Argument erstellen, aber es ist sehr unbequem für die Anwendung:

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

Die Variante von @ildjarn inspiriert:

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

Genau so habe ich es umgesetzt! Aber ich dachte, ein Operator >> wäre viel besser zu lesen. – Hymir