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
EndMacroThis 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)
"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
EndMethodMethodName 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
EndDestructorThe 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
EndMethodCode: 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



