Tuesday, 19 September 2017

C++ (Somehow) limit struct to parent union size



I'm attempting to create a color class of variable size- given a template-determined array of values, I'd like to create named aliases of each value in the array, ie:



template
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 mandates a size of 4 bytes (the 'a' member). Alternatively, using a mathematically expressed bitfield size for a (struct named a, anonymous T member, size evaluates to 1 at C>3), the compiler issues a permissive warning (non-suppressible, as per In gcc, how to mute the -fpermissive warning? ), something unsuitable for a larger-scale API.



How would I go about allowing a single class to handle different numbers of variables, while retaining per-variable names and without implementing recursive-include macro magic (tried this, shouldn't have). Thanks in advance!




Edit: To clarify the question, an answer to any of the following will solve this problem:




  • Suppress GCC's -fpermissive errors (#pragma diagnostic ignored doesn't work for permissive)

  • Set maximum size of union or child struct not to exceed C bytes

  • Allow bitfield length of 0 for members not covered by C bytes (GCC allows mathematical expressions for bitfield length, such as (C-3 > 0)?8:0; )

  • Disable members not covered by C bytes by some other means (ie, mythical static_if() )


Answer



You could make a specialization of the struct for different cases of C:




template  union Color;

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


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



Note that anonymous structs are non-standard.



If using member functions is a possibility, I think that would be a better way to go:



template 
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 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 typename = typename std::enable_if<(C2>3)>::type>

const T& a() const
{
return v_[3];
}

private:
Values v_;
};



You can then use it like this:



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

No comments:

Post a Comment

casting - Why wasn&#39;t Tobey Maguire in The Amazing Spider-Man? - Movies &amp; TV

In the Spider-Man franchise, Tobey Maguire is an outstanding performer as a Spider-Man and also reprised his role in the sequels Spider-Man...