February 26, 2014 Re: Cumulative | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Teale Attachments:
| Hello, I'm coming late to the discussion, but I believe that you can use the following idiom to achieve the same results in a different way: class C { // This is not overridable public final void doStuff() { doSomethingWhichNeverChanges(); doExtraStuff(); } protected void doExtraStuff() { // This one can be overridden } } Or did I miss something? Cheers, LMB On Mon, Feb 24, 2014 at 5:41 AM, Steve Teale <steve.teale@britseyeview.com>wrote: > 25 years ago, when I was trying to write some sort of library to go with Walter's C++ compiler, I had a wish, and it still pops into my head from time to time. > > What I wanted was functions that were declared in a base class as 'cumulative', or something similar. They would have been generally like virtual functions, except that any derived class that wanted to do something extra - as opposed to something different, would simply define an 'extend', and just specify the extra code. The compiler would then automatically add a call to the same function in whatever base class last defined or extended the method. > > extend void foo() // Declared in base class as cumulative void foo() > { > (cast(BaseClass) this).foo(); // Compiler does this for you > // similar to changing a light bulb ;=) > > // the extra stuff > } > > I think also that it might be necessary for the base class function to return on behalf of the derived method as opposed to to it. > > Does this make any sense? > > Steve > |
February 26, 2014 Re: Cumulative | ||||
---|---|---|---|---|
| ||||
Posted in reply to Leandro Motta Barros | On Wednesday, 26 February 2014 at 13:30:15 UTC, Leandro Motta Barros wrote:
> Hello,
>
> I'm coming late to the discussion, but I believe that you can use the
> following idiom to achieve the same results in a different way:
>>
Yes we went through that, if you go back a page you'll find a post I did that makes the thing work better using a class variable in the base class that is an array of delegates.
That way you don't have to do super, super, super back to root, you just have to iterate over the delegates until you find one that accepts the command.
It does just what I wanted to do in the first place without compiler assistance
;=).
Steve
|
February 27, 2014 Re: Cumulative | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Teale | On 2014-02-26 16:05:19 +0000, Steve Teale said:
> On Wednesday, 26 February 2014 at 13:30:15 UTC, Leandro Motta Barros wrote:
>> Hello,
>>
>> I'm coming late to the discussion, but I believe that you can use the
>> following idiom to achieve the same results in a different way:
>>>
> Yes we went through that, if you go back a page you'll find a post I did that makes the thing work better using a class variable in the base class that is an array of delegates.
>
> That way you don't have to do super, super, super back to root, you just have to iterate over the delegates until you find one that accepts the command.
>
> It does just what I wanted to do in the first place without compiler assistance
> ;=).
>
> Steve
Actually, D has the ability to walk the object hierarchy at compile time, and generate a function call containing all the super classes's handleCommands. You could also even tag them with an attribute structure to list which command #'s they accept to be able to generate a switch, or bunch of if's to avoid function calls.
-S.
|
February 28, 2014 Re: Cumulative | ||||
---|---|---|---|---|
| ||||
Posted in reply to Shammah Chancellor | On Thursday, 27 February 2014 at 21:44:20 UTC, Shammah Chancellor wrote:
> On 2014-02-26 16:05:19 +0000, Steve Teale said:
>
> Actually, D has the ability to walk the object hierarchy at compile time, and generate a function call containing all the super classes's handleCommands. You could also even tag them with an attribute structure to list which command #'s they accept to be able to generate a switch, or bunch of if's to avoid function calls.
>
> -S.
Yes, I'd been thinking about tagging them at least.
|
March 06, 2014 Re: Cumulative | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Teale | On Monday, 24 February 2014 at 08:41:06 UTC, Steve Teale wrote: > 25 years ago, when I was trying to write some sort of library to go with Walter's C++ compiler, I had a wish, and it still pops into my head from time to time. > > What I wanted was functions that were declared in a base class as 'cumulative', or something similar. They would have been generally like virtual functions, except that any derived class that wanted to do something extra - as opposed to something different, would simply define an 'extend', and just specify the extra code. The compiler would then automatically add a call to the same function in whatever base class last defined or extended the method. >OK, I'm back to this because I have done quite a bit of work trying to get to what I wanted. I have converted my app so that it conforms roughly to the above, as a test. I've also noticed from the responses, and from responses to associated questions, that OOP has become almost a dirty word in the D community. It's old fashioned and slow. So if you're in that camp, you can stop reading now. I need to handle signals of some sort - let's say just represented by an int. In my base class I define a method final void handleSignal(int), to which all signals are directed. In the same place there's a virtual function bool signalHandler(int). The base class also has a data member bool delegate(int)[] handlers. In classes Base, Intermediate, and Leaf, say, the constructor has a statement: handlers ~= &Base.signalHandler; handlers ~= &Intermediate.signalHandler; handlers ~= &Leaf.signalHandler; The final handleSignal() method in the base class just traverses the array, calling each delegate in turn until one returns true, and then throws an exception or something if none of them do. This works nicely. A lot of duplicate code in my original handler functions is eliminated. Any particular leaf class just has to cover the cases peculiar to it's own requirements. Intermediate classes just deal with their generic cases, and the base class handles all the signals that all leaves require. The compiler doesn't help to ensure that the 'handlers ~=' bit is present in all constructors, so I thought I would be clever, and provide some mixin template or template function that all the constructors in the hierarchy used, so that it was a fairly simple thing to insist on, even if only by example. But I have been frustrated in this desire. A member function like final void doMyThing(string CLASSNAME)() { handlers ~= mixin("&"~CLASSNAME~".signalHandler;"); } defined in the base class does not work, because the compiler complains about the type of the this pointer being Base, despite the fact that I am being explicit about the class name. Attempts from other directions like calling back down the super() chain don't work because I can't have anything but declarations in a mixin template, and a super() call inside a template function called by the constructor fails because the call is not in a constructor. Anyone have any bright ideas about how this could be regulated? I can't say forced, because I think the compiler would have to do that. Steve |
March 07, 2014 Re: Cumulative | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Teale | On 3/6/2014 11:48 AM, Steve Teale wrote: > > I've also noticed from the responses, and from responses to associated > questions, that OOP has become almost a dirty word in the D community. > It's old fashioned and slow. So if you're in that camp, you can stop > reading now. > FWIW, I don't think that's really the prevailing attitude towards OO. OO definitely has its uses and benefits (and outside performance-critical sections its performance is perfectly fine), it's just not the one-size-fits-all mold to force everything into like it often got treated as ~10 or so years ago. Even the entity/component-based systems that I mentioned games commonly use are still frequently employing OO, too (ex: Unity3D uses an entity/component-based design for game objects, but the API for dealing with the game object, components, etc is still an OO API). Entities, metaprogramming, OO - none of these are "either/or" deals, just as OO doesn't actually try to replace procedural programming but is rather used together with it. > I need to handle signals of some sort - let's say just represented by an > int. > > In my base class I define a method final void handleSignal(int), to > which all signals are directed. In the same place there's a virtual > function bool signalHandler(int). [...] > Anyone have any bright ideas about how this could be regulated? I can't > say forced, because I think the compiler would have to do that. > A stab in the dark here, but can you just add "bool delegate(int)[] additionalHandlers" as a required parameter for your base class's constructor? Then the base class's constructor does "this.handlers ~= additionalHandlers" Or maybe something like this?: class MyBaseClass { bool addHandlersCalled = false; bool delegate(int)[] handlers; /// Subclasses must call this in their ctor. protected void addHandlers(bool delegate(int)[] handlers) { this.handlers = handlers; addHandlersCalled = true; } invariant() { assert(addHandlersCalled); } } |
March 07, 2014 Re: Cumulative | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nick Sabalausky | On Friday, 7 March 2014 at 02:15:44 UTC, Nick Sabalausky wrote:
> On 3/6/2014 11:48 AM, Steve Teale wrote:
>>
>>>
> class MyBaseClass {
> bool addHandlersCalled = false;
> bool delegate(int)[] handlers;
>
> /// Subclasses must call this in their ctor.
> protected void addHandlers(bool delegate(int)[] handlers)
> {
> this.handlers = handlers;
> addHandlersCalled = true;
> }
>
> invariant() {
> assert(addHandlersCalled);
> }
> }
Thanks Nick. A voice of sanity. Interestingly I'd decided on the argument to the constructor while programming in bed last night.
The change of fashion is very noticeable though - structs for everything seems to be a mantra these days. I have started to wonder if the two styles could be bridged somewhat if we had the default class method type as virtual, and then 'plain' functions as well as final ones.
Steve
|
March 07, 2014 Re: Cumulative | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Teale | On Friday, 7 March 2014 at 06:18:47 UTC, Steve Teale wrote:
> On Friday, 7 March 2014 at 02:15:44 UTC, Nick Sabalausky wrote:
>> On 3/6/2014 11:48 AM, Steve Teale wrote:
>>>
>>>>
>> class MyBaseClass {
>> bool addHandlersCalled = false;
>> bool delegate(int)[] handlers;
>>
>> /// Subclasses must call this in their ctor.
>> protected void addHandlers(bool delegate(int)[] handlers)
>> {
>> this.handlers = handlers;
>> addHandlersCalled = true;
>> }
>>
>> invariant() {
>> assert(addHandlersCalled);
>> }
>> }
>
> Thanks Nick. A voice of sanity. Interestingly I'd decided on the argument to the constructor while programming in bed last night.
>
> The change of fashion is very noticeable though - structs for everything seems to be a mantra these days. I have started to wonder if the two styles could be bridged somewhat if we had the default class method type as virtual, and then 'plain' functions as well as final ones.
>
> Steve
How would these plain functions be different from final ones?
|
March 07, 2014 Re: Cumulative | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nick Sabalausky | On Friday, 7 March 2014 at 02:15:44 UTC, Nick Sabalausky wrote: > On 3/6/2014 11:48 AM, Steve Teale wrote: I can get tantalizingly close to what I want using the extra constructor argument. In the base class I define a template function: string initString(T)() { return "string sname = \""~T.stringof~"\"~to!string(++nextOid);" "HandlerDelegates[] ahdg = [ HandlerDelegates( &"~ T.stringof~".notifyHandler, &"~T.stringof~".undoHandler)];"; } Then in the leaf class constructor I can just do: mixin(initString!Arrow()); super(aw, parent, sname, AC_ARROW, ACGroups.SHAPES, ahdg); Sadly the compiler does not complain if I comment out the leaf notifyHandler() method, cos its virtual, and there's one in its parent class. Does contract programming to provide a way to say that some function definition is required? This is where I'd like to be able to say: class Base { // Method is called directly, but unlike a final // method it can be overidden in a derived class. direct bool notifyHandler(...) { ... } } class Inter { // Kill the warning about hiding the base class method with override override bool notifyHandler(...) { ... } } As a side benefit, I think that mixing in a string generated by a template function gives me a way of generating something approximating a mixin template that allows more than just declarations - insertion of parameterized code at compile time ;=) Steve |
March 07, 2014 Re: Cumulative | ||||
---|---|---|---|---|
| ||||
Posted in reply to John Colvin | On Friday, 7 March 2014 at 09:04:29 UTC, John Colvin wrote:
> How would these plain functions be different from final ones?
You would be able to redefine them in a derived class using override to tell the compiler that it was intentional. Final would remain as-is - final.
I think Walter made a good choice for the times when he chose to have functions virtual by default, but a system programming language should allow you to do dangerous things if you give the compiler your permission.
Steve
|
Copyright © 1999-2021 by the D Language Foundation