Well I have to say im very impressed with something that "happened" out of the blue today.

I origionally was writting some source to help RadioSys handle his MaskedEdit box custom control question, when I hit the wall wishing i has a good robust String class for OOP. This led me to designing an OOP class for such, but then hit the wall due to the length (there is about 20 or so methods). With the current NaN & Thomas OOP model, you would have alot of redundat memory allocations as each instance would get a copy of the function pointers.

And it was here it hit me how to actually implement Vtables :grin: for our OOP model. Im 80% finished after one night, and its about 1/3 the macro source as well! It appears full Inheritance will also be supported, and i might be able to do something towards Overloading methods as well.

I will finish it up very soon. Im upset i have to stop, to sleep for work tomorrow ;) ! Im really getting into this, watching it come together so well.... As well this String class has OLE functions to convert to BSTR and back (which will be very usefull in COM ). Just gotta finish the OOP engine that will drive this class via. VTables.

Posted on 2003-07-11 00:17:03 by NaN
It's not difficult to implement VTable by using macros(how powerful the assembly macro is :) ).
How ever, VTable macros will generate too much symbols and take too much assembly time.
MASM doesn't support internal VTable, so maybe such macros are necessary for MASM.
Posted on 2003-07-11 05:26:04 by KomsBomb
Macros are quite powerfull... and there is not a noticable overhead in using them (im not concerned here personally).

I have to say tho, im nearly finished, and it works 50x better than the previous version. The thing is im amazed at how fast it is to develope this time around. The old model took Thomas and I weeks to hack out. But this time I got a stronger model together in just under 12 hours!!

I guess experience has alot to say for it self. A good portion of the previous development time went into MASM R&D, to sorta find its limits with macros... I dont have to do that this time around....

Anywho, I have full inheritance working at this point. The objects are static vtables of function pointers, and no longer than their 'stand alown' size. That is the size of the object as uninherited. When you inherit to make a new object, its vtable size is only the addtional methods above and beyond the inherited object class. (which also saves memory space). Data is totaly private and called from the heap. Each heap data block constitutes the ENTIRE instance memory. Only one pointer extra is included (for the vtable pointer) ~ which is a definite bonus for memory savings!

I have also cleaned up the coding requirements to 'define' classes. There is alot less work needed (class formatting etc) than the previous version. Only downside i can see at the moment is i abandoned initializing the data on creation. If you want to initialize an instance's data, you will have to spell it out in the constructor. (This is not a big deal tho. It was a trade off to have this feature and a great deal more complexity, or give it up for simplicity... I took the latter.... ;)

Method calling is done via a recursive method macro, which will retrace through as many vtables as it needs to call methods... (this was the hardest part ~ its all done at compile time, so there is only 2 or 3 lines of run time assembly).

My only remaining problem that i see is Polymorphism. I think it can be done too, but it will be more of a manual step than a macro automated one. Im still pondering on this... WHen inheritance happens, the highest level vtable is pointed to by the instance object (heap memory). This vtable has all the methods for it, and also points to an inherited vtable (if used), etc. etc. So the object vtable is a linked list of vtables. As more object classes are defined more than one vtable can point the a common other. So if i modify the 'other' for polymorphism, all other inherited vtables would also recieve this (which may not be desired).

The alternate is to "copy" the vtable, make polymorphic rerouting to alternate function pointers, and the change the vtble links to route through the altered copy on a specific class... this will most definitely need to be a run-time proceedure in the constructor of the class that offers a polymorphic function(s)...

Anywho here is a small taste of what it looks like at the moment:

CMETHOD data1, input_data:DWORD

A1 dw ?
A2 dw ?

dd offset Function1
dd offset Function3

This is all the 'overhead' required, asside from the functions for the class. This class is called 'A' and has two methods "data1" and "data2" with params as needed. It has two WORD size private data, and the Vtable for it (with the function offsets following is).

I hope your impressed by the size ;) . I am :alright:
Posted on 2003-07-11 17:48:23 by NaN

Man, you know your on the right track when it just falls together...

Turns out i dont need to make any run-time processing for polymorphism/interfaces... It only took one more 4 line macro and 3 extra lines of code in the recursive METHOD macro... lol!

Its working pretty sweet at the moment... Last thing to do is see if i can get method overloading working... last step. I know how to do it, just not sure how it will weigh in with the rest of the macro systems...

Couple more hours and i will release the source...
Posted on 2003-07-11 21:26:40 by NaN
LOL, NaN got a geekasm.
Posted on 2003-07-12 01:32:04 by Paulicles the Philosopher
Ya i guess so <lol>...

Down side is I discovered my solution to polymorphism is only half correct... i have a bit of a snag on my hands at the moment.

Example: I have three classes, IDraw (draw interface), CLine, CSquare

