Okee, no need to blabber, so i will get to the point:

Our model is solely based around using STRUCTURES. Every class is a structure.. (so dont forget this!). Our model uses some needed macro's (which i will explain l8r), in particulare, the MACRO called "CLASS".
CLASS NewClass

...
..
ENDS


The class macro actually starts the begining of the structure, via its scripting. So when you read "CLASS" as shown, just think, "its a structure called 'NewClass' "


If your ok at this point your doing great! :)

Now the class structure has some specific points that need mentioning:

- 1 - Methods always come first in class definitions (make inheritance work smooth)

- 2 - The first method entry MUST be a destructor method! (this is always true, even if you think it isnt, as inheritance provides a seemingly special case)

- 3 - Methods are always 32 bit DWORDs in the structure. (They hold the FUNCTION POINTER to the method in question ~ 32 bit address).

- 4 - All Variables follow the methods. Just like any old variable your use to sticking in any other structure. NOTHING NEW HERE.


With another macro, we easily define methods in the class structure with "CMETHOD" macro. Here is an example Class:
CLASS MyClass

CMETHOD destructor
CMETHOD getNumber
CMETHOD setNumber
theNumber dd ?
ENDS


This class will have three methods, and one variable.


What is a method?

Methods are quite simply PROC's. They are defined in asm as PROC's! We just call them "methods" because its a high level lingo. METHODS == PROCS.

The thing that makes OOP unique, is that the "methods" are written to act ONLY on the variables found within the class. You by no way are resticted to this, since we are ASM programmers, and could access some global value just like a PROC, but you would loose the re-usable simplicity of objects. You would now have a condition to provide for, the global varible, in re-using this class in future projects. Bottom line, unless you feel advanced with Objects, i urge you to keep all needed variables spelled out in the class structure (such as the above "theNumber" dword variable).

Another major asset to Objects is code re-use! A method is writen to work on itself. WHy? for created class instances.

An instance of a class is the "incantation" if you will of actually creating a space in memory for your "instance" of the Class. REMEMBER, a class is only a structure!! Stuctures are only templates! They dont take up memory!. To instanciate a class into an object, memory is allocated dynamically off the heap, determined by the size of the class structure!. These allocated bytes is the "instance". In this case, 3 methods = 3*4 bytes, and 1 variable dword = 1*4 bytes, makeing 16 bytes in total for every instance!

Example time:

Say i create 3 instances of the above class. This means, at run time, i dynamically allocate 3 groups of bytes 16 bytes, off the heap.

Say the method "setNumber" points to this proc:


setNumberFunction PROC lpTHIS:DWORD, ANumber:DWORD

mov (myClass PTR [lpTHIS]).theNumber, ANumber
ret

setNumberFunction ENDP


The above "lpTHIS" is the address of "THIS INSTNACE's group of bytes". So its written above to use the passed address of "some" instance (lpTHIS), and then use the class structure definition to offset to the "theNumber" variable, and set it with the value passed to the function (ANumber).

This method function doesnt "know" really what its working on!! Its written to only know that it will work on ITSELF!, and uses the lpTHIS to help itself out.

So when i have 3 instances, the above method code (actual proc) will service each instance individually. And thus keep each instance UNIQUE. This means i can set one instance to have the number 3, the second to number 0Ah, and the third to 7!, and each will remember in their specific memeory group, the stored number.

In practice, and adhearing to our model, the above conversation would become this:


.data?
hInstance1 dd ?
hInstance2 dd ?
hInstance3 dd ?

.code
; allocate MyClass (16 bytes) from the heap, and save
; the address in "hInstance1"
mov hInstance1, $NEW( MyClass )

mov hInstance2, $NEW( MyClass )
mov hInstance3, $NEW( MyClass )

>>>>>>>>>> HEAP MEMORY <<<<<<<<<<<<<<

hInstance1: xx xx xx xx ; destructor pointer
xx xx xx xx ; getNumber pointer
xx xx xx xx ; setNumber pointer
00 00 00 00 ; theNumber variable

