www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Members as first class citizens!

reply Patience <Patience.Is Virtue.Life> writes:
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;
    }
}

(This is pseudo D/C# code)


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
parent Simen Kjaeraas <simen.kjaras gmail.com> writes:
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;
    }
 }

 (This is pseudo D/C# code)


 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