Hey guys

The question is mainly for the authors of ObjAsm and ATC, but I'd be happy to hear other peoples thoughts on the matter.
So lets start with the following questions.

1. is multiple inheritance possible with a macro model, or do we need a preprocessor?
2. is it a desired feature, or is single inheritance enough?
3. and if the interest is there, how do we implement it while maintaining minimal overhead?
Posted on 2005-03-02 01:37:35 by Maelstrom
The response from the JAVA authors I know is generally that multiple inheritance leads to headache and heartache.
I have enough of both of these in my day to day life without them spilling over into my programming time (which I consider quality time).
If I want to code stuff that uses multiple inheritance, I think I'll use JAVA.
That is, if I can think of a valid reason to apply it.
Since I cannot for the life of me think of a single situation which warrants multiple inheritance, I'm happy to leave sleeping dogs lie.

Can it be implemented using buildtime macros? Yes. Buildtime macros can act as a preprocessor.

Should we implement this in ATC? No. ATC was never designed to be that powerful. It was meant to be Light and Fast, which it is.

Can you think of a single situation where infinite-depth single inheritance is not capable of doing the job of multiple N-deep inheritance?
Posted on 2005-03-02 06:53:47 by Homer

If I want to code stuff that uses multiple inheritance, I think I'll use JAVA.

I thought one of the design decisions of JAVA was to NOT include multiple inheritance support?

The benefit of multiple inheritance is that you can make "mixin" classes - classes that can add functionality without messing up your class hierarchy. So, you can write these mixin classes *once*, and use them with multiple other classes.

Probably not very useful in assembly, though :)
Posted on 2005-03-02 07:04:07 by f0dder

If I want to code stuff that uses multiple inheritance, I think I'll use JAVA.

I thought one of the design decisions of JAVA was to NOT include multiple inheritance support?

Java includes a limited version of multiple inheritance in the form of Interfaces. It allows your class to implement multiple interfaces, but only allows you to extend (and re-use implementation of) a single base class (or if you like, "super class").
The benefit of multiple inheritance is that you can make "mixin" classes - classes that can add functionality without messing up your class hierarchy. So, you can write these mixin classes *once*, and use them with multiple other classes.

That is correct, "mix-in" classes can prove to be a useful design technique. However, it's not the only benefit of multiple-inheritance... I believe it was Grady Booch that said, that multiple-inheritance is like a parachute; you don't need it often, but when you do, you really want to have it. Bertrand Meyer also says that removing (or should I say, limiting) multiple-inheritance from an OO language leads to emulation in ways that are usually inappropriate. Personally, I'm not a Java programmer, so I can't provide my own examples... but in C++ I use it when appropriate and have no issues with it. I think Bjarne Stroustrup makes it most clear why and when multiple-inheritance is needed.
Probably not very useful in assembly, though :)

IMHO using a paradigm in a language that doesn't support it makes no sense. So I dislike the whole notion of OO in assembly as it exists on this forum.

P.S. If anyone wants references to above mentioned opinions of people other than me, just ask.
Posted on 2005-03-02 12:42:37 by death
IMHO using a paradigm in a language that doesn't support it makes no sense. So I dislike the whole notion of OO in assembly as it exists on this forum.


I see languages like languages and techniques like tehniques.

I always say and remember that OO and structuerd programming born in diferent days than the high-level languages. Even that this last ones use the technique (insert the technique like reserved word of the language) like a model you will find that some guys dont get how to use some constructions/patterns or models because they dosent understand it, they understand the high-level meaing (like I understand while ... then) but they dont understand the paradigm of structured programming.


Also some times you will find that some ones dont get the idea of modelate instructions and modelate data. For example dont get where to put a "for", then they see see a exampe and use it and say, hey it is working!!!!!!!, then you use such model without understand, but because you know that it works ;).




Im not a language expert, but I think that have some ral support in assemblers for OO(objet-oriented), SS(structured programming) or other paradigm should be nice :).




Whant a proof that the HLLS dosent implicate x paradigm???, delete while, for, if, case, but you will get the core of the HLL, ie the isntructions that are really part of the language ;), also delete the OO paradigm for example of the resrver words of java and you wll get the core of the language. One thing that all languages have is the secuence (one instruction follow the other at less in the source).






I think that there is a missunderstood when is sayed for example that structured progarmming the porpuose is to delete te GOTO statement, while you can still programm in a structured way (we know it, because we know that if use jumps), the point is not to remove the GOTO statement, the porpuose of structured progarmming is have structure in the programms!, is writed in a way of HLL statements (if, while, then,...), but is only a representation of the language (ie the sintaxis).




