August 14, 2008
On Thu, 14 Aug 2008 02:50:00 +0400, Chris R. Miller <lordSaurontheGreat@gmail.com> wrote:

> Denis Koroskin wrote:
>> I use MI often and have positive experience with it. One good pattern
>> that I use is the Intrusive container.
>> Suppose you have an item that you want to store in a list.
>> Unfortunately, putting stuff into the single- or double-linked list
>> leads to a memory allocation (unless some pool is used). Sometimes it is
>> desirable to put next and prev elements into the item itself, so that no
>> memory allocation is ever needed. Besides, now you can easily say
>> whether an item is stored in any container. This can be implemented via
>> inheritance:
>>
>> class IntrusiveContainerNode(T)
>> {
>>     alias T ValueType;
>>     package T next;
>>     package T prev;
>> }
>>
>> class IntrusiveContainer(TNode)
>> {
>>     alias TNode.ValueType ValueType;
>>
>>     void add(ValueType value);
>>     void remove(ValueType value);
>> }
>>
>> class MyClass : public Node!(MyClass)
>> {
>>    // ...
>> }
>>
>> This imposes the restriction that an item can be stored in 1 container
>> at a time. However, you can subclass twise in order to be storable in
>> different containers:
>>
>> typedef EventOneListener IntrusiveContainerNode;
>> typedef EventTwoListener IntrusiveContainerNode;
>>
>> class MyClass : EventOneListener!(MyClass), EventTwoListener!(MyClass)
>> {
>>     // ...
>> }
>>
>> MyClass instance = new MyClass();
>> eventOneListeners.add(instance);
>> eventTwoListeners.add(instance);
>>
>> It is currently impossible to implement this approach using mixins (due
>> to a bug I'm yet to submit).
>
> Chris E. implemented something similar to that using mixins:
>
> http://www.dprogramming.com/list.php
>
> It works fairly well.  Your example of typdefing to support multiple
> lists is pretty cool though :^)
>

Well, yes, it uses the same idea, but implementation is different. It has a few problems, though: an item is limited to a single container (I can do the same with single inheritance) and is a list itself (although it is useful in some cases, it is undesired in general).

I'll post my implementation with mixins soon (today, hopefully).
August 14, 2008
On Thu, 14 Aug 2008 16:09:57 +0400, Manfred_Nowak <svv1999@hotmail.com> wrote:

> superdan wrote:
>
>> no idea why walt chose to disallow that.
>
> It may cause some problems:
> interface Customer
> {
>     string ssn();
>     string name();
>     string uniqueName() { return name ~ "(ssn: " ~ ssn ~ ")"; }
> }
>
> interface SpecialCustomer
> {
>     string ssn();
>     string name();
>     string uniqueName() { return name ~ "!!!!!(ssn: " ~ ssn ~ ")"; }
> }
>
> class D: Costumer, SpecialCostumer{ string ssn="", name="";}
>
>
>
> 1) What is the result of `(new D).uniqueName'  ?
> 2) Is the locality of code controlable?
> 3) ...
>
> -manfred

I think the class should become abstract unless it reimplements the uniqueName(), ssn() and name() methods. Problem is, ssn and name variables are in a conflicting state with the methods of the same name.

class D : class D: Costumer, SpecialCostumer {
    string ssn="", name="";
}

class E : D
{
    string ssn() { return super.ssn; }
    string name() { return super.name; }
    string uniqueName() { return Costumer.uniqueName() }
}
August 14, 2008
Manfred_Nowak Wrote:

> superdan wrote:
> 
> > and you also have to allow for holes in objects
> 
> Then it isn't fixed layout alone, but also avoiding slices of unused memory within objects. If you advocate for this under the reason, that this scheme allows for efficient access for members by sparing one indirection, then: why do you accept interfaces?

don't quote me out of context. the `also' came after the whole hierarchy knowledge. that's the real killer.

