Experiment with object-oriented "child interface" of module

Share your advanced PureBasic knowledge/code with the community.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Experiment with object-oriented "child interface" of module

Post by Mistrel »

As we cannot declare modules within modules, it's not possible to create nested namespaces. This makes it difficult to create hierarchies of named modules such as Entity::LivingEntity::Goblin::new() or Shape::Triangle::new(), etc.

Although we cannot declare modules within modules, we CAN define variables inside of them with an interface. This was an experiment to see how useful this would be with shapes.

Summary of findings:
  • Due to this problem, I couldn't find a way to use a macro to define Shape2i as SHAPE to hide its type.
  • This method makes UseModule unappealing as it ends up polluting the local namespace with variable names of the generic interface variables. For example, "UseModule Shape2i" means that I can't make a variable named "triangle".
  • Because interfaces cannot declare other interfaces as return types, this usefulness is rather limited.
  • This provides a workaround for only one additional depth. This is still useful if modules are designed with this in mind but duplicates a lot of code.
  • PureBasic's lack of support for arrays outside of structures is annoying and adds unnecessary verbosity
Readability also suffers due to when and where we can perform a line break.

For example, this:

Code: Select all

Interface IClass
  fromXY(_IN x1.Type::INT_32,
    _IN y1.Type::INT_32,
    _IN x2.Type::INT_32,
    _IN y2.Type::INT_32,
    _IN x3.Type::INT_32,
    _IN y3.Type::INT_32)
EndInterface
Would read a lot better like this:

Code: Select all

Interface IClass
  Declare.IObject
  fromXY(
      _IN x1.Type::INT_32,
      _IN y1.Type::INT_32,
      _IN x2.Type::INT_32,
      _IN y2.Type::INT_32,
      _IN x3.Type::INT_32,
      _IN y3.Type::INT_32
    )
EndInterface
Note that interfaces and procedures at this time do not support the declaration of either structures or interfaces as their return type.

Some people elsewhere on the forum have expressed confusion over some of my reports and feature requests. I really enjoy programming in PureBasic but I find that I have a habit of it to the limit and frustrating myself in the process; that's why I disappear from time to time.

Anyways, here is the result of the experiment. Note that this is just a sample and some of the modules are only portioned such as SAL and Type.

Enjoy!

Modules:

Code: Select all

;-- SAL

DeclareModule SAL
Macro _IN
EndMacro

Macro _IN_OPT
EndMacro
EndDeclareModule

Module SAL
EndModule

;-- Type

DeclareModule Type
  Macro VOID
    i
  EndMacro
  
  Macro POINTER
    i
  EndMacro
  
  Macro INT_32
    l
  EndMacro
  
  Macro INT_32_64
    i
  EndMacro
  
  Structure POINTER_TYPE
  EndStructure
EndDeclareModule

Module Type
EndModule

;-- Geometry

DeclareModule Geometry
  Structure Point2i
    x.Type::INT_32
    y.Type::INT_32
  EndStructure
  
  Structure Line2i
    x1.Type::INT_32
    y1.Type::INT_32
    x2.Type::INT_32
    y2.Type::INT_32
  EndStructure
  
  Structure Point2iArray
    point.Point2i[0]
  EndStructure
  
  Structure Point2iArray3
    point.Point2i[3]
  EndStructure
  
  Structure Point2iSizeArray
    size.Type::INT_32_64
    point.Point2i[0]
  EndStructure
  
  Structure Point2iSizeArray3
    size.Type::INT_32_64
    point.Point2i[3]
  EndStructure
EndDeclareModule

Module Geometry
EndModule

;-- Polygon2i

DeclareModule Polygon2i
  Structure PointArray Extends Geometry::Point2iArray
  EndStructure
  
  Structure PointSizeArray Extends Geometry::Point2iSizeArray
  EndStructure
EndDeclareModule

Module Polygon2i
EndModule

;-- Triangle2i

