Operator Cast () Reference?

I

Immortal Nephi

Someone posted his Byte class code on the previous thread. I have a
question about operator cast (). Please explain the difference
between local object and reference object. Why do you need reference
object?
The object is still the same if you remove ampersand between operator
unsigned char and ().

class Byte {
public:
Byte( unsigned char& c ) : c_( c ) {
}

Byte& operator=( unsigned char val ) {
c_ = val;
return *this;
}

operator unsigned char&() { // ???? reference ????
return c_;
}

// operator unsigned char() { // ???? local ????
// return c_;
// }

private:
unsigned char& c_;
};

int main() {
unsigned char data = 0x41, data2 = 0x23;
Byte byte( data );
data2 = byte; // data2 is overwritten from 0x23 to 0x41

return 0;
}
 
F

Francesco S. Carta

Immortal Nephi said:
        Someone posted his Byte class code on the previous thread..  I have a
question about operator cast ().  Please explain the difference
between local object and reference object.  Why do you need reference
object?
        The object is still the same if you remove ampersand between operator
unsigned char and ().

class Byte {
public:
        Byte( unsigned char& c ) : c_( c ) {
        }

        Byte& operator=( unsigned char val ) {
                c_ = val;
                return *this;
        }

        operator unsigned char&() { // ???? reference ????
                return c_;
        }

//      operator unsigned char() { // ???? local ????
//              return c_;
//      }

private:
        unsigned char& c_;

};

int main() {
        unsigned char data = 0x41, data2 = 0x23;
        Byte byte( data );
        data2 = byte; // data2 is overwritten from 0x23 to 0x41

        return 0;

}

Eh, nice question... somebody here would eventually be able to explain
why my code gives such an output:

-------
#include <iostream>

using namespace std;

class IntByRef {
public:
IntByRef(int i) : datum(i) {};
operator int&() {
return datum;
}
private:
int datum;
};

class IntByVal {
public:
IntByVal(int i) : datum(i) {};
operator int() {
return datum;
}
private:
int datum;
};

int main()
{
int one = 1;
int two = 2;

IntByRef intbyref(one);
IntByVal intbyval(two);

cout << intbyref << endl;
cout << intbyval << endl;

intbyref = 42;
intbyval = 42;

cout << intbyref << endl;
cout << intbyval << endl;

return 0;
}
-------

Output:
-------
1
2
42
42
-------

I would have expected the compiler to choke on the assignment to
intbyval, or, at least, to print "2" as last line of output...

(just for the sake of learning something new...)
 
T

Twixer Xev

the reference is a pointer to the var passed to the ctor, so modifying
the object modifies the original var passed through. remove the
reference and you would not change the original values by assigning to
those objects.
 
F

Francesco S. Carta

Twixer Xev said:
the reference is a pointer to the var passed to the ctor, so modifying
the object modifies the original var passed through. remove the
reference and you would not change the original values by assigning to
those objects.

That's what puzzles me: the IntByVal returns an int, not a reference
to an int, still, the datum variable gets modified.

Compiler's glitch? GCC 4.4.0 here...
 
F

Francesco S. Carta

Francesco S. Carta said:
That's what puzzles me: the IntByVal returns an int, not a reference
to an int, still, the datum variable gets modified.

Compiler's glitch? GCC 4.4.0 here...

Wait... maybe you were replying to the OP... I'm passing no reference
to my constructors...

Still puzzled.
 
T

Twixer Xev

Ah, I see the error. the ctor for the reference version should take a
reference parameter. look at the OP's posting. the ctor takes a ref and
that is the behavior they were asking about.

Your code, as it is, runs correctly.
 
T

Twixer Xev

I should clarify. Your assignments never invoke the cast operator. Put a
trace in there and you'll see what I mean. Assignment is covered by the
ctor's, which happen to be identicle for each class.
 
F

Francesco S. Carta

Twixer Xev said:
I should clarify. Your assignments never invoke the cast operator. Put a
trace in there and you'll see what I mean. Assignment is covered by the
ctor's, which happen to be identicle for each class.

Ah, I see what you mean (silly me). In fact, if I declare the
constructors as explicit both assignments halt the compiler. Thanks
for the pointer :)
 
F

Francesco S. Carta