Cline inherits IDraw
CSquare inherits CLine

If i go CSquare:Draw, i get a "Draw Square"
If i go CLine:Draw, i get a "Draw Line"
If i go CLine:Draw on the CSquare handle, i get "Draw Line" <<< Problem here

So in essence i have OVERRIDING inherited methods working, but polymorphic calling is not.. I have to somehow identify at compile time what the handle points to (type of class), or like I said above, custom vtables... either way its not looking like a quick solution....

Posted on 2003-07-12 12:05:22 by NaN
I see this thread is getting alot of views, so I decided to explain more about what the model is.

Below is an image of how its memory is organized. I have two examples shown on it. A Class call Line, and a Class called Square. The line class has its own methods, A, B, C, D. The Square Class INHERITS the Line class and adds four more additional methods, E, F, G, H.

Instance data is private to each instance, and is not broken apart. Line has 4 data members, and (not shown correctly) square would have these four and an additional 4 in one complete heap memory object. (( To save space, the picture still shows 4 instance data members for the square )).

Inheritance is done by pointing back to the vtable that a class inherits from. Through some macro magic and a couple of text equates, I have managed the decision process as to weather or not a method is nested further back down the inheritance list or not.

This scheme works very efficiently, since different instance objects overlap and reuse vtables for different classes. This greatly saves .data memory if you have alot of object classes included in your program. However, this efficiency is also the thorn in my side, since it doesnt work very well with polymorphism. If i take the Square intance object, and say "call Line.Method_C", the MACROS at compile time will follow the lower route just as if the Square Instance was actually a Line Instance. After all the computer has no clue what is what, it just works off the info you give it. This is why you have to say "Square.method name" or "Line.method name". The program uses the first param "square" or "line" to know what STRUCTURE template to apply on the instance object.

Inheritance "wraps" around the inherited class, so the alignment of the inherited methods are the same location as the base class itself. So in essence the closes to the begining of a class structure, the more likely the method was inherited. Again, for strait inheritance with no need for polymorphism, this is the idea situation!
Posted on 2003-07-13 17:31:07 by NaN
Now the question im still fighting over is *how* to I work with this structure and allow for polymorphism as well? My only working answer I can come up with is to make a uniquely new vtble for classes that want to override inherited methods for polymorphism.

This would mean the class would be one VTBLE deep, and its inheritance ptr would point to null automatically. It also means that if there is many layers of inheritance, it would have to copy all the vtble layers recursively into a separate vtble just for this *special* class. This will work, becuase to allow for polymorphism, it basically nullifys the ability to tansverse back down the inheritance tree to the method pointer its looking for, and instead provide a complete list at the top level vtble, in order as dictated by the class method structure.

Now the problem with this: Destructors, and Constructors. Destructors are called automatically, and do not have method arguments that is defined by the programmer. They are called in order from the highest level, back to the base level, in an inheritance scheme. This ensures the "wapped" layers can free and close down memory, utilizing inherited methods, before this layer is negatted. etc.

I can not do this if i have compiled the layers into one! There will be only ONE destructor pointer, because there is only one vtble layer. As well Constructors work in the reverse. They start from the base level, and allocate resources as needed, before running the inherited constructor, which may require the presence of inherited methods (and resources). Again, im not sure how i would handle this if i compiled this into one construtor layer.

I hope you see my problem here with polymorphism. If you have suggestions, or questions to my explainations, please offer/ask them! This model is far better than the previous OOP model, IMHO. Its coding requirments are far simpler, and there is less macro overhead as well. I just need to figure this out to make it a good strong OOP model for use by MASM users.

Posted on 2003-07-13 17:41:19 by NaN
Negative pointer offsets? That's really classy*, like how they make BSTRs hold their own length at [-4] bytes from the pointer.

I bet you could figure out MULTIPLE INHERITANCE (Ta ta ta!) by creating as table of inheritance pointers, or a pointer to table, or something.

I have always wished for a compiler or assembler that would have monstrously powerful preprocessor features, like lists and variables that would hold information. If I ever write a compiler, I will do something like that.

* No pun intended
Posted on 2003-07-14 00:56:11 by Paulicles the Philosopher
Ya, the negative offsets came out of necessity. I want to use a structure at compile time to organize the methods into a class, but i DONT want to have vtables any longer than they need, and link them as shown above.

Problem was having the destructor on each class, you cant simply chain them together, or else you get repeated destructors in the middle of a class structure. So thats when i caught on to the fact that Struct.StructValue == is always positive. If i make the destructor and other stuff negative from a base point, i *can* chain vtables together ;) . ANd this is how it works now. And works very well i might add!

Still thinking on what to do with polymorphism tho. (Took the weekend off)...
Posted on 2003-07-14 16:59:36 by NaN
What about multiple inheritance?

How does class cDOG inherate from both cPAWS and cNOSE?

Remineds me: My dog has no nose.

Passer by: Oh dear, how does he smell?

me: AWEFUL!!!
Posted on 2003-07-14 22:45:49 by Ernie
this type of stuff is why i use c with classes A.K.A c++. :alright:
Posted on 2003-07-15 00:03:10 by Qages
Its designed to inherit (single) back as far as MASM can recurse. It will not inherit two interfaces/classes at once however. I will leave that stuf up to Java ;)

I now have polymorphism working. ( big wide-eye grin )

It was probably the hardest part. If you know your class needs to be used polymorphically with other such classes that inherit a common base/interface, you have to specify in the class layout. It builds an alternate vtable with no linking (as shown above). Instead it parses what *would* have been the vtable list above, and copies out all the function pointers into a flat, single level vtable. With this done, you can OVERRIDE methods with alternate methods.

Here is my example source:


DEF_CLASS_METHODS CLine ; Define class methods

DEF_CLASS_DATA CLine ; Define Class data members
A1 dd ?

BUILD_VTABLE CLine ; Define Class method functions
@PROC CLine.Draw, Function1
@PROC CLine.Dar2, Function1
@PROC CLine.Dar3, Function1
@PROC CLine.Dar4, Function1


DEF_CLASS_METHODS CCircle, CLine ; Circle Methods Inherits Line

DEF_CLASS_DATA CCircle, CLine ; Circle Data inherits Line
A2 dd ?

BUILD_SINGLE_VTABLE CCircle, CLine ; Polymorphic VTABLE, & additional method
@PROC CCircle.Draw2, Function4


DEF_CLASS_METHODS CSquare, CCircle ; Square Methods Inherits Circle

DEF_CLASS_DATA CSquare, CCircle ; Square Data Inherits Circle
A3 dd ?

BUILD_SINGLE_VTABLE CSquare, CCircle ; Polymorphic VTABLE & additional method
@PROC CSquare.Draw3, Function4


If your confused, the three class method structures would look like:




The source used to test these classes is:
   OVERRIDE CCircle, Draw, Function2

OVERRIDE CSquare, Draw, Function3

mov hObj, eax
METHOD hObj, CLine.Draw, 1

PrintText "========================="

mov hObj, eax
METHOD hObj, CLine.Draw, 1

PrintText "========================="

mov hObj, eax
METHOD hObj, CLine.Draw, 1

The resulting outputs:
Line Constructor

Draw a Line
Line Destructor
Circle Constructor
Draw a Circle
Circle Destructor
CSquare Constructor
Draw a CSquare
CSquare Destructor

The think to see here is in all cases the object was called with the "CLine.Draw" indication. Since i have made *special* vtables for polymorphism, the OVERRIDE macro is used to redirect the function pointer to a more suitable function for the particular class method. This is how you end up with "Draw a Square" and "Draw a Circle" for outputs.

If i didnt want to use polymorphism, memeory would be better utilized as outlined above, and internal vtable linking would take place, from square to circle, to line. The downside (as discussed above) is that Callin CLine.Draw on the square hObj handle, will result in the CLine.Draw method "Draw a Line", since that is what was explicitly asked for.

I will clean up the macro file tomorrow and release it for those interested. For now this is about all i want to provide... Im tired, and happy ;)

Posted on 2003-07-15 00:05:40 by NaN
BTW, the OVERRIDE statements must be in the code segment, but doesnt need to be outside of the class's. I only put it there for simplicity in testing. The best place to override a class's methods is in its constructor. That way its out of your face, and you dont need to think about it.

Posted on 2003-07-15 00:19:53 by NaN
NaN, I am taowen. I am receiving military training, so I do not have much time to see what new things you have discovered. I am very happy to see you pick up OOP again.
I have done a lot of work around it, and I have published it. And now, I have some new idea about it, and after I think it over, I will post it, too.
I don't know you have got what version of my MOOP. I can give you a newest version. And I recently tend to using a external exe file to do coverting that is a preprocessor. I have thought the difficult and trade-off in my design, and the problem of porting, using a preprocessor just let the IDE to do one more step but provide more flexible and powerful ability.
8.4, I will be free. Then, I can use MACRO to complete a final version. You can see the syntax of asm will be changed greatly. After such final version, I will do some experiment on preprocessor or even a new assembler.

Can you explain the TYPE system of your oop model which is very important to OOP. typed assembly language is somewhat hard to design. And how to invoke the method of object using a ptr, where a register is a must, use which register and how to use is a big problem.

Thank you for your help when I was designing my work, and if you need, I can help you when you designing your model.
Posted on 2003-07-15 00:51:47 by taowen2002