Full Fledged Properties

js.mdnq js_adddot+mdng at gmail.com
Mon Dec 17 19:06:03 PST 2012


Traditionally properties only have a getter and setter. We can 
create full fledged properties in D using the following templates 
and methodology. Unfortunately, it is somewhat terse. The 
compiler could do this all for us.

The following code, creates a simple class _cChannel, which could 
represent something about a communication channel. It has a 
"property" called strength which could represent the signal 
strength.

e.g.,

class cChannel { double Strength; }

But the built in value has little functionality. It may be 
difficult to use. If we need to convert the value into different 
formats(dbu, dbV, or whatever), or have methods to compare, they 
must be somewhere else.

Using the templates I came up with, I'm able to create a value 
wrapper for Strength that allows one to encapsulate all the 
functionality into a struct called sStrength. sStrength, for all 
practical purposes, is a double(or whatever). It has a getter and 
setter, so to speak, but also other functionality.

More, so, the whole point of all this, is that a strength value 
can access it's parent(just as you can do with getters and 
setters) AND does not use any additional storage than the type it 
wraps(hence, it is "free", so to speak).

The problem? Messy to use. Not pretty. The compiler could clean 
up all this. If the compiler did this, the code could look like 
this



property pStrength(T)
{
    private:
       propertyvalue T Strength;         // Specifies that 
Strength is the wrapper for this class. Basically does `alias 
Strength this` unless we explicitly override it. Not needed.
    public:

       void Do::cChannel() { writeln("asdf"); }  // Add's Do to 
pStrength only when it is created inside a cChannel.
       void Do::cSignal() { writeln("Signal Strength of", 
Parent.SignalName, " ", Strength); }
}

class cChannel { pStrength!(double) Strength; }


Notice how Strength is a full fledged property with as much or 
little functionality we want but it only takes up a double even 
though it can access cChannel's members.  The compiler would 
supply default setter, getter, and casts because it realizes that 
pStrenght(T) is a wrapper around a T and hence can be converted 
to and from the specified propertyvalue without issue. We can 
override this if we want to alter the behavior by explicitly 
specifiying the opAssigns, alias this, and opCasts.

We could also nest properties in classes and structs. If this is 
done then the Parent defaults to the outer. But we could still 
specialize the parent in cause the property is used in another 
class to define a value.

e.g.,


class cChannel {

     property pStrength(T)
     {
                ...
           void Do() { writeln("asdf"); }  // Parent is implicitly 
a cChannel since nested. (note we changed Do::Channel() to Do())
           void Do::cSignal() { writeln("Signal Strength of", 
Parent.SignalName, " ", Strength); }
     }

     pStrength!(double) Strength;
}

class cSignal { pStrength!(int) Strength; } // Strength's Do will 
use Do::cSignal



Add these to be able to override or accomplish certain tasks in 
the property above. I left them out to reduce the visual bloating 
since they are not necessarily needed.

       //alias opGet this;               // Explicitly override 
getter to supply our own
       //T opGet() { return Strength; }
       //alias typeof(this) tthis;
       //auto opAssign(tthis)(ref tthis sstrength) { this.Strength 
= sstrength.Strength; return this; } // a custom setter between 
pStrengths
       //tthis opAssign()(T Strength) { this.Strength = Strength; 
return this; }	   // A custom setter from T
       // static if (typeof(Parent) == cChannel) { void Do() { 
writeln("asdf"); } }   // Allows us to insert Do when the Parent 
is cChannel, i.e., it is equivalent to the Do::cChannel syntax.




It might even be possible to have a propertyClass which would be 
a class like property that has inheritance. They would be pretty 
much identical to a class except have the ability to use the 
parent specialization notation. It wouldn't offer much benefit as 
one can already do this sort of thing with classes, unless a 
propertyClass as a value type rather than a reference type, so it 
would be in between a struct and a class. It would be more for 
just expediency since a property could be converted to a 
propertyClass rather easily if one finds they need inheritance.



vs




Templates:
http://dpaste.dzfl.pl/d8bb92ab



class _cChannel(bool _NestLevel = true)
{
	enum string __ClassNameFix = __traits(identifier, 
typeof(this))~"!("; enum string __NestLevelFix = "_NestLevel";
public:
	alias double Int;
	mixin tp_sStrength; mixin(StructNestType!("sStrength!(Int)", 
"Strength"));   // Strength of Channel
		
	static if (_NestLevel)
	{
		this() { }
	}
} alias _cChannel!() cChannel;


static template tp_sStrength() {
	struct sStrength(T, int _Offset = 0)  {
		private:
			T Strength;
		public:
			alias typeof(this) tthis;
			mixin(StructNestParent!("Parent"));

			alias opGet this;
			T opGet() { return Strength; }

                         void Do() { writeln("asdf"); }

			static if (_Offset == 0)
			{

				// Conversion operators to "orphaned" structs. (This allows 
orphaned structs to have valid parents)
				auto opAssign(T)(ref T b)
				{
					this.Parent = b.Parent;
					return this;
				}

				this(A)(ref A b)
				{
					this.Parent = b.Parent;
					this.Strength= cast(T)b.Strength;
				}

			}
			else
			{

				auto opAssign(tthis)(ref tthis sstrength) { this.Strength = 
sstrength.Strength; return this; }
				tthis opAssign()(T Strength) { this.Strength = Strength; 
return this; }			
			}

	}
}








More information about the Digitalmars-d mailing list