Operator Cast () Reference?

Discussion in 'C++' started by Immortal Nephi, Jun 10, 2010.

  1. 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;
    }
     
    Immortal Nephi, Jun 10, 2010
    #1
    1. Advertising

  2. Immortal Nephi <> wrote:
    >         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...)
     
    Francesco S. Carta, Jun 10, 2010
    #2
    1. Advertising

  3. Immortal Nephi

    Twixer Xev Guest

    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.
     
    Twixer Xev, Jun 10, 2010
    #3
  4. Twixer Xev <> wrote:
    > 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...

    --
    FSC
    http://userscripts.org/scripts/show/59948
     
    Francesco S. Carta, Jun 10, 2010
    #4
  5. "Francesco S. Carta" <> wrote:
    > Twixer Xev <> wrote:
    > > 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...
    >


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

    Still puzzled.

    --
    FSC
    http://userscripts.org/scripts/show/59948
     
    Francesco S. Carta, Jun 10, 2010
    #5
  6. Immortal Nephi

    Twixer Xev Guest

    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.
     
    Twixer Xev, Jun 10, 2010
    #6
  7. Immortal Nephi

    Twixer Xev Guest

    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.
     
    Twixer Xev, Jun 10, 2010
    #7
  8. Twixer Xev <> wrote:
    > 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 :)

    --
    FSC
    http://userscripts.org/scripts/show/59948
     
    Francesco S. Carta, Jun 10, 2010
    #8
  9. "Francesco S. Carta" <> wrote:
    > Twixer Xev <> wrote:
    > > 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 :)
    >


    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 ;-)

    --
    FSC
    http://userscripts.org/scripts/show/59948
     
    Francesco S. Carta, Jun 10, 2010
    #9
  10. Immortal Nephi

    Twixer Xev Guest

    > (*) 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.
     
    Twixer Xev, Jun 10, 2010
    #10
  11. Immortal Nephi

    Goran Guest

    On Jun 10, 3:45 am, "Francesco S. Carta" <> wrote:
    > Immortal Nephi <> wrote:
    > >         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...)


    Try:

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

    You have a bad case of conversionconstructoritis there.

    Goran.
     
    Goran, Jun 10, 2010
    #11
  12. Goran <> wrote:
    > On Jun 10, 3:45 am, "Francesco S. Carta" <> wrote:
    >
    >
    >
    > > Immortal Nephi <> wrote:
    > > >         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...)

    >
    > 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 :)

    --
    FSC
    http://userscripts.org/scripts/show/59948
     
    Francesco S. Carta, Jun 10, 2010
    #12
  13. Twixer Xev <> wrote:
    > > (*) 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.


    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).

    --
    FSC
    http://userscripts.org/scripts/show/59948
     
    Francesco S. Carta, Jun 10, 2010
    #13
  14. Immortal Nephi <> writes:

    > 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
     
    Paul Bibbings, Jun 10, 2010
    #14
  15. Paul Bibbings <> writes:

    > 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
     
    Paul Bibbings, Jun 10, 2010
    #15
  16. Paul Bibbings <> wrote:
    > Immortal Nephi <> writes:
    > >    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 ;-)

    --
    FSC
    http://userscripts.org/scripts/show/59948
     
    Francesco S. Carta, Jun 10, 2010
    #16
  17. On Jun 10, 9:23 am, Paul Bibbings <> wrote:
    > Immortal Nephi <> writes:
    > >    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!
     
    Immortal Nephi, Jun 10, 2010
    #17
  18. Immortal Nephi <> writes:

    > 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
     
    Paul Bibbings, Jun 10, 2010
    #18
  19. On Jun 10, 5:12 pm, Paul Bibbings <> wrote:
    > Immortal Nephi <> writes:
    > > 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.


    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.
     
    Immortal Nephi, Jun 11, 2010
    #19
  20. Immortal Nephi <> writes:

    > On Jun 10, 5:12 pm, Paul Bibbings <> wrote:
    >> 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


    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<Bits>
    >

    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
     
    Paul Bibbings, Jun 11, 2010
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. MSG

    to cast or not to cast malloc ?

    MSG, Feb 6, 2004, in forum: C Programming
    Replies:
    38
    Views:
    1,117
    Dan Pop
    Feb 10, 2004
  2. EvilRix
    Replies:
    8
    Views:
    677
    Martin Dickopp
    Feb 14, 2004
  3. John Goche
    Replies:
    2
    Views:
    369
    Frederick Gotham
    Sep 4, 2006
  4. Immortal Nephi
    Replies:
    6
    Views:
    386
    Paul Bibbings
    Jul 21, 2010
  5. Pavel
    Replies:
    7
    Views:
    561
    Pavel
    Sep 19, 2010
Loading...

Share This Page