On 03/08/2012 10:40 AM, H. S. Teoh wrote:
On Thu, Mar 08, 2012 at 02:57:00PM +0100, Andrej Mitrovic wrote:

I think what Chad is looking for is a BlackHole/WhiteHole equivalent
which doesn't need abstract functions but which figures out all the
methods of a class at compile time and creates a subclass that
throws/does nothing on method invocation. An 'alias this' field would
be used that is default-initialized with this sentry object. I don't
know why we don't have __traits(allFunction). We have
'getVirtualFunctions' but it requires a function name, but using
allMembers to filter out function names is damn difficult if you ask
me. I've never had an easy time interacting with __traits.

Yep. That would be cool. Although there should be ways of doing the same thing for arrays and possibly even structs. For structs I wouldn't mind adding a boolean field to keep track of its empty-or-not status.


        foreach (name; __traits(allMembers, typeof(obj))) {
                static if (__traits(compiles,&__traits(getMember, obj,
                                name)))
                {
                        alias typeof(__traits(getMember, obj, name))
                                type;
                        static if (is(type==function)) {
                                // name refers to a function of type
                                // 'type' here
                        }
                }
        }

I've never had an easy time interacting with __traits.

Me too. I'm guessing that __traits is the way it is due to ease of
implementation in the compiler. It's certainly not very friendly to use.


T


Tried this, but it wasn't picking everything up.

I also suspect that inheriting the class being wrapped and picking on its methods is going to be a losing battle in the long run for this thing. It doesn't allow the sentry to throw on field access. It also wouldn't work for final classes. I wonder if opDispatch would do better.

Another mess I was running into was forwarding templated methods. I wonder if this is even possible. I wish __traits had some way of picking up on previous template instantiations (with cycles forbidden, of course). If __traits were beefed up enough, maybe it would be better than opDispatch after all. Hmmm.

I did try this as a struct with an "private T t; alias t this;" in it. It looks like this:

-----------------------------------

import std.c.stdlib;
import std.stdio;

struct Emptiable(T)
{
        private static Emptiable!T m_sentinel;
        // private NotNull!T t; // TODO
        private T t;
        
        alias t this;
        
        public static @property Emptiable!T sentinel()
        {
                return m_sentinel;
        }
        
        static this()
        {
                void* sentinelMem = malloc(T.sizeof);
                m_sentinel = cast(T)sentinelMem;
        }
        
        this(A...)(A args)
        {
                t = new T(args);
        }
        
        @property bool isEmpty() const
        {
                if ( this is m_sentinel )
                        return true;
                else
                        return false;
        }
}

static auto makeEmpty(T)(ref T v)
{
        v = T.sentinel;
        return v;
}

class Bar
{
        int i;
        
        this(int j) { i = j; }
        
        void blah()
        {
                writefln("blah!");
        }
        
        /+void letsTemplate(T)(T f)
        {
                writefln("%s",f);
        }+/
}

void main()
{
        auto bar = Emptiable!(Bar).sentinel;
        auto foo = new Emptiable!Bar(5);
        
        if ( bar.isEmpty )
                writefln("bar is empty, as it should be.");
        
        if ( !foo.isEmpty )
                writefln("foo is full, as it should be.");
        
        //foo.letsTemplate("Just a string.");
        
        writefln("foo.i is %s",foo.i);
        foo.i = 2;
        writefln("foo.i is %s",foo.i);
        
        /+
        makeEmpty(foo);
        if ( foo.isEmpty )
                writefln("foo is now empty.");
        writefln("foo.i is %s",foo.i);
        +/
}

-----------------------------------

Prints:
bar is empty, as it should be.
foo is full, as it should be.
foo.i is 5
foo.i is 2

-----------------------------------

It's super incomplete. I am starting to realize that this sort of thing leads to a huge amount of weird corner cases.

Also note what happens when it tries to use makeEmpty: it fails because "auto foo = new Emptiable!Bar(5);" allocates an instance of the Emptiable struct on the heap, which then allocates a separate instance of Bar. Yuck. It's that old struct-vs-class construction schism again.

Reply via email to