hInstance2: xx xx xx xx ; destructor pointer
xx xx xx xx ; getNumber pointer
xx xx xx xx ; setNumber pointer
00 00 00 00 ; theNumber variable

hInstance3: xx xx xx xx ; destructor pointer
xx xx xx xx ; getNumber pointer
xx xx xx xx ; setNumber pointer
00 00 00 00 ; theNumber variable

>>>>>>>>>> BACK TO CODE <<<<<<<<<<<<<<

; METHOD is a macro that works like invoke.
; Param1 == InstanceHandle (address, which is lpTHIS)
; Param2 == Class Type of the Instance (to use the struct)
; Param3 == method to call
; Param+ == method parameters other than the lpTHIS

METHOD hInstance1, MyClass, setNumber, 3
METHOD hInstance2, MyClass, setNumber, 0Ah
METHOD hInstance3, MYClass, setNumber, 7

>>>>>>>>>> HEAP MEMORY <<<<<<<<<<<<<<

hInstance1: xx xx xx xx ; destructor pointer
xx xx xx xx ; getNumber pointer
xx xx xx xx ; setNumber pointer
03 00 00 00 ; theNumber variable (little endian)

hInstance2: xx xx xx xx ; destructor pointer
xx xx xx xx ; getNumber pointer
xx xx xx xx ; setNumber pointer
0A 00 00 00 ; theNumber variable (little endian)

hInstance3: xx xx xx xx ; destructor pointer
xx xx xx xx ; getNumber pointer
xx xx xx xx ; setNumber pointer
07 00 00 00 ; theNumber variable (little endian)

>>>>>>>>>> BACK TO CODE <<<<<<<<<<<<<<


While i can create 1000 instances, there will always be only ONE set of method functions. No more, and they will work on every instance uniquely! This saves exe code size in big projects!!! as only the method code is writen into the exe. Everything else is dynamically alocated!, and dynamically processeded with FUNCTION pointers to these methods.

Some of you may think this radically new, some may think this is how objects work.. Im sorry but i dont want to leave any assumptions.

Also, some may be wondering what the bytes 'xx' are set to. They are set when the NEW$() macro gets called to point to the proper methods. I will explain this tomorrow, as well as why the destructor comes first in all classes. I will try to analyse an actually compiled class for you. (the above was simplified somewhat, an will not compile as is).

Please.. give feedback.. questions.. am i too fast/slow.. anything not making sense??

NaN
Posted on 2001-09-18 19:07:18 by NaN
Now you have me interested. I especially like this part:

- 1 - Methods always come first in class definitions (make inheritance work smooth)

- 2 - The first method entry MUST be a destructor method! (this is always true, even if you think it isnt, as inheritance provides a seemingly special case)

- 3 - Methods are always 32 bit DWORDs in the structure. (They hold the FUNCTION POINTER to the method in question ~ 32 bit address).

- 4 - All Variables follow the methods. Just like any old variable your use to sticking in any other structure. NOTHING NEW HERE.


I like the ridgid structure. Probably easier to find bugs that way.

C++ should have the same type of restrictions, IMO. Encapsulate everything into classes so no one can see what a mess they are.
Posted on 2001-09-18 19:37:22 by ThoughtCriminal
Nice interface, but I have one query.

Wouldn't it save you memory to place the function ptrs in a virtual table since you only need one set of function ptrs per class. The first entry in the class structure would be a vtable ptr and the first entry in the vtable would be the destructor.

Inheritance would still work as the class would inherit from the base class and the vtable would inherit from the base class vtable.
Posted on 2001-09-18 21:52:47 by Maelstrom
This is something i am still thinking thru. I realize the current model could be more optomal, but for r&d purposes it keeps things more readable and contained.

I have a working "vtable" solution as well, but there is some quirks with superclassing, and polymorphism, however your right, inheritance in general is working. As well, there needs to be two more structures defined for each class to make it all work, with proper inheritance, AND proto type checking all around.

