digitalmars.D - Members as first class citizens!
- Patience (29/29) Feb 27 2016 Ok, maybe not but this is what I mean:
- Simen Kjaeraas (66/96) Feb 27 2016 There is absolutely no technical reason, no. C++ actually has
Ok, maybe not but this is what I mean:
Why can't we pass member as as sort of "objects" in there own
right to be used for accessing objects?
e.g.,
class A
{
int? foo;
A Parent;
T GetAncestorValue(member<T> field) // member is a new keyword
{
var p = this;
while (!p && !p.field.HasValue)
{
p = p.Parent;
}
return p.field.Value;
}
}
Then
auto x = a.GetAncestorValue(A:foo) would the properly initialized
x.
The code is simple, logical, and makes sense(since foo is just an
"offset" and a type. It has type safety and doesn't resort to
reflection and passing members as strings, etc. It allows for
general access of members rather than having to jump through a
bunch of hoops. It should be much faster too.
Is there any fundamental theoretical reason why such a semantic
could not be implemented in current object oriented compilers?
Feb 27 2016
On Saturday, 27 February 2016 at 18:48:27 UTC, Patience wrote:
Ok, maybe not but this is what I mean:
Why can't we pass member as as sort of "objects" in there own
right to be used for accessing objects?
e.g.,
class A
{
int? foo;
A Parent;
T GetAncestorValue(member<T> field) // member is a new
keyword
{
var p = this;
while (!p && !p.field.HasValue)
{
p = p.Parent;
}
return p.field.Value;
}
}
Then
auto x = a.GetAncestorValue(A:foo) would the properly
initialized x.
The code is simple, logical, and makes sense(since foo is just
an "offset" and a type. It has type safety and doesn't resort
to reflection and passing members as strings, etc. It allows
for general access of members rather than having to jump
through a bunch of hoops. It should be much faster too.
Is there any fundamental theoretical reason why such a semantic
could not be implemented in current object oriented compilers?
There is absolutely no technical reason, no. C++ actually has
this feature. The reason it has not been implemented in D is it's
a (very) rarely used feature in other languages and perfectly
possible to implement type-safely and efficiently in a library:
struct nullable(T) {
T value;
bool hasValue = false;
}
class A {
nullable!int foo;
A parent;
auto GetAncestorValue(T...)(Member!T field) {
auto p = this;
while (p && !field(p).hasValue) {
p = p.parent;
}
return field(p).value;
}
}
struct Member(T, U) {
private int fieldId;
disable this();
private this(int id) {
fieldId = id;
}
auto opCall(T that) {
foreach (i, e; __traits(allMembers, T)) {
static if (is(typeof(__traits(getMember, that, e)) ==
U)) {
if (i == fieldId) {
return __traits(getMember, that, e);
}
}
}
assert(false);
}
}
template member(alias m) {
import std.typetuple : TypeTuple;
alias parentMembers = TypeTuple!(__traits(allMembers,
__traits(parent, m)));
template memberIndex(int n) {
static if (parentMembers[n] == __traits(identifier, m)) {
enum memberIndex = n;
} else {
enum memberIndex = memberIndex(n+1);
}
}
enum member = Member!(__traits(parent, m),
typeof(m))(memberIndex!0);
}
void main() {
A a = new A();
A b = new A();
a.parent = b;
b.foo.hasValue = true;
b.foo.value = 3;
a.foo.value = 15;
assert(a.GetAncestorValue(member!(A.foo)) == 3);
}
Now, you lose the 'p.field' sugar, and it's possible the built-in
feature could drop some safeguards in release mode to make it
more efficient, but this should cover most of your concerns.
--
Simen
Feb 27 2016








Simen Kjaeraas <simen.kjaras gmail.com>