Francesco S. Carta said:
Ah, I see what you mean (silly me). In fact, if I declare the
constructors as explicit both assignments halt the compiler. Thanks
for the pointer :)

In addition, if I got it straight:

-------
#include <iostream>

using namespace std;

class IntByRef {
public:
explicit IntByRef(int i) : datum(i) {};
operator int&() {
return datum;
}
private:
int datum;
};

class IntByVal {
public:
explicit IntByVal(int i) : datum(i) {};
operator int() {
return datum;
}
private:
int datum;
};

int main()
{
int one = 1;
int two = 2;

IntByRef intbyref(one);

IntByVal intbyval(two);

cout << intbyref << endl; // prints 1

int& rint = intbyref;
rint = 42;
cout << intbyref << endl; // prints 42

// int& rint2 = intbyval; // chokes the compiler (*)

const int& crint = intbyval; // fine (**)
const int& crint2 = 78; // fine (**)

intbyval = IntByVal(10000);

cout << crint << endl; // prints 2
cout << crint2 << endl; // prints 78
cout << intbyval << endl; // prints 10000

return 0;
}
-------

(*) The compiler complains because it is not possible to create a non-
const reference to something that isn't modifiable.

(**) Fine? Seems so. Compiles and gives the expected result ;-)
 
T

Twixer Xev

(*) The compiler complains because it is not possible to create a non-
const reference to something that isn't modifiable.

(**) Fine? Seems so. Compiles and gives the expected result ;-)

Yes, looks like you got it.
 
G

Goran

Eh, nice question... somebody here would eventually be able to explain
why my code gives such an output:

-------
#include <iostream>

using namespace std;

class IntByRef {
    public:
        IntByRef(int i) : datum(i) {};
        operator int&() {
            return datum;
        }
    private:
        int datum;

};

class IntByVal {
    public:
        IntByVal(int i) : datum(i) {};
        operator int() {
            return datum;
        }
    private:
        int datum;

};

int main()
{
    int one = 1;
    int two = 2;

    IntByRef intbyref(one);
    IntByVal intbyval(two);

    cout << intbyref << endl;
    cout << intbyval << endl;

    intbyref = 42;
    intbyval = 42;

    cout << intbyref << endl;
    cout << intbyval << endl;

    return 0;}

-------

Output:
-------
1
2
42
42
-------

I would have expected the compiler to choke on the assignment to
intbyval, or, at least, to print "2" as last line of output...

(just for the sake of learning something new...)

Try:

explicit IntByVal/Ref(int i) : datum(i) {};

You have a bad case of conversionconstructoritis there.

Goran.
 
F

Francesco S. Carta

Goran said:
Try:

explicit IntByVal/Ref(int i) : datum(i) {};

You have a bad case of conversionconstructoritis there.

Eheheheh, yes :)

Luckily, such an illness has been solved without the need of any
medical prescription :)
 
F

Francesco S. Carta

Twixer Xev said:
Yes, looks like you got it.

Yep... the ugly thing is that I faced such kind of issues before...
never mind, subtle pitfalls like this happen to happen, I should
definitely rehearse (bit rusty, doing too much javascript and html
lately).
 
P

Paul Bibbings

Immortal Nephi said:
Someone posted his Byte class code on the previous thread. I have a
question about operator cast ().

I think that would be me.
Please explain the difference
between local object and reference object. Why do you need reference
object?

If you remember, the Byte class was introduced as a `proxy' for your
Array class, which stored a pointer to an array of unsigned char. A
Byte instance was returned by your Array::eek:p[], so:

Byte Array::eek:perator[](int index) { return pData[index]; }

It is created from pData[index] using the non-explicit constructor:

Byte::Byte(unsigned char&);

You will see that it stores the unsigned char at pData[index] *by
reference*. This is the whole point of using the proxy. Basically it
`represents' the stored data so that any operation upon the proxy is
*really* an operation upon the data it represents (stores).

The reason that you need:

Byte::eek:perator unsigned char&();

and not:

Byte::eek:perator unsigned char();

is that you need to allow assignment to the underlying unsigned char
through a Byte proxy. Consider:

Array array(4);
// ...
array[0] = 42U;

The last call is equivalent to:

Byte b(array[0]);
b.operator unsigned char&() = 42U; // LHS returns reference to array[0]