DeclareModule Triangle2i
  UseModule SAL
  
  #SIZE=3
  
  Macro OBJECT_POINTER
    Type::POINTER
  EndMacro
  
  Macro BYTE_ARRAY
    Type::POINTER_TYPE
  EndMacro
  
  Macro POINT_ARRAY
    Type::POINTER_TYPE
  EndMacro
  
  Structure PointArray
    point.Geometry::Point2i[3]
  EndStructure
  
  Structure PointSizeArray Extends Geometry::Point2iSizeArray3
  EndStructure
  
  Structure IClassVTable
    *new
    *fromPoints
    *fromXY
  EndStructure
  
  Structure IObjectVTable
    *copy
    *delete
    *size
    *toByteArray
    *toPointArray
    *toPointSizeArray
  EndStructure
  
  Structure Class
    *vTable.IClassVTable
  EndStructure
  
  Structure Object
    *vTable.IObjectVTable
    *class.IClass
    size.Type::INT_32_64
    shape.PointArray
  EndStructure
  
  Interface IClass
    new(_IN_OPT *other.Object=0)
    
    fromPoints(_IN *point1.Geometry::Point2i,
      _IN *point2.Geometry::Point2i,
      _IN *point3.Geometry::Point2i)
    
    fromXY(_IN x1.Type::INT_32,
      _IN y1.Type::INT_32,
      _IN x2.Type::INT_32,
      _IN y2.Type::INT_32,
      _IN x3.Type::INT_32,
      _IN y3.Type::INT_32)
  EndInterface
  
  Interface IObject
    copy()
    delete()
    size()
    toByteArray()
    toPointArray()
    toPointSizeArray()
  EndInterface
  
  Declare.OBJECT_POINTER new(_IN_OPT *other.Object=0)
  
  Global class.IClass
EndDeclareModule

Module Triangle2i
  UseModule Type
  
  Macro BYTE_ARRAY_POINTER
    POINTER
  EndMacro
  
  Macro POINT_ARRAY_POINTER
    POINTER
  EndMacro
  
  Macro POINT_SIZE_ARRAY_POINTER
    POINTER
  EndMacro
  
  Macro CLASS_POINTER
    POINTER
  EndMacro
  
  Macro VTABLE_POINTER
    POINTER
  EndMacro
  
  Declare.VOID setIClassVTable(_IN_OPT *vTable.IClassVTable=0)
  Declare.VOID setIObjectVTable(_IN_OPT *vTable.IObjectVTable=0)
  
  ;/ Static
  Global classVTable.IClassVTable
  setIClassVTable(@classVTable)
  
  *instance.Class
  *instance=AllocateStructure(Class)
  *instance\vTable=@classVTable
  
  class=*instance
  
  ;/ STATIC
  Procedure.OBJECT_POINTER new(_IN_OPT *other.Object=0)
    ProcedureReturn class\new()
  EndProcedure
  
  ;/ STATIC
  Procedure.OBJECT_POINTER newInstance(_IN *this.Class,
    _IN_OPT *other.Object=0)
    
    Protected *instance.Object
    
    *instance=AllocateStructure(Object)
    *instance\vTable=setIObjectVTable()
    *instance\class=class
    
    *instance\size=#Size
    
    If *other
      CompilerIf #PB_Compiler_Debugger
      If Not *other\size=#Size
        DebuggerError("Other object is not the right size")
      EndIf
      CompilerEndIf
      
      CopyMemory(@*other\shape,@*instance\shape,SizeOf(PointArray))
    EndIf
    
    ProcedureReturn *instance
  EndProcedure
  
  ;/ STATIC
  Procedure.OBJECT_POINTER fromPoints(_IN *this.Class,
    _IN *point1.Geometry::Point2i,
    _IN *point2.Geometry::Point2i,
    _IN *point3.Geometry::Point2i)
    
    Protected *shape.Object=class\new()
    
    *shape\shape\point[0]\x=*point1\x
    *shape\shape\point[0]\y=*point1\y
    *shape\shape\point[1]\x=*point2\x
    *shape\shape\point[1]\y=*point2\y
    *shape\shape\point[2]\x=*point3\x
    *shape\shape\point[2]\y=*point3\y
    
    ProcedureReturn *shape
  EndProcedure
  
  ;/ STATIC
  Procedure.OBJECT_POINTER fromXY(_IN *this.Class,
    _IN x1.INT_32,
    _IN y1.INT_32,
    _IN x2.INT_32,
    _IN y2.INT_32,
    _IN x3.INT_32,
    _IN y3.INT_32)
    
    Protected *shape.Object=class\new()
    
    *shape\shape\point[0]\x=x1
    *shape\shape\point[0]\y=y1
    *shape\shape\point[1]\x=x2
    *shape\shape\point[1]\y=y2
    *shape\shape\point[2]\x=x3
    *shape\shape\point[2]\y=y3
    
    ProcedureReturn *shape
  EndProcedure
  
  Procedure.OBJECT_POINTER copy(_IN *this.Object,
    _IN *other.Object)
    
    ProcedureReturn new(*other)
  EndProcedure
  
  Procedure.INT_32_64 size(_IN *this.Object)
    ProcedureReturn *this\size
  EndProcedure
  
  Procedure.VOID delete(_IN *this.Object)
    FreeStructure(*this)
  EndProcedure
  
  Procedure.BYTE_ARRAY_POINTER toByteArray(_IN *this.Object)
    ProcedureReturn @*this\shape
  EndProcedure
  
  Procedure.POINT_ARRAY_POINTER toPointArray(_IN *this.Object)
    ProcedureReturn @*this\shape
  EndProcedure
  
  Procedure.POINT_SIZE_ARRAY_POINTER toPointSizeArray(_IN *this.Object)
    ProcedureReturn @*this\size
  EndProcedure
  
  ;/ Private
  Procedure.VTABLE_POINTER setIClassVTable(_IN_OPT *vTable.IClassVTable=0)
    If *vTable=0
      *vTable=AllocateStructure(IClassVTable)
    EndIf
    
    *vTable\new=@newInstance()
    *vTable\fromPoints=@fromPoints()
    *vTable\fromXY=@fromXY()
    
    ProcedureReturn *vTable
  EndProcedure
  
  Procedure.VTABLE_POINTER setIObjectVTable(_IN_OPT *vTable.IObjectVTable=0)
    If *vTable=0
      *vTable=AllocateStructure(IObjectVTable)
    EndIf
    
    *vTable\copy=@copy()
    *vTable\size=@size()
    *vTable\delete=@delete()
    *vTable\toByteArray=@toByteArray()
    *vTable\toPointArray=@toPointArray()
    *vTable\toPointSizeArray=@toPointSizeArray()
    
    ProcedureReturn *vTable
  EndProcedure