August 14, 2008
Chris R. Miller wrote:
> Understand, I'm NOT demanding ANYTHING.
> 
> What is the current state of thought about Multiple Inheritance for classes in D?  I'd like to have that feature, since it'd make some stuff I want to do a bit easier.  Is it not there because it's not worth the effort to implement?  Because it's evil and needs to die (I don't know, some people could possibly be adamantly anti-MI)?  I don't know.  I know I can add a lot with mixins, but I'd just like to know what the state of the feature is.
> 
> The reason is I was trying to explain how cool D is to some other friends of mine, and they asked about Multiple Inheritance (of classes) and they were sort of put off by it's lack of it.  Then again, he was an Objective-C programmer...  ;-)
> 
> So please don't take offense, since none is meant, I just wanted to know what I could hope for in the future.
> 

I don't think MI is evil however I think there's only a few places where  it's actually a good design choice.  I think inheritance should mainly be used for polymorphism and not "reuse" (see : 101 Rules, Guidelines, and Best Practices (C++ In-Depth Series) -> by Herb Sutter and Andrei Alexandrescu).  By reuse of course I mean inheriting from a class that has partial implementation.

- Components provide better encapsulation because you have to go though the components interface to get at anything, which means you can provide a very strong invariant shield.
- Components can be many.  That is say you had a uni-cycle and you wanted to add more wheels.  You can with components.
- Components are not so tightly coupled.  You can (many times) remove the component without changing the interface.  You can also make changes to the components interface and have a very small ripple effect across the project.
- Components can provide there own interface and can be bound at runtime.  This means you can switch out the component as needed.
- Components can be passed around without dragging the entire class along for the ride.
- Not in the design area but... some companies working on consoles use components to get around v-table issues.

(Note I've only mentioned a few of the advantages of components.)

Anyway given that most of the time IMHO you shouldn't be using inheritance for implementation details / data.  Add to that mixins and bolt-in templates and it becomes even less likely that multiple inheritance with implementation is the best design for your application.  I think D has taken the right approach, which is to make it hard to write error-prone code.

-Joel
August 14, 2008
"Denis Koroskin" wrote
> On Wed, 13 Aug 2008 20:52:24 +0400, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>
>> "Denis Koroskin" wrote
>>> On Wed, 13 Aug 2008 19:46:51 +0400, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>>>
>>>> "superdan" wrote
>>>>> Steven Schveighoffer Wrote:
>>>>>
>>>>>> "superdan" wrote
>>>>>> > Lars Ivar Igesund Wrote:
>>>>>> >
>>>>>> >> Chris R. Miller wrote:
>>>>>> >>
>>>>>> >> > Understand, I'm NOT demanding ANYTHING.
>>>>>> >> >
>>>>>> >> > What is the current state of thought about Multiple Inheritance
>>>>>> for
>>>>>> >> > classes in D?  I'd like to have that feature, since it'd make
>>>>>> some
>>>>>> >> > stuff
>>>>>> >> > I want to do a bit easier.  Is it not there because it's not
>>>>>> worth
>>>>>> >> > the
>>>>>> >> > effort to implement?  Because it's evil and needs to die (I
>>>>>> don't
>>>>>> >> > know,
>>>>>> >> > some people could possibly be adamantly anti-MI)?
>>>>>> >>
>>>>>> >> This is actually the reason, not the adamantly anti-MI part, just
>>>>>> that
>>>>>> >> MI
>>>>>> >> is
>>>>>> >> evil and that is well acknowledged almost everywhere. You will
>>>>>> find
>>>>>> >> good
>>>>>> >> argumentation against it if you look, too.
>>>>>> >
>>>>>> > appeal to authority. appeal to ridicule. appeal to the majority.
>>>>>> all
>>>>>> in
>>>>>> > one sentence. wow. at least could you space out your fallacies a
>>>>>> bit
>>>>>> > more.
>>>>>> >
>>>>>> > the man has kindly asked a sensible question. he deserves a good
>>>>>> > answer.
>>>>>> > if u can't give one just don't reply. this is just ignorance.
>>>>>>
>>>>>> This kind of bullying bullshit does nothing to further communication,
>>>>>> or
>>>>>> help anyone in the least.  You've managed to call many of the
>>>>>> brightest
>>>>>> developers for D idiots, usually based on useless crap like this
>>>>>> (which
>>>>>> has
>>>>>> no bearing on anything).  So shut the fuck up.
>>>>>
>>>>> or what, u gonna kick my ass. relax. you can always block me. (but
>>>>> hold
>>>>> onto that a bit more. dee's plea is just too cool.) my post did
>>>>> further
>>>>> communication. it exposed the hackneyed "mi is evil" shit... i mean
>>>>> poop.
>>>>> (damn.) it may have helped someone. you know what i like about walter.
>>>>> when he doesn't know something he is open in admitting it. for that
>>>>> alone
>>>>> i'd wash his feet. i didn't call the poster any name. but that
>>>>> particular
>>>>> post was bull... i mean crap and i just said it. the fact that the
>>>>> post
>>>>> sucked bears nothing on the fact that he's good or anything. even
>>>>> worse,
>>>>> if he's good then why would he use his goodwill to get away with
>>>>> statements like that. they only reveal ignorance and attempt at
>>>>> continuing
>>>>> ignorance because it puts a stigma on anyone investigating mi. it's
>>>>> silly
>>>>> we need to still talk about it. now shall we just move on to something
>>>>> technical.
>>>>
>>>> I don't want to block you.  You have some good things to say (although
>>>> colorful).  Just can it with the "what you said is stupid, so don't
>>>> post
>>>> here"  It makes tentative posters not want to post for fear of being
>>>> ridiculed (not me BTW :) )  Some of them might have interesting things
>>>> to
>>>> say.  If you want to argue against someone's point, argue the point
>>>> (which
>>>> you did later, and I found it interesting, although my personal
>>>> experience
>>>> with MI (on C++) is that it sucks, and should never be used).
>>>>
>>>> What Lars said basically is that many people don't like MI, and you can
>>>> find
>>>> proof of that (people don't like it) if you search online.  This is the
>>>> reason Walter doesn't implement it, because he's in that camp.  I think
>>>> that
>>>> is a reasonable answer to the question given.
>>>>
>>>> It's sort of like most the reasons Walter gives for everything new he
>>>> comes
>>>> out with: "X is fundamentally broken in C++".  Substitute fundamentally
>>>> broken with evil, substitute threading, const, etc. for X.  Can't say I
>>>> always disagree, but his proof is certainly lacking ;)
>>>>
>>>>>> > interface Customer
>>>>>> > {
>>>>>> >    string ssn();
>>>>>> >    string name();
>>>>>> >    string uniqueName() { return name ~ "(ssn: " ~ ssn ~ ")"; }
>>>>>> > }
>>>>>> >
>>>>>> > so uniqueName formats a specific way. a descendant can choose to
>>>>>> change
>>>>>> > that or just use the default. no idea why walt chose to disallow
>>>>>> that.
>>>>>> > walt?
>>>>>>
>>>>>> What do you pass as the 'this' pointer?  When you call a function on
>>>>>> an
>>>>>> interface, the compiler uses the offset of an interface to the 'this'
>>>>>> pointer to get to the object, but in this case, there is no object,
>>>>>> so
>>>>>> what
>>>>>> does the compiler do to call the ssn() and name() functions while
>>>>>> implementing this function?
>>>>>
>>>>> i'm unclear about this so maybe it ain't as easy as i thought.
>>>>> but i'm thinking the same problem goes for the global string
>>>>> uniqueName(Customer c) { return c.name ~ "(ssn: " ~ c.ssn ~ ")"; }
>>>>
>>>> Not the same as the above problem, because the vtable layout for
>>>> Customer is
>>>> always the same.  The name() function expects a pointer to the object,
>>>> and
>>>> the implementation knows everything about the object including the
>>>> vtable.
>>>> The problem with putting the implementation in the interface is that a
>>>> member function gets a pointer to the *object* not the *interface*.  In
>>>> your
>>>> new example, the 'c' parameter is a pointer to the interface, so that
>>>> problem doesn't exist.
>>>>
>>>>> a pointer to this function should be put in the vtable if the object
>>>>> does
>>>>> not implement it.
>>>>
>>>> That is fine for calling the function, but not for what to do when
>>>> compiling
>>>> it.
>>>>
>>>>>
>>>>>> If you pass the interface pointer as the 'this' pointer, then how do
>>>>>> you
>>>>>> override it in an Object that implements the interface?  The function
>>>>>> in
>>>>>> the
>>>>>> concrete class can't be passed the interface pointer, so you can't
>>>>>> really
>>>>>> override it.
>>>>>
>>>>> that pretty much kills what i wrote above eh. but thunking will take
>>>>> care
>>>>> of it. if there's no impl in an object put a pointer to a thunk that
>>>>> adjusts the pointer (from obj to interface) and calls the default impl
>>>>> with the adjusted pointer. the latter is a direct call which makes it
>>>>> fast.
>>>>
>>>> Hm... I think this would work actually, as I think this is a runtime
>>>> lookup.
>>>> This sounds like a reasonable tradeoff, and having the final modifier
>>>> if
>>>> you
>>>> want it to be quicker (I think the compiler can statically do the thunk
>>>> if
>>>> you know the concrete object type).  Actually, it would have to do a
>>>> thunk
>>>> for a derived interface, even if you put final on it, as it can't
>>>> always
>>>> know the offset between 2 interfaces in a particular object (or can
>>>> it?).
>>>>
>>>> Perhaps someone with better knowledge of the way interfaces work could
>>>> tell
>>>> if it would be possible?
>>>>
>>>> -Steve
>>>>
>>>>
>>>
>>> I use MI often and have positive experience with it. One good pattern
>>> that
>>> I use is the Intrusive container.
>>> Suppose you have an item that you want to store in a list.
>>> Unfortunately,
>>> putting stuff into the single- or double-linked list leads to a memory
>>> allocation (unless some pool is used). Sometimes it is desirable to put
>>> next and prev elements into the item itself, so that no memory
>>> allocation
>>> is ever needed. Besides, now you can easily say whether an item is
>>> stored
>>> in any container. This can be implemented via inheritance:
>>>
>>> class IntrusiveContainerNode(T)
>>> {
>>>     alias T ValueType;
>>>     package T next;
>>>     package T prev;
>>> }
>>>
>>> class IntrusiveContainer(TNode)
>>> {
>>>     alias TNode.ValueType ValueType;
>>>
>>>     void add(ValueType value);
>>>     void remove(ValueType value);
>>> }
>>>
>>> class MyClass : public Node!(MyClass)
>>> {
>>>    // ...
>>> }
>>>
>>> This imposes the restriction that an item can be stored in 1 container
>>> at
>>> a time. However, you can subclass twise in order to be storable in
>>> different containers:
>>>
>>> typedef EventOneListener IntrusiveContainerNode;
>>> typedef EventTwoListener IntrusiveContainerNode;
>>>
>>> class MyClass : EventOneListener!(MyClass), EventTwoListener!(MyClass)
>>> {
>>>     // ...
>>> }
>>>
>>> MyClass instance = new MyClass();
>>> eventOneListeners.add(instance);
>>> eventTwoListeners.add(instance);
>>>
>>> It is currently impossible to implement this approach using mixins (due
>>> to
>>> a bug I'm yet to submit).
>>
>> I think you can do this without multiple inheritance:
>>
>> class MyClass
>> {
>>    IntrusiveContainerNode!(MyClass) eventOne, eventTwo;
>> }
>>
>> If you make IntrusiveContainerNode a struct, then there is no extra
>> memory
>> allocation necessary.
>>
>> What is wrong with a solution like that?
>>
>> -Steve
>>
>>
>
> 1) Functionality. You loose an opportunity to iterate over elements of the
> container.
> It can be resolved by extending the IntrusiveContainerNode class to store
> a thisPtr as well (4 more bytes per instance):
>

