C ++ (somehow) limit the structure to the size of the parent union

I am trying to create a color class with a variable size - given the array of values ​​defined by the template, I would like to create named aliases for each value in the array, i.e.:

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

However, if I try to use the same class for C = 3, the union sets the size to 4 bytes (member 'a'). Alternatively, using the mathematically expressed bitfield size for (a structure named a, anonymous member T, the size is evaluated to 1 at C> 3), the compiler generates an enable warning (not suppressed, according to gcc, how do I disable the -fpermissive warning? ) , something inappropriate for a larger API.

How can I allow one class to process different numbers of variables, preserving the variable names and not using the macro of the magic of recursive inclusion (this should not have happened). Thanks in advance!

Edit: To clarify the question, answering any of the following questions will help solve this problem:

  • Suppress GCC errors -fpermissive (#pragma diagnostics ignored does not work for permissions)
  • Set the maximum size of a union or child structure not exceeding C bytes
  • Allow bitfield length 0 for members not covered by C bytes (GCC allows mathematical expressions for bitfield lengths such as (C-3> 0)? 8: 0;)
  • Disable elements that are not covered by C bytes in some other ways (i.e. mythical static_if ())
+6
source share
2 answers

You can do structure specialization for different C cases:

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

Please note that anonymous structures are non-standard.

If using member functions is an option, I think that would be a better way:

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

Then you can use it as follows:

 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"; } 
+4
source

Ok, so now @VaughnCato is in front of me, but I will still post my answer using std::enable_if . It declares Color as a struct because it makes no sense to have a class when everything is publicly available (and there is no good reason for declaring the data member [v in question] private), adds template aliases for more syntactic sugar, and uses static_assert to make sure the user does not use strange values ​​for template parameters. It also uses std::enable_if slightly different way, which I would say is a little readable.

 #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>; 
+2
source

Source: https://habr.com/ru/post/983550/


All Articles