One structure is for heap allocation. One structure for vtable offsets. One structure for CMETHOD's (proto type pointing).

The heap struc would then be just variables.

The vtable offsets are just this, stored in .data.

The proto struct is for using Invoke:

invoke ( proto_struct.fucnt ptr ).funct, lpHeapData

The vtable and the proto structs need to be parallels of each other. As well, the vtable is real, written to exe. But the proto struct is only compile level info. Lots of wierd things going on, which *definitely* wouldn't be attractive to follow by manual editing, expecially if some people are learning this first time thu.


BUT, if i can get the bumps out over the coming months, i will definitely move this way, to save the memory allocated.


(( Im surprised this was the second message :) , its the only loose end im still pondering over ))

NaN
Posted on 2001-09-19 02:35:51 by NaN
NaN mentioned 'superclassing' can someone explain this please?
Posted on 2001-09-19 06:07:28 by Maelstrom
Im borrowing the term from java.

When you inherit something, you take on their methods. Polymorphism is where you alter an inherited method to do something different, when called from the class that inherited it.

Superclassing is where you decided i want to actually call the SUPER (origionally unaltered method), from within a polymorphic method of the same name.

Hard to explain, so i do it simply like this:


Class A
MEHTOD -> giveMeANumber
ENDS

GiveMeANumber_Function Proc ...
return ( 5 )
endp

>>>>>>>>>>>>>>>>>>>>>>>>

Class B Inherits Class A
A <>
ENDS

OVERRIDE giveMeANumber WITH offset NEW_GiveMe

NEW_GiveMe Proc ...
return(6)
ENDP

>>>>>>>>>>>>>>>>>>>>>>>>>

Now if you call:
"A.giveMeANumber" you will get a 5.
"B.giveMeANumber" you will get a 6.

This is polymorphism.

What if i wanted to further my code re-use,
by using already copiled code from Class A:

NEW_GiveMe Proc ...

; calls the super class (A)
SUPER( giveMeANumber )

add eax, 1
return (eax) ; still returning a 6!

ENDP

This is what im calling "superclassing" by re-using inherited method code (A.getMeANumber), and still altering the final result for methods that are polymorphic (B.getMeANumber).


Hope this helps...

NaN
Posted on 2001-09-19 12:12:01 by NaN
*keeps reading, because this is so interesting...*

Now, I apologize ahead of time, for my lack of understanding, but I have a question: What if you were to have a class that performed in a different way, like this:


class myclass {
parent: //what this unit belonges to
(id, x, y, z) world // parent to world class
id dd ?
x real4 ?
y real4 ?
z real4 ?

functions: // what units can do
createunit
(real4, real4) moveunit
(int) modifyunit
(real4, real4, real4, int) shootunit //unit shoots at a vector with what
clearunit //clear unit from memory, after he is blown up
unitpropflags dd ? //these are the flags to the unit telling what type it is.

children:
wheels
turret
}

So when I create the world, the world has its own properties, but then when I make a unit from my template above, the unit belongs to a world using the id, and sets its existance up. I then call createunit, and the unit not has a 3d mesh because I created a vertex buffer for it, and other things like that to render it.

Once I've established the unit, I can tell the unit moveunit(204,304), and this unit will move from it's current location to the new location over time (not instantanous). I can also tell the unit to fire and give it a vector, this fire code will modify the child of turret as it's getting ready to fire, or fireing, just the same as a move command would modify the wheel child to spin depending where the unit is going.

But, to further that, the functions are inhereted ALSO. My move command will require some AI, so now that function becomes a child the AI class, and AI now has one more thing to calculate when called.

-------------------------------

Ok, this was a horrible example, but do you kind of get what I'm getting at?
Posted on 2001-09-19 13:04:59 by Kenny
I know what your outlining, but Im not sure if i got your question.

With our current model, you CAN inherit from only ONE parent class, per class. But nothing is stopping you to USE any number of other classes.