How does C++ MI do it?  I mean, if you have a pointer to class X, which is the second base class of (layout-wise), how does one find the pointer to Y? It can't be a standard offset because you have no idea how many base classes are there.  I would think that MI has to store the offset anyways.

> 2) Readability. In addition to the complexity above, you are now forced to
> write the following code instead:
> eventOneListeners.add(&instance.eventOne);
> eventTwoListeners.add(&instance.eventTwo);

I guess :)  It seems more clear this way to me.  what if you have 3 event listener containers, but you only ever put an element into 2 of them at a time?  Should you always store 3 IntrusiveContainerNode base classes in all elements?

>
> 3) Usability. You have to remember all the names like "eventOne", "eventTwo" or whatever name you give to the node. In some cases users are forced to stick with some predefined names, code becomes templated and losses virtuality. The following code shows the issues:
>
> class UpdateDispatcher
> {
>     void addEventListener(T)(T listener) // a template. non-virtual.
>     {
>         updateContainer.add(listener.updateEvent); // updateEvent is a
> predefined name.
>         //All the classes that want to recieve an Update should have a
> public "updateEvent" field in their body!
>     }
> }

Then use a hashtable to look up the lists (with an id supplied by the list as the key).  You can have as many lists as you want.  If you are looking for virtuality, or non-naming conventions (i.e. extendability), then why is your code specifically tuned to the implementation (i.e. only 2 specific lists)?  You can pass in the member name as a mixin string if necessary.

