2015-03-09 8 views
6

Ich versuche, eine Farbklasse mit variabler Größe zu erstellen - angesichts einer Vorlage bestimmten Array von Werten, möchte ich benannte Aliase von jedem Wert im Array erstellen, dh:C++ (Irgendwie) Struct auf Eltern Union Größe beschränken

template<int C = 3, typename T = unsigned char> 
class Color { 
public: 
    union { 
    T v[C]; 
    struct { 
     T r, g, b, a; 
    }; 
    }; 
}; 

Allerdings, wenn ich versuche, die gleiche Klasse für C = 3, die Gewerkschaftsmandate eine Größe von 4 Bytes zu verwenden (das 'a' Mitglied). Alternativ verwendet der Compiler eine mathematisch ausgedrückte Bitfeldgröße für a (Struktur namens a, anonymer T-Member, Größe ergibt 1 bei C> 3) und gibt eine permissive Warnung aus (nicht unterdrückbar, wie in In gcc, how to mute the -fpermissive warning?), etwas ungeeignet für einen größeren -Skala-API.

Wie würde ich gehen über eine einzelne Klasse mit verschiedenen Zahlen von Variablen zu behandeln, unter Beibehaltung per-Variablen-Namen und ohne Implementierung von rekursiven Include-Makro-Magie (versucht, sollte dies nicht haben). Danke im Voraus!

Edit: die Frage zu klären, eine Antwort auf eine der folgenden Bedingungen wird dieses Problem lösen:

  • Suppress GCC -fpermissive Fehler (#pragma Diagnose für permissive nicht ignoriert funktioniert)
  • Set maximale Größe der Vereinigungs- oder untergeordneten Struktur nicht überschreiten C Bytes
  • Erlaube Bitfeldlänge von 0 für Mitglieder, die nicht durch C-Bytes abgedeckt sind (GCC erlaubt mathematische Ausdrücke für Bitfeldlänge wie (C-3> 0) 8: 0;)
  • Deaktivieren Sie Member, die nicht durch C Bytes abgedeckt sind, durch einige o ther Mittel (dh mythische static_if())
+0

Also, wenn ich richtig verstehe Sie wollen die Anzahl der Mitglieder der anonymen Struktur mit dem Template-Parameter 'C' variieren? –

+0

Korrekt - im Idealfall könnte so etwas wie ein static_if zusätzliche a, x, y, z, w Mitglieder basierend auf C> = 4 ... – Precursor

+0

einschalten. Brauchst du das 'T v [C]; Fügen Sie es ein, weil Sie erwartet haben, dass es für die Implementierung benötigt wird? – jPlatte

Antwort

4

Sie könnten eine Spezialisierung der Struktur für verschiedene Fälle von C machen:

template <int C = 3, typename T = unsigned char> union Color; 

template <typename T> 
union Color<3,T> { 
    T v[3]; 
    struct { 
    T r,g,b; 
    }; 
}; 

template <typename T> 
union Color<4,T> { 
    T v[4]; 
    struct { 
    T r,g,b,a; 
    }; 
}; 

Beachten Sie, dass anonyme Strukturen sind nicht-Standard.

Bei der Verwendung von Elementfunktionen eine Möglichkeit ist, denke ich, dass ein besserer Weg wäre, zu gehen:

template <int C,typename T> 
class Color { 
    public: 
    using Values = T[C]; 

    Values &v() { return v_; } 

    const Values &v() const { return v_; } 

    T& r() { return v_[0]; } 
    T& g() { return v_[1]; } 
    T& b() { return v_[2]; } 

    template <int C2 = C, 
     typename = typename std::enable_if<(C2>3)>::type> 
    T& a() 
    { 
     return v_[3]; 
    } 

    const T& r() const { return v_[0]; } 
    const T& g() const { return v_[1]; } 
    const T& b() const { return v_[2]; } 

    template <int C2 = C, 
     typename = typename std::enable_if<(C2>3)>::type> 
    const T& a() const 
    { 
     return v_[3]; 
    } 

    private: 
    Values v_; 
}; 

Sie können es dann wie folgt verwenden:

int main() 
{ 
    Color<3,int> c3; 
    Color<4,int> c4; 

    c3.v()[0] = 1; 
    c3.v()[1] = 2; 
    c3.v()[2] = 3; 

    std::cout << 
    c3.r() << "," << 
    c3.g() <<"," << 
    c3.b() << "\n"; 

    c4.v()[0] = 1; 
    c4.v()[1] = 2; 
    c4.v()[2] = 3; 
    c4.v()[3] = 4; 

    std::cout << 
    c4.r() << "," << 
    c4.g() << "," << 
    c4.b() << "," << 
    c4.a() << "\n"; 
} 
+0

Wirklich zu schätzen, die Details in Ihrer Antwort zur Verfügung gestellt - Ich war zögerlich, um Member-Funktionen zu verwenden, aber spezialisierte Strukturen (teilweise Templating) war genau das, was ich brauchte. Danke vielmals! – Precursor

2

Okay, jetzt @ VaughnCato hatte das vor mir, aber ich poste meine Antwort immer noch mit std::enable_if. Es deklariert Farbe als struct, weil es wirklich keinen Sinn hat, eine Klasse zu haben, wenn alles öffentlich ist (und es gibt keinen guten Grund, das Datenelement [v in der Frage] privat zu deklarieren), fügt Template-Aliase für ein bisschen mehr syntaktischen Zucker hinzu und verwendet static_assert, um sicherzustellen, dass der Benutzer keine merkwürdigen Werte für die Vorlagenparameter verwendet. Es verwendet auch std::enable_if in einer etwas anderen Art und Weise, die ich ein bisschen besser lesbar wäre.

#include <type_traits> 
#include <cstdint> 

template<unsigned nChans, typename T = std::uint8_t> 
struct Color 
{ 
    static_assert(nChans >= 3 || nChans <= 4, "number of color channels can only be 3 or 4"); 
    // allow integral types only 
    //static_assert(std::is_integral<T>::value, "T has to be an integral type"); 
    // also allow floating-point types 
    static_assert(std::is_arithmetic<T>::value, "T has to be an arithmetic (integral or floating-point) type"); 

    T data[nChans]; 

    T& r() { return data[0]; } 
    T& g() { return data[1]; } 
    T& b() { return data[2]; } 

    //template<typename U = T, typename EnableIfT = std::enable_if<(nChans == 4), U>::type> // C++11 
    template<typename U = T, typename EnableIfT = std::enable_if_t<(nChans == 4), U>> // C++14 
    T& a() { return data[3]; } 

    const T& r() const { return data[0]; } 
    const T& g() const { return data[1]; } 
    const T& b() const { return data[2]; } 

    //template<typename U = T, typename EnableIfT = std::enable_if<(nChans == 4), U>::type> 
    template<typename U = T, typename EnableIfT = std::enable_if_t<(nChans == 4), U>> 
    T const& a() const { return data[3]; } 
}; 

template<typename T = std::uint8_t> using RgbColor = Color<3, T>; 
template<typename T = std::uint8_t> using RgbaColor = Color<4, T>; 
+0

Ich stimme zu, das ist ein schöner 'enable_if'-Stil. –