Search code examples
c++templatesunions

How to have inheritance between template with union?


I have the following two objects. Im wondering if there is a way to have Pixel as a base class of PixelBGR so that any operator (+,-,*,/, [], etc.) could be used without redefining them ?

template<class T, std::size_t N>
struct Pixel
{
    T ch[N];

    inline T& operator[](const int x)
    {
        return ch[x];
    }
};


template<class T>
struct PixelBGR
{
    union
    {
        struct
        {
            T b;
            T g;
            T r;
        };
        T ch[3];
    };

    inline T& operator[](const int x)
    {
        return ch[x];
    }
};

EDIT: As suggested by πάντα ῥεῖ, here more details about what Im trying to do.

Im trying to have a generic class Pixel, which will be template to handle any type or size.

The usual are 1,2,3,4,8 or 16. The class with defines some operator such as +,-,*, etc.

Since most of the time, the Pixel<T,3> is a BGR pixel, I would like to define rapid access to r,g and b to avoid confusion, but still store it as BGR.

But the derived class should also provide the Operator which will be generic based on N.

EDIT2: By reading the comment of SergeyA, I forgot to say that the struct Pixel must not change size.

So I think balki answer is the best, by using member function. I was trying to make it with variables to avoid too much char ie: adding the (), but it seems to be too complicated for nothing. I still investigating CRTP, but I dont get it well, Im reading on that.


Solution

  • First thanks to all of you for advise, and special thanks to @Constantinos Glynos, @balki and @SergeyA for the example they provide, those help me to achieve a solution that match my need.

    I implemented the BGR and BGRA to show that the N works fine, now I just need to implement all the operator, and functions that I require.

    Please feel free to edit, or tell me if there is something wrong with this.

    Pixel.h

    #include <cstdio>   // std::size_t
    #include <iostream>     // std::cout
    
    
    template<typename T, std::size_t N, template<typename, std::size_t> class B >
    struct Pixel
    {
        T ch[N];
    
        // ==============================================================
        // Overload the accessor (so .ch[0] == direct access with [0].
        T& operator[](std::size_t x){ return ch[x]; }
    
        // ==============================================================
        // Copy-assignement
        Pixel& operator=( const Pixel &t )
        {
            for ( int i = 0; i < N; i++ )
                ch[i] = t.ch[i];
            return *this;
        }
    
        // ==============================================================
        // Operator
        B<T, N> operator+( const B<T, N> &t )
        {
            B<T, N> tmp;
            for ( int i = 0; i < N; i++ )
                tmp[i] = ch[i] + t.ch[i];
            return tmp;
        }
    
        B<T, N> operator-( const B<T, N> &t )
        {
            B<T, N> tmp;
            for ( int i = 0; i < N; i++ )
                tmp[i] = ch[i] - t.ch[i];
            return tmp;
        }
    
        template<typename T, std::size_t N, template<typename, std::size_t> class B >
        friend std::ostream& operator<<( std::ostream& os, const Pixel &t );
    };
    
    // To print the vector
    template<typename T, std::size_t N, template<typename, std::size_t> class B >
    std::ostream& operator<<( std::ostream& os, const B<T, N> &t )
    {
        os << "Pixel: (" << t.ch[0];
        for ( int i = 1; i < N; i++ )
            os << ", " << t.ch[i];
        os << ")";
        return os;
    }
    
    
    
    
    template<typename T, std::size_t N = 3>
    struct BGR : Pixel<T, N, BGR>
    {
        T& b() { return ch[0]; }
        T& g() { return ch[1]; }
        T& r() { return ch[2]; }
    };
    
    
    template<typename T, std::size_t N = 4>
    struct BGRA : Pixel<T, N, BGRA>
    {
        T& b() { return ch[0]; }
        T& g() { return ch[1]; }
        T& r() { return ch[2]; }
        T& a() { return ch[3]; }
    };
    

    Main.cpp

    int main() {
        std::cout << "Sizeof a float BGR: " << sizeof(BGR<float>) << std::endl;
        std::cout << "Sizeof a float BGRA: " << sizeof(BGRA<float>) << std::endl;
    
        BGR<int> p;
        p.r() = 25;
        p.g() = 14;
        p.b() = 58;
    
        std::cout << p << std::endl;
        std::cout << p[0] << " , " << p[1] << " , " << p[2] << std::endl;
        std::cout << p.b() << " , " << p.g() << " , " << p.r() << std::endl;
    
        BGR<int> q;
        q = p;
        std::cout << q[0] << " , " << q[1] << " , " << q[2] << std::endl;
    
        BGR<int> res1;
        res1 = q + p;
        std::cout << res1.r() << " , " << res1.g() << " , " << res1.b() << std::endl;
    
        BGR<int> res2;
        res2 = q - p;
        std::cout << res2.r() << " , " << res2.g() << " , " << res2.b() << std::endl;
    
        BGRA<float> a;
        a.r() = 255.0f;
        a.g() = 0.0f;
        a.b() = 0.0f;
        a.a() = 128.5f;
    
        BGRA<float> b = a;
        std::cout << a << std::endl;
    
        return 0;
    }