Original Post
For some time I have been upset that C++ does not support properties the way that C# and Java do. As a result, I wrote the following property library and used it for my color components. The idea was that the set method could clamp the values. Unfortunately, I could not find a way to make the Color class itself legable. Thus, although client code can use the properties naturally, I fear that client code would be incapable of implementing its own properties due to the complexity involved. Does anyone have any ideas as to how to emulate C#/Java properties better?
/***************************************
* Property.h *
* *
* Author: Jason Hise *
* Created: 08.12.2004 *
* Purpose: Emulate C#/Java properties *
* *
***************************************/
#ifndef _PROPERTY_H_
#define _PROPERTY_H_
// warns about using 'this' pointer in initializer lists,
// which we have to do to initialize self
#pragma warning ( disable : 4355 )
namespace utils
{
template < typename C, typename T >
struct Property
{
protected:
C * const self;
virtual const T & Get ( ) const = 0;
virtual void Set ( const T & value ) = 0;
public:
Property ( C * const self );
operator const T & ( ) const;
template < typename RHS >
const Property < C, T > & operator = ( const RHS & rhs );
template < typename RHS >
const Property < C, T > & operator += ( const RHS & rhs );
template < typename RHS >
const Property < C, T > & operator -= ( const RHS & rhs );
template < typename RHS >
const Property < C, T > & operator *= ( const RHS & rhs );
template < typename RHS >
const Property < C, T > & operator /= ( const RHS & rhs );
template < typename RHS >
const Property < C, T > & operator %= ( const RHS & rhs );
template < typename RHS >
const Property < C, T > & operator >>= ( const RHS & rhs );
template < typename RHS >
const Property < C, T > & operator <<= ( const RHS & rhs );
template < typename RHS >
const Property < C, T > & operator &= ( const RHS & rhs );
template < typename RHS >
const Property < C, T > & operator |= ( const RHS & rhs );
template < typename RHS >
const Property < C, T > & operator ^= ( const RHS & rhs );
Property < C, T > & operator ++ ( );
T operator ++ ( int );
Property < C, T > & operator -- ( );
T operator -- ( int );
};
template < typename C, typename T >
Property < C, T > :: Property ( C * const self ) :
self ( self )
{
}
template < typename C, typename T >
Property < C, T > :: operator const T & ( ) const
{
return Get ( );
}
template < typename C, typename T >
template < typename RHS >
const Property < C, T > & Property < C, T > :: operator = ( const RHS & rhs )
{
Set ( rhs );
return *this;
}
template < typename C, typename T >
template < typename RHS >
const Property < C, T > & Property < C, T > :: operator += ( const RHS & rhs )
{
Set ( Get ( ) + rhs );
return *this;
}
template < typename C, typename T >
template < typename RHS >
const Property < C, T > & Property < C, T > :: operator -= ( const RHS & rhs )
{
Set ( Get ( ) - rhs );
return *this;
}
template < typename C, typename T >
template < typename RHS >
const Property < C, T > & Property < C, T > :: operator *= ( const RHS & rhs )
{
Set ( Get ( ) * rhs );
return *this;
}
template < typename C, typename T >
template < typename RHS >
const Property < C, T > & Property < C, T > :: operator /= ( const RHS & rhs )
{
Set ( Get ( ) / rhs );
return *this;
}
template < typename C, typename T >
template < typename RHS >
const Property < C, T > & Property < C, T > :: operator %= ( const RHS & rhs )
{
Set ( Get ( ) % rhs );
return *this;
}
template < typename C, typename T >
template < typename RHS >
const Property < C, T > & Property < C, T > :: operator >>= ( const RHS & rhs )
{
Set ( Get ( ) >> rhs );
return *this;
}
template < typename C, typename T >
template < typename RHS >
const Property < C, T > & Property < C, T > :: operator <<= ( const RHS & rhs )
{
Set ( Get ( ) << rhs );
return *this;
}
template < typename C, typename T >
template < typename RHS >
const Property < C, T > & Property < C, T > :: operator &= ( const RHS & rhs )
{
Set ( Get ( ) & rhs );
return *this;
}
template < typename C, typename T >
template < typename RHS >
const Property < C, T > & Property < C, T > :: operator |= ( const RHS & rhs )
{
Set ( Get ( ) | rhs );
return *this;
}
template < typename C, typename T >
template < typename RHS >
const Property < C, T > & Property < C, T > :: operator ^= ( const RHS & rhs )
{
Set ( Get ( ) ^ rhs );
return *this;
}
template < typename C, typename T >
Property < C, T > & Property < C, T > :: operator ++ ( )
{
T val ( Get ( ) );
++val;
Set ( val );
return *this;
}
template < typename C, typename T >
T Property < C, T > :: operator ++ ( int )
{
T val ( Get ( ) );
T ans ( val++ );
Set ( val );
return ans;
}
template < typename C, typename T >
Property < C, T > & Property < C, T > :: operator -- ( )
{
T val ( Get ( ) );
--val;
Set ( val );
return *this;
}
template < typename C, typename T >
T Property < C, T > :: operator -- ( int )
{
T val ( Get ( ) );
T ans ( val-- );
Set ( val );
return ans;
}
}
#endif//_PROPERTY_H_
/*********************************************************
* Color.h *
* *
* Author: Jason Hise *
* Created: 04.14.2004 *
* Purpose: Define color type capable of alpha blending *
* *
* Revised: Jason Hise, 08.12.2004 *
* Implemented components as clamped properties *
* *
*********************************************************/
#ifndef _COLOR_H_
#define _COLOR_H_
#include <algorithm>
#include <Types.h>
#include <Vector4D.h>
#include <Property.h>
namespace gfx
{
struct Color
{
private:
utils::real32 r, g, b, a;
struct property_r : public utils::Property < Color, utils::real32 >
{
using utils::Property < Color, utils::real32 >::operator =;
property_r ( Color * const self );
virtual const utils::real32 & Get ( ) const;
virtual void Set ( const utils::real32 & value );
};
struct property_g : public utils::Property < Color, utils::real32 >
{
using utils::Property < Color, utils::real32 >::operator =;
property_g ( Color * const self );
virtual const utils::real32 & Get ( ) const;
virtual void Set ( const utils::real32 & value );
};
struct property_b : public utils::Property < Color, utils::real32 >
{
using utils::Property < Color, utils::real32 >::operator =;
property_b ( Color * const self );
virtual const utils::real32 & Get ( ) const;
virtual void Set ( const utils::real32 & value );
};
struct property_a : public utils::Property < Color, utils::real32 >
{
using utils::Property < Color, utils::real32 >::operator =;
property_a ( Color * const self );
virtual const utils::real32 & Get ( ) const;
virtual void Set ( const utils::real32 & value );
};
public:
property_r R;
property_g G;
property_b B;
property_a A;
Color ( );
Color ( const Color & rhs );
Color ( utils::uint32 RGBA );
Color ( utils::real32 R, utils::real32 G, utils::real32 B, utils::real32 A = 1.0f );
Color ( utils::uint8 R, utils::uint8 G, utils::uint8 B, utils::uint8 A = 0xFF );
Color ( int R, int G, int B, int A = 255 );
Color ( utils::Vector4D < utils::real32 > color );
utils::Vector4D < utils::real32 > CastVector4D ( ) const;
utils::Vector3D < utils::real32 > CastVector3D ( ) const;
const Color & operator = ( const Color & rhs );
Color operator * ( const Color & rhs ) const;
Color operator * ( const utils::real32 & rhs ) const;
const Color & operator *= ( const Color & rhs );
const Color & operator *= ( const utils::real32 & rhs );
bool operator == ( const Color & rhs ) const;
bool operator != ( const Color & rhs ) const;
static Color Blend ( const Color & c1, const Color & c2, utils::real32 per1 = 0.5f, utils::real32 per2 = 0.5f );
static Color AlphaBlend ( const Color & ontop, const Color & beneath );
};
extern const Color InvalidColor;
extern const Color Black;
extern const Color Gray;
extern const Color Silver;
extern const Color White;
extern const Color Red;
extern const Color Green;
extern const Color Blue;
extern const Color Yellow;
extern const Color Cyan;
extern const Color Magenta;
extern const Color BrightRed;
extern const Color BrightGreen;
extern const Color BrightBlue;
extern const Color DarkRed;
extern const Color DarkGreen;
extern const Color DarkBlue;
extern const Color DarkYellow;
extern const Color DarkCyan;
extern const Color DarkMagenta;
extern const Color Orange;
extern const Color Brown;
}
#endif//_COLOR_H_
//Color.cpp--------------------------------------
#include <stdexcept>
#include "../Include/Color.h"
namespace gfx
{
// PROPERTIES
Color :: property_r :: property_r ( Color * const self ) :
utils::Property < Color, utils::real32 > ( self )
{
}
const utils::real32 & Color :: property_r :: Get ( ) const
{
return self->r;
}
void Color :: property_r :: Set ( const utils::real32 & value )
{
self->r = value;
if ( self->r > 1.0f ) self->r = 1.0f;
if ( self->r < 0.0f ) self->r = 0.0f;
}
Color :: property_g :: property_g ( Color * const self ) :
utils::Property < Color, utils::real32 > ( self )
{
}
const utils::real32 & Color :: property_g :: Get ( ) const
{
return self->g;
}
void Color :: property_g :: Set ( const utils::real32 & value )
{
self->g = value;
if ( self->g > 1.0f ) self->r = 1.0f;
if ( self->g < 0.0f ) self->r = 0.0f;
}
Color :: property_b :: property_b ( Color * const self ) :
utils::Property < Color, utils::real32 > ( self )
{
}
const utils::real32 & Color :: property_b :: Get ( ) const
{
return self->b;
}
void Color :: property_b :: Set ( const utils::real32 & value )
{
self->b = value;
if ( self->b > 1.0f ) self->b = 1.0f;
if ( self->b < 0.0f ) self->b = 0.0f;
}
Color :: property_a :: property_a ( Color * const self ) :
utils::Property < Color, utils::real32 > ( self )
{
}
const utils::real32 & Color :: property_a :: Get ( ) const
{
return self->a;
}
void Color :: property_a :: Set ( const utils::real32 & value )
{
self->a = value;
if ( self->a > 1.0f ) self->a = 1.0f;
if ( self->a < 0.0f ) self->a = 0.0f;
}
// END PROPERTIES
Color :: Color ( ) :
R ( this ),
G ( this ),
B ( this ),
A ( this )
{
}
Color :: Color ( const Color & rhs ) :
R ( this ),
G ( this ),
B ( this ),
A ( this )
{
*this = rhs;
}
Color :: Color ( utils::uint32 RGBA ) :
R ( this ),
G ( this ),
B ( this ),
A ( this ),
r ( ( ( RGBA & 0xFF000000 ) >> 24 ) / 255.0f ),
g ( ( ( RGBA & 0x00FF0000 ) >> 16 ) / 255.0f ),
b ( ( ( RGBA & 0x0000FF00 ) >> 8 ) / 255.0f ),
a ( ( RGBA & 0x000000FF ) / 255.0f )
{
}
Color :: Color ( utils::real32 R, utils::real32 G, utils::real32 B, utils::real32 A ) :
R ( this ),
G ( this ),
B ( this ),
A ( this ),
r ( R ),
g ( G ),
b ( B ),
a ( A )
{
}
Color :: Color ( utils::uint8 R, utils::uint8 G, utils::uint8 B, utils::uint8 A ) :
R ( this ),
G ( this ),
B ( this ),
A ( this ),
r ( R / 255.0f ),
g ( G / 255.0f ),
b ( B / 255.0f ),
a ( A / 255.0f )
{
}
Color :: Color ( int R, int G, int B, int A ) :
R ( this ),
G ( this ),
B ( this ),
A ( this ),
r ( R / 255.0f ),
g ( G / 255.0f ),
b ( B / 255.0f ),
a ( A / 255.0f )
{
}
Color :: Color ( utils::Vector4D < utils::real32 > color ) :
R ( this ),
G ( this ),
B ( this ),
A ( this ),
r ( color.X ),
g ( color.Y ),
b ( color.Z ),
a ( color.W )
{
}
utils::Vector4D < utils::real32 > Color :: CastVector4D ( ) const
{
return utils::Vector4D < utils::real32 > ( R, G, B, A );
}
utils::Vector3D < utils::real32 > Color :: CastVector3D ( ) const
{
return utils::Vector3D < utils::real32 > ( R, G, B );
}
const Color & Color :: operator = ( const Color & rhs )
{
R = rhs.R;
G = rhs.G;
B = rhs.B;
A = rhs.A;
return *this;
}
Color Color :: operator * ( const Color & rhs ) const
{
return Color ( R * rhs.R, G * rhs.G, B * rhs.B, A * rhs.A );
}
Color Color :: operator * ( const utils::real32 & rhs ) const
{
return Color ( R * rhs, G * rhs, B * rhs, A * rhs );
}
const Color & Color :: operator *= ( const Color & rhs )
{
return *this = *this * rhs;
}
const Color & Color :: operator *= ( const utils::real32 & rhs )
{
return *this = *this * rhs;
}
bool Color :: operator == ( const Color & rhs ) const
{
return R == rhs.R && G == rhs.G && B == rhs.B && A == rhs.A;
}
bool Color :: operator != ( const Color & rhs ) const
{
return !( *this == rhs );
}
// take percentage of each color
Color Color :: Blend ( const Color & c1, const Color & c2, utils::real32 per1, utils::real32 per2 )
{
return Color ( ( c1.R * per1 ) + ( c2.R * per2 ),
( c1.G * per1 ) + ( c2.G * per2 ),
( c1.B * per1 ) + ( c2.B * per2 ),
( c1.A * per1 ) + ( c2.A * per2 ) );
}
// ontop's alpha determines how much ontop to use and how much beneath shows through
// resulting color has an alpha closer to one than both of its components
Color Color :: AlphaBlend ( const Color & ontop, const Color & beneath )
{
return Color ( ontop.A * ontop.R + ( 1.0f - ontop.A ) * beneath.R,
ontop.A * ontop.G + ( 1.0f - ontop.A ) * beneath.G,
ontop.A * ontop.B + ( 1.0f - ontop.A ) * beneath.B,
1.0f - ( 1.0f - ontop.A ) * ( 1.0f - beneath.A ) );
}
const Color InvalidColor = Color ( 0x00000000 );
const Color Black = Color ( 0x000000FF );
const Color Gray = Color ( 0x808080FF );
const Color Silver = Color ( 0xC0C0C0FF );
const Color White = Color ( 0xFFFFFFFF );
const Color Red = Color ( 0xFF0000FF );
const Color Green = Color ( 0x00FF00FF );
const Color Blue = Color ( 0x0000FFFF );
const Color Yellow = Color ( 0xFFFF00FF );
const Color Cyan = Color ( 0x00FFFFFF );
const Color Magenta = Color ( 0xFF00FFFF );
const Color BrightRed = Color ( 0xFF8080FF );
const Color BrightGreen = Color ( 0x80FF80FF );
const Color BrightBlue = Color ( 0x8080FFFF );
const Color DarkRed = Color ( 0x800000FF );
const Color DarkGreen = Color ( 0x008000FF );
const Color DarkBlue = Color ( 0x000080FF );
const Color DarkYellow = Color ( 0x808000FF );
const Color DarkCyan = Color ( 0x008080FF );
const Color DarkMagenta = Color ( 0x800080FF );
const Color Orange = Color ( 0xFF8040FF );
const Color Brown = Color ( 0x804000FF );
}