Skip to main content
GameDev.net gamedev.net
🔒 Locked

C++ 'properties'

Started by OrigamiMan64 Aug 12, 2004 at 9:26 PM 11 replies 1.7k views
Original Post
OrigamiMan64
OrigamiMan64
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 );
}


MaulingMonkey
MaulingMonkey
Quote:
Original post by OrigamiMan64Unfortunately, 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.


Err, could you elaborate please? Do you mean, you want the color class to be implemented as a property, with it's subproperties? I'm not seeing what the problem is 0_o.

I also hate properties on principle, but well, I won't comment unduly on that ^_^.

Also, what the heck is up with this "self" pointer?!? self == this correct?!? **is too lazy to read all the code**
Shannon Barber
Shannon Barber
Quote:
Original post by OrigamiMan64
For some time I have been upset that C++ does not support properties the way that C# and Java do.


It's common extention, it's just not standard. Which compiler are you using?
The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
DrPizza
DrPizza
Quote:
and Java do

Huh?
char a[99999],*p=a;int main(int c,char**V){char*v=c>0?1[V]:(char*)V;if(c>=0)for(;*v&&93!=*v;){62==*v&&++p||60==*v&&--p||43==*v&&++*p||45==*v&&--*p||44==*v&&(*p=getchar())||46==*v&&putchar(*p)||91==*v&&(*p&&main(0,(char**)(--v+2))||(v=(char*)main(-1,(char**)++v)-1));++v;}else for(c=1;c;c+=(91==*v)-(93==*v),++v);return(int)v;} /*** drpizza@battleaxe.net ***/
the Speed Bump
the Speed Bump
Personally, I would avoid using property-like semantics in C++ code right now, unless you're absolutely sure that you never, ever want anybody else to read it. ;)

Properties are not a C++ concept, which is to say that programmers in "C++ mode" do not think in terms of properties, and don't expect to see them.

