Page 1 of 1

The Chaos that is Object Oriented Programming

Posted: Mon Dec 22, 2008 12:24 pm
by Dreglor
A while back I found a solution of natively using a simple object oriented type of programming found here: http://www.purebasic.fr/english/viewtopic.php?t=27986
That set of code has matured over the years internally, I've never released it, and I have always wanted to expand so that it had a better and more simple way of defining the object and all of its counterparts. I think I've come to a partial solution to the problem that I have obsessed over for a while. It took me a few days to write and debug the main part of the code, there is only a slight problem that doesn't have a nice work around but it all works out. It is in a kind of beta due to it's nature of the code and I can't test every case given the code is very young I hope that I can refine it through your guy's opinions, even being that this raw code is pretty ugly, the end-user's code is not so. Personally I find that this is acceptable code but I wanted this to be used by more that just internal use so that is why I am bouncing this around here.

Code: Select all

;////////////////////////////////////////////////////////////////
;//
;// Title: Object Base
;// Author: Steven "Dreglor" Garcia
;// Version: 2.0.0.0
;// Date: 12/22/08
;// Notes: Allows for a nice (almost) clean object based style of
;// of coding. It's quite a feat of hacking the PB language.
;//
;// Warning This Code is Considered Harmful.
;//
;////////////////////////////////////////////////////////////////

;- Prototype

Prototype __Structers(*Base.i, Parameters.i)

;- Macros

Macro _BeginMacro1
  Mac;
EndMacro

Macro _BeginMacro2
  ro;
EndMacro

Macro BeginMacro ;Macros within macros, madness! (Thanks to technicorn)
  :_BeginMacro1#_BeginMacro2
EndMacro

Macro _EndOfMacro1
  EndM;
EndMacro

Macro _EndOfMacro2
  acro;
EndMacro

Macro EndOfMacro ;to solve the multimacro with macro problem :D
  :_EndOfMacro1#_EndOfMacro2
EndMacro

