xx77aBs
xx77aBs

Reputation: 4768

Circular cast operators between two unions

I have the following two unions:

union rgb_color
{
  struct {
    float red;
    float green;
    float blue;
  };
  float raw[3];
};

union hsv_color
{
  struct {
    float hue;
    float saturation;
    float value;
  };
  float raw[3];
}

I'd like to add operator hsv_color() to rgb_color union, and operator rgb_color() to hsv_color union. Is there a way to do it? If I forward-declare hsv_color, the compiler throws the following error:

error: return type 'union hsv_color' is incomplete

The more I'm trying to implement this, the more I'm thinking I should just create two functions for conversion instead of using implicit cast operators. Still, I'd like to know if this is possible. Any suggestions?

Upvotes: 0

Views: 107

Answers (2)

mvidelgauz
mvidelgauz

Reputation: 2214

I would suggest the code below, but your basic assumption that struct of 3 floats will take exactly the same memory as array of 3 floats is probably wrong. There is structure member alignment. You'll need to use some "pragma pack" directive. Read here for example

struct hsv_color;

struct rgb_color
{
  union
  {
      struct 
      {
        float red;
        float green;
        float blue;
      }rgb;
      float raw[3];
  };
  operator hsv_color();
 };

struct hsv_color
{
    union
    {
      struct 
      {
        float hue;
        float saturation;
        float value;
      } hsv;
      float raw[3];
    };

    operator rgb_color();
};

rgb_color::operator hsv_color()
{
    hsv_color ret;

    // convert 'this' to hsv_color
    ret.hsv.hue = 0;//todo: perform appropriate calculation here
    ret.hsv.saturation = 0;//todo: perform appropriate calculation here
    ret.hsv.value = 0;//todo: perform appropriate calculation here

    return ret;
}

hsv_color::operator rgb_color ()
{
    rgb_color ret;

    // convert 'this' to rgb_color
    ret.rgb.red = 0;//todo: perform appropriate calculation here
    ret.rgb.green = 0;//todo: perform appropriate calculation here
    ret.rgb.blue = 0;//todo: perform appropriate calculation here

    return ret;
}

Upvotes: 2

Richard Hodges
Richard Hodges

Reputation: 69902

I would avoid a type-punning union as it becomes UB under the strict aliasing rule.

I take it that you are involving an array because you need to pass this array to an API, such as opengl.

In which case I would simply use the array and provide accessors to access r, g, b, h, s and v semantically.

You can provide the necessary conversion constructor with a forward-declaration:

// forward declare
struct hsv_color;

struct rgb_color
{
  rgb_color(float r, float g, float b)
    : raw { r, g, b }
  {}

  // forward declare
  rgb_color(hsv_color);

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

  const float* data() const  { return raw; }
  float* data()  { return raw; }

  private:
  float raw[3];
};

struct hsv_color
{
  hsv_color(float h, float s, float v)
   : raw { h, s, v }
  {}

  hsv_color(rgb_color rgb) { /*conversion here*/ }

  // more accessors here

  float raw[3];
};

// define

rgb_color::rgb_color(hsv_color hsv) { /* implement here */ }

Upvotes: 1

Related Questions