That being said, there are lots of proprietary extensions for them, all of which are specific to MSVC. (actually, that's probably not true at all, but MSVC and g++ are the only C++ compilers I use regularly)

// MSVC 6 and on:struct Example {    int getX() { ... }    void setX(int value) { ... }    __declspec(property(get=getX, set=setX)) int x;    int getItem(int x, int y) { ... }    void setItem(int x, int y, int value) { ... }    // looks like you can also do this (!!!)    __declspec(property(get=getItem, set=setItem) int x[][];};// MSVC 7 and on.  Produced code must be managed.  Requires .NET etc__gc struct Example {    // get_ and set_ prefixes are mandatory and important    __property int get_x() { ... }    __property void set_x(int value) { ... }};// MSVC8 less achieves property zen.// Can be used even in unmanaged code, so there's no .NET dependancy.// still nonstandard, but I'll be damned if it isn't the// prettiest evil proprietary C++ extension I ever saw:struct Test {    property int Count {        int get() {            static int counter = 0;            return counter++;        }    }};


And that pretty much sums up the world's effort to loosen C++'s requirement that functions use the function application operator. (except for all the stuff that I don't know about)

(come to think of it, Pascal has never required parenthesis on functions which recieve no arguments)

Also, just for the heck of it, here's a property template that doesn't automatically handle the "self" bit, but should hypothetically be completely inlinable by the compiler. I wrote it awhile ago for kicks. (it's pretty dumb, though: properties made with this can't define their own constructors to set data members)
/** * Neat property template junk. * Coded by Andy Friesen * 13 December 2003 * * This file is released into the public domain. * * The Property template accepts two arguments: the type, and * the type of a getter/setter helper struct.  This struct must have * a few methods, depending on context.  Normally, it should be: * struct MyGetSet { *    T& get() { ... } *    void set(const T& rhs) { ... } * }; * * If the type of the property is const, then the property is a readonly * property.  The getter/setter then only needs one method: * struct MyGet { *     const T& get() { ... } // note that this method does not need to be const! * }; * * Lastly, if the type is a const PDT, it gets even simpler: * struct MyGet { *     T get() { ... } * }; * * For nonvoid pointer types, operators ->, [] and * are overridden for * convenience. * * Example: *    struct MyGet { *        int q; *        int get() { return q; } *    }; *    Property<int, MyGet> myProperty; *    myProperty = 9;                                   // calls MyGet::set() *    int i = myProperty;                               // i is set to the return value of MyGet::get() * * Another example: *    struct MyGetSet { *        SomeStruct*& get() { *           return SomeStruct::getSingletonInstance(); *        } *        void set(SomeStruct* ss) { *           assert(SomeStruct::getSingletonInstance() == 0); // be sure the singleton instance hasn't been set yet *           SomeStruct::setSingletonInstance(ss);            // and set it.  (it's a stupid example.  pretend SomeStruct is polymorphic) *        } *    }; *    Property<SomeStruct*, MyGetSet> myProperty; *    myProperty = new SomeStruct;                      // calls set.  Be sure you don't leak if you do this! *    myProperty = new SomeStruct;                      // would raise an assertion failure (see above) *    myProperty->member = 9;                           // calls get, sets the member *    myProperty->method();                             // should work normally *    SomeStruct& someReference = *myProperty;          // should also work */#ifndef PROPERTY_H#define PROPERTY_H/** * The only time I can possibly see this as being useful is when a better * GetSetter is going to replace this soon.  Maybe it should be an error to * omit GetSet. */template <typename T>struct DefaultGetSet{    T t;    inline T& get() { return t; }    inline void set(const T& u) { t = u; }};template <typename T>struct DefaultGetSet<const T>; // A default const property doesn't sound very useful to me.  Make it an error./** * Convenience base class thingie. */template <typename T, typename GetSet>struct PropertyBase{protected:    GetSet helper;};/** * if T is const, then it is a readonly property. * If it was just a constant variable, you could use a constant.  This is much * more interesting. :) * * It also means we need to hack around our shameless adding of yet another * possible meaning to const. */template <typename T, typename GetSet>struct PropertyBase<const T, GetSet>{protected:    mutable GetSet helper;};template <typename T, typename GetSet=DefaultGetSet<T> >struct Property : PropertyBase<T, GetSet>{    operator T&() {        return helper.get();    }    Property& operator = (const T& rhs) {        helper.set(rhs);        return *this;    }};template <typename T, typename GetSet>struct Property<T*, GetSet> : PropertyBase<T, GetSet>{    operator T&() {        return helper.get();    }    Property& operator = (const T& rhs) {        helper.set(rhs);        return *this;    }    T& operator ->() {        return helper.get();    }    T& operator *() {        return helper.get();    }    T& operator [](int i) {        return helper.get();    }};template <typename T, typename GetSet>struct Property<const T, GetSet> : PropertyBase<T, GetSet>{    operator const T&() const {        return helper.get();    }};template <typename T, typename GetSet>struct Property<const T*, GetSet> : PropertyBase<T, GetSet>{    operator const T&() const {        return helper.get();    }    const T& operator ->() const {        return helper.get();    }    const T& operator *() const {        return helper.get();    }    const T& operator [](int i) const {        return helper.get();    }};template <typename GetSet>struct Property<const void*, GetSet> : PropertyBase<void*, GetSet>{    operator const void*() const {        return helper.get();    }};template <typename GetSet>struct Property<void*, GetSet> : PropertyBase<void*, GetSet>{    operator void*&() {        return helper.get();    }    Property& operator = (void* rhs) {        helper.set(rhs);        return *this;    }};/* * Convenience specializations for const PDT types. * get() can just return a T, instead of having to go to all the trouble of * returning a const T& */template <typename T, typename GetSet>struct PDTProperty : PropertyBase<T, GetSet>{    operator T() const { return helper.get(); }};template <typename GetSet> struct Property<const int, GetSet> : PDTProperty<const int, GetSet>{};template <typename GetSet> struct Property<const unsigned int, GetSet> : PDTProperty<const unsigned int, GetSet>{};template <typename GetSet> struct Property<const short, GetSet> : PDTProperty<const short, GetSet>{};template <typename GetSet> struct Property<const unsigned short, GetSet> : PDTProperty<const unsigned short, GetSet>{};template <typename GetSet> struct Property<const char, GetSet> : PDTProperty<const char, GetSet>{};template <typename GetSet> struct Property<const unsigned char, GetSet> : PDTProperty<const unsigned char, GetSet>{};template <typename GetSet> struct Property<const signed char, GetSet> : PDTProperty<const signed char, GetSet>{};template <typename GetSet> struct Property<const float, GetSet> : PDTProperty<const float, GetSet>{};template <typename GetSet> struct Property<const double, GetSet> : PDTProperty<const double, GetSet>{};#endif
"There is only one everything"
amag
amag
I wrote a simple property template a while back posted here, although considered evil by most other posters (I think they were just envious I thought of it first ;)), I think it's quite neat and has the closest resemblance to C#-properties in C++ that I have seen up to date (however I don't use properties very much myself so I don't search for better versions either).
wild_pointer
wild_pointer
I never understood properties. I usually consider a plethora of get/set pairs an indicator of poor design.

I realized this sounded like a troll. I'm actually genuinely interested in why it seems to be such a highly regarded language feature in C# and Java, and why it's so commonly implemented by compilers and libraries for use in C++.
[size=2]
RenderTarget
RenderTarget
Properties are great for black-box object design. Instead of using a method to get or set data, you get or set it like a field, but it can do smart things inside that you don't need to worry about.

*shrug* It's a convenience and a design preference, but I like them.
[sub]My spoon is too big.[/sub]
antareus
antareus
Properties allow you to refactor PODs into classes with get/set without making any code changes. Properties are also a lot more lucid.

Consider a Window class. Which would you rather do:
mainWindow.Position = Point(mainWindow.Position.X + 5, mainWindow.Position.Y);

or
mainWindow.setPosition(Point(mainWindow.getPosition().X, 25));

Getters and setters don't really do anything by getting/setting a value, rather they expose a detail about an object which the outside world can manipulate. Certainly, getters and setters maintain class invariants, but properties can as well. It is all about making the code more expressive. Methods are reserved for things that *actually* do something. It is, of course, possible to err too much on the side of properties and obfuscate their use. Once you see properties, get/set looks like a horrible hack.

Oh, and Java's "this one way is the Right Way(tm)" ideology would never have properties.
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
CpMan
CpMan
If you're using managed C++ the new version of c++/CLI (which is going to be ecma standardized), has support for properties.
VSEDebug Visual Studio.NET Add-In. Enhances debugging in ways never thought possible.
the Speed Bump
the Speed Bump
Quote:
Original post by wild_pointer
I realized this sounded like a troll. I'm actually genuinely interested in why it seems to be such a highly regarded language feature in C# and Java, and why it's so commonly implemented by compilers and libraries for use in C++.

I like them because they allow programmers to write code that says what it means as opposed to what it does.

To expound on antareus's example, we could write it in a number of ways:
mainWindow.setPosition(mainWindow.getPosition().x + 5, mainWindow.getPosition().y);// orPoint pos = mainWindow.getPosition();pos.x += 5;mainWindow.setPosition(pos);// ormainWindow.Position.X += 5;
The first two flood the reader with so much syntax that the meaning is completely buried. Granted, the second is much better because the "+=5" bit is quite visible, but "position" and "mainWindow" still pop up twice as often as they need to.

The last one, however, says what it means and not a whit more.

By the way, Java can never have properties because of its symbol resolution rules:
public class Test {    private int value;    public int value() { return value; }    public void value(int v) { value = v; }}
Since the Java designers apparently didn't put much stock in first-class functions, this is never ambigious. Java method names must always be followed by parenths.
"There is only one everything"
OrigamiMan64
OrigamiMan64
The difficulty I am facing is that there is no way to avoid the property being an object itself, which is what adds all the overhead. Perhaps I could use macros to wrap up all the uglines (as if macros aren't ugly ;) ), but still the client code would have to do extra initialization, dressed up or not, in each constructor. There must be some way to tie the property together tighter so that all the extra requirements of the property could be centralized and be easilly replacable by macros.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.