As im reading it, i would have your base "tank" class as you discribe it, USE an IA class for moving, USE an DDRAW class to draw the tank moving, USE a WORLD class to manage global positions etc.

Now if you wanted to build a BETTER tank, I would INHERIT from "tank" and get all the goodies that tank already has, and build more features into this new "better_tank" class.

With polymorphism, you can also OVERRIDE the "fire" method inherited from tank, and provide code that would 'fire-better' than its parent's code. All the while having the same method name for both. (fire).

Am i helping here?

NaN
Posted on 2001-09-19 16:03:46 by NaN
I mean: is it possible, or am I setting myself up for a LOT of work to be able to make a framework like that without bugs?

What the final code would do is this:
Go through the topmost class and just work its way down the line to all the children, and as it does that, it updates everything in the game, and then renders it.

Wait, I just confused myself. I DON'T KNOW WHAT I'M ASKING lol :)
Posted on 2001-09-19 16:09:42 by Kenny
Wait, ok just give me some thoughts on it then because I'm really scatter brained. Has something like this been done before, where a class modifies another class, where the class might be a parent to a certain class, but not to a different one.

Wow, ok my brain hurts now. I'm still trying to figure out the whole point of this gay class thing. Is it for code reuse, or faster code write?

You know what, lemme think on this one a little more. I'm pretty dense when it comes to this area, but I'm trying my HARDEST to understand :)
Posted on 2001-09-19 16:17:17 by Kenny
Kenny

The main advantages of classes (IMO) are:

1. Code reuse.
2. Inheritance - which allows you to extend the capabilities of a class while still making use of the existing code base.
3. Polymorphism - which gives you the ability to perform different actions using the same interface.
4. Code maintanence.

So don't give up, I found classes confusing when I started.

NaN

How do you handle 'superclassing' with your model?

Using the vtable version, you would have to call the base function by using the vtable of the base class, but that would require making the vtables global or storing the base vtable ptr with the class.

:confused:
Posted on 2001-09-19 17:44:24 by Maelstrom
Ahhh, FINALLY I get to see it.
Dang, now all I need is the time to study it.

A few minor points:

MSVC and several other major C++ compilers all place the vtable inside the object. Thus, each instance of the class (or each object) carries the memory to hold another instance of the vtable, a vtable identical to every other instance of that class.

The real reason why is not apparent, however, I'm sure lots of clever fellows found some reason for this, probably pertaining to instancing inherited classes. It seems an object is assembled by running the constructor of the most inherited class (the baseest one), and out to the outer inheriting class.

Keep in mind C++ supports both single AND multiple inheritance on every level.

