Page 1 of 1

Memory protective V-Table without DataSection.

Posted: Thu Nov 01, 2007 2:47 am
by Hroudtwolf
Hello,

The most people here could know memory protective V-Tables in DataSections.
I want to show you another way.

Code: Select all

; PureBasic-Lounge.de
; Date: 31. October 2007
; OS: Windows, Linux, Mac
; Demo: Yes


Interface IcMyClass
   Set   (sText.s)
   Get.s ()
EndInterface

Structure cMyClass
   *VTABLE
   
   sText.s
EndStructure

Procedure.l cMyClass_Set (*This.cMyCLass , sText.s)
   *this\sText = sText
   ProcedureReturn #True
EndProcedure

Procedure.s cMyClass_Get (*This.cMyCLass)
   ProcedureReturn *this\sText
EndProcedure

Procedure CreateObject_cMyClass ()
   Protected *This.cMyClass = AllocateMemory (SizeOf (cMyClass))
   Static    *cMyClass_Vtable ; <--- Static pointer to our V-Table
   
   If Not *This
      ProcedureReturn #Null
   EndIf
   
   ; Generation a V-Table for this class, if not already exist.
   If Not *cMyClass_Vtable
      *cMyClass_Vtable = AllocateMemory (SizeOf(IcMyClass))
      If Not *cMyClass_Vtable 
         FreeMemory (*This)
         ProcedureReturn #Null
      EndIf
      ; Adding methodes
      ; Use PokeL and the interface offsets.  Its a easy way to do that.
      PokeL (*cMyClass_Vtable + OffsetOf (IcMyClass\Set ()) , @cMyClass_Set ())
      PokeL (*cMyClass_Vtable + OffsetOf (IcMyClass\Get ()) , @cMyClass_Get ())
   EndIf
   
   ; announce the address of the V-Table for our new object.   
   *This\VTABLE = *cMyClass_Vtable  
      
   ProcedureReturn *This
EndProcedure

;--- Test

*MyObj.IcMyClass = CreateObject_cMyClass ()
*MyObj\Set ("Hello World")
Debug *MyObj\Get ()


Best regards

Wolf


PS: Sorry for my funny english. :)

Posted: Wed Mar 26, 2008 3:37 am
by Dare
Hi mate.

Find I need to use interfaces and found your post, which I can understand to a degree so I am going to use your approach. Thanks for this.

Some quick questions:

With
  • Static *cMyClass_Vtable
Does it have to be static, and if so why?

How do you go about releasing the object (*This\VTABLE and *This itself)?

Am I making problems for myself doing the following with protected rather than static (and with the vt memory not freed):

Code: Select all

Structure object_o
  *vt.l
  amt.l
EndStructure
Interface object_i
  set(amt.l)
  get()
EndInterface
Structure object_m
  set.l
  get.l
EndStructure


Procedure set(*this.object_o,amt)
  *this\amt = amt
EndProcedure
Procedure get(*this.object_o)
  ProcedureReturn *this\amt
EndProcedure

Procedure freeObject(*this)
; ? how to release vt?
  FreeMemory(*this)
EndProcedure
Procedure newObject()
  Protected *obj.object_o
  Protected *vt.object_m
  
  *obj = AllocateMemory(SizeOf(object_o))
  If *obj
    *vt = AllocateMemory(SizeOf(object_m))
    If *vt
      *vt\set = @set()
      *vt\get = @get()
      *obj\vt = *vt
    Else
      FreeMemory(*obj)
      *obj = 0
    EndIf
  EndIf
  ProcedureReturn *obj
EndProcedure

Debug SizeOf(object_i)
Debug SizeOf(object_m)
Debug SizeOf(object_o)

*myObj.object_i = newObject()
*myOther.object_i = newObject()
Debug *myObj
Debug *myOther
*myObj\set(10)
*myOther\set(100)

Debug *myObj\get()
Debug *myOther\get()

freeObject(*myObj)
freeObject(*myOther)
Thanks again.

Posted: Wed Mar 26, 2008 5:22 am
by netmaestro
The pointer needs to be static for only one (good) reason that I can see: If it is static, subsequent calls of the procedure find it holding a value, and thus can skip the redundant rebuilding of the vtable. This way it's only built once by the first instance of the object being created.

Posted: Wed Mar 26, 2008 5:29 am
by Dare
Hi mate!

How are you going? :)

Thanks.