Macro DoubleQuote ;fix for the macro continuation operator (#) weridness
  "
EndMacro

Macro Comma ;the ugliness comes from this
  ,
EndMacro

CompilerIf #PB_Compiler_Processor = #PB_Processor_x64 ;interface offsets have a bug up to 4.30 final
  Macro MethodOffsetOf(Value) ;fred has called this one "fixed"
    (OffsetOf(Value) * 2) ;reutnrs the correct offset
  EndMacro
CompilerElse
  Macro MethodOffsetOf(Value)
    (OffsetOf(Value)) ;32bit is just fine
  EndMacro
CompilerEndIf

Macro Object(ObjectName) ;begin the definition
  CompilerIf Defined(__#ObjectName#_Declared, #PB_Constant)
    CompilerError DoubleQuote#ObjectName#DoubleQuote + " Is already a defined object"
  CompilerEndIf
  #__#ObjectName#_Declared = #True
  
  BeginMacro ObjectName#Methods ;begin method (interface) definitions
    CompilerIf Defined(__#ObjectName#_Methods, #PB_Constant)
      CompilerError DoubleQuote#ObjectName#DoubleQuote + " has already had it's method interfaces defined"
    CompilerEndIf
    #__#ObjectName#_Methods = #True
    
    Interface __#ObjectName#_Methods
  EndOfMacro
  
  BeginMacro End#ObjectName#Methods ;end the method defintion and define the vector table
    EndInterface
    Structure __#ObjectName#_VectorTable
      Address.i[SizeOf(__#ObjectName#_Methods)/SizeOf(integer)]
    EndStructure
    Global __#ObjectName#_VectorTable.__#ObjectName#_VectorTable
  EndOfMacro
  
  BeginMacro ObjectName#Properties ;start the beginning of the properties (structure)
    CompilerIf Defined(__#ObjectName#_Properties, #PB_Constant)
      CompilerError DoubleQuote#ObjectName#DoubleQuote + " has already had it's method interfaces defined"
    CompilerEndIf
    #__#ObjectName#_Properties = #True
    
    Structure __#ObjectName#_Properties
  EndOfMacro
  
  BeginMacro End#ObjectName#Properties ;end the property definition
    EndStructure
  EndOfMacro
EndMacro

Macro MethodExtends(ObjectName) ;if you ever want to extend your methods here you go
  Extends __#ObjectName#_Methods
EndMacro

Macro PropertyExtends(ObjectName) ;...or properties
  Extends __#ObjectName#_Properties
EndMacro

Macro EndObject(ObjectName) ;finish up the object definition
  CompilerIf Defined(__#ObjectName#_Declared, #PB_Constant) = #False
    CompilerError "End Object with out a Begin Object"
  CompilerEndIf
  
  Interface ObjectName Extends __#ObjectName#_Methods ;add on the contructors
    Construct(*Parameters.i = #NULL)
    Destruct(*Parameters.i = #NULL)
  EndInterface
  
  Structure ObjectName#_ ;and get the general object definition up
    *MethodPointer.i
    Properties.__#ObjectName#_Properties
    Size.i
    *Self.__#ObjectName#_Methods
    VectorTable.i[SizeOf(__#ObjectName#_Methods)/SizeOf(integer)]
    Constructor.__Structers
    Destructor.__Structers
  EndStructure
EndMacro

Macro StructureObject(Field, ObjectName) ;for putting objects into structures (with access to its methods and properties)
  Field#__.ObjectName#_
  Field.ObjectName
  *Field#_.__#ObjectName#_Properties
EndMacro

Macro Constructor(ObjectName) ;contructor wrap
  CompilerIf Defined(__#ObjectName#_Constructor, #PB_Procedure)
    CompilerError "Constructor for " + DoubleQuote#ObjectName#DoubleQuote + " has already been defined"
  CompilerEndIf
  Declare __#ObjectName#_Destructor(*Base.ObjectName#_, Parameter.i = #NULL)
  Procedure __#ObjectName#_Constructor(*Base.ObjectName#_, Parameter.i = #NULL)
    If *Base = #NULL
      ProcedureReturn #NULL
    EndIf
    Protected Base.ObjectName = *Base
    Protected This.__#ObjectName#_Methods = *Base
    Protected *This.__#ObjectName#_Properties = @*Base\Properties
    *Base\MethodPointer = @*Base\VectorTable
    *Base\Size = SizeOf(ObjectName)
    *Base\Self = *Base
    CopyMemory(__#ObjectName#_VectorTable, @*Base\VectorTable, SizeOf(__#ObjectName#_Methods))
    *Base\Constructor.__Structers = @__#ObjectName#_Constructor()
    *Base\Destructor.__Structers = @__#ObjectName#_Destructor()
EndMacro
  
Macro EndConstructor
    ProcedureReturn *Base
  EndProcedure
EndMacro

Macro ConstructorFail ;if failure, collect garbage
    Base\Destructor()
    ProcedureReturn #NULL
EndMacro

Macro DeclareConstructor(ObjectName) ;if you need to declare your constructors...
  Declare __#ObjectName#_Constructor(*Base.ObjectName#_, Parameter.i = #NULL)
EndMacro

Macro IsConstructed(ObjectName) ;if auto contruct has been turned off
  (Not (ObjectName\VectorTable[0] <> 0))
EndMacro

Macro Destructor(ObjectName) ;destructor wrap
  CompilerIf Defined(__#ObjectName#_Destructor, #PB_Constant)
    CompilerError "Destructor for " + DoubleQuote#ObjectName#DoubleQuote + " has already been defined"
  CompilerEndIf
  #__#ObjectName#_Destructor = #True
  Procedure __#ObjectName#_Destructor(*Base.ObjectName#_, Parameter.i = #NULL)
    If *Base = #NULL
      ProcedureReturn #NULL
    EndIf
    Protected Base.ObjectName = *Base
    Protected This.__#ObjectName#_Methods = *Base\Properties
    Protected *This.__#ObjectName#_Properties = @*Base\Properties
EndMacro
  
Macro EndDestructor
    ZeroMemory_(*This, *Base\Size)
    ProcedureReturn *This
  EndProcedure
EndMacro

Macro DeclareDestructor(ObjectName)
  Declare __ObjectName#_Destructor(*Base.ObjectName#_, Parameter.i = #NULL)
EndMacro

Macro Method(ObjectName, Method, Parameters = __NULL = #NULL) ;when starting a new method this has to be used (commits the methods to the vector table) as well as defining a few variables to use
  CompilerIf Defined(__#ObjectName#_#Method#, #PB_Procedure)
    CompilerError DoubleQuote#Method#DoubleQuote + " has already been defined for object " + DoubleQuote#ObjectName#DoubleQuote
  CompilerEndIf
  Declare __#ObjectName#_#Method#(*Base.ObjectName#_,Parameters)
  __#ObjectName#_VectorTable\Address[MethodOffsetOf(__#ObjectName#_Methods\Method()) / SizeOf(integer)] = @__#ObjectName#_#Method#()
  Procedure __#ObjectName#_#Method#(*Base.ObjectName#_,Parameters)
    Protected This.__#ObjectName#_Methods = *Base
    Protected *This.__#ObjectName#_Properties = @*Base\Properties
EndMacro
  
Macro EndMethod
  EndProcedure
EndMacro

Macro New(Variable, ObjectName, Parameter = #NULL, AutoConstruct = #True) ;when defineing a new varable using a object use this
  __#Variable.ObjectName#_
  __#Variable\MethodPointer = @__#Variable\VectorTable
  __#Variable\Size = SizeOf(ObjectName#_)
  __#Variable\Self = __#Variable
  __#Variable\Constructor = @__#ObjectName#_Constructor()
  __#Variable\Destructor = @__#ObjectName#_Destructor()
  *Variable.__#ObjectName#_Properties = @__#Variable\Properties
  Variable.ObjectName = __#Variable
  If AutoConstruct
    Variable\Construct(Parameter)
  EndIf
EndMacro

Macro NewInto(Variable, ObjectName, Parameter = #NULL, AutoConstruct = #True) ;put a object into a predefined structure
  Variable = Variable#__
  Variable#_ = Variable + OffsetOf(ObjectName#_\Properties )
  Variable#__\MethodPointer = Variable + OffsetOf(ObjectName#_\VectorTable)
  Variable#__\Size = SizeOf(ObjectName#_)
  Variable#__\Self = Variable#__
  Variable#__\Constructor = @__#ObjectName#_Constructor()
  Variable#__\Destructor = @__#ObjectName#_Destructor()
  If AutoConstruct
    Variable\Construct(Parameter)
  EndIf
EndMacro

Macro Copy(SourceVariable, DestinationVariable)
  CopyMemory(SourceVariable, DestinationVariable, SizeOf(SourceVariable))
  *__DestinationVariable\MethodPointer = @__DestinationVariable\VectorTable
  *__DestinationVariable\Self = *__DestinationVariable
EndMacro

Macro Delete(Variable, Parameter = #NULL) ;Destroy the created object
  Variable\Destruct()
  Variable = #NULL
  *Variable = #NULL
  *__#Variable = #NULL
EndMacro

Macro DeleteFrom(Variable, Parameter = #NULL) ;Delete the object from the structure
  Variable\Destruct()
  Variable = #NULL
  Variable#_ = #NULL
EndMacro
Does your head hurt yet? Or do you haven't a clue what this code actually does or how to use it.
This is were I explain all of that :)

Given the example that we wanted to make a home brewed Linked list. We define the object as such:

Code: Select all

Object(LinkedList)
  LinkedlistMethods
    Index() ;Get the Index of current Element
    Count() ;Count the Elements that are containted in the list
    Reset() ;Reset the Current Element so that the current element is not set or NULL
    Clear() ;deletes all of the elements that are in the list
    FirstElement() ;assigns the current element as the first element
    NextElement() ;assigns the current element as the next element in the list
    PreviousElement() ;assigns the current element as the previous element in the list
    ChangeCurrentElement(*NewCurrent.Element) ;assigns the current element as the element pointed in the parameter
    SelectElement(Index.i) ;changes the current element to reflect that of the given index
    LastElement() ;sets the current element to the last element in the list
    AddElement() ;adds a new element after the current element in the list
    InsertElement() ;inserts a new element before the current element in the list
    DeleteElement(Flag.l = #False) ;deletes the current element
    SwapElements(*ElementA.Element, *ElementB.Element) ;swaps element positions in the list
    GetElement() ;returns the pointer of the current element
    SetElement(DataPointer.i) ;copies the data of the given point to the current element
    Search(*ComparePointer.i) ;compares the data in each element to the given data and returns if found a match
    Save(File.s) ;saves the linked list state and data into a file
    Load(File.s) ;loads the linked list state and data into the list
    IsElement(*Element.Element) ;checks of given pointer is a vaild element
    DebugHead() ;debugs the linked list object structure
    DebugRange(FromIndex.i = #NULL, ToIndex.i = #PB_Any) ;debug a range of elements structures
  EndLinkedlistMethods
  
  LinkedlistProperties
    ListIndex.i ;holds the current index of the list
    ListCount.i ;holds the number of elements that are in the list
    Size.i ;memory alloted for the elements (not including the headers)
    *First.Element ;pointer to the first element
    *Current.Element ;pointer to the current element
    *Last.Element ;pointer to the last element
  EndLinkedListProperties
EndObject(LinkedList)
now theres a lot there but here is the important parts

"Object(LinkedList)" or "Object(<ObjectName>)" is what starts the defining of the object it also sets up some extra macros to allow method and property defining now one of the awkwardness that comes from this is that fact the macros created in beginning of the object definition and must be unique other wise the compiler crashes. I put some simple checks to stop that however, you can possibly wind up crashing the compiler one way or another using this code, I have many times. "LinkedlistMethods" or "<Object Name>Methods" starts the definitions of methods the same goes for "<ObjectName>Properties" there are "End<ObjectName>Methods" and "End<ObjectName>Properties" for there respective blocks. It doesn't matter which way you define the methods and properties (methods first or last), my preference is Methods then Properties. You can also extend other you methods and properties independently of each other to your other object with the "MethodExtends(<ObjectName>)" and "PropertyExtends(<ObjectName>)" being that "<ObjectName>" is the name of the object your extending from. We finish off with "EndObject(<ObjectName>)" keyword which creates some of the other base structures and interfaces that complete the object, which usually not needed but in some extreme case you wanted to alter the base of the object (inheritance or polymorphisms) those structures are defined there.

Now onwards to the method definition, just to state that this is were I could find a solution to an ugly outcome of me trying to simplify the method generation code I wrote to accompany some of my hand written methods. here is a pulled example method from the Linked List libary:

Code: Select all

Method(LinkedList, SwapElements, *ElementA.Element Comma *ElementB.Element) ;swaps element (by location)
  If *ElementA = #NULL Or *ElementB = #NULL
    ProcedureReturn #False
  EndIf
  *ElementA - OffsetOf(Element\Element) ;Set Real element
  *ElementB - OffsetOf(Element\Element) ;Set Real element
  
  ;swaps only the location not the data
  ;problem would only arise if the swapped element is the current element or if the user was using the
  ;absolute memory locations to access the elements
  Swap *ElementA\Next, *ElementB\Next
  Swap *ElementA\Previous, *ElementA\Previous
  
  ProcedureReturn #True
EndMethod
Never mind the code with in the procedure and take notice to the "Method" and "EndMethod" Keywords. Method is a general wrapper for procedure just a bit more complex and some stuff tacked on the top and bottom of it. so the general definition of the method is "Method(<ObjectName>, <MethodName>, <Parameters>)"
MethodName being what is called from the interface (method definition), and parameters being a list of parameters that get passed to the method during the call. Parameters gives me the most grief because of it's limitation. Zero and single parameter methods work just fine how ever 2 and more you have to use the macro i have defined; "Comma" to separate the parameters as shown in this example. I try to stick to my simple parameter definitions :).
There are a few end-user variables that are auto defined within the method: There is "*Base" this variable is the base pointer and structure to the entire object holds the method pointer, properties, size, vector table, ect. "This" this holds the interface for the object you can call other methods of your own object some times handy. "*this" this variable hold the pointer to your object's properties where you can access them like any other structure.
Again to complete the wrap we have "EndMethod".

Moving along, Constructors and Destructors, These guys are pretty straight forward, they the do the preassigning or invalidating all you need to do is write the code that execute when they are constructed and Destructed. Constructors and Destructors are procedures that are wrapped with extra code to complete the setup of the object, you could in theory have a "Null" constructor or Destructor just using the wrappers around empty white space and let the code already written handle the rest. These constructors also do not need to be in any place in the code as long as they are after the object definition (it requires the structures and interfaces defined with in the "Object" block). Also these functions also have the predefined variables that methods hold (*Base, *this, This).
here is how the linked list library handles the constructing and destructing:

Code: Select all

Constructor(LinkedList) ;Creates a new instance of a linked list
  ;Setup Properties
  *This\ListIndex = -1 ; negitive is not vaild
  *This\ListCount = 0
  *This\Size = Parameter
  *This\First = #NULL
  *This\Current = #NULL
  *This\Last = #NULL
EndConstructor

Destructor(LinkedList);destroys a specified instance of a linked list
  ;Destroy Elements
  If Parameter = #False
    This\Clear()
  EndIf
  ;Null Properties
  *This\ListIndex = 0
  *This\ListCount = 0
  *This\Size = 0
  *This\First = #NULL
  *This\Current = #NULL
  *This\Last = #NULL
EndDestructor
Now for the easy part (well sorta of), To create variable I've created the keyword "New" for this. It Create a series of variables that you can use to modify your object nicely, to create a new object you use: "New(<VariableName>, <ObjectName>, <ConstructorParameter = #NULL>, <AutoConstruct = #True>)"
The first 2 parameters are required and for the most part you only need the first 3 but the fourth parameters is just there to give the option of constructing later. New's variables for that are created from the macro are defined by name but are similar to how methods predefined variables are named, as en example we will create a new object named "MyList" when we use the new keyword it will create; "__MyList" which is the absolute base for the object and holds everthing, "*MyList" that will hold the pointer to the properties and how you would normally access yuor objects properties, and "MyList" which will hold the methods for your object.
If you need to use a variable in a structure I have a macro for this which comes in handy in many situations; "StructureObject(<FieldName>, <ObjectName>)" Field name being the name of the field in the structure. This will create the normal 3 variables (or in this case fields), like new does but doesn't construct them until you use NewInto (ex: NewInto(MyStructuredLists\ListA, LinkedList))

if you need to free your object you would use "<YourObject>\Destruct(<Parameter> = #NULL)"
or "Delete(<Variable>, <Parameter = #NULL>)" that does the same thing essentially but also NULL's the other variables for you to prevent weird accessing of released objects. There is another keyword that deletes but for structured objects; "DeleteFrom(<Variable>, <Parameter = #NULL>).

There are a few helper macros that are in the object base file here are a brief description for each of them:

"ConstructorFail" this keyword is there for simple garbage collection if your constructor failed to initialize properly

"DeclareConstructor(<ObjectName>)" pretty self explanatory works like normal declare except it is for the constructor.

"DeclareDestructor(<ObjectName>)" same but for destructor

"Copy(<SourceVariable>, <DestinationVariable>)" this copies an object to another identical object with all of it's object (no cross object copies) and alters the base to reflect the new object.


So with all that out of the way the here is the full source to the linked list library this should be pretty stable if anything crashes here it's probably the Object Base stuff.

Code: Select all

;////////////////////////////////////////////////////////////////
;//
;// Title: Linked List Libary
;// FileName: LinkedList.pbi
;// Version: 2.2.0.4
;// Date: 12/21/08
;// Notes: Provide a dynamic linked list for all apps
;//
;////////////////////////////////////////////////////////////////


;- Includes

XIncludeFile "ObjectBase.pbi"

;- Classes

;{ Linked List Class
;- Constants

#LinkedListMarker = "DLL2204"

;- Structures

Structure ElementAccess
  StructureUnion
    Characters.c[0]
    Bytes.b[0]
    Words.w[0]
    Longs.l[0]
    Floats.f[0]
    Integers.i[0]
    Quads.q[0]
    Doubles.d[0]
  EndStructureUnion
EndStructure

Structure Element
  *Next.Element
  *Previous.Element
  Element.ElementAccess ;Flexible Read/Write Pointer
  ;^^^ Purely abstract
EndStructure

;- Objects

Object(LinkedList)
  LinkedlistMethods
    Index() ;Get the Index of current Element
    Count() ;Count the Elements that are containted in the list
    Reset() ;Reset the Current Element so that the current element is not set or NULL
    Clear() ;deletes all of the elements that are in the list
    FirstElement() ;assigns the current element as the first element
    NextElement() ;assigns the current element as the next element in the list
    PreviousElement() ;assigns the current element as the previous element in the list
    ChangeCurrentElement(*NewCurrent.Element) ;assigns the current element as the element pointed in the parameter
    SelectElement(Index.i) ;changes the current element to reflect that of the given index
    LastElement() ;sets the current element to the last element in the list
    AddElement() ;adds a new element after the current element in the list
    InsertElement() ;inserts a new element before the current element in the list
    DeleteElement(Flag.l = #False) ;deletes the current element
    SwapElements(*ElementA.Element, *ElementB.Element) ;swaps element positions in the list
    GetElement() ;returns the pointer of the current element
    SetElement(DataPointer.i) ;copies the data of the given point to the current element
    Search(*ComparePointer.i) ;compares the data in each element to the given data and returns if found a match
    Save(File.s) ;saves the linked list state and data into a file
    Load(File.s) ;loads the linked list state and data into the list
    IsElement(*Element.Element) ;checks of given pointer is a vaild element
    DebugHead() ;debugs the linked list object structure
    DebugRange(FromIndex.i = #NULL, ToIndex.i = #PB_Any) ;debug a range of elements structures
  EndLinkedlistMethods
  
  LinkedlistProperties
    ListIndex.i ;holds the current index of the list
    ListCount.i ;holds the number of elements that are in the list
    Size.i ;memory alloted for the elements (not including the headers)
    *First.Element ;pointer to the first element
    *Current.Element ;pointer to the current element
    *Last.Element ;pointer to the last element
  EndLinkedListProperties
EndObject(LinkedList)

;- Macros

Macro ForAllElements(LinkedList, Element)
  Element = LinkedList\FirstElement()
  While Element <> #NULL
EndMacro

Macro ForAllEnd(LinkedList, Element)
  Element = LinkedList\NextElement()
  Wend
EndMacro

;- Constructors

Constructor(LinkedList) ;Creates a new instance of a linked list
  ;Setup Properties
  *This\ListIndex = -1 ; negitive is not vaild
  *This\ListCount = 0
  *This\Size = Parameter
  *This\First = #Null
  *This\Current = #Null
  *This\Last = #Null
EndConstructor

Destructor(LinkedList);destroys a specified instance of a linked list
  ;Destroy Elements
  If Parameter = #False
    This\Clear()
  EndIf
  ;Null Properties
  *This\ListIndex = 0
  *This\ListCount = 0
  *This\Size = 0
  *This\First = #Null
  *This\Current = #Null
  *This\Last = #Null
EndDestructor

;- Methods

Method(LinkedList, Index) ;Get the current element index in the list (Fast, Cached)
  ProcedureReturn *This\ListIndex
EndMethod

Method(LinkedList, Count) ;returns the number of elements in the list (Fast, Cached)
  ProcedureReturn *This\ListCount
EndMethod

Method(LinkedList, Reset) ;Resets the current list element to be before the first element
  *This\Current = #Null
EndMethod

Method(LinkedList, Clear) ;Clears the List if elements
  ;For Each Element
  *This\Current = *This\First
  While *This\Current <> #Null
    NextElement = *This\Current\Next
    FreeMemory(*This\Current) 
    *This\Current = NextElement
  Wend
  
  *This\ListIndex = -1
  *This\ListCount = 0
  *This\First = #Null
  *This\Current = #Null
  *This\Last = #Null
EndMethod

Method(LinkedList, FirstElement) ;changes the current element to the first element
  *This\Current = *This\First
  If *This\Current = #Null
    ProcedureReturn #Null
  Else
    ProcedureReturn *This\Current\Element
  EndIf
EndMethod

Method(LinkedList, NextElement) ;changes the current element to the next element
  If *This\Current <> #Null And *This\Current\Next <> #Null
    *This\Current = *This\Current\Next
    ProcedureReturn *This\Current\Element
  EndIf
  
  ProcedureReturn #Null
EndMethod

Method(LinkedList, PreviousElement) ;changes the current element to the previous element
  If *This\Current <> #Null And *This\Current\Previous <> #Null
    *This\Current = *This\Current\Previous
    ProcedureReturn *This\Current\Element
  EndIf
  
  ProcedureReturn #Null
EndMethod
 
Method(LinkedList, ChangeCurrentElement, *NewCurrent.Element) ;changes the current element to the a user specified element via address
  If *NewCurrent = #Null
    *This\Current = #Null
    *This\ListIndex = -1
  Else
    *NewCurrent - OffsetOf(Element\Element) ;Set to Real Element
    
    ;For Each Element
    *This\Current = *This\First
    Found = #False
    Index = 0
    Repeat 
      If *This\Current = *NewCurrent
        Found = #True
        Break
      EndIf
      Index + 1
      *This\Current = *This\Current\Next
    Until *This\Current = #Null
    
    If Found = #False
      ProcedureReturn #False
    EndIf
    
    *This\ListIndex = Index
    *This\Current = *NewCurrent
  EndIf
  ProcedureReturn #True
EndMethod
  
Method(LinkedList, SelectElement, Index.i) ;changes the current element to the specified index
  If Index < 0 Or Index > *This\ListCount
    ProcedureReturn #Null
  EndIf
  
  ;Enumerate Elements
  *This\Current = *This\First
  For Element = 0 To Index
    *This\Current = *This\Current\Next
  Next
  
  ProcedureReturn *This\Current\Element
EndMethod

Method(LinkedList, LastElement)
  *This\Current = *This\Last
  
  If *This\Current = #Null
    ProcedureReturn #Null
  Else
    ProcedureReturn *This\Current\Element
  EndIf
EndMethod

Method(LinkedList, AddElement) ;adds a element after the current element
  *New.Element = AllocateMemory(SizeOf(Element) + *This\Size)
  If *This\Current = #Null
    If *This\First = #Null ;if in a Empty List
      ;[New]
      ;This Element
      *New\Next = #Null
      *New\Previous = #Null
      
      ;no current element to alter
      
      ;Base
      *This\Current = *New
      *This\First = *New
      *This\Last = *New
    Else ;if at the beginning of a list
      ;[New][Element1][...]
      ;This Element
      *New\Next = *This\First ;New Pointing To Element1
      *New\Previous = #Null
      
      ;Current Element
      *This\First\Previous = *New ;Element1 pointing to New
      
      ;Base
      *This\Current = *New ;Change the Current Element
      *This\First = *New ;Change the First Element
    EndIf
  Else
    If *This\Current = *This\Last ;If at end of the list
      ;[Element1][New][...]
      ;This Element
      *New\Next = #Null
      *New\Previous = *This\Last ;New pointing to Element1
      
      ;Current Element
      *This\Last\Next = *New ;Element1 pointing to New
      
      ;Base
      *This\Current = *New ;Change the Current Element
      *This\Last = *New ;Change the First Element
    Else ;if somewhere in the middle of the list
      ;[...][Element1][New][Element2][...]
      ;This Element
      *New\Next = *This\Current\Next ;Element2
      *New\Previous = *This\Current ;Element1
      
      ;Current Element
      *This\Current\Next\Previous = *New ;Element2 Pointing to New
      *This\Current\Next = *New ;Element1 Pointing to New
      
      
      ;Base
      *This\Current = *New ;Change the Current Element
    EndIf
  EndIf
  *This\ListIndex + 1 ;Placing the element 1 ahead of the current
  *This\ListCount + 1 ;always adding a new element
  
  ProcedureReturn *This\Current\Element
EndMethod

Method(LinkedList, InsertElement) ;inserts a new element before the current element
  *New.Element = AllocateMemory(SizeOf(Element) + *This\Size)
  
  If *This\Current = #Null
    If *This\First = #Null ;if in a Empty List
      ;[New]
      ;This Element
      *New\Next = #Null
      *New\Previous = #Null
      
      ;no current element to alter
      
      ;Base
      *This\Current = *New
      *This\First = *New
      *This\Last = *New
    Else ;if at the beginning of a list
      ;[New][Element1][...]
      ;This Element
      *New\Next = *This\First ;New Pointing To Element1
      *New\Previous = #Null
      
      ;Current Element
      *This\First\Previous = *New ;Element1 pointing to New
      
      ;Base
      *This\Current = *New ;Change the Current Element
      *This\First = *New ;Change the First Element
    EndIf
    *This\ListIndex = 0
  Else ;if somewhere in the middle of the list or the end
    If *This\Current = *This\First ;at the beginning
      ;[New][Element1][Element2][...]
      ;This Element
      *New\Next = *This\Current ;Element2
      *New\Previous = #Null ;Element1
      
      ;Current Element
      *This\Current\Previous = *New
      
      ;Base
      *This\Current = *New ;Change the Current Element
      *This\First = *New ;Change the First Element
      *This\ListIndex = 0
    Else ;middle or end
      ;[...][New][Element1][Element2][...]
      ;This Element
      *New\Next = *This\Current ;Element2
      *New\Previous = *This\Current\Previous ;Element1
      
      ;Current Element
      *This\Current\Previous\Next = *New ;Element2 Pointing to New
      *This\Current\Previous = *New ;Element1 Pointing to New
      
      ;Base
      *This\Current = *New ;Change the Current Element
      *This\ListIndex - 1
    EndIf
  EndIf
  
  *This\ListCount + 1 ;always adding a new element
  
  ProcedureReturn *This\Current\Element
EndMethod

Method(LinkedList, DeleteElement, flags.c) ;deletes a element
  If *This\Current = #Null ;if no elements to delete or if there is no current element
    ProcedureReturn #Null
  EndIf
  
  ;patch surrounding elements
  If *This\Current\Next <> #Null ;If Last
    *This\Current\Next\Previous = *This\Current\Previous
  Else
    *This\Last = *This\Current\Previous
  EndIf
  If *This\Current\Previous <> #Null ;If First
    *This\Current\Previous\Next = *This\Current\Next
  Else
    *This\First = *This\Current\Next
  EndIf
  ElementToBeDeleted = *This\Current
  
  If flags = 0
    *This\Current = *This\Current\Previous
    *This\ListIndex - 1
    ;works like backspace button on keyboard
  Else
    *This\Current = *This\Current\Next
    ;works like delete button on keyboard
  EndIf
  
  FreeMemory(ElementToBeDeleted)
  *This\ListCount - 1 ;deleting an element
  If *This\Current = #Null
    ProcedureReturn #Null
  Else
    ProcedureReturn *This\Current\Element
  EndIf
EndMethod

Method(LinkedList, SwapElements, *ElementA.Element Comma *ElementB.Element) ;swaps element (by location)
  If *ElementA = #Null Or *ElementB = #Null
    ProcedureReturn #False
  EndIf
  *ElementA - OffsetOf(Element\Element) ;Set Real element
  *ElementB - OffsetOf(Element\Element) ;Set Real element
  
  ;swaps only the location not the data
  ;problem would only arise if the swapped element is the current element or if the user was using the
  ;absolute memory locations to access the elements
  Swap *ElementA\Next, *ElementB\Next
  Swap *ElementA\Previous, *ElementA\Previous
  
  ProcedureReturn #True
EndMethod

Method(LinkedList, GetElement) ;retrives the current element data pointer
  If *This\Current <> #Null
    ProcedureReturn *This\Current\Element
  Else
    ProcedureReturn #Null
  EndIf
EndMethod

Method(LinkedList, SetElement, *DataPointer.i) ;copys the data into the current element
  If *This\Current <> #Null And DataPointer <> #Null
    ProcedureReturn CopyMemory(DataPointer, @*This\Current\Element, SizeOf(*This\Size))
  Else
    ProcedureReturn #False
  EndIf
EndMethod

Method(LinkedList, Search, *ComparePointer.i) ;Finds an element that matches the data compared by the compare pointer
  If ComparePointer = #Null Or *This\First = #Null
    ProcedureReturn #Null
  EndIf
  
  *This\Current = *This\First
  Repeat
    If CompareMemory(*This\Current\Element, ComparePointer, *This\Size) = #True
      ProcedureReturn *This\Current\Element
    EndIf
    *This\Current = *This\Current\Next
  Until *This\Current = #Null
EndMethod

Method(LinkedList, Save, File.s) ;Saves The Current Linked List Into a Empty File
  If File = #Null$ Or *This\First = #Null
    ProcedureReturn #False
  EndIf
  
  FileHandle = CreateFile(#PB_Any, File)
  If IsFile(FileHandle) = #Null
    ProcedureReturn #False
  EndIf
  
  WriteStringN(FileHandle, #LinkedListMarker)
  WriteInteger(FileHandle, *This\Size)
  *This\Current = *This\First
  Repeat
    WriteData(FileHandle, *This\Current\Element, *This\Size)
    *This\Current = *This\Current\Next
  Until *This\Current = #Null
  
  FlushFileBuffers(FileHandle)
  CloseFile(FileHandle)
  
  ProcedureReturn #True
EndMethod

Method(LinkedList, Load, File.s) ;Loads the Linked List From File (Clears and Changes the element size if needed)
  If File = #NULL$
    ProcedureReturn #False
  EndIf
  
  ;Clear the List
  If *This\First <> #NULL
    *This\Current = *This\First
    Repeat 
      NextElement = *This\Current\Next
      FreeMemory(*This\Current) 
      *This\Current = NextElement
    Until *This\Current = #NULL
  EndIf
    
  *This\ListIndex = -1
  *This\ListCount = 0
  *This\First = #NULL
  *This\Current = #NULL
  *This\Last = #NULL
  
  FileHandle = ReadFile(#PB_Any, File)
  If IsFile(FileHandle) = #NULL
    ProcedureReturn #False
  EndIf
  
  If ReadString(FileHandle) <> #LinkedListMarker
    CloseFile(FileHandle)
    ProcedureReturn #False
  EndIf
  
  *This\Size = ReadInteger(FileHandle)
  
  Repeat
    ReadData(FileHandle, This\AddElement(), *This\Size)
  Until Eof(FileHandle) <> #NULL
  
EndMethod

Method(LinkedList, IsElement, *Element.Element) ;Simple check for any linked list
  If *Element <= OffsetOf(Element\Element)
    ProcedureReturn #False ;prevents the NULL or SizeOf(INTEGER) problem
  EndIf
  ProcedureReturn #True
EndMethod

Method(LinkedList, DebugHead)
  Debug "------Debugging Linked List-------"
  Debug "Instance: $" + RSet(Hex(*This), SizeOf(integer), "0")
  Debug "Elements Index: " + Str(*This\ListIndex)
  Debug "Number Of Elements: " + Str(*This\ListCount)
  Debug "Element Size: " + Str(*This\Size)
  Debug "First Element: $" + RSet(Hex(*This\First), SizeOf(integer), "0")
  Debug "Current Element: $" + RSet(Hex(*This\Current), SizeOf(integer), "0")
  Debug "Last Element: $" + RSet(Hex(*This\Last), SizeOf(integer), "0")
  Debug "----------------------------------"
EndMethod

Method(LinkedList, DebugRange, FromIndex = 0 Comma ToIndex = #PB_Any)
  If ToIndex <= 0 Or ToIndex > *This\ListCount
    ToIndex = *This\ListCount
  EndIf
  If FromIndex < 0
    FromIndex = 0
  EndIf
  RecallElement = *This\Current
  
  Debug "--Debugging Linked List Elements--"
  Debug "Instance: $" + RSet(Hex(*This), SizeOf(integer), "0")
  Debug "Range: [" + Str(FromIndex) + " - " + Str(ToIndex) + "]"
  
  *This\Current = *This\First
  For Index = 1 To *This\ListCount
    Debug ""
    Debug "Element Index: " + Str(Index)
    Debug "Element: $" + RSet(Hex(*This\Current), SizeOf(integer), "0")
    Debug "Next Element: $" + RSet(Hex(*This\Current\Next), SizeOf(integer), "0")
    Debug "Previous Element: $" + RSet(Hex(*This\Current\Previous), SizeOf(integer), "0")
    If *This\Current\Next = #NULL
      Debug "Continutity Error - Next Is Null"
    EndIf
    If *This\Current\Previous = #NULL
      Debug "Continutity Error - Previous Is Null"
    EndIf
    *This\Current = *This\Current\Next
  Next
  Debug "----------------------------------"
  *This\Current = RecallElement
EndMethod
and here is a small app demonstrating both now all this does is loads some ascii art character by character into a multidimensional dynamic linked list and displays it back into a console. it's a horrible application but displays the ease of using such a system.

Code: Select all

;- Includes

XIncludeFile "LinkedList.pbi"
XIncludeFile "ObjectBase.pbi"

;- Structure

Structure Char
  c.c
EndStructure

Structure Line
  StructureObject(Char, LinkedList)
EndStructure

;- Entry Point
CallDebugger
New(AsciiArt, LinkedList, SizeOf(Line))
For Line = 0 To 34
  *CurrentLine.Line = AsciiArt\AddElement()
  NewInto(*CurrentLine\Char,  LinkedList, SizeOf(Char))
  For Char = 0 To 53
    *Current.Char = *CurrentLine\Char\AddElement()
    *Current\c = Asc(PeekS(?BeginAsciiArt + (Line * 54) + Char, 1))
  Next
Next

OpenConsole()

ForAllElements(AsciiArt, *CurrentLine.Line)
  ForAllElements(*CurrentLine\Char, *Current.Char)
    Print(Chr(*Current\c))
  ForAllEnd(*CurrentLine\Char, *Current.Char)
ForAllEnd(AsciiArt, *CurrentLine.Line)


CallDebugger
DataSection
  BeginAsciiArt:
  Data.s "                                 ,_-=(!7(7/zs_.       " + Chr(#CR) + Chr(#LF)
  Data.s "                              .='  ' .`/,/!(=)Zm.     " + Chr(#CR) + Chr(#LF)
  Data.s "                .._,,._..  ,-`- `,\ ` -` -`\\7//WW.   " + Chr(#CR) + Chr(#LF)
  Data.s "           ,v=~/.-,-\- -!|V-s.)iT-|s|\-.'   `///mK%.  " + Chr(#CR) + Chr(#LF)
  Data.s "         v!`i!-.e]-g`bT/i(/[=.Z/m)K(YNYi..   /-]i44M. " + Chr(#CR) + Chr(#LF)
  Data.s "       v`/,`|v]-DvLcfZ/eV/iDLN\D/ZK@%8W[Z..   `/d!Z8m " + Chr(#CR) + Chr(#LF)
  Data.s "      //,c\(2(X/NYNY8]ZZ/bZd\()/\7WY%WKKW)   -'|(][%4." + Chr(#CR) + Chr(#LF)
  Data.s "    ,\\i\c(e)WX@WKKZKDKWMZ8(b5/ZK8]Z7%ffVM,   -.Y!bNMi" + Chr(#CR) + Chr(#LF)
  Data.s "    /-iit5N)KWG%%8%%%%W8%ZWM(8YZvD)XN(@.  [   \]!/GXW[" + Chr(#CR) + Chr(#LF)
  Data.s "   / ))G8\NMN%W%%%%%%%%%%8KK@WZKYK*ZG5KMi,-   vi[NZGM[" + Chr(#CR) + Chr(#LF)
  Data.s "  i\!(44Y8K%8%%%**~YZYZ@%%%%%4KWZ/PKN)ZDZ7   c=//WZK%!" + Chr(#CR) + Chr(#LF)
  Data.s " ,\v\YtMZW8W%%f`,`.t/bNZZK%%W%%ZXb*K(K5DZ   -c\\/KM48 " + Chr(#CR) + Chr(#LF)
  Data.s " -|c5PbM4DDW%f  v./c\[tMY8W%PMW%D@KW)Gbf   -/(=ZZKM8[ " + Chr(#CR) + Chr(#LF)
  Data.s " 2(N8YXWK85@K   -'c|K4/KKK%@  V%@@WD8e~  .//ct)8ZK%8` " + Chr(#CR) + Chr(#LF)
  Data.s " =)b%]Nd)@KM[  !'\cG!iWYK%%|   !M@KZf    -c\))ZDKW%`  " + Chr(#CR) + Chr(#LF)
  Data.s " YYKWZGNM4/Pb  '-VscP4]b@W%     'Mf`   -L\///KM(%W!   " + Chr(#CR) + Chr(#LF)
  Data.s " !KKW4ZK/W7)Z. '/cttbY)DKW%     -`  .',\v)K(5KW%%f    " + Chr(#CR) + Chr(#LF)
  Data.s " 'W)KWKZZg)Z2/,!/L(-DYYb54%  ,,`, -\-/v(((KK5WW%f     " + Chr(#CR) + Chr(#LF)
  Data.s "  \M4NDDKZZ(e!/\7vNTtZd)8\Mi!\-,-/i-v((tKNGN%W%%      " + Chr(#CR) + Chr(#LF)
  Data.s "  'M8M88(Zd))///((|D\tDY\\KK-`/-i(=)KtNNN@W%%%@%[     " + Chr(#CR) + Chr(#LF)
  Data.s "   !8%@KW5KKN4///s(\Pd!ROBY8/=2(/4ZdzKD%K%%%M8@%%     " + Chr(#CR) + Chr(#LF)
  Data.s "    '%%%W%dGNtPK(c\/2\[Z(ttNYZ2NZW8W8K%%%%YKM%M%%.    " + Chr(#CR) + Chr(#LF)
  Data.s "      *%%W%GW5@/%!e]_tZdY()v)ZXMZW%W%%%*5Y]K%ZK%8[    " + Chr(#CR) + Chr(#LF)
  Data.s "       '*%%%%8%8WK\)[/ZmZ/Zi]!/M%%%%@f\ \Y/NNMK%%!    " + Chr(#CR) + Chr(#LF)
  Data.s "         'VM%%%%W%WN5Z/Gt5/b)((cV@f`  - |cZbMKW%%|    " + Chr(#CR) + Chr(#LF)
  Data.s "            'V*M%%%WZ/ZG\t5((+)L\'-,,/  -)X(NWW%%     " + Chr(#CR) + Chr(#LF)
  Data.s "                 `~`MZ/DZGNZG5(((\,    ,t\\Z)KW%@     " + Chr(#CR) + Chr(#LF)
  Data.s "                    'M8K%8GN8\5(5///]i!v\K)85W%%f     " + Chr(#CR) + Chr(#LF)
  Data.s "                      YWWKKKKWZ8G54X/GGMeK@WM8%@      " + Chr(#CR) + Chr(#LF)
  Data.s "                       !M8%8%48WG@KWYbW%WWW%%%@       " + Chr(#CR) + Chr(#LF)
  Data.s "                         VM%WKWK%8K%%8WWWW%%%@`       " + Chr(#CR) + Chr(#LF)
  Data.s "                           ~*%%%%%%W%%%%%%%@~         " + Chr(#CR) + Chr(#LF)
  Data.s "                              ~*MM%%%%%%@f`           " + Chr(#CR) + Chr(#LF)
  Data.s "                                   '''''              " + Chr(#CR) + Chr(#LF)
EndDataSection
Like I said at the beginning please tell me how you guys think about this code and to be honest I use it all the time and for some people I can understand if this is way too much for what you do or need but, I like to work with large code projects and using objects tend too help manage the code, for smaller things things, like quick apps or demos, this can be overkill and has a bit of overhead memory wise but I think that outwieghts the matter of how the memory is manage in PB rather than in the OS level like the older version of the object base.

Posted: Mon Dec 22, 2008 3:18 pm
by SFSxOI
WoW! what a lot of work you must have put into this. Thank You. :)

What are the advantages or disadvantages for this vs linked lists the way they are used in PB now?

Posted: Mon Dec 22, 2008 8:22 pm
by Rook Zimbabwe
Shhh you are going to get the OOP peeps here mad at you! :wink:

Excellent program and nice ASCII example!!!

Posted: Mon Dec 22, 2008 9:54 pm
by rsts
Whoa :shock: Now I see why you've been absent for a spell.

Mind boggling yet nice.

Thanks for the stimulation.

Posted: Mon Dec 22, 2008 11:54 pm
by Anonymous

Code: Select all

Macro _BeginMacro1
  Mac;
EndMacro

Macro _BeginMacro2
  ro;
EndMacro 

héhé , very clever :D

Posted: Tue Dec 23, 2008 12:52 am
by Dreglor
SFSxOI:
The linked List library only is useful if wanted to nest them or use the extra functions I've provided for it like save and load I wanted to have a sort in there but but wouldn't be very good (like the search method right now). The other reason i saw a demand for this is to simplify the syntax in linked lists, I always felt that the built in syntax was kind of out of order so to speak.

I seam to forget the 32bit aspect if this. it crashes and with the debugger on it crashes outside of the debugger :|
Even weirder is that on 64bit on the first CallDebugger when I look at the stack I crashes trying to access location 21 but runs fine other wise...
Hmm I've traced the bug to AddElement() method, infact it's not the method it self but rather the code calling it consider lines 20 and 23 of the sample

Code: Select all

  *CurrentLine.Line = AsciiArt\AddElement()

Code: Select all

    *Current.Char = *CurrentLine\Char\AddElement()
these too lines screw the stack up, apon entering the stack has 3 items on it after completing line 20 the stack has 2 elements and then after line 23 of the first run through the stack has zero elements and on the second run it is "below the program start" which lead me to believe that there was a bug in the method but I'm hesitant to call that because I'm not doing anything strange other than a bit of pointer logic (even in the macro all I doing is a declare and more pointer assigning) and running over the code again doesn't yield anything. Out of curiosity I thew InsertElement in there rather than AddElement and the results were the same, I also Fixed the bug with insert while i was there. I'm beginning to think that this is a bug with interface calls pop stuff off the stack when it shouldn't.

I'm going to have to call in some help on this one :?
*turns on the Fred Spot Light*

Posted: Tue Dec 23, 2008 4:12 am
by Rescator
Just to be Evil(tm) here is the same, but in "Pure" procedural Basic instead :twisted: :

I'm not saying OOP suck, just that it has it's place, and in this spesific example I would never use OOP, the following is a very tight loop,
pointers instead of Peek etc.
considering the chars and lines used fixed numbers this is easy.

(PS! I fixed the lack of Unicode support, no idea if your code handles that or not as your example crashed the debugger here. :? )

Code: Select all

EnableExplicit

Define line.i,char.i,*pos.Character,text$

Dim AsciiArt.c(34,53)

*pos=?BeginAsciiArt
For line=0 To 33
 For char=0 To 53
  AsciiArt(line,char)=*pos\c
  *pos+SizeOf(Character)
 Next
 *pos+SizeOf(Character)
Next

OpenConsole()

text$=Space(54)

For line=0 To 33
 *pos=@text$
 For char=0 To 53
  *pos\c=AsciiArt(line,char)
  *pos+SizeOf(Character)
 Next
 PrintN(text$)
Next

Input()
CloseConsole()

DataSection
  BeginAsciiArt:
  Data.s "                                 ,_-=(!7(7/zs_.       "
  Data.s "                              .='  ' .`/,/!(=)Zm.     "
  Data.s "                .._,,._..  ,-`- `,\ ` -` -`\\7//WW.   "
  Data.s "           ,v=~/.-,-\- -!|V-s.)iT-|s|\-.'   `///mK%.  "
  Data.s "         v!`i!-.e]-g`bT/i(/[=.Z/m)K(YNYi..   /-]i44M. "
  Data.s "       v`/,`|v]-DvLcfZ/eV/iDLN\D/ZK@%8W[Z..   `/d!Z8m "
  Data.s "      //,c\(2(X/NYNY8]ZZ/bZd\()/\7WY%WKKW)   -'|(][%4."
  Data.s "    ,\\i\c(e)WX@WKKZKDKWMZ8(b5/ZK8]Z7%ffVM,   -.Y!bNMi"
  Data.s "    /-iit5N)KWG%%8%%%%W8%ZWM(8YZvD)XN(@.  [   \]!/GXW["
  Data.s "   / ))G8\NMN%W%%%%%%%%%%8KK@WZKYK*ZG5KMi,-   vi[NZGM["
  Data.s "  i\!(44Y8K%8%%%**~YZYZ@%%%%%4KWZ/PKN)ZDZ7   c=//WZK%!"
  Data.s " ,\v\YtMZW8W%%f`,`.t/bNZZK%%W%%ZXb*K(K5DZ   -c\\/KM48 "
  Data.s " -|c5PbM4DDW%f  v./c\[tMY8W%PMW%D@KW)Gbf   -/(=ZZKM8[ "
  Data.s " 2(N8YXWK85@K   -'c|K4/KKK%@  V%@@WD8e~  .//ct)8ZK%8` "
  Data.s " =)b%]Nd)@KM[  !'\cG!iWYK%%|   !M@KZf    -c\))ZDKW%`  "
  Data.s " YYKWZGNM4/Pb  '-VscP4]b@W%     'Mf`   -L\///KM(%W!   "
  Data.s " !KKW4ZK/W7)Z. '/cttbY)DKW%     -`  .',\v)K(5KW%%f    "
  Data.s " 'W)KWKZZg)Z2/,!/L(-DYYb54%  ,,`, -\-/v(((KK5WW%f     "
  Data.s "  \M4NDDKZZ(e!/\7vNTtZd)8\Mi!\-,-/i-v((tKNGN%W%%      "
  Data.s "  'M8M88(Zd))///((|D\tDY\\KK-`/-i(=)KtNNN@W%%%@%[     "
  Data.s "   !8%@KW5KKN4///s(\Pd!ROBY8/=2(/4ZdzKD%K%%%M8@%%     "
  Data.s "    '%%%W%dGNtPK(c\/2\[Z(ttNYZ2NZW8W8K%%%%YKM%M%%.    "
  Data.s "      *%%W%GW5@/%!e]_tZdY()v)ZXMZW%W%%%*5Y]K%ZK%8[    "
  Data.s "       '*%%%%8%8WK\)[/ZmZ/Zi]!/M%%%%@f\ \Y/NNMK%%!    "
  Data.s "         'VM%%%%W%WN5Z/Gt5/b)((cV@f`  - |cZbMKW%%|    "
  Data.s "            'V*M%%%WZ/ZG\t5((+)L\'-,,/  -)X(NWW%%     "
  Data.s "                 `~`MZ/DZGNZG5(((\,    ,t\\Z)KW%@     "
  Data.s "                    'M8K%8GN8\5(5///]i!v\K)85W%%f     "
  Data.s "                      YWWKKKKWZ8G54X/GGMeK@WM8%@      "
  Data.s "                       !M8%8%48WG@KWYbW%WWW%%%@       "
  Data.s "                         VM%WKWK%8K%%8WWWW%%%@`       "
  Data.s "                           ~*%%%%%%W%%%%%%%@~         "
  Data.s "                              ~*MM%%%%%%@f`           "
  Data.s "                                   '''''              "
EndDataSection

Posted: Sun Jan 04, 2009 1:07 am
by technicorn
Thanks Dreglor, for mentioning my name :)

Made some more investigation on the generating macros with macros problem, and came up with this solution:

Code: Select all

EnableExplicit

Macro _EndMacro(p1=EndMac,p2=ro)
  p1#p2
EndMacro

Macro GenerateMacro(name)
  Macro name(p1,p2)
  p1 = p2
  :_EndMacro()
EndMacro

GenerateMacro(SetV1ToV2)

Define a.l, b.l = 123

SetV1ToV2(a,b)
Debug a
It isn't the "Macro" that makes it problematic to generate them, it's only the "EndMacro".
And the ":" before _EndMacro() is still needed to make the compilerer reevaluate the generated "EndMacro"

This shorter form makes writing macro generating macros even more enjoyable :twisted:

BTW: I have a procedural version of dynamic linked list in the making,
it can even handle linked list in linked list with automated cleanup on deleting, it will automaticly erase any sub (and sub sub sub...) list,
when you delete an element thas has a sub list in it.
It also implements sorting using merge sort, if you like, you can
integrate that sorting algo in your objective linked list.
The sorting can even handle fixed size strings, what you can't do with standard lists.

Belated happy new year to all!
technicorn

Posted: Sun Jan 04, 2009 3:46 pm
by Kale
Lol at the title of this thread. It made me laugh. Surely it should be changed to 'The Chaos that is Object Oriented Programming in PB'

Nice code though. :wink: