Page 1 of 2
Confusion on how to implement virtual tables on structures
Posted: Tue Dec 17, 2024 12:40 am
by Quin
I appologise if this is obvious and/or has been answered other places, but I've tried searching the forum and looking at the DialogManager's code from the IDE, and there's just some small piece that I'm missing. As is usually the case around here, I'm sure that someone will show me a simple fix in like 5 lines of what I'm doing wrong, but sometimes that's what you need I guess

I'm trying to make a basic system for applets that have a couple basic functions as well as some properties.
Here's what I'm trying:
Code: Select all
EnableExplicit
Interface IApplet
Show()
Destroy()
EndInterface
Structure Applet
*VTable
Name.s
EndStructure
Procedure NewApplet(Name.s, VTable)
Protected *This.Applet
*This = AllocateStructure(Applet)
If *This
*This\VTable = VTable
*This\Name = Name
EndIf
ProcedureReturn *This
EndProcedure
Procedure Test_Show(*This.Applet)
MessageRequester("Show was called on applet", *This\Name)
EndProcedure
Procedure Test_Destroy(*This.Applet)
MessageRequester("Destory was called on", *This\Name)
EndProcedure
Define *Test.Applet = NewApplet("Test", ?VTable)
*Test\Show() ; Doesn't work.
DataSection
VTable:
Data.i @Test_Show(), @Test_Destroy()
EndDataSection
Any help is most certainly appreciated!
Re: Confusion on how to implement virtual tables on structures
Posted: Tue Dec 17, 2024 1:08 am
by idle
define the pointer as IApplet
Code: Select all
Define *Test.iApplet = NewApplet("Test", ?VTable)
Re: Confusion on how to implement virtual tables on structures
Posted: Tue Dec 17, 2024 1:16 am
by Quin
idle wrote: Tue Dec 17, 2024 1:08 am
define the pointer as IApplet
Code: Select all
Define *Test.iApplet = NewApplet("Test", ?VTable)
That works, but what if I want to access fields on this variable as well? More specifically I want to maintain a global map of them, with the name being the key and the value being an Applet struct. The reason I didn't just have the name be the key and the VTable be the value is because I plan on having more properties than just this in the future.
Re: Confusion on how to implement virtual tables on structures
Posted: Tue Dec 17, 2024 3:22 am
by idle
you can do it with Extends
Code: Select all
EnableExplicit
Interface IApplet
Show()
Destroy()
EndInterface
Interface ifoo Extends IApplet
dofoo(x)
EndInterface
Interface ibar Extends IApplet
dobar(x)
EndInterface
Structure Applet
*VTable
Name.s
EndStructure
Structure foo Extends Applet
x.i
EndStructure
Structure bar Extends Applet
x.i
EndStructure
Procedure NewApplet(Name.s,type.s)
Protected *This.Applet,VT
Select type
Case "foo"
*This = AllocateStructure(foo)
vt = ?VTableFoo
Case "bar"
*This = AllocateStructure(bar)
vt = ?VTableBar
EndSelect
If *This
*This\Name = Name
*this\VTable = vt
ProcedureReturn *This
EndIf
EndProcedure
Procedure Applet_Show(*This.Applet)
MessageRequester("Show was called on applet", *This\Name)
EndProcedure
Procedure Applet_Destroy(*This.Applet)
MessageRequester("Destory was called on", *This\Name)
EndProcedure
Procedure Do_Foo(*this.foo,val)
*this\x = val << 1
ProcedureReturn *this\x
EndProcedure
Procedure Do_Bar(*this.bar,val)
*this\x = val >> 1
ProcedureReturn *this\x
EndProcedure
Define *foo.ifoo = NewApplet("myfoo","foo")
*foo\Show()
Debug *foo\dofoo(64)
Define *bar.ibar = NewApplet("myBar","bar")
*bar\Show()
Debug *bar\dobar(64)
DataSection
VTableFoo:
Data.i @Applet_Show(), @Applet_Destroy(),@Do_Foo()
VTableBar:
Data.i @Applet_Show(), @Applet_Destroy(),@Do_Bar()
EndDataSection
Re: Confusion on how to implement virtual tables on structures
Posted: Tue Dec 17, 2024 3:44 am
by Quin
Thanks for the reply!
Sorry, I should've been more specific. What I meant was being able to access the name field directly with something like:
I'm also trying to store all Applet instances in a Map, but am unsuccessful, getting the error that I can't assign a value to a structure
Code: Select all
EnableExplicit
Interface IApplet
Show()
Destroy()
EndInterface
Structure Applet
*VTable
Name.s
EndStructure
Global NewMap Applets.Applet()
Procedure NewApplet(Name.s, VTable)
Protected *This.Applet
*This = AllocateStructure(Applet)
If *This
*This\VTable = VTable
*This\Name = Name
AddMapElement(Applets(), Name)
Applets() = *This
EndIf
ProcedureReturn *This
EndProcedure
Procedure Test_Show(*This.Applet)
MessageRequester("Show was called on applet", *This\Name)
EndProcedure
Procedure Test_Destroy(*This.Applet)
MessageRequester("Destroy was called on", *This\Name)
EndProcedure
Define *Test.IApplet = NewApplet("Test", ?VTable)
*Test\Show()
DataSection
VTable:
Data.i @Test_Show(), @Test_Destroy()
EndDataSection
Re: Confusion on how to implement virtual tables on structures
Posted: Tue Dec 17, 2024 3:50 am
by idle
you can store them as pointer
Code: Select all
EnableExplicit
Interface IApplet
Show()
Destroy()
EndInterface
Structure Applet
*VTable
Name.s
EndStructure
Global NewMap *Applets.Applet()
Procedure NewApplet(Name.s, VTable)
Protected *This.Applet
*This = AllocateStructure(Applet)
If *This
*This\VTable = VTable
*This\Name = Name
AddMapElement(*Applets(), Name)
*Applets() = *This
EndIf
ProcedureReturn *This
EndProcedure
Procedure Test_Show(*This.Applet)
MessageRequester("Show was called on applet", *This\Name)
EndProcedure
Procedure Test_Destroy(*This.Applet)
MessageRequester("Destroy was called on", *This\Name)
EndProcedure
Define *Test.IApplet = NewApplet("Test", ?VTable)
*Test\Show()
DataSection
VTable:
Data.i @Test_Show(), @Test_Destroy()
EndDataSection
Re: Confusion on how to implement virtual tables on structures
Posted: Tue Dec 17, 2024 2:29 pm
by Quin
Thanks, Idle! This has been most helpful.
So I assume there's no way for me to access both properties of the struct and methods at the same time? If so that's not a dealbreaker, if I absolutely need to I can make interface methods to expose them, although it would be nice.
I'm also slightly confused why this works, the map stores *IApplet pointers and I'm allocating a *Applet pointer, why does that not cause issues?
Thanks!
Re: Confusion on how to implement virtual tables on structures
Posted: Tue Dec 17, 2024 8:59 pm
by idle
you can cast the pointer to get the properties
Code: Select all
EnableExplicit
Interface IApplet
Show()
Destroy()
EndInterface
Structure Applet
*VTable
Name.s
EndStructure
Global NewMap *Applets.Applet()
Procedure NewApplet(Name.s, VTable)
Protected *This.Applet
*This = AllocateStructure(Applet)
If *This
*This\VTable = VTable
*This\Name = Name
AddMapElement(*Applets(), Name)
*Applets() = *This
EndIf
ProcedureReturn *This
EndProcedure
Procedure Test_Show(*This.Applet)
MessageRequester("Show was called on applet", *This\Name)
EndProcedure
Procedure Test_Destroy(*This.Applet)
MessageRequester("Destroy was called on", *This\Name)
EndProcedure
Define *Test.IApplet = NewApplet("Test", ?VTable)
*Test\Show()
Define *props.Applet = *test
Debug *props\Name
DataSection
VTable:
Data.i @Test_Show(), @Test_Destroy()
EndDataSection
Re: Confusion on how to implement virtual tables on structures
Posted: Wed Dec 18, 2024 12:18 pm
by Quin
Very cool, thanks for all your help! My love for PB deepens

