| |
 | Posted by js.mdnq | Permalink Reply |
|
js.mdnq 
| 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; }
}
}
}
|