Page 2 of 2 FirstFirst 12
Results 26 to 41 of 41

Thread: A lot About Lape..

  1. #26
    Join Date
    Jan 2007
    Posts
    8,876
    Mentioned
    123 Post(s)
    Quoted
    327 Post(s)

    Default

    Quote Originally Posted by stata View Post
    obviously there are multiple methods of declaration... that's why I'm asking

    that last declaration confuses me. Is that the same as this
    procedure bob(a: ^TPointArray);

    seems like an odd way to send a pointer, particularly since it seems to imply that "a" cannot be changed by the procedure

    and do "in" and "out" work as they did in PS? I'm aware of those...
    I understood you know there's multiple methods, but you asked, so I answered.

    As for the "procedure bob(const a: TPointArray);". You are correct in that it can't be changed by the procedure:
    Simba Code:
    procedure bob(const a: TPointArray);
    var
      b : TPointArray;
    begin
      setLength(a, 0); // Will fail because a is not directly editable.
      b := a; // Will not fail as a is not edited.
    end;
    The only reason to do this is (as you probably already know) to spare memory.

    As far as I am aware, "in" and "out" works just like PS. I rarely use them with Simba, to be honest.

  2. #27
    Join Date
    Aug 2013
    Posts
    41
    Mentioned
    0 Post(s)
    Quoted
    16 Post(s)

    Default

    Quote Originally Posted by Zyt3x View Post
    I understood you know there's multiple methods, but you asked, so I answered.

    As for the "procedure bob(const a: TPointArray);". You are correct in that it can't be changed by the procedure:
    Simba Code:
    procedure bob(const a: TPointArray);
    var
      b : TPointArray;
    begin
      setLength(a, 0); // Will fail because a is not directly editable.
      b := a; // Will not fail as a is not edited.
    end;
    The only reason to do this is (as you probably already know) to spare memory.

    As far as I am aware, "in" and "out" works just like PS. I rarely use them with Simba, to be honest.
    Sorry, just was getting a little tired of being "talked down" to as if I was an idiot. I do appreciate the helpful nature of the community here though.

    Yeah, I also rarely use in and out as well, but good to know

  3. #28
    Join Date
    Jan 2007
    Posts
    8,876
    Mentioned
    123 Post(s)
    Quoted
    327 Post(s)

    Default

    Quote Originally Posted by stata View Post
    Sorry, just was getting a little tired of being "talked down" to as if I was an idiot. I do appreciate the helpful nature of the community here though.

    Yeah, I also rarely use in and out as well, but good to know
    I'm sorry; that was not at all the intention of my post (!)
    I think you'll have to look for a while in order to find nicer communities, tbh Welcome here

  4. #29
    Join Date
    Nov 2007
    Location
    46696E6C616E64
    Posts
    3,069
    Mentioned
    44 Post(s)
    Quoted
    302 Post(s)

    Default

    @Brandon;

    How would I do something like this:
    Simba Code:
    type
      PTest = ^TTest;

      TTest = record
        __object: Integer;
        __parent: PTest;
      end;

    function TTest.create(obj: Integer): TTest;
    begin
      result.__object := obj;
      if (obj <> 100) then
      begin
        result.__parent := @TTest.create(100);
      end;
    end;

    function TTest.test: Integer;
    begin
      writeln(self.__parent^);
      result := self.__parent^.__object;
    end;

    var
      test: TTest;

    begin
      test := TTest.create(50);
      writeln(test.__object);
      writeln(test.__parent^);
      writeln(test.__parent^.__object);
      writeln(test.test());
    end.
    As it is currently, the pointer to the parent is invalid after exiting TTest.create function.
    There used to be something meaningful here.

  5. #30
    Join Date
    Oct 2006
    Location
    Netherlands
    Posts
    3,285
    Mentioned
    105 Post(s)
    Quoted
    494 Post(s)

    Default

    Quote Originally Posted by Frement View Post
    As it is currently, the pointer to the parent is invalid after exiting TTest.create function.
    These aren't objects like in java/c++. I think that when the new created TTest goes out of scope it will be gone.
    Working on: Tithe Farmer

  6. #31
    Join Date
    Nov 2007
    Location
    46696E6C616E64
    Posts
    3,069
    Mentioned
    44 Post(s)
    Quoted
    302 Post(s)

    Default

    Quote Originally Posted by masterBB View Post
    These aren't objects like in java/c++. I think that when the new created TTest goes out of scope it will be gone.
    Yes, and I need a solution for that issue
    There used to be something meaningful here.

  7. #32
    Join Date
    Jan 2007
    Posts
    8,876
    Mentioned
    123 Post(s)
    Quoted
    327 Post(s)

    Default

    @Frement; You could take a look in the SRL-6 include if you want to discover some of lape's functionality.
    Simba Code:
    type
      PTest = ^TTest;
      TTest = record
        __object: Integer;
        __parent: PTest;
      end;

    procedure TTest.create(obj: Integer; parent : PTest);
    begin
      self.__object := obj;
      self.__parent := parent;
    end;

    function TTest.test: Integer;
    begin
      writeln(self.__parent^);
      result := self.__parent^.__object;
    end;

    var
      test: TTest;
      parent : TTest;

    begin
      parent.create(50, nil);
      test.create(100, @parent);
      writeln(test);
      writeLn(test.__parent^);
    end.
    Code:
    Compiled successfully in 703 ms.
    {__OBJECT = 100, __PARENT = "parent"::0x96B9AD0 ({__OBJECT = 50, __PARENT = nil})}
    {__OBJECT = 50, __PARENT = nil}
    Successfully executed.

  8. #33
    Join Date
    Nov 2007
    Location
    46696E6C616E64
    Posts
    3,069
    Mentioned
    44 Post(s)
    Quoted
    302 Post(s)

    Default

    Quote Originally Posted by Zyt3x View Post
    @Frement; You could take a look in the SRL-6 include if you want to discover some of lape's functionality.
    Simba Code:
    type
      PTest = ^TTest;
      TTest = record
        __object: Integer;
        __parent: PTest;
      end;

    procedure TTest.create(obj: Integer; parent : PTest);
    begin
      self.__object := obj;
      self.__parent := parent;
    end;

    function TTest.test: Integer;
    begin
      writeln(self.__parent^);
      result := self.__parent^.__object;
    end;

    var
      test: TTest;
      parent : TTest;

    begin
      parent.create(50, nil);
      test.create(100, @parent);
      writeln(test);
      writeLn(test.__parent^);
    end.
    Code:
    Compiled successfully in 703 ms.
    {__OBJECT = 100, __PARENT = "parent"::0x96B9AD0 ({__OBJECT = 50, __PARENT = nil})}
    {__OBJECT = 50, __PARENT = nil}
    Successfully executed.
    I did try to look in SRL-6 but didn't find anything I could apply to this situation, and the code you have there, isn't what I want, the create() function needs to create the parent.
    There used to be something meaningful here.

  9. #34
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by Frement View Post
    Yes, and I need a solution for that issue

    There is simply undefined behaviour by accessing stack allocated objects.. You need a method of allocating on the heap while not destroying garbage collection. Your structure looks like a singly-linked-list but that isn't possible in Simba without hacks/tricks.

    Problem:
    Simba Code:
    type
      PTest = ^TTest;

      TTest = record
        __object: Integer;
        __parent: PTest;
      end;

    Function TTest.Create(Obj: Integer): TTest;
    Begin
      Result.__object := Obj;
      writeln('Constructed: ', Obj);

      If (Obj <> 100) Then
        Result.__parent := @(TTest.Create(100));

      writeln('My parent is: ', Result.__Parent);
      Writeln('Unwinding..');
    End;


    var
      test: TTest;
    begin
      test := TTest.Create(50);
      writeln();

      writeln('Parent value: ', test.__parent^.__object);
      writeln('Parent: ', test.__parent^);
      writeln('Parent value: ', test.__parent^.__object);
    end.

    Progress Report:
    Compiled successfully in 203 ms.
    Constructed: 50
    Constructed: 100
    My parent is: nil
    Unwinding..
    My parent is: 0x911B850 ({__OBJECT = 100, __PARENT = nil})
    Unwinding..
    
    Parent value: 100
    Parent: {__OBJECT = 100, __PARENT = nil ({__OBJECT = 543975790, __PARENT = 0x43450028})}
    Parent value: 152156224 //UNDEFINED BEHAVIOUR..
    Successfully executed.


    In the above, Test.Create is called recursively. The first call is to create the current object that will be returned. If the parameter is NOT 100, then the stack is pushed down to save the return value. Finally, it calls itself again to allocate an object on the current stack. When the unwind section is hit, the stack unwinds/pops and the object created is destroyed. It should print nil.

    When unwind is called again, the value of __parent should be undefined/random/nil because the pushed down stack is now popped and the result is returned. That result has nothing allocated to it. It isn't initialized to nil or 0 either..


    Why does the object get destroyed during stack unwinding? Stack unwinding is how functions destroy local variables.. The memory is reclaimed upon stack unwinding. This is why we never have to "free" stack variables. This technique is known as setting up a stack-frame in assembly.


    The trick to getting around this is to allocate memory on the heap. Memory on the heap lives until it is freed by the user manually.

    There is ONLY ONE way to do that in Lape (dynamic arrays) and since there is garbage collection, even heap allocations get destroyed when their reference count reaches 0.

    So how do we trick/hack this into leaving the memory alone? Well.. we simply allocate a large enough byte array and set is reference count to more than 1 so that it survives the garbage collection upon stack unwinding.. We cast this memory to our structure type and store what we want in it. When the stack unwinds once, the reference count will drop to 1. When the script terminates, that reference count will hit 0 and the heap will be de-allocated automatically. Rarely should you ever use a reference count greater than 2! If you do, you must free the heap allocation yourself!

    With the above technique of keeping references at a max of 2, we don't leak memory AND we get to keep garbage collection without messing stuff up as well as surviving stack unwinding..


    Solution:
    Simba Code:
    type
      PTest = ^TTest;

      TTest = record
        __object: Integer;
        __parent: PTest;
      end;



    Function HeapAlloc(Size, ReferenceCount: UInt32): Array Of Byte;
    type
      PUInt32 = ^UInt32;
    Begin
      SetLength(Result, Size);
      (PUInt32(@Result[0]) - (sizeof(UInt32) * 2))^ := ReferenceCount;
    End;

    Procedure HeapFree(var Arr: Array Of Byte);      //Only call if reference count > 2.
    type
      PUInt32 = ^UInt32;
    Begin
      (PUInt32(@Arr[0]) - (sizeof(UInt32) * 2))^ := 0;
      SetLength(Arr, 0);
    End;

    Function TTest.Create(Obj: Integer): TTest;
    var
      P: ^Byte;
    Begin
      Result.__object := Obj;
      writeln('Constructed: ', Obj);

      If (Obj <> 100) Then
      begin
        P := @HeapAlloc(SizeOf(Self), 2)[0];  //Will only survive stack deallocation ONCE! 2 - 1..
        PTest(P)^ := TTest.Create(100);
        Result.__parent := PTest(P);
      end;

      writeln('My parent is: ', Result.__Parent);
      Writeln('Unwinding..');
    End;


    type
      PUInt32 = ^UInt32;
      P = Array of Byte;
      PP = ^P;

    var
      test: TTest;
    begin
      test := TTest.Create(50);
      writeln();

      writeln('Parent value: ', test.__parent^.__object);
      writeln('Parent: ', test.__parent^);
      writeln('Parent value: ', test.__parent^.__object);

      writeln('Reference Count: ', (PUInt32(test.__parent) - (sizeof(Int32) * 2))^);  //Should print 1 before script termination! Otherwise call HeapFree!
    end.

    Progress Report:
    Compiled successfully in 719 ms.
    Constructed: 50
    Constructed: 100
    My parent is: nil
    Unwinding..
    My parent is: 0x8EA9058 ({__OBJECT = 100, __PARENT = nil})
    Unwinding..
    
    Parent value: 100
    Parent: {__OBJECT = 100, __PARENT = nil}
    Parent value: 100
    Reference Count: 1
    Successfully executed.



    Additional sweetness to the allocation and de-allocation routines.. Instead of returning array of bytes and having to cast, you can return an underlying opaque pointer to the memory:

    Simba Code:
    Function HeapAlloc(Size, ReferenceCount: UInt32): Pointer;
    type
      PUInt32 = ^UInt32;
    var
      Memory: Array Of Byte;
    Begin
      SetLength(Memory, Size);
      (PUInt32(@Memory[0]) - (sizeof(UInt32) * 2))^ := ReferenceCount;
      Result := @Memory[0];
    End;

    Procedure HeapFree(var Memory: Pointer);  //Only call if ReferenceCount > 2.
    type
      PUInt32 = ^UInt32;
      Ptr = Array Of Byte;
    Begin
      (PUInt32(@Ptr(Memory)[0]) - (sizeof(UInt32) * 2))^ := 0;
      SetLength(Ptr(Memory), 0);
    End;

    Function TTest.Create(Obj: Integer): TTest;
    var
      Mem: Pointer;
    Begin
      Result.__object := Obj;
      writeln('Constructed: ', Obj);

      If (Obj <> 100) Then
      begin
        Mem := HeapAlloc(SizeOf(Self), 2);
        PTest(Mem)^ := TTest.Create(100);
        Result.__parent := PTest(Mem);
      end;

      writeln('My parent is: ', Result.__Parent);
      Writeln('Unwinding..');
    End;

    NOTES: You might never ever actually have to call HeapFree. If you think about it, no matter what amount of reference counts an array has, it is always freed in Lape. That function is just there for safety and paranoia.
    Last edited by Brandon; 04-04-2014 at 06:17 PM.
    I am Ggzz..
    Hackintosher

  10. #35
    Join Date
    Nov 2007
    Location
    46696E6C616E64
    Posts
    3,069
    Mentioned
    44 Post(s)
    Quoted
    302 Post(s)

    Default

    Yeah, exactly what I wanted, thanks Also your explanations are always so fully detailed.
    There used to be something meaningful here.

  11. #36
    Join Date
    Mar 2013
    Posts
    13
    Mentioned
    0 Post(s)
    Quoted
    2 Post(s)

    Default

    Really nice tutorial, will look into it later!

  12. #37
    Join Date
    Dec 2007
    Posts
    289
    Mentioned
    4 Post(s)
    Quoted
    86 Post(s)

    Default

    This was really useful, thank you.

    The discussion on methods of declaration was especially useful.
    It definitely dug me out of a hole!

  13. #38
    Join Date
    Sep 2016
    Posts
    41
    Mentioned
    0 Post(s)
    Quoted
    14 Post(s)

    Default

    nice

  14. #39
    Join Date
    Sep 2016
    Location
    the Simba forums
    Posts
    25
    Mentioned
    1 Post(s)
    Quoted
    1 Post(s)

    Default

    This is an AWESOME tutorial. Thank you so much.

  15. #40
    Join Date
    Sep 2014
    Location
    C:\Simba\
    Posts
    565
    Mentioned
    9 Post(s)
    Quoted
    71 Post(s)

    Default

    What are Records @poobear12;

    Edit: Good job, you're learning!
    Last edited by Joopi; 10-04-2016 at 05:23 PM.
    Feel free to ask me any questions, I will do my best to answer them!

    Previously known as YouPee.

  16. #41
    Join Date
    Sep 2016
    Location
    the Simba forums
    Posts
    25
    Mentioned
    1 Post(s)
    Quoted
    1 Post(s)

    Default

    Quote Originally Posted by Joopi View Post
    What are Records @poobear12;
    Records as far as I understand are information blocks that hold information blocks. Kind of like storing books in a book shelf for more logical ordering of info.

    This is in my own words! I hope you like my explanation haha.

Page 2 of 2 FirstFirst 12

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •