0

I want to write convinient Color management class, that would allow me to use different orders of components and basically different setups. I want it to be distinguishable at compile-time. Lets say I have this code:

template <typename _valueType>
struct RGBAColorData {
    using ValueType = _valueType;
    union {
        struct { ValueType r, g, b, a; };
        ValueType components[4];
    };
};

This (even if anonymous structs are non-standard) works fine, when I want to use it like this:

RGBAColorData color;
color.r = whatever;

However, this is not the final form of my code. I want it to have "owning" class template, that would in compile-time select between XYZColorData. Lets say it looks like this:

template <typename _valueType, template <typename> _dataScheme>
struct Color
{
    using ValueType = _valueType;
    using DataScheme = _dataScheme<ValueType>;

    // what now?
    // DataScheme data; // ???
};

This makes a problem, because I want my code to be used like this:

using RGBAColorF = Color<float, RGBAColorData>;
RGBAColorF brushColor;
brushColor.r = whatever;

This would make a really convinient way to use colors, however I can't think of any solution to this problem. Finally, maybe I have wrong approach to this and maybe this can be done with less effort, however I can't think about any other method that wouldn't involve massive amount of template class specializations.

Poeta Kodu
  • 1,120
  • 8
  • 16
  • 1
    You need to inherit. – Ben Voigt Feb 21 '18 at 22:31
  • 4
    Unfortunately, your "works fine" code has undefined behavior in the way I'm almost certain you plan to use it. – Ben Voigt Feb 21 '18 at 22:32
  • Aren't you violating strict aliasing rules with your intended use of that union? – Walter Feb 21 '18 at 22:32
  • 2
    @Walter: It's not so much the strict aliasing rule, as the "read from a union member which is not active" rule. Related: https://stackoverflow.com/q/36051084/103167 – Ben Voigt Feb 21 '18 at 22:33
  • I don't understand. Any explaination link please? – Poeta Kodu Feb 21 '18 at 22:35
  • 3
    You have two questions here. The one you asked, which I paraphrase as "How can I reuse a collection of variant members?", has an easy answer: Use inheritance. The one being raised in the comments, "When I use a union to overlay a structure with multiple elements of the same type on top of an array, can I use the structure member names and array indexes interchangeably?" has a more complicated answer: No, it might look to you like it works but it is not safe. – Ben Voigt Feb 21 '18 at 22:41
  • Wow, you astonished me. I am pretty sure I've read about that way of representing a vector (x,y components mixed with array with 2 indices) in stackoverflow answer. So there isn't way to union array and free-standing named components, even using C++ "hacks"? According to asked question - I didn't think about inheritance here. It is good idea. Write an answer and I will accept it. – Poeta Kodu Feb 21 '18 at 22:49
  • 1
    https://stackoverflow.com/questions/702658/c-union-array-and-vars This answers are misleading. – Poeta Kodu Feb 21 '18 at 23:14
  • 1
    Have you considered overloading `operator[]` instead of using an array ? It seems to me that you want some members and an index based interface to it, or am I missing something? – luk32 Feb 21 '18 at 23:33
  • @luk32, yes, however I don't want it to use ifs to check which component should be returned. C++17 `constexpr if` can do the work with no runtime overhead, but it would look ugly and would be a lot of work to change. Look at my answer, I found perfect solution. – Poeta Kodu Feb 21 '18 at 23:39
  • Compiler is pretty smart about the `switch` see https://godbolt.org/g/gGstp2 it bypassed the struct altogheter and worked directly on registers. I am not sure if you can be as fast with additional indirection layer from the answer you chose. – luk32 Feb 21 '18 at 23:57

2 Answers2

1

Don't do it !

Tricking around for obtaining some nice syntactic effects is full of danger and might obliterate future evolution.

First of all, in C++ only one union member can be active at any moment. so switching the use of the array and of the struct is not guaranteed to work, even if on many compilers this may lead to the expected results.

Then, there is no guarantee for structure members to be contiguous, so that if mixing use of array and struct would work, it might still not lead to a correct result, even if on many compilers this will work as expected.

Or do it with a safer approach...

If you still like to mix the use of specific color components r, g, b and of the array, you should consider a safer approach:

template <typename _valueType>
struct RGBAColorData {
    using ValueType = _valueType;
    ValueType components[4];
    ValueType &r=components[0], &g=components[1], 
                   &b=components[2], &a=components[3]; // ATTENTION (see explanations)
};

ATTENTION: I made it quick and dirty. You should better implement the rule of three, with a proper constructor, a copy constructor and an assignment operator, to make sure that the references are not messed up.

I do not like this solution so much, but it works safely (online demo): the trick is to make r, g, b, a references to specific array items. You are then sure that you can mix the access way, and you are absolutely sure that the mapping between the two is correct.

But prefer clean encapsulation

The problem with your initial approach and my workaround is that they oth break the encapsulation: you have to know the inner structure of your color in order to use it.

With this approach, you'll never able to evolve. For example switching to a CMYK color scheme, or adopting a bit fields encoding would be compromised.

The proper way would be to have a set of getters and setters to completely hide the inner structure to the outside world. Of course, syntactically it does not look so nice, but then you'd really be able to write truly generic color code, where the encoding scheme could be a compile-time strategy.

Community
  • 1
  • 1
Christophe
  • 68,716
  • 7
  • 72
  • 138
  • 2
    The solution in your snippet has some significant memory overhead. – HolyBlackCat Feb 21 '18 at 23:18
  • 1
    Adding references makes the struct large - depending on architecture, it can add even 32 extra bytes. My classes should work with shaders used by graphics APIs, therefore I need it to be compile-time distinguishable. How do you imagine encapsulating RGBA and CMYK so they will be used in the similar way? Usage of templates guarantees copy safety and does not allow implicit casting. You will always know what type and scheme the color uses. – Poeta Kodu Feb 21 '18 at 23:20
  • 1
    @razzorflame I just showed a way to ensure that the interface you want is compliant with the standard and not UB that works now but might be broken at every release of your compiler. As said, I DO NOT RECOMMEND THIS APPROACH. If I were you, I'd choose one access way (either individual components or array) and use it consistently, or better, use getters and setters: mainstream compilers will inline such code so that it's not a performance overhead. – Christophe Feb 21 '18 at 23:37
0

Finally, I decided to use inheritance (as Ben Voigt said). I fixed the other problem, with unions that made code unsafe, using the brilliant method proposed by this answer:
https://stackoverflow.com/a/494760/4386320

Poeta Kodu
  • 1,120
  • 8
  • 16