Im not an expert in languages, but is some thing that atrack me, then this is what I think about the subject ;).
Posted on 2005-03-02 13:58:45 by rea
Hey guys

Keep the opinions coming, I'd still like to hear from NaN, Biterider, and Ultrano.
Posted on 2005-03-02 15:06:09 by Maelstrom
Hello rea.

I didn't quite grasp most of what you're trying to say, but I think you're treating a certain 'feature' or 'paradigm support' of a language as a separate part added to the language without considering interaction with other language features. This doesn't work... there aren't any successful languages that have a "feature shopping-list". One part of the language must interact well, and also support other parts of the language.

You also seem to treat such language constructs as 'keywords'. They are not just keywords, but are also concepts. They have semantics, and the syntax is usually over-emphasized (that is not to say syntax is not important... after all, it's what you see on the screen). However, from reading the last paragraph you've written, I think you also realize that a paradigm is really all about concepts. A language supports a paradigm by realizing those concepts into language semantics.

Native assembly does not, and should not, IMHO, support these concepts directly. You don't have classes in assembly. You don't have nesting in assembly. Assembly is the building blocks for such support in a higher-level language. After all, what's a higher-level language providing other than a higher-level abstraction?

One thing that all languages have is the secuence (one instruction follow the other at less in the source).


Well, that's not necessarily true. I have vague idea of things such as dataflow/reduction machines, parallelized-execution languages and perhaps some non-imperative languages might fit in as well.

I think that there is a missunderstood when is sayed for example that structured progarmming the porpuose is to delete te GOTO statement, while you can still programm in a structured way (we know it, because we know that if use jumps), the point is not to remove the GOTO statement, the porpuose of structured progarmming is have structure in the programms!, is writed in a way of HLL statements (if, while, then,...), but is only a representation of the language (ie the sintaxis).


There is a difference between an OO language and a language supporting OO. C++ for example, is not an OO language. Java is. C++ actually supports four (as I see it) different paradigms directly, which can all be used in a valid way. Those are C-esque, Abstract Data Typing, Object Orientation and Generic Programming. Some times, these paradigms support each other, and other times, they don't. A good C++ programmer knows how to make them interact well. The same thing can be said for SP; An SP language wouldn't have a GOTO construct. A language supporting SP may have one.
Posted on 2005-03-02 15:13:39 by death
I understand that a certain paradigm impact the language or is like I see it, becasuse a paradigm is a way to modelate and in such way modelate wath can be modeleted with the paradigm provided.



For example we have english, but if you write poetry your style of writing should be influenced a little be the poetry style (you see a paradigm there), altought the langage himself can offer the words for suport that style, the style himself dosent depend in the language (altought I think and near to be sure that write poetry in english is diferent than spanish, but poetry still poetry in any language).


I know some languages, but the best example to show that they use techniques is exactly C++ with the same set language (ie. yes, delete the suport of SS and OO) you get the same set, this is the part that interact with such paradigms. If the "core" language (int, bool, +, &, ...), altought some things will be hard to choice if is part of the paradigm or the language.



Im separating the paradigm from the language because some times dosent matter that the language try to impose the semantic to use, if te programer dont know the paradigm, is for that Im separating the language from the paradigm, I agree that the language must follow some "specs" for use the paradigm, but certainly when you tallk about OO you are not talking about certain langage, thus OO is not necesary a high level language, is a model/tool.


Another proof that certain paradigm not implicate high level language is that there are a lot o HLLs suporting SS or OO. Then the language can be impacted be the paradigm to help in the modelation. (correction here he->the)





Another proof that HLL dosent implicate the paradigm is that a new comer to the language dosent matter that have a nice sintaxis to follow, ie class name {...}, also dosent matter that have structs/unions or any to modelate data, if the user dosent understand the paradigm/modelation, he/she will not able to use the HLL, is for that I put before the example about the "for" and a array, that they know where to use, but dont know why (they have watched and example and know to use the example but dont get how the modelation of data impact the modelation of instructions and viceversa).....

Following that if you know the paradigm should be very easy to switch from one language to other at less that the other language have a very dificult sintaxis or other paradigm implied this last will modificate the way you use the language....

Another proof that those paradigms dosent implicate a HLL is that we can write a entire book about the subject without insert a HLL. Altought we should use something for anotate :).








And what I have learned/watched from some friends is that they dosent know/see where start the language definition and semantic of the language ad where start the paradigm, is for that some people dont get if divide a function in subfunctions, put a array, traverse the structure, .....




