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.