Using a reference here 42U gets assigned to the actual unsigned char at
array[0]. If you consider the alternative, which would be:

Byte b(array[0]);
b.operator unsigned char() = 42U; // LHS returns *temporary*
// array[0] *not* changed

then what you would be attempting to do is assigne 42U to a *temporary*
returned by b.operator unsigned char(), which is not what you want.

Regards

Paul Bibbings
 
P

Paul Bibbings

Paul Bibbings said:
The reason that you need:

Byte::eek:perator unsigned char&();

and not:

Byte::eek:perator unsigned char();

is that you need to allow assignment to the underlying unsigned char
through a Byte proxy.

Of course, ultimately you will want to implement both in order to
introduce const correctness, with (perhaps):

Byte::eek:perator unsigned char&();
Byte::eek:perator unsigned char() const;

and a similar const overload for Array::eek:p[].

Regards

Paul Bibbings
 
F

Francesco S. Carta

Paul Bibbings said:
Immortal Nephi said:
   Someone posted his Byte class code on the previous thread.  I have a
question about operator cast ().  

I think that would be me.
Please explain the difference
between local object and reference object.  Why do you need reference
object?

If you remember, the Byte class was introduced as a `proxy' for your
Array class, which stored a pointer to an array of unsigned char.  A
Byte instance was returned by your Array::eek:p[], so:

   Byte Array::eek:perator[](int index) { return pData[index]; }

It is created from pData[index] using the non-explicit constructor:

   Byte::Byte(unsigned char&);

You will see that it stores the unsigned char at pData[index] *by
reference*.  This is the whole point of using the proxy. Basically it
`represents' the stored data so that any operation upon the proxy is
*really* an operation upon the data it represents (stores).

The reason that you need:

   Byte::eek:perator unsigned char&();

and not:

   Byte::eek:perator unsigned char();

is that you need to allow assignment to the underlying unsigned char
through a Byte proxy.  Consider:

   Array array(4);
   // ...
   array[0] = 42U;

The last call is equivalent to:

   Byte b(array[0]);
   b.operator unsigned char&() = 42U;  // LHS returns reference to array[0]

Using a reference here 42U gets assigned to the actual unsigned char at
array[0].  If you consider the alternative, which would be:

   Byte b(array[0]);
   b.operator unsigned char() = 42U;   // LHS returns *temporary*
                                       // array[0] *not* changed

then what you would be attempting to do is assigne 42U to a *temporary*
returned by b.operator unsigned char(), which is not what you want.


Furthermore, the last assignment above will be rejected by the
compiler, thus ensuring that one will not get what one doesn't
want ;-)
 
I

Immortal Nephi

Immortal Nephi said:
   Someone posted his Byte class code on the previous thread.  I have a
question about operator cast ().  

I think that would be me.
Please explain the difference
between local object and reference object.  Why do you need reference
object?

If you remember, the Byte class was introduced as a `proxy' for your
Array class, which stored a pointer to an array of unsigned char.  A
Byte instance was returned by your Array::eek:p[], so:

   Byte Array::eek:perator[](int index) { return pData[index]; }

It is created from pData[index] using the non-explicit constructor:

   Byte::Byte(unsigned char&);

You explain very clear. Look very good. I want to add. I might
want to add more variables to the Byte’s constructor. I may want to
name Byte to be RGB8 (Red, Green, and Blue for pixels ).
RGB8 is limited to 8 bit. Red has 2 bits. Green and blue have 3
bits.
The array class has more data members like that.

class Array {
…..
…..
private:
friend class Byte;
unsigned char *m_Data;

int m_RedMax;
int m_GreenMax;
int m_BlueMax;
int m_ColorMax;

int m_ShiftRed; // m_ShiftRed = 6
int m_ShiftGreen; // m_ShiftGreen = 3

unsigned char m_RedMask; // m_RedMask = 0x3F
unsigned char m_GreenMask; // m_GreenMask = 0xC3
unsigned char m_BlueMask; // m_BlueMask = 0xF8

int m_Size;
};

You can always write four parameters in Constructor function
according to your preference.

I have one problem. I want to add ShiftRed_Ref and ShiftGreen_Ref to
Byte constructor. I have no reason to add three or more parameters in
Byte constructor, but two parameters should be sufficient. First
parameter is to be data_Ref and second parameter is to be Array_Ref.
I will need to establish bidirectional reference between Array object
and Byte object.

Byte Array::eek:perator[](int index) {
Byte byte( pData[ index ], *this );

return byte;
}

Notice friend class Byte. Array object’s operator[] uses Byte’s
member functions to modify pData and read some other Array’s data
members through bidirectional communication.

Please let me know if bidirectional communication is a good idea. If
not, what are you suggesting alternative? I like proxy Byte class.

You will see that it stores the unsigned char at pData[index] *by
reference*.  This is the whole point of using the proxy. Basically it
`represents' the stored data so that any operation upon the proxy is
*really* an operation upon the data it represents (stores).