(OK, a small voice in the back of my head keeps saying "but they still only need ONE vtable! That is true, but may not be worth the work effort to save a few bytes) (FEW bytes? Didn't you read appendix A of "Essential COM"?)


Other things:

1. Code reuse

Ugh... Fragile base classes. Not a good idea.



2. Inheritance - which allows you to extend the capabilities of a class while still making use of the existing code base.

Actually, inheritance comes in two forms. Interface inheritance, and implimentation inheritance. Implimentation inheritance is what is usually meant, where the base class gives some of the methods, and in inheritor gives others. The problem here is since a class is a 'black box' of functionality, one never knows if the base class method shold also be invoked after (or before) the inherited method is run. (That's the classic 'fragile base class.')



3. Polymorphism - which gives you the ability to perform different actions using the same interface.

From my understanding, polymorphism means different classes can support similar methods, which may have different functions. So while a cDocument object and a cPicture object may both have a .Print method that causes the printer to waste more paper, it's doubtfull that a cElephant's object and the cAutomobile object's
.Move method have anything in common.



4. Code maintanence.

Actually, I don't think classes have anything to add to this. Code is tough to maintain period (although COM objects (which are C++ objects in fancy clothes) may help end the 'dll hell' problem).
Posted on 2001-09-19 20:30:59 by Ernie
Kenny... stick with it :) , your on the right track! (Basically, a Manager class to manage all things that are to be painted on the screen, each thing (object) will inherit from a comon interface (PaintMe), thus thu polymorphism, the manager wont need to know if its drawing a tank, or a person, when going thu its list of objects, and telling each of them 'paint!')

Ernie, thanx for the CPP news, as it sides a bit with our model. To be honest, this entire project had ZERO research and all R&D. Im sure i re-invented the wheel somewhere, i wanted to see if i could do it based on grey matter alone (with Thomas' help of couse). Currently every instance, gets a copy of the needed function pointers within themselves.


Maelstrom, its a bit complicated.. i planned on hitting this issue further down the road.. but here is an early attempt.

You have to think, WHEN? When do you need to SUPERCLASS a method? A. when your within a polymorphic method! If not you got access by inheritance.

There is no point in externally SUPERCLASSING... makes no sence. You only superclass within a class method definition. SOOO, as the class is being compiled, it already KNOWS what the inheriting class is (!KEY!). Knowing where the method sits in the INHERITED class, i can determin an offset, from some base. And since each class pointer table is labeled systematically, based on its class name, i can get the BASE from the .data segment, regardless if there is any instances.

So this is how i find the offset, and where to 'base' from, once an instance has been overridded polymorphicly.

NaN

PS: I got a shit load of work dumped on me this week, by my course supervisor :( . To quote him, "..oh, i forgot about you guys, i should have had you in last week. Hmm, oh well, heres last week's stuff and this week's stuff.. have it ready for next week". So long story short, i got alot of researching to do, and may have to make these conversations a bit more sparce. I dont know Thomas' schedual, but he may add to the conversation, as he understands the model as well as I. (oh.. and no new post tonight or tomorrow... will try for friday!!! Sorry.... )
Posted on 2001-09-20 00:04:25 by NaN
I haven't worked much with C++ and COM although I do know they use a vtable. I think the problem with the vtable will be the implementation in masm. I tried to follow Visual C's assembler ouput on some very simple classes I created to see what happens when a member of an inherited class is overwritten by a new class.
It seems that the vtable will still contain both addresses, one with the original function (for superclassing), and one for the new function. Problem here is that it's the C compiler that desides which one to use on a certain method call, and this is much harder in MASM because we have to do that with macros..
But I may be wrong, I haven't read that much about COM and C++..
I am thinking of writing a small tutor about using objects.inc.. I'll try but no promises yet..
NaN explained the basics pretty good so far. :alright:

Thomas
Posted on 2001-09-20 08:57:07 by Thomas
Unfortunately I don't have much time right now to do a full tutor but I managed two write two examples today..
I'll try to comment them some day and write something about them, but for those who can't wait, here they are.
The first example creates a CSprite class, which can display a sprite on some DC. It shows the general use of a class.
You don't have to look at all the files, the codeparts.asm file, CSprite.asm, and drawproc.asm file contain all important stuff, the rest is just standard window creation and setting up DCs..
It shows a bunch of smilies on a window that all move randomly.
The second example demonstrates inheritance and superclassing. A new class is created, called CSmiley. This class inherits from CSprite so it has all the basic functions of a sprite, but in addition it can let the sprite leave footprints when enabled... Herefore it overrules the 'draw' method of the CSprite class, by first superclassing it's original method (i.e. it calls the original draw proc from CSprite), but then adding it's own code to put the footsteps on a footstepDC (another DC that is drawn as background on the backbuffer). Polymorphism is not illustrated but you can do that yourself, just try to replace 'CSmiley', into 'CSprite' (the original), but only in the drawing procedure. It will still work, as a CSmiley object is also a CSprite object (because it inherits from it), so you are able to treat it that way.
That's about it, sorry for the missing comments in the source code, but at least it's something :)

Thomas
Posted on 2001-09-20 13:52:12 by Thomas
Thomas, you rock!.. Those are some neat examples... I like the little devil dudes :)

NaN
Posted on 2001-09-20 16:12:33 by NaN