Re: Confusion on how to implement virtual tables on structures
Posted: Wed Dec 18, 2024 9:18 pm
by mk-soft
This is actually not the right way.
There should be the GetName and SetName methods.
The vTable should not be passed as a parameter, but should be set internally.
Modules are very useful here to create the classes.
Do not create the methods in the DataSetion either, but use the memory for them when creating the class. This means that you can also overwrite methods during inheritance without overwriting the original method.
Multiple inheritance is also possible. However, the base class must always contain the vTable
I have created an example based on your model.
I'm a little behind with the description.
Update Example With Description v1.03
- Added a macro to set methods
Code: Select all
;-TOP OOP-Example by mk-soft, v1.03, 22.12.2024 ;)
; Link: https://www.purebasic.fr/english/viewtopic.php?p=632409#p632409
EnableExplicit
DeclareModule ClassCommon
Structure sMethod
*Index[0]
EndStructure
Structure sClass
ClassName.s ; Name of class
Size.i ; Size of interface in bytes
*Methods.sMethod ; Pointer to method table
EndStructure
Global NewMap Classes.sClass()
Declare NewClass(ClassName.s, SizeOfInterface, ClassExtends.s = "")
Macro SetMethod(Method)
*Class\Methods\Index[OffsetOf(iMethods\Method()) / SizeOf(Integer)] = @Method()
EndMacro
EndDeclareModule
Module ClassCommon
Procedure NewClass(ClassName.s, SizeOfInterface, ClassExtends.s = "")
Protected *Class.sClass, *ClassExtends.sClass
With Classes()
; Check class is always exists
If FindMapElement(Classes(), ClassName)
Debug "ClassCommon Error: Class always exists: " + ClassName
ProcedureReturn 0
EndIf
; If use extends, check exists extends class
If ClassExtends
*ClassExtends = FindMapElement(Classes(), ClassExtends)
If Not *ClassExtends
Debug "ClassCommon Error: Extends class not exists: " + ClassExtends
ProcedureReturn 0
EndIf
EndIf
; Create new class
*Class = AddMapElement(Classes(), ClassName)
If *Class
; Store class name
\ClassName = ClassName
; Store byte size of method table
\Size = SizeOfInterface
; Allocate memory for method table
\Methods = AllocateMemory(SizeOfInterface)
If Not \Methods
Debug "ClassCommon Error: Out Of Memory:" + ClassName
DeleteMapElement(Classes())
ProcedureReturn 0
EndIf
; If use extends, copy extends methods to new method table
If *ClassExtends
CopyMemory(*ClassExtends\Methods, *Class\Methods, *ClassExtends\Size)
EndIf
ProcedureReturn *Class
Else
Debug "ClassCommon Error: Out Of Memory:" + ClassName
ProcedureReturn 0
EndIf
EndWith
EndProcedure
EndModule
; ****
;- AppletBase
DeclareModule AppletBase
Interface iAppletBase
Show()
Destroy()
EndInterface
Structure sAppletBase
*vTable
EndStructure
Declare New()
EndDeclareModule
Module AppletBase
EnableExplicit
UseModule ClassCommon
; Static pointer to class
Global *Class.sClass
; Macro Interface Helper
Interface iMethods Extends iAppletBase
EndInterface
Procedure New()
Protected *This.sAppletBase
If *Class
*This = AllocateStructure(sAppletBase)
If *This
*This\vTable = *Class\Methods
EndIf
EndIf
ProcedureReturn *This
EndProcedure
Procedure Show(*This.sAppletBase)
Debug *Class\ClassName + ": Show was called"
EndProcedure
Procedure Destroy(*This.sAppletBase)
Debug *Class\ClassName + ": Destroy was called"
; Free all resources here:
; - Do not forget to delete fonts, images, files, etc.
FreeStructure(*This)
EndProcedure
Procedure InitClass()
; Create new class
*Class = NewClass("AppletBase", SizeOf(iAppletBase))
If *Class
With *Class
; Poke method address into method table
; - OffsetOf get the byte address of interface method
; Set methods
SetMethod(Show) ; Macro -> *Class\Methods\Index[OffsetOf(iMethods\Show()) / SizeOf(Integer)] = @Show()
; Write direct method address
PokeI(\Methods + OffsetOf(iAppletBase\Destroy()), @Destroy())
EndWith
Else
Debug "Error: NewClass AppletBase"
CallDebugger
EndIf
EndProcedure : InitClass()
EndModule
; ----
;- Applet
DeclareModule Applet
Interface iApplet Extends AppletBase::iAppletBase
SetName(Name.s)
GetName.s()
EndInterface
Structure sApplet Extends AppletBase::sAppletBase
Name.s
EndStructure
Declare New()
EndDeclareModule
Module Applet
EnableExplicit
UseModule ClassCommon
; Static pointer to class
Global *Class.sClass
; Macro Interface Helper
Interface iMethods Extends iApplet
EndInterface
Procedure New()
Protected *This.sApplet
If *Class
*This = AllocateStructure(sApplet)
If *This
*This\vTable = *Class\Methods
EndIf
EndIf
ProcedureReturn *This
EndProcedure
Procedure Destroy(*This.sApplet)
Debug *Class\ClassName + ": Destroy was called"
; Free all resources here:
; - Do not forget to delete fonts, images, files, etc.
FreeStructure(*This)
EndProcedure
Procedure SetName(*This.sApplet, Name.s)
Debug *Class\ClassName + ": Set name: " + Name
*This\Name = Name
EndProcedure
Procedure.s GetName(*This.sApplet)
Debug *Class\ClassName + ": Get name: " + *This\Name
ProcedureReturn *This\Name
EndProcedure
Procedure InitClass()
; Create new class
*Class = NewClass("Applet", SizeOf(iApplet), "AppletBase")
If *Class
With *Class
; Overwrite Method Destroy
SetMethod(Destroy)
; *Class\Methods\Index[OffsetOf(iMethods\Destroy()) / SizeOf(Integer)] = @Destroy()
; PokeI(\Methods + OffsetOf(iApplet\Destroy()), @Destroy())
; Set new methods
SetMethod(SetName)
SetMethod(GetName)
EndWith
Else
Debug "Error: NewClass Applet"
CallDebugger
EndIf
EndProcedure : InitClass()
EndModule
; ----
;- AppletEx
DeclareModule AppletEx
Interface iAppletEx Extends Applet::iApplet
SetValue(Value)
GetValue()
EndInterface
Structure sAppletEx Extends Applet::sApplet
Value.i
EndStructure
Declare New()
EndDeclareModule
Module AppletEx
EnableExplicit
UseModule ClassCommon
; Static pointer to class
Global *Class.sClass
; Macro Interface Helper
Interface iMethods Extends iAppletEx
EndInterface
Procedure New()
Protected *This.sAppletEx
If *Class
*This = AllocateStructure(sAppletEx)
If *This
*This\vTable = *Class\Methods
EndIf
EndIf
ProcedureReturn *This
EndProcedure
Procedure Show(*This.sAppletEx)
Debug *Class\ClassName + ": Show was called"
EndProcedure
Procedure Destroy(*This.sAppletEx)
Debug *Class\ClassName + ": Destroy was called"
; Free all resources here:
; - Do not forget to delete fonts, images, files, etc.
FreeStructure(*This)
EndProcedure
Procedure.s GetName(*This.sAppletEx)
Debug *Class\ClassName + ": Get name: " + *This\Name + " = " + *This\Value
ProcedureReturn *This\Name + " = " + *This\Value
EndProcedure
Procedure SetValue(*This.sAppletEx, Value)
Debug *Class\ClassName + ": Set value: " + value
*This\Value = Value
EndProcedure
Procedure GetValue(*This.sAppletEx)
Debug *Class\ClassName + ": Get value: " + *This\Value
ProcedureReturn *This\Value
EndProcedure
Procedure InitClass()
*Class = NewClass("AppletEx", SizeOf(iAppletEx), "Applet")
If *Class
With *Class
; Overwrite methods
SetMethod(Show)
SetMethod(Destroy)
SetMethod(GetName)
; Set new methods
SetMethod(SetValue)
SetMethod(GetValue)
EndWith
Else
Debug "Error: NewClass AppletEx"
CallDebugger
EndIf
EndProcedure : InitClass()
EndModule
; ----
;-Test
Define *Obj1.AppletBase::iAppletBase = AppletBase::New()
*Obj1\Show()
Define *Obj2.Applet::iApplet = Applet::New()
*Obj2\Show()
*Obj2\SetName("Hello World")
Debug *Obj2\GetName()
Define *Obj3.AppletEx::iAppletEx = AppletEx::New()
*Obj3\Show()
*Obj3\SetName("Result")
*obj3\SetValue(0815)
Debug *Obj3\GetName()
*Obj3\Destroy() : *Obj3 = 0
*Obj2\Destroy() : *Obj2 = 0
*Obj1\Destroy() : *Obj1 = 0
Re: Confusion on how to implement virtual tables on structures
Posted: Thu Dec 19, 2024 1:50 am
by Quin
Hi mk-soft,
Thanks for your reply! If you don't mind, would you mind explaining this a little bit more? I struggle to just take flat definitions as fact like this and want to know the reasons, know why you do it like this. What's wrong with using DataSections, and passing the VTable directly? I'm actually curious here, you've written an OOP system for PB so I'm sure you know what you're talking about, but I'm just trying to understand the logic