The reason that you need:

   Byte::eek:perator unsigned char&();

and not:

   Byte::eek:perator unsigned char();

is that you need to allow assignment to the underlying unsigned char
through a Byte proxy.  Consider:

   Array array(4);
   // ...
   array[0] = 42U;

The last call is equivalent to:

   Byte b(array[0]);
   b.operator unsigned char&() = 42U;  // LHS returns reference to array[0]

Using a reference here 42U gets assigned to the actual unsigned char at
array[0].  If you consider the alternative, which would be:

   Byte b(array[0]);
   b.operator unsigned char() = 42U;   // LHS returns *temporary*
                                       // array[0] *not* changed

then what you would be attempting to do is assigne 42U to a *temporary*
returned by b.operator unsigned char(), which is not what you want.

Make sense!
 
P

Paul Bibbings

Immortal Nephi said:
I want to add. I might
want to add more variables to the Byte¡¯s constructor. I may want to
name Byte to be RGB8 (Red, Green, and Blue for pixels ).
RGB8 is limited to 8 bit. Red has 2 bits. Green and blue have 3
bits.

So, if I understand you, your RGB8 type will be able to handle 4 reds, 8
blues and 8 greens. OK.
The array class has more data members like that.

class Array {
¡­..
¡­..
private:
friend class Byte;
unsigned char *m_Data;

int m_RedMax;
int m_GreenMax;
int m_BlueMax;
int m_ColorMax;

int m_ShiftRed; // m_ShiftRed = 6
int m_ShiftGreen; // m_ShiftGreen = 3

unsigned char m_RedMask; // m_RedMask = 0x3F
unsigned char m_GreenMask; // m_GreenMask = 0xC3
unsigned char m_BlueMask; // m_BlueMask = 0xF8

int m_Size;
};

Please correct me if I am wrong, but all your members of type int
(except m_Size) and unsigned char are logically constants? I would also
suggest, if you wish them to be members of your class Array, it would be
better if the were static const members, and also that they were
public. I do not say that they *must* be so, but the sense that I have
of how you would like to use them, suggests that they are part of the
interface of class Array. Also, if they are so, then you may even be
able to avoid what you are calling the `bidirectional communication'
between Array and RGB8. As I understand it, your reason for wanting to
pass a reference to your Array instance in initializing your Byte return
value in Array::eek:p[] is merely to have access later to these members of
Array. If, however, they are indeed const and are declared public and
static, then you would not have the need for this bidirectional
communication at all (which, I have to say, I do not like).

One other point. Though I can only guess at the purpose of the
m_... members of Array above, I would have thought that the masks should
be:

m_RedMask = 0xC0 (1100 0000)
m_GreenMask = 0x38 (0011 1000)
m_BlueMask = 0x07 (0000 0111)

given how you describe them above. Here I am supposing that you want
the mask to do things like:

unsigned char aColor = anArray[0];
unsigned char redOnly = aColor & Array::m_RedMask;
unsigned char greenOnly = aColor & Array::m_GreenMask;
unsigned char blueOnly = aColor & Array::m_BlueMask;
You can always write four parameters in Constructor function
according to your preference.

I have one problem. I want to add ShiftRed_Ref and ShiftGreen_Ref to
Byte constructor.

You do not say what ShiftRed_Ref and ShiftGreen_Ref are. I can only
guess that they are references to Array::m_ShiftRed and
Array::m_ShiftGreen. If that is the case then they are also constants
and are better being public static const members of Array so that you
don't need to pass these at all.
I have no reason to add three or more parameters in
Byte constructor, but two parameters should be sufficient. First
parameter is to be data_Ref and second parameter is to be Array_Ref.
I will need to establish bidirectional reference between Array object
and Byte object.

Byte Array::eek:perator[](int index) {
Byte byte( pData[ index ], *this );

return byte;
}

Notice friend class Byte. Array object¡¯s operator[] uses Byte¡¯s
member functions to modify pData and read some other Array¡¯s data
members through bidirectional communication.

Here you speak about "read[ing] some other Array's data members through
bidirectional communication." If you are referring to the members of
Array that you have shown already, I hope that I have already given you
an idea as to how you might avoid your `bidirectional communication'
altogether, which I would suggest is the right thing to do, if I have
understood you correctly.