> 4) Performance. Additional pointer dereference (node->thisPtr->data) introduced (minor one, but nevertheless).

Again, how does C++ MI do it?  I'd guess they have to do the same thing.

> All these issues make the idiom unusable.

Nothing here conveys that to me.  What I see is that MI is possible in D, but hard to implement.  But you are exposing the ugly nature of what MI requires, and that makes you understand more of what is necessary to make MI work.  It results in less errors IMO, because you aren't confused why the compiler didn't just take care of it for you , and you have some obscure access violation bug (as was my experience).

-Steve


August 19, 2008
Denis Koroskin wrote:
>> I think you can do this without multiple inheritance:
>>
>> class MyClass
>> {
>>    IntrusiveContainerNode!(MyClass) eventOne, eventTwo;
>> }
>>
>> If you make IntrusiveContainerNode a struct, then there is no extra memory
>> allocation necessary.
>>
>> What is wrong with a solution like that?
>>
>> -Steve
>>
>>
> 
> 1) Functionality. You loose an opportunity to iterate over elements of the container.
> It can be resolved by extending the IntrusiveContainerNode class to store a thisPtr as well (4 more bytes per instance):
> 
> // I don't want to have additional memory allocations so it is a struct now
> struct IntrusiveContainerNode(T)
> {
>     alias T ValueType;
>     package T next;
>     package T prev;
>     package T thisPtr;
> }
> 
> and adding initialization code:
> 
> class MyClass
> {
>     this()
>     {
>         eventOne.thisPtr = this;
>         eventTwo.thisPtr = this;
>     }
> 
>     IntrusiveContainerNode!(MyClass) eventOne, eventTwo;
>     // ...
> }