By the way, because the paradigm serves more to modelate is for that some people doesnt know how to programm, even that they can understand the sintaxis of the language and sometimes the intrinsic semantic of such language (the core) ie, like where to put locals and his scope..... that are the more clear or easy semantics (altought dont know if you can consider this some part of the semantic of the language) that I can see from the language himself, but the paradigm have his own semantic not related to the language himself but how you use the paradigm for get a final modelation.





Like you have stated, asm is the "final" end of the compilers (operations and operands ), you consider asm like a suport for get those languages to support SS, OO, procedural, etc. I consider such paradigms only techniques for modelation (even the language can suport or be orientated/specialized). Like that can be used in any language even asm or a secuence of any operation and operand, altought I accept the part that some part of the language must interact nice with the other like a "glue" between the language and the paradigm.


as a separate part added to the language without considering interaction with other language features.
I agree with that, but the paradigm dosent depend in the language, depend in himself, the understand (concient/inconcient) of such paradigm will help in design programs in such language, pheraphs more than know the sintaxis and pheraphs the semantics that are implied.


A language supports a paradigm by realizing those concepts into language semantics.


I agree, but there is a interesting draw back, the one that I have talked, because the paradigm dosent depend in the langauge himself, and that altought the language have some semantics, the user o te language will haev to suffer in the side of understand the semantics of the language and pheraphs the semantics of the paradigm himself. That is... for example, some one can understand finally what is a local/scope and this is the semantic of the language, but it will cost more to understand where to use locals, I have friends that dosent practice much programming, one has been watching when I was doing a programm and say some like "how you can go with writing like that, that is I see that you start in some place and a the next moment you have others functions and the program is nearly working, I cant do that", by the way, Im sure that this person know the sintaxis of the language and can understand vaguely or pheraphs near to nice the semantics of the language (instead of sintaxis), but If Im not wrong is that this person doesnt know the semantics of Structured programming.

I see such example other time like a proof that HLL dosent implicate a paradigm and viceversa. The semantics of SS are somewhat "hided" and even that the persons can understand "while" because is clear meaning (altought they are not hided because there is a clear name of a construction but dosent implicate that you will use in the correct way), they hardly will go with more complex constructions because they dosent understand the paradigm. But sometimes happend that is sufficient to understand the semantics of the language, or watch some examples for get the idea and stick with the patern that you have learned.


Im sure you use SS in assember, or some of us use it, even that some dosent use .while, .repeat, .if...., remember that is the concept of have structure in the programs, never is talked about a sintaxis for maintain such structure... only is spelled that there should be a easy way to see such structure.... but I think they are not refering to "if" easely.


;), keep in touch.
Posted on 2005-03-02 23:08:19 by rea
For example we have english, but if you write poetry your style of writing should be influenced a little be the poetry style (you see a paradigm there), altought the langage himself can offer the words for suport that style, the style himself dosent depend in the language (altought I think and near to be sure that write poetry in english is diferent than spanish, but poetry still poetry in any language).

Which is an important point. Realizing that poetry is different in English than in Spanish is the key. You can say Smalltalk is an object-oriented language, and you can say Java is an object-oriented language, but writing a Smalltalk-style object-oriented program in Java is not going to work (well).

Im separating the paradigm from the language because some times dosent matter that the language try to impose the semantic to use, if te programer dont know the paradigm, is for that Im separating the language from the paradigm, I agree that the language must follow some "specs" for use the paradigm, but certainly when you tallk about OO you are not talking about certain langage, thus OO is not necesary a high level language, is a model/tool.

That is partially correct. The general meaning of a paradigm is a point-of-view of how things should be done. I most definitely don't claim that you can't do "object-oriented programming" in assembly, I just claim that it is not worth it 99% of the time. As I said above, what is considered good in one language may not fit well in another language.
Posted on 2005-03-03 05:53:34 by death
The C++ model of multiple inheritance gives some idea of how a compatible and "efficient" form can be created for ASM. But it feels awkward.

I have also looked at the Eiffel model, but that's difficult or impossible to do under a macro system, as it can identify multiple inheritance of the same base class as a single base class.

The alternate (Smalltalk) model of inheritance does not use "embedding" or "inlining" of inherited components. It uses delegation, where the object asks another object to handle a method if it doesn't have a definition. Embedding is merely an optimization for this model. I get the impression that those who use such object systems see no need for multiple inheritance.
Posted on 2005-03-03 17:44:30 by tenkey
The c++ model of multiple inheritance can be quite messy! that's quite one way to do it...
Posted on 2005-03-03 22:47:56 by f0dder
Hey guys

Does anyone have a link or info on how C++ handles OOP?