By "Array object's operator[] uses Byte's member function to modify
pData," do you mean something like you asked for in an earlier post,
like:

array[0].Set_HighNibble(...).Set_LowNibble
?
Please let me know if bidirectional communication is a good idea. If
not, what are you suggesting alternative? I like proxy Byte class.

To summarise, assuming that I have understood you correctly, I would
suggest that `bidirectional communication' - in the sense that
Array::eek:p[] returns a Byte object which holds a reference to the Array
object - is not a good idea. As I have shown, it is probably better
that the members of Array that are needed by Byte objects are logically
constant and can be public static members of the interface of Array.

Regards

Paul Bibbings
 
I

Immortal Nephi

So, if I understand you, your RGB8 type will be able to handle 4 reds, 8
blues and 8 greens.  OK.

Yes! Also would be 2 red, 2 green, 2 blues, 2 unused bits as RGB6 if
I want.
   The array class has more data members like that.
class Array {
…..
…..
private:
   friend class Byte;
   unsigned char *m_Data;
   int m_RedMax;
   int m_GreenMax;
   int m_BlueMax;
   int m_ColorMax;
   int m_ShiftRed; // m_ShiftRed = 6
   int m_ShiftGreen; // m_ShiftGreen = 3
   unsigned char m_RedMask; // m_RedMask = 0x3F
   unsigned char m_GreenMask; // m_GreenMask = 0xC3
   unsigned char m_BlueMask; // m_BlueMask = 0xF8
   int m_Size;
};

Please correct me if I am wrong, but all your members of type int
(except m_Size) and unsigned char are logically constants?  I would also
suggest, if you wish them to be members of your class Array, it would be
better if the were static const members, and also that they were
public.  I do not say that they *must* be so, but the sense that I have
of how you would like to use them, suggests that they are part of the
interface of class Array. Also, if they are so, then you may even be
able to avoid what you are calling the `bidirectional communication'
between Array and RGB8.  As I understand it, your reason for wanting to
pass a reference to your Array instance in initializing your Byte return
value in Array::eek:p[] is merely to have access later to these members of
Array.  If, however, they are indeed const and are declared public and
static, then you would not have the need for this bidirectional
communication at all (which, I have to say, I do not like).

Yes, I can choose either.

You are correct. Array object is very flexible. m_ShiftRed,
m_Green, m_RedMask, m_GreenMask, and m_BlueMask are not constants nor
static. They will be initialized by Array constructor.
Sometimes, I declare RGB3 or RGB6 or RGB8.

RGB3 has one bit of red, green, and blue. The total colors are 8.
RGB6 has two bit of red, green, blue. The total colors are 64.
RGB8 has two bit of red and three bits of green and blue. The total
colors are 256.

The bidirectional communication is the best option.
One other point.  Though I can only guess at the purpose of the
m_... members of Array above, I would have thought that the masks should
be:

   m_RedMask   = 0xC0 (1100 0000)
   m_GreenMask = 0x38 (0011 1000)
   m_BlueMask  = 0x07 (0000 0111)

given how you describe them above.  Here I am supposing that you want
the mask to do things like:

Should be correct values as reversed bits below.

m_RedMask = 0x3F (0011 1111)
m_GreenMask = 0xC7 (1100 0111)
m_BlueMask = 0xF8 (1111 1000)

Byte object has eight member functions when I declare RGB8

Byte& Set_Color( unsigned char val ) { // Assigns 8 bits red, green, &
blue
dataRef = val;
return *this;
}

