www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Static opCall Factory Method Doesn't Work for Inner Classes

reply eris <jvburnes gmail.com> writes:
Hello fellow D'ers...

I'm writing a message passing kernel and scheduler.  "User-space" programs
communicate with each other and the kernel through inboxes and outboxes which
are allocated and tracked by the kernel.  If a user-space program wants to
allocate an inbox called "stdin" and an outbox called "stdout" currently they
simply ask the kernel to create one for them.

For example:   

auto my_stdin = kernel.createInbox(String)("stdin");
auto my_stdout = kernel.createOutbox(String)("stdout");

This works fine for me.  It is a pain, however, to make a new "create" method
everytime I want to define a new structure.

I'd much rather use the "static <classname> opCall" mechanism which allows you
to simply call the classname to have it generate an instance of that class.

Now the static opCall pattern I've gotten to work:

class A {

...

   static A opCall(args) {
        return new A(args);
   }

}

auto a = A(args);

That works fine if you want instances of a factory created by calling the
factory.  For example:

CarFactory car = CarFactory(args);

It's a little syntactically awkward, but it works.  My problem is I want the
kernel to act as a factory for inboxes, outboxes and a bunch of other things. 
I'd like this to work:

class Kernel {
...
   class Inbox {
      ...
      static Inbox opCall(char[] name) {
         return new Inbox(name);
      }
   }

   class Outbox {
   ...
      static Outbox opCall(char[] name) {
         return new Outbox(name);
      }
   }

  ...
}

That way I just create a static opCall method for each kernel object and I get
a factory method which could be used like this...

auto my_inbox = kernel.Inbox("stdin");
auto my_outbox = kernel.Outbox("stdout");

BUT....

Every syntax combination in D to express a static opcall on an inner class
doesn't work.  Both 'ldc' and 'gdc' generate compiler errors that say that
'this' is not available for static opCalls.  When I take the 'static' keyword
off the opCall declaration the compiler error becomes something like: 'this' is
required to access the opCall method.

I was getting pretty tired when I gave up on this last night, but the problem
is still eluding me.  Am I just trying to do this the wrong way?  I know the
"createInbox" method works. Perhaps I should make separate factories for every
structure, but that seems harder than just making create methods.

If I can get this to work, I think I could create a mixin that would generate
automatic factory methods.

(This is probably obvious and I was just too tired  last night.)

Thanks for your help,

eris
 
Jan 12 2010
parent reply "Simen kjaeraas" <simen.kjaras gmail.com> writes:
eris <jvburnes gmail.com> wrote:

 Every syntax combination in D to express a static opcall on an inner  
 class doesn't work.  Both 'ldc' and 'gdc' generate compiler errors that  
 say that 'this' is not available for static opCalls.  When I take the  
 'static' keyword off the opCall declaration the compiler error becomes  
 something like: 'this' is required to access the opCall method.

Does the inbox need a pointer to the kernel class? If so, you're ferked. Well, not really, but then you have to explicitly pass it to opCall. See, to get this to work, you do this: class foo { static class bar { // static inner classes have no 'outer' pointer static bar opCall( ) { return new bar( ); } } } Hope this helps. -- Simen
Jan 12 2010
parent eris <jvburnes gmail.com> writes:
Simen kjaeraas Wrote:

 eris <jvburnes gmail.com> wrote:
 
 Every syntax combination in D to express a static opcall on an inner  
 class doesn't work.  Both 'ldc' and 'gdc' generate compiler errors that  
 say that 'this' is not available for static opCalls.  When I take the  
 'static' keyword off the opCall declaration the compiler error becomes  
 something like: 'this' is required to access the opCall method.

Does the inbox need a pointer to the kernel class? If so, you're ferked. Well, not really, but then you have to explicitly pass it to opCall. See, to get this to work, you do this: class foo { static class bar { // static inner classes have no 'outer' pointer static bar opCall( ) { return new bar( ); } } } Hope this helps. -- Simen

Thanks, Simen. I'll take a look at my code. This whole effort is the result of a "single responsibility" refactoring where the kernel has responsibility for inbox and outbox creation since they implement kernel functionality. The user space programs really only have an interface into these objects, such as... interface Inbox(T) { T read(); bool isEmpty(); .. } interface Outbox(T) { void send(T msg); .. } class Kernel { ... // kernel inbox functionality including blocking read on empty class MsgBox(T) : Inbox!(T) { static MsgBox(T) opCall(T)(char[] name) { return new MsgBox!(T)(name); } T read() { if (isEmpty()) Fiber.yield; else return ... } ... } auto my_inbox = kernel.MsgBox!(String)("stdin"); Or at least that is what I was trying to achieve -- an interface into the kernel, literally. This way the kernel maps the internal MsgBox object to an interface for the userland program to use. Userland just sees my_inbox.read and not the implementation and doesn't care about kernel blocking. I'm going to do another round of my refactoring and see if the problem doesn't resolve itself. Perhaps the kernel should use external classes for the factories that aren't normally available to user-space programs. Or maybe I'm making it too hard. If userland wants it's own inbox, fine, but the kernel won't recognize it unless it has been registered with it. I think part of the issue is that message sending and receiving between mailbox methods contain blocking functions if an outbox is full or an inbox is empty. Those blocking functions are actually implemented by kernel primitives. That's why userland needs an interface into them, but the actual mailbox methods need to be running in the kernel. If that makes *any* sense. :-) (the problem may be that the program I'm working on so relatively large and I accidentally started working on one of the intermediate refactors) eris
Jan 12 2010