EndModule

;-- Shape2i

DeclareModule Shape2i
  Macro BYTE_ARRAY
    Type::POINTER_TYPE
  EndMacro
  
  Structure PointArray Extends Geometry::Point2iArray
  EndStructure
  
  Structure PointSizeArray Extends Polygon2i::PointSizeArray
  EndStructure
  
  Interface ITriangle Extends Triangle2i::IObject
  EndInterface
  
  Global Triangle.Triangle2i::IClass=Triangle2i::class
EndDeclareModule

Module Shape2i
EndModule
Example code:

Code: Select all

UseModule Type

Define triangle.Shape2i::ITriangle
Define i.INT_32_64

;/ Testing new()
triangle=Shape2i::Triangle\new()

Define *pointArray.Shape2i::PointArray

;/ Testing toPointArray()
*pointArray=triangle\toPointArray()

;/ Testing size()
For i=0 To triangle\size()-1
  *pointArray\point[i]\x=i
  *pointArray\point[i]\y=i
Next i

;/ Testing assignment()
For i=0 To triangle\size()-1
  Debug "*pointArray\point["+Str(i)+"]\x "+Str(*pointArray\point[i]\x)
  Debug "*pointArray\point["+Str(i)+"]\y "+Str(*pointArray\point[i]\y)
Next i
Debug ""

Define temp.Shape2i::ITriangle

;/ Testing copy()
temp=triangle\copy()

;/ Testing delete()
triangle\delete()
triangle=temp

;/ Testing copy assignment()
For i=0 To triangle\size()-1
  Debug "*pointArray\point["+Str(i)+"]\x "+Str(*pointArray\point[i]\x)
  Debug "*pointArray\point["+Str(i)+"]\y "+Str(*pointArray\point[i]\y)
Next i
Debug ""

triangle\delete()

;/ Testing fromXY()
triangle=Shape2i::Triangle\fromXY(65,7, 113,91, 17,91)

;/ Testing toPointSizeArray()
Define *pointSizeArray.Shape2i::PointSizeArray

*pointSizeArray=triangle\toPointSizeArray()

For i=0 To *pointSizeArray\size-1
  Debug "*pointSizeArray\point["+Str(i)+"]\x "+Str(*pointSizeArray\point[i]\x)
  Debug "*pointSizeArray\point["+Str(i)+"]\y "+Str(*pointSizeArray\point[i]\y)
Next i