Byte& Set_Red( unsigned char val ) { // Assigns 2 bit red
dataRef &= m_RedMask; // 0x3F -- Clear 7-6 bits and preserve 5-0 bits
unsigned char temp = val << m_ShiftRed;
dataRef |= temp;
return *this;
}

Byte& Set_Green( unsigned char val ) { // Assigns 3 bit green
dataRef &= m_GreenMask; // 0xC3 -- Clear 5-3 bits and preserve 7-6
bits and 2-0 bits
unsigned char temp = val << m_ShiftGreen;
dataRef |= temp;
return *this;
}

Byte& Set_Blue( unsigned char val ) { Assigns 3 bit blue
dataRef &= m_BlueMask; // 0xF8 -- Clear 2-0 bits and preserve 7-3
bits
dataRef |= val;
return *this;
}

unsigned char Get_Color() {
return dataRef;
}

unsigned char Get_Red() {
return ( dataRef >> m_ShiftRed ) & 0x02;
}

unsigned char Get_Green()
return ( dataRef >> m_ShiftGreen ) & 0x03;
}

unsigned char Get_Blue()
return dataRef & 0x03;
}

All 8 member functions need to read array object’s data members
through bidirectional communication.
   unsigned char aColor    = anArray[0];
   unsigned char redOnly   = aColor & Array::m_RedMask;
   unsigned char greenOnly = aColor & Array::m_GreenMask;
   unsigned char blueOnly  = aColor & Array::m_BlueMask;

I prefer that red, green, and blue bits are LSB after use right
shifts.
   You can always write four parameters in Constructor function
according to your preference.
   I have one problem.  I want to add ShiftRed_Ref and ShiftGreen_Ref to
Byte constructor.  

You do not say what ShiftRed_Ref and ShiftGreen_Ref are.  I can only
guess that they are references to Array::m_ShiftRed and
Array::m_ShiftGreen.  If that is the case then they are also constants
and are better being public static const members of Array so that you
don't need to pass these at all.
I have no reason to add three or more parameters in
Byte constructor, but two parameters should be sufficient.  First
parameter is to be data_Ref and second parameter is to be Array_Ref.
I will need to establish bidirectional reference between Array object
and Byte object.
Byte Array::eek:perator[](int index) {
   Byte byte( pData[ index ], *this );
   return byte;
}
   Notice friend class Byte.  Array object’s operator[] uses Byte’s
member functions to modify pData and read some other Array’s data
members through bidirectional communication.

Here you speak about "read[ing] some other Array's data members through
bidirectional communication."  If you are referring to the members of
Array that you have shown already, I hope that I have already given you
an idea as to how you might avoid your `bidirectional communication'
altogether, which I would suggest is the right thing to do, if I have
understood you correctly.

I will not want to avoid bidirectional communication unless some data
members are not constants nor static.
By "Array object's operator[] uses Byte's member function to modify
pData," do you mean something like you asked for in an earlier post,
like:

   array[0].Set_HighNibble(...).Set_LowNibble
?

Yes, member functions are examples of previous thread post.

would be array[0].Set_Red(...).Set_Green(...).Set_Blue(...)
   Please let me know if bidirectional communication is a good idea..  If
not, what are you suggesting alternative?  I like proxy Byte class.

To summarise, assuming that I have understood you correctly, I would
suggest that `bidirectional communication' - in the sense that
Array::eek:p[] returns a Byte object which holds a reference to the Array
object - is not a good idea.  As I have shown, it is probably better
that the members of Array that are needed by Byte objects are logically
constant and can be public static members of the interface of Array.

You understood very well. Good suggestions! Are you going to
agree? I will not want to avoid bidirectional communications unless
Array object is very flexible to configure inialization.
 
P

Paul Bibbings

Immortal Nephi said:
Should be correct values as reversed bits below.

m_RedMask = 0x3F (0011 1111)
m_GreenMask = 0xC7 (1100 0111)
m_BlueMask = 0xF8 (1111 1000)

Byte object has eight member functions when I declare RGB8

I understand.
Byte& Set_Color( unsigned char val ) { // Assigns 8 bits red, green, &
blue
dataRef = val;
return *this;
}