With the object release (which is just a free-up of memory?) I am now doing this:

Code: Select all

Procedure freeObject(*this)
  Protected *obj.object_o
  *obj = *this
  FreeMemory(*obj\vt)
  FreeMemory(*this)
EndProcedure
Which I hope is ok.

Posted: Wed Mar 26, 2008 4:57 pm
by Demivec
Dare wrote:Hi mate!

How are you going? :)

Thanks.

With the object release (which is just a free-up of memory?) I am now doing this:

Code: Select all

Procedure freeObject(*this)
  Protected *obj.object_o
  *obj = *this
  FreeMemory(*obj\vt)
  FreeMemory(*this)
EndProcedure
Which I hope is ok.
Wouldn't this also work, and be simpler?

Code: Select all

Procedure freeObject(*this.object_o)
  FreeMemory(*this\vt)
  FreeMemory(*this)
EndProcedure
I think the simplest method is the one suggested by Hroudtwolf. If the vt table was static you wouldn't need to free it, you would only free the object's memory. It's the same as a vt table stored in a DataSection. There would be only one at any one time. If you make a copy of it, then you would need to free it and also you would be multiplying the memory needed for the identical copy that each object would have. I think Hroudtwolf's code is a good alternative to making multiple copies.

Note: if you did use a vt table in a DataSection the same alternative exists. You would just point to it, not reserve additional memory for it, just for the pointer. The example in your code copies it instead. Is that what you really wanted to do?

Posted: Thu Mar 27, 2008 12:58 am
by Dare
Heya Demivec,

Thanks for the explanation, it was very useful.


Demivec wrote:Is that what you really wanted to do?
lol, I had no real idea of what I wanted to do. Or more accurately, what was needed to do.

However thanks to Hroudtwolf's code, which kept everything nice and simple, I could get a grasp of the concept. With the info from yourself and netty I now have a (slightly) better idea of what is going on. :)

Time will tell. :D

Posted: Sat Mar 29, 2008 2:57 pm
by superadnim
I saw this method a lot in the pb lounge german forums but I still think the static method is better, at least for .. well .. static methods.

Posted: Sun Mar 30, 2008 2:17 pm
by Hroudtwolf
Hi,

The method with DataSections inflates the size of your binaries if your classes has a lot of methods.
If you allocate the memoryarea of your vtable dynamicly,the size of your binary will not be affected.

Best regards

Wolf

Posted: Sun Mar 30, 2008 4:24 pm
by tinman
Hroudtwolf wrote:The method with DataSections inflates the size of your binaries if your classes has a lot of methods.
If you allocate the memoryarea of your vtable dynamicly,the size of your binary will not be affected.
At what point does the dynamic vtable become better than the datasection version? If I compile your example to an exe and an equivalent datasection one then the datasection one is the smaller exe.

Also, with the code you have shown, each class will have an overhead of the following code in the exe:

Code: Select all

   If Not *cMyClass_Vtable
      *cMyClass_Vtable = AllocateMemory (SizeOf(IcMyClass))
      If Not *cMyClass_Vtable
         FreeMemory (*This)
         ProcedureReturn #Null
      EndIf
      ; Adding methodes
      ; Use PokeL and the interface offsets.  Its a easy way to do that.
      PokeL (*cMyClass_Vtable + OffsetOf (IcMyClass\Set ()) , @cMyClass_Set ())
      PokeL (*cMyClass_Vtable + OffsetOf (IcMyClass\Get ()) , @cMyClass_Get ())
   EndIf
Wouldn't a simple "Data.l @method()" always be smaller?

Posted: Sun Mar 30, 2008 5:01 pm
by Hroudtwolf
I think, you should read my post exactlier ;-)
With large classes, the size of your binary will inflate.

Also, you can recycle the dynamic generated vtables like the static vtables for other classes.
So, you can minimize the size of the binaries, too.

And...
The topic of this thread was not "Dynamicly generated vtables are better".
It is about memory protective dynamicly generated vtables.
"One dynamicly generated vtable for each instances of a class".

Posted: Sun Mar 30, 2008 5:28 pm
by tinman
Hroudtwolf wrote:I think, you should read my post exactlier ;-)
With large classes, the size of your binary will inflate.
OK, I tried it again. There are now around 80 methods in the interface and the datasection executable is 3kB larger. I never would have thought that "Data.l @procedure()" would end up bigger than "PokeL(addr, @procedure())".

Thanks for the tip.