I have a problem with D

rikki cattermole via Digitalmars-d digitalmars-d at puremagic.com
Mon Jun 27 23:26:57 PDT 2016


On 28/06/2016 4:37 PM, Adam Sansier wrote:
> Hi,
>
> I have designed a class based system that involves self-delegation
> instead of override.
>
> It is similar to event based programming.
>
> I have defined an event as a container type that holds functions(or
> possibly delegates, but the desire is to avoid them).
>
>
>
> class Base
> {
>    alias EventMethod = void function(Base _this);
>    public Event!EventMethod MyEvents
>
>    public MyEvent()
>    {
>        // Go ahead and inform subscribed handlers
>        MyEvents(this);
>
>        // Do other stuff here
>    }
> }
>
> The outside world can attach their own method to the event as normal.
> They act as effective members to Base but with only access to public
> members.
>
> Now, the normal workflow in OOP is to derive from Base
>
> class Derived : Base
> {
>
>    public override MyEvent()
>    {
>       // must call super.TriggerMyEvents for design to work, else events
> won't be called. This is dangerous
>
>       // Other work done here.
>    }
> }
>
>
> To prevent the dangerous scenario of the deriving user from not calling
> the base class method, the workflow is changed to
>
> class Derived : Base
> {
>     public static myEvent(Derived _this)
>     {
>         // Other work done here
>     }
>
>     this()
>     {
>        // Subscribe to Base's MyEvent
>        MyEvents += &myEvent;
>     }
>
> }
>
> The idea is that the Derived type tries to behave as much as possible
> like a non-derived type when it can. This separates certain behaviors
> from the base class. It helps in other areas of the design(because then
> those behaviors can be reused since they do not directly depend on the
> base type). It also free's up the methods to be overridable if necessary
> without breaking the event processing, since now the event processing is
> mainly done through subscriber model. Also, The derived class or anyone
> else can now unhook it's own event handling. If we need to temporarily
> "un-override" some behavior, we cannot do this in the inheritance model
> without mucking with the VTable which is dangerous.
>
> Problems with this setup that are unfortunate but seem to be due to
> language limitations and no fundamental reasons:
>
> If myEvent is static:
>
> 1. myEvent cannot use this, but _this is functionally the same. We have
> to prepend every access to the object's members with _this. It becomes
> very ugly code.

Well yeah, that's how function pointers work. Hence why we have delegates.

> 2. We also can only access public members because it is not a dynamic
> member. Yet it is clear that myEvent, being defined inside the class and
> having a _this ptr is meant to have full access like any normal member.

Not sure about protected, but private members definitely should be 
accessible from such a method. Since it is in the same module as the module.

> If myEvent is non-static:
>
> 1. Since Event only takes functions, we cannot directly add to it. There
> are some nasty hacks that seem to work but they have created other
> problems in the design.

Well that's easy fixed. Don't use function pointers since you need a 
context pointer too.

> 2. It then invokes the GC even though there is no reason this is
> required. The design supplies the this ptr.

Well of course it does, you're allocating via the GC.

> ---
>
> One has an either or situation here and neither are desirable.
>
> It seems that D can do better.
>
>
> 1. Allow for module level, static member functions with a special _this
> parameter to act as a in between a function and a delegate. this = _this
> inside the body and has the class/struct type. The only difference
> between this an C++ delegates is that _this is explicitly passable.
>
> This avoids the need to prefix _this to every member access.
>
> 2. Allow for C++ delegate style method. This is essentially 1 but hides
> the explicit passing of _this. It can be hacked though.
>
> 3. Allow access to private members of the enclosing type. For all
> practical purposes, it is part of the class construction and does not
> need to have the members hidden from it. It is both inside the module
> and inside the type and has a this type of parameter. It should be a
> first class citizen then.
>
> 4. Allow these new method types to be virtual. This is a bit of
> complexity that might not be desirable at this point though, but would
> allow them to be extended instead of acting like static or final methods.
>
> The goal being to allow for derived types to participate in the oop
> hierarchy as general types do but to have special privileges. Maybe only
> protected members could be accessed by _this to allow for some level of
> control(but somewhat meaningless I believe).
>
>
>
> These functions should be implicitly convertible to a function with
> first parameter of the class/struct type.
>
> Maybe there is a cleaner way to accomplish this with templates though. I
> believe it essentially requires the assignment to this though, which I
> believe is illegal?
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> import std.stdio;
>
>
> class Event(T, P)
> {
>     T[] callbacks;
>
>     void opCall(P p)
>     {
>         foreach(e; callbacks)
>             e(p);
>     }
>
>     void opOpAssign(string op, T)(T c)
>     {
>         static if (op == "~")
>             callbacks ~= c;
>     }
> }
>
>
> class Base
> {
>     string test = "This is a test, only a test!";
>     alias EventMethod = void function(Base _this);
>     //alias EventMethod = void delegate(Base _this);
>     auto MyEvents = new Event!(EventMethod, Base)();
>
>     void MyEvent()
>     {
>         MyEvents(this);
>     }
>
>     void MyEventTrigger()
>     {
>         MyEvents(this);
>     }
> }
>
> class Derived : Base
> {
>
>     this()
>     {
>         MyEvents ~= cast(void function(Base
> _this))(cast(void*)&myEventHandler);        // Notice the ugly hack to
> insert our event hanlder but works
>         //MyEvents ~= cast(void delegate(Base
> _this))(cast(void*)&myEventHandler);      // can't cast! depreciated
>     }
>
>     override void MyEvent()
>     {
>         // Oops, what if we forgot to call base or did something hokey?
>     }
>
>     // Lets keep things separated
>     static void myEventHandler(Derived _this)    // If not static and
> function events used, crashes.
>     {
>         writeln("Hello D-World! " ~ _this.test);
>     }
>
> }
>
> int main(string[] argv)
> {
>
>     auto d = new Derived();
>
>     d.MyEvent();   // Oops, nothing!!!
>     d.MyEventTrigger();  // Better!
>
>
>     return 0;
> }

Please jump on IRC (Freenode #d) at some point, it'll be much easier to 
discuss this there and come up with an actual solution for your needs.



More information about the Digitalmars-d mailing list