This can be replaced (but doesn't have to be) by:

Byte& Byte::eek:perator=(unsigned int new_col);

(see example below).
unsigned char Get_Color() {
return dataRef;
}

This can be replaced (but doesn't have to be) by:

Byte::eek:perator unsigned char&();
Byte::eek:perator unsigned char() const;

(see example below).
unsigned char Get_Red() {
return ( dataRef >> m_ShiftRed ) & 0x02;
^^^^
Should be 0x03
}

unsigned char Get_Green()
return ( dataRef >> m_ShiftGreen ) & 0x03;
^^^^
Should be 0x07
}

unsigned char Get_Blue()
return dataRef & 0x03;
^^^^
Should be 0x07
You understood very well. Good suggestions! Are you going to
agree? I will not want to avoid bidirectional communications unless
Array object is very flexible to configure inialization.

Before you decide on using `bidirectional communication', consider
something like the following, which uses a `traits' type. Note that I
have changed some of the names to match what it is that I think you are
trying to do.

#include <iostream>
#include <new>

enum bits { rgb3 = 3, rgb6 = 6, rgb8 = 8 };

template<bits>
struct color_traits;

template<>
struct color_traits<rgb3> { /* ... */ };

template<>
struct color_traits<rgb6> { /* ... */ };

template<>
struct color_traits<rgb8>
{
// ...

static const int RedBits = 0x03;
static const int GreenBits = 0x07;
static const int BlueBits = 0x07;

static const int ShiftRed = 6;
static const int ShiftGreen = 3;

static const unsigned char ClearRedMask = 0x3F;
static const unsigned char ClearGreenMask = 0xC7;
static const unsigned char ClearBlueMask = 0xF8;
};

template<
bits Bits,
typename Tr = color_traits said:
class Color {
public:
Color(unsigned char& col)
: m_col(col)
{ }
~Color() { }
Color(const Color& other)
: m_col(other.m_col)
{ }
Color& operator=(const Color& other)
{
if (this != &other)
{
this->~Color();
new (this) Color(other);
}
return *this;
}
Color& operator=(unsigned char new_col) // Set_Color
{
m_col = new_col;
return *this;
}
Color& Set_Red(unsigned char red)
{
m_col &= Tr::ClearRedMask;
unsigned char temp = red << Tr::ShiftRed;
m_col |= temp;
return *this;
}
Color& Set_Green(unsigned char green)
{
m_col &= Tr::ClearGreenMask;
unsigned char temp = green << Tr::ShiftGreen;
m_col |= temp;
return *this;
}
Color& Set_Blue(unsigned char blue)
{
m_col &= Tr::ClearBlueMask;
m_col |= blue;
return *this;
}
operator unsigned char&() { return m_col; } // Get_Color
operator unsigned char() const { return m_col; } // Get_Color
unsigned char Get_Red() const
{
return (m_col >> Tr::ShiftRed) & Tr::RedBits;
}
unsigned char Get_Green() const
{
return (m_col >> Tr::ShiftGreen) & Tr::GreenBits;
}
unsigned char Get_Blue() const
{
return m_col & Tr::BlueBits;
}
private:
unsigned char& m_col;
};

template<bits Bits>
class ColorArray {
public:
ColorArray(int size)
: pData(new unsigned char[size])
{ }
~ColorArray() { delete [] pData; }
Color<Bits> operator[](int index)
{
return pData[index];
}
const Color<Bits> operator[](int index) const
{
return pData[index];
}
private:
unsigned char *pData;
};

typedef ColorArray<rgb3> RGB3;
typedef ColorArray<rgb6> RGB6;
typedef ColorArray<rgb8> RGB8;

int main()
{
RGB8 col_array8(4);
col_array8[0].Set_Red(0x01).Set_Green(0x07).Set_Blue(0x05);
col_array8[1].Set_Red(col_array8[0].Get_Red() + 1)
.Set_Green(col_array8[0].Get_Green() - 3)
.Set_Blue(col_array8[0].Get_Blue() - 2);
std::cout << "red : " << int(col_array8[1].Get_Red()) << '\n';
std::cout << "green: " << int(col_array8[1].Get_Green()) << '\n';
std::cout << "blue : " << int(col_array8[1].Get_Blue()) << '\n';
}

/**
* Output:
* red : 2
* green: 4
* blue : 3
*/

I do not say that this is the right way to do it, and it may not work
for you for other reasons. However, it is an idea, at least.

Regards

Paul Bibbings
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top