Can you think of a single situation where infinite-depth single inheritance is not capable of doing the job of multiple N-deep inheritance?


Not if your creating the object hierarchy from scratch, but it you want to derive from several *existing* classes that you don't want to modify, or can't modify, then the only way to create an object that inherits these classes is multiple inheritance. Correct me if I'm wrong. :-D
Posted on 2005-03-04 16:46:21 by Maelstrom
Obviously, MI can be emulated, like most anything else... The question is why prefer manual emulation to direct support?

For information on how some C++ implementations implement the object model, there's a great (but a bit out-dated) book by Lippman - "Inside The C++ Object Model". Some parts of it are not an easy read.

C++ doesn't enforce this model. From C++ point of view, there are no such things as vtbl or vptr... but I guess it's the de-facto standard (with many little deviations, but the concept is standard).
Posted on 2005-03-04 17:18:04 by death
Does anyone have a link or info on how C++ handles OOP?

Try here
Posted on 2005-03-04 17:44:13 by drhowarddrfine
Can you think of a single situation where infinite-depth single inheritance is not capable of doing the job of multiple N-deep inheritance?


Not if your creating the object hierarchy from scratch, but it you want to derive from several *existing* classes that you don't want to modify, or can't modify, then the only way to create an object that inherits these classes is multiple inheritance. Correct me if I'm wrong. :-D


You can create a new hierarchy and hide the old object classes behind proxy objects. Less efficient, but it still allows reuse of existing code. You can even call these proxy objects "interfaces".

I'm pretty much a fan of aggregation in most forms of "derivation". Where inheritance makes most sense to me, so far, is in separating interface (abstract class with zero code) from implementation, and in shallow frameworks which provide subroutine callback "hooks" and basic services through inheritance.
Posted on 2005-03-04 18:50:17 by tenkey
Hey guys

tenkey
Do you mean something like what DX does to support previous versions?

drhowarddrfinedrhoward
Thanks for the link but I was looking for how C++ constructs an object in memory, I should have been more specific.

death
Thanks, the book sounds like it contains what I was looking for.
Posted on 2005-03-04 19:48:53 by Maelstrom
When I was coding ATC, I thought a bit about this matter, but since I saw no-one uses it, the ratio usefulness/effort is 0. So, I didn't bother to implement it, and I won't. But since you all are interested how this stuff looks in memory and code, below's an example. All the functions are virtual, so that I show the most complex form of the layout with 3 classes. If all functions were normal ("void" in ATC), it would be easier - remove the vTables, calling is direct... and well - you get scalar programming.



;Foreword: "one" and "two" must not know and care what derived
;classes like "three" do. So, "three" has to do all the fun.
;I'll skip the "C++ compatible" keyword at three places here,
;for clarity


class one
virtual f1
virtual f2
long x1
long x2
endclass
class two
virtual f3
virtual f4
long y1
long y2
endclass

class three,one+two ; derive from both, order matters
virtual f5
long z1
long z2
endclass

;================[ AUTOMATICALLY CREATED BY ATC ]=======================\
one_vTable:
dd one_destructor
dd one_f1
dd one_f2

two_vTable:
dd two_destructor
dd two_f3
dd two_f4

three_vTable1:
dd three_destructor
dd one_f1
dd one_f2
dd three_f5
three_vTable2:
dd three_destructor2
dd two_f3
dd two_f4




three struct
vTable1 dd ?
x1 dd ?
x2 dd ?

vTable2 dd ?
y1 dd ?
y2 dd ?

z1 dd ?
z2 dd ?
three ends






three_constructor proc ; ECX is _this
;----[ call constructors ]---------------------------\
push ecx
call one_one
pop ecx

push ecx
add ecx,12 ; from this offset, the object data of two begins
; why 12 ? because we have the dwords vTable1+x1+x2 at offset 0
call two_two ; "two" doesn't know anything about the outside world
pop ecx
;----------------------------------------------------/
;-----[ update vTable1 and vTable2 ]--------------------\
mov [ecx].three.vTable1,offset three_vTable1
mov [ecx].three.vTable2,offset three_vTable2
;-------------------------------------------------------/

;---[ finally, call three_three ]-------\
push ecx
call three_three
pop ecx
;---------------------------------------/
mov eax,ecx ; EAX should have _this, but I want ECX to have it too in ATC
ret
three_constructor endp

three_destructor proc IgnorableArgument1:DWORD
push ecx
call three_$three
pop ecx

push ecx
add ecx,12
call two_$two
pop ecx

push ecx
call one_$one
pop ecx

free ecx

