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