Thanks!
Re: Confusion on how to implement virtual tables on structures
Posted: Thu Dec 19, 2024 9:41 am
by mk-soft
There is nothing wrong with working with DataSection as long as you do not need inheritance of methods. This is also the right way.
With inheritance, you need a separate method table for each class to overwrite individual methods without changing the original method table.
When creating the new class, a new table is created from which the inherited methods are copied into the new class.
Here you have to know that with inheritance (Extends) the interface (and also structures) is placed in front of the new interface.
When I am at home I will document my example a little better.
Re: Confusion on how to implement virtual tables on structures
Posted: Thu Dec 19, 2024 12:59 pm
by Quin
Ah, I see, so that's more if you want inheritence. Thanks for the explaination, and I look forward to the updated version

Re: Confusion on how to implement virtual tables on structures
Posted: Thu Dec 19, 2024 7:25 pm
by StarBootics
Hello everyone,
The following example show another way to do OOP and Polymorphism but without using Interfaces.
For the Color3f "Class", I have used a Global variable defined inside a Module for the virtual table. For the Material "Class", I have used an Allocated structure for the virtual table.
Code: Select all
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Project name : Polymorphic Materials
; File Name : Polymorphic Materials.pb
; File version: 1.0.0
; Programming : OK
; Programmed by : StarBootics
; Date : December 19th, 2024
; Last Update : December 19th, 2024
; PureBasic code : V6.11 LTS
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
DeclareModule Color3f
Structure Color3f
Red.f
Green.f
Blue.f
*vt.Color3fVirtualTable
EndStructure
Prototype.f Proto_Color3f_Get(*Color.Color3f)
Prototype Proto_Color3f_Set(*Color.Color3f, ColorComponent.f)
Prototype Proto_Color3f_Update(*Color.Color3f, Red.f, Green.f, Blue.f)
Structure Color3fVirtualTable
GetRed.Proto_Color3f_Get
GetGreen.Proto_Color3f_Get
GetBlue.Proto_Color3f_Get
SetRed.Proto_Color3f_Set
SetGreen.Proto_Color3f_Set
SetBlue.Proto_Color3f_Set
Update.Proto_Color3f_Update
EndStructure
Declare Init(*This.Color3f, Red.f = 0.0, Green.f = 0.0, Blue.f = 0.0)
EndDeclareModule
Module Color3f
Procedure.f Color3f_GetRed(*This.Color3f)
ProcedureReturn *This\Red
EndProcedure
Procedure.f Color3f_GetGreen(*This.Color3f)
ProcedureReturn *This\Green
EndProcedure
Procedure.f Color3f_GetBlue(*This.Color3f)
ProcedureReturn *This\Blue
EndProcedure
Procedure Color3f_SetRed(*This.Color3f, Red.f)
*This\Red = Red
EndProcedure
Procedure Color3f_SetGreen(*This.Color3f, Green.f)
*This\Green = Green
EndProcedure
Procedure Color3f_SetBlue(*This.Color3f, Blue.f)
*This\Blue = Blue
EndProcedure
Procedure Color3f_Update(*This.Color3f, Red.f, Green.f, Blue.f)
*This\Red = Red
*This\Green = Green
*This\Blue = Blue
EndProcedure
Global Color3fVirtualTable.Color3fVirtualTable
Color3fVirtualTable\GetRed = @Color3f_GetRed()
Color3fVirtualTable\GetGreen = @Color3f_GetGreen()
Color3fVirtualTable\GetBlue = @Color3f_GetBlue()
Color3fVirtualTable\SetRed = @Color3f_SetRed()
Color3fVirtualTable\SetGreen = @Color3f_SetGreen()
Color3fVirtualTable\SetBlue = @Color3f_SetBlue()
Color3fVirtualTable\Update = @Color3f_Update()
Procedure Init(*This.Color3f, Red.f = 0.0, Green.f = 0.0, Blue.f = 0.0)
*This\vt = @Color3fVirtualTable
*This\vt\Update(*This, Red, Green, blue)
EndProcedure
EndModule
DeclareModule Material
Enumeration
#TYPE_INVALID
#TYPE_LAMBERTIAN
#TYPE_METAL
EndEnumeration
Structure Lambertian
Albedo.Color3f::Color3f
EndStructure
Structure Metal Extends Lambertian
Roughness.f
EndStructure
Structure Material
Type.i
StructureUnion
Lambertian.Lambertian
Metal.Metal
EndStructureUnion
*vt.MaterialVirtualTable
EndStructure
Prototype.i Proto_Material_GetType(*This.Material)
Prototype.i Proto_Material_GetInstance(*This.Material)
Prototype Proto_Material_Wipeout(*This.Material)
Prototype Proto_Material_DebugInstance(*This.Material)
Structure MaterialVirtualTable
GetType.Proto_Material_GetType
GetInstance.Proto_Material_GetInstance
Wipeout.Proto_Material_Wipeout
DebugInstance.Proto_Material_DebugInstance
EndStructure
Declare Init_Lambertian(*This.Material, *Albedo.Color3f::Color3f)
Declare Init_Metal(*This.Material, *Albedo.Color3f::Color3f, Roughness.f)
EndDeclareModule
Module Material
Procedure.i Material_GetType(*This.Material)
ProcedureReturn *This\Type
EndProcedure
Procedure.i Material_GetInstance(*This.Material)
If *This\Type = #TYPE_LAMBERTIAN
ProcedureReturn *This\Lambertian
ElseIf *This\Type = #TYPE_METAL
ProcedureReturn *This\Metal
EndIf
ProcedureReturn #Null
EndProcedure
Procedure Material_Wipeout(*This.Material)
If *This\vt <> #Null
FreeStructure(*This\vt)
*This\vt = #Null
EndIf
EndProcedure
Procedure Material_Lambertian_DebugInstance(*This.Material)
Debug "I'm a Lambertian Material debugged the easy way!"
Debug "Red = " + StrF(*This\Lambertian\Albedo\vt\GetRed(*This\Lambertian\Albedo), 3)
Debug "Green = " + StrF(*This\Lambertian\Albedo\vt\GetGreen(*This\Lambertian\Albedo), 3)
Debug "Blue = " + StrF(*This\Lambertian\Albedo\vt\GetBlue(*This\Lambertian\Albedo), 3)
EndProcedure
Procedure Material_Metal_DebugInstance(*This.Material)
Debug "I'm a Metal Material debugged the easy way!"
Debug "Red = " + StrF(*This\Metal\Albedo\vt\GetRed(*This\Metal\Albedo), 3)
Debug "Green = " + StrF(*This\Metal\Albedo\vt\GetGreen(*This\Metal\Albedo), 3)
Debug "Blue = " + StrF(*This\Metal\Albedo\vt\GetBlue(*This\Metal\Albedo), 3)
Debug "Roughness = " + StrF(*This\Metal\Roughness, 3)
EndProcedure
Procedure Init(*This.Material, Type.i)
*This\Type = Type
*This\vt = AllocateStructure(MaterialVirtualTable)
*This\vt\GetType = @Material_GetType()
*This\vt\GetInstance = @Material_GetInstance()
*This\vt\Wipeout = @Material_Wipeout()
EndProcedure
Procedure Init_Lambertian(*This.Material, *Albedo.Color3f::Color3f)
Init(*This, #TYPE_LAMBERTIAN)
*This\vt\DebugInstance = @Material_Lambertian_DebugInstance()
Color3f::Init(*This\Lambertian\Albedo)
*This\Lambertian\Albedo\vt\SetRed(*This\Lambertian\Albedo, *Albedo\vt\GetRed(*Albedo))
*This\Lambertian\Albedo\vt\SetGreen(*This\Lambertian\Albedo, *Albedo\vt\GetGreen(*Albedo))
*This\Lambertian\Albedo\vt\SetBlue(*This\Lambertian\Albedo, *Albedo\vt\GetBlue(*Albedo))
EndProcedure
Procedure Init_Metal(*This.Material, *Albedo.Color3f::Color3f, Roughness.f)
Init(*This, #TYPE_METAL)
*This\vt\DebugInstance = @Material_Metal_DebugInstance()
Color3f::Init(*This\Metal\Albedo)
*This\Metal\Albedo\vt\SetRed(*This\Metal\Albedo, *Albedo\vt\GetRed(*Albedo))
*This\Metal\Albedo\vt\SetGreen(*This\Metal\Albedo, *Albedo\vt\GetGreen(*Albedo))
*This\Metal\Albedo\vt\SetBlue(*This\Metal\Albedo, *Albedo\vt\GetBlue(*Albedo))
*This\Metal\Roughness = Roughness
EndProcedure
EndModule
CompilerIf #PB_Compiler_IsMainFile
NewList Materials.Material::Material()
AlbedoA.Color3f::Color3f
AlbedoB.Color3f::Color3f
Color3f::Init(AlbedoA, 1.0, 0.5, 0.5)
Color3f::Init(AlbedoB, 0.25, 0.25, 0.75)
AddElement(Materials())
Material::Init_Lambertian(Materials(), AlbedoA)
AddElement(Materials())
Material::Init_Metal(Materials(), AlbedoB, 0.85)
ForEach Materials()
Select Materials()\vt\GetType(Materials())
Case Material::#TYPE_LAMBERTIAN
*Lambertian.Material::Lambertian = Materials()\vt\GetInstance(Materials())
Debug "I'm a Lambertian material debugged the hard way!"
Debug StrF(*Lambertian\Albedo\vt\GetRed(*Lambertian\Albedo), 3)
Debug StrF(*Lambertian\Albedo\vt\GetGreen(*Lambertian\Albedo), 3)
Debug StrF(*Lambertian\Albedo\vt\GetBlue(*Lambertian\Albedo), 3)
Case Material::#TYPE_METAL
*Metal.Material::Metal = Materials()\vt\GetInstance(Materials())
Debug "I'm a Metal material debugged the hard way!"
Debug StrF(*Metal\Albedo\vt\GetRed(*Metal\Albedo), 3)
Debug StrF(*Metal\Albedo\vt\GetGreen(*Metal\Albedo), 3)
Debug StrF(*Metal\Albedo\vt\GetBlue(*Metal\Albedo), 3)
Debug StrF(*Metal\Roughness, 3)
EndSelect
Debug ""
Next
Debug ""
ForEach Materials()
Materials()\vt\DebugInstance(Materials())
Debug ""
Materials()\vt\Wipeout(Materials())
Next
CompilerEndIf
; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
Beat regards
StarBootics
Re: Confusion on how to implement virtual tables on structures
Posted: Thu Dec 19, 2024 7:50 pm
by mk-soft
Ok:
Update Example with Description
For complex OOP programming via macros see signature OOP-BaseClass