ret
three_destructor endp

three_destructor2 proc IgnorableArgument1:DWORD ; called when "two" tries to commit suicide
sub ecx,12
invoke three_destructor,IgnorableArgument1
ret
three_destructor2 endp

;=======================================================================/

;OK, now let's make some proc in ATCv99

main proc
local t
set t as three

mov t,new(three)

pcall t.f1
pcall t.f2
pcall t.f3
pcall t.f4
pcall t.f5

mov ecx,t
mov [ecx].three.z1,7
mov [ecx].three.z2,8

delete t
ret
main endp

;The above code will result in:

main proc
local t
mov t,malloc(sizeof three)
mov ecx,eax
call three_constructor


;---[ t->f1() ]--------------\
mov ecx,t
mov eax,[ecx].three.vTable1
call dword ptr[eax+4]
;----------------------------/

;---[ t->f2() ]------------\
mov ecx,t
mov eax,[ecx].three.vTable1
call dword ptr[eax+8]
;--------------------------/

;---[ t->f3() ]---------------\
mov ecx,t
mov eax,[ecx].three.vTable2
add ecx,12
call dword ptr[eax+4]
;-----------------------------/

;---[ t->f4() ]--------------\
mov ecx,t
mov eax,[ecx].three.vTable2
add ecx,12
call dword ptr[eax+8]
;----------------------------/

;---[ t->f5() ]----------\
mov ecx,t
mov eax,[ecx].three.vTable1
call dword ptr[eax+12]
;--------------------------/

; t->z1 = 7
mov ecx,t
mov [ecx].three.z1,7

; t->z2 = 8
mov [ecx].three.z2,8

;----[ delete t ]----------------------------\
push 1 ; C++ standard, where we delete from
mov ecx,t
mov eax,[ecx]
call dword ptr[eax] ; in other words, calls three_destructor (unless we have derived from "three")
;--------------------------------------------/

ret
main endp


It's possible to be done with macros, it won't be really hard to make, but with that ratio of 0 (and with my personal disinterest in this), ATC won't support multiple inheritance.
Posted on 2005-03-04 21:51:22 by Ultrano
Hey Ultrano

Thanks for the info :alright:

Do you know how C++ handles virtual bases?



A A A
| | / \
B C --> B C
\ / \ /
D D
Posted on 2005-03-06 21:50:59 by Maelstrom
It's quite easy. Now, let's change the above class definition of "three" to:


class three,one+two ; derive from both, order matters
virtual f5
long z1
long z2
virtual f2 ; override one_f2
virtual f3 ; override two_f3
void f7 ; a static method
endclass


The only changes in the whole code will be in the virtual table of "three":



three_vTable1:
dd three_destructor
dd one_f1
dd three_f2 ; <---- changed
dd three_f5
three_vTable2:
dd three_destructor2
dd three_f3 ; <----- changed
dd two_f4


The icall/pcall/ncall/ocall/hcall macros will not behave any differently when calling overriden methods - they just read the proc address from the method offset in the given vTable, and call the code at that retrieved address.


pcall t.f7 ; t->f7();
Normal (static) methods are not registered in the vTable, they are converted to

mov ecx,t
call three_f7

In other words, brought down to simple scalar programming. At compile-time the compiler (or ATC/ObjAsm) decides which proc it should really call in here.




A good reference for these things can be ATC itself - when you force it to display the code that it generates. This is done by specifying:
DisplayClassHardcode = 1
before you include class.inc
Another good reference is the asm output of any VC++ -compliant C++ compiler - with debugging and optimizations disabled (otherwise a lot of garbage code can get in your way).
It's actually best to examine both references - since ATC by default has the destructor being virtual, while in C++ you usually define a static destructor. I disabled static destructors in ATC, because they're something you'll never really need in a Cpp-compatible asm app.
Posted on 2005-03-07 11:20:53 by Ultrano
Hey Ultrano

Thanks for the info on virtual functions but I was asking about virtual bases. I should have been a little more verbose in my post but I was in a hurry at the time :grin:

With multiple inheritance you could end up with the same class included multiple times, but if you specify the base as virtual C++ will only place a single instance of the class in the object.

Consider the following class hierarchy.



A A
| |
B C
\ /
D


Class D contains two copies of class A.
C++ allows you to specify class A as a virtual base in classes B and C which prevents more than one copy of A making its way into class D.
The class hierarchy now looks like this.



A
/ \
B C
\ /
D


Do you know how C++ handles this?
I would look at C++ asm output but I don't have a copy of C++.
Posted on 2005-03-07 15:04:48 by Maelstrom