One technique that's worked well for me is to put the link-list controller in free functions (or a helper class).  The link list looks like this:

 struct ContainerNode(T)
 {
     T* next;
     T* prev;
 }

Then the helper has functions like this (note I'm converting from a more complex C++ so I haven't tested this).

class List(T, alias listName)
{
	T* root;
	Owner find(...) //Note: this could be a free function if your looking for something more light weight.
	{
		T* node = root;
		while(node.listName)
		{
			node = node.listName.Next;
			...
		}
	}

	void Add(T* node)
	{
		...
	}

}


//Use

class MyClass
{
    ContainerNode(MyClass) eventOne, eventTwo;
}

List!(MyClass, eventOne) eventList1;
List!(MyClass, eventTwo) eventList2;

MyClass c;
eventList1.add(c);

Another way that I've used is bolt-in templates, although I much prefer the component form as that way you get all the advantages that components bring to the table.

-Joel
December 26, 2008
Java allows only a few concepts and design-patterns like MVC or Delegation and polymorphism. But there is not one correct and one wrong design-pattern. D tried to accept more concepts like templates and mixins, you could also accept MI.
Policy-based Design (http://en.wikipedia.org/Policy-Based_Design) is a very powerful, modern and flexible design-pattern wich makes usage of MI.
STL and Boost use MI and are excellent designed.
Features cannot be evil, but they can be used in a wrong way. MI would allow more flexibility, sometimes more elegant code and more design-patterns.
D is not a prototype anymore, now it could add new features,  http://www.pmg.lcs.mit.edu/papers/bidirectional.pdf shows us, how it could work.

The User
December 26, 2008
The User wrote:
> Java allows only a few concepts and design-patterns like MVC or Delegation and polymorphism. But there is not one correct and one wrong design-pattern. D tried to accept more concepts like templates and mixins, you could also accept MI.
> Policy-based Design (http://en.wikipedia.org/Policy-Based_Design) is a very powerful, modern and flexible design-pattern wich makes usage of MI.
> STL and Boost use MI and are excellent designed.
> Features cannot be evil, but they can be used in a wrong way. MI would allow more flexibility, sometimes more elegant code and more design-patterns.
> D is not a prototype anymore, now it could add new features,  http://www.pmg.lcs.mit.edu/papers/bidirectional.pdf shows us, how it could work.
>
> The User

MI is in fact evil. especially the C++ kind.
there are two concepts conflated here - subtyping and subclassing. They shouldn't be connected but they in fact are. MI aggravates this a thousand fold.

the first is subtyping:
you want to model your objects such that they'll be subtypes of two different types, for example. Java/C#/D easy solution:
interface A {..}
interface B {..}

interface/class ident : A, B {..}
you want to subclass two types (i.e copy the implementation)
Easy in D:
template A {..}
template B {..}
class ident { mixin A; mixin B; ..}

you want to do both (which is what C++ MI does):
class ident : A, B {
mixin impl_A;
mixin impl_B;
...
}

here, D supports MI. without all the problems in C++
C++ style MI is EVIL exactly because it conflate two separate concepts into one.

If you explore C++ use of MI you'll see that 95% of cases it is used to do the subtyping (interfaces). and doing subclassing with it is very rare and usually discouraged due to complexity.

December 26, 2008
The User:

This is the wikipedia page you refer to: http://en.wikipedia.org/wiki/Policy-based_design

The signal in that C++ code is almost lost in syntactic noise. This is a D version, I hope the original meaning isn't lost:

import std.stdio: writefln;

struct HelloLanguagePolicyEnglish {
    static string message() {
        return "Hello, World!";
    }
}

struct HelloLanguagePolicyGerman {
    static string message() {
        return "Hallo Welt!";
    }
}

struct HelloOutputPolicy {
    static void print(T)(T message) {
        writefln(message);
    }
}

struct Hello(OutputPolicy, LanguagePolicy) {
    // behaviour method
    static public void run() {
        // two policy methods
        OutputPolicy.print(LanguagePolicy.message());
    }
}

void main() {
    // prints Hello World!
    Hello!(HelloOutputPolicy, HelloLanguagePolicyEnglish).run;

    // does the same but uses another policy, the language has changed
    // prints Hallo Welt!
    Hello!(HelloOutputPolicy, HelloLanguagePolicyGerman).run;
}


An alternative D version:

import std.stdio: writefln;

template HelloLanguagePolicyEnglish() {
    string message() {
        return "Hello, World!";
    }
}

template HelloLanguagePolicyGerman() {
    string message() {
        return "Hallo Welt!";
    }
}

template HelloOutputPolicy() {
    void print(T)(T message) {
        writefln(message);
    }
}

struct Hello(alias OutputPolicy, alias LanguagePolicy) {
    // behaviour method
    static void run() {
        // two policy methods
        OutputPolicy!().print(LanguagePolicy!().message());
    }
}

void main() {
    // prints Hello World!
    Hello!(HelloOutputPolicy, HelloLanguagePolicyEnglish).run;

    // does the same but uses another policy, the language has changed
    // prints Hallo Welt!
    Hello!(HelloOutputPolicy, HelloLanguagePolicyGerman).run;
}

Bye,
bearophile
December 27, 2008
The User wrote:
> Java allows only a few concepts and design-patterns like MVC or Delegation and polymorphism. But there is not one correct and one wrong design-pattern. D tried to accept more concepts like templates and mixins, you could also accept MI.
> Policy-based Design (http://en.wikipedia.org/Policy-Based_Design) is a very powerful, modern and flexible design-pattern wich makes usage of MI.
> STL and Boost use MI and are excellent designed.
> Features cannot be evil, but they can be used in a wrong way. MI would allow more flexibility, sometimes more elegant code and more design-patterns.
> D is not a prototype anymore, now it could add new features,  http://www.pmg.lcs.mit.edu/papers/bidirectional.pdf shows us, how it could work.
> 
> The User


Andrei invented Policy-based design, and he's one of the key designers of D. So don't worry about D missing out on functionality in that area <g>.
1 2 3 4 5 6 7
Next ›   Last »