Recent

Author Topic: Problem with for in loop and TList  (Read 2217 times)

dubst3pp4

  • Jr. Member
  • **
  • Posts: 80
  • Retro computing ~ GNU/Linux
    • me on Mastodon
Problem with for in loop and TList
« on: December 14, 2018, 03:22:11 pm »
Hello,

I'm playing around with pas2js and am stuck when iterating over a TList instance. In the documentation I read that it is possible to iterate over a TList instance with the for in loop, see https://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/utils/pas2js/docs/translation.html?view=co#enumerators.

But iterating over the TList gives me the following compiler error:

Quote
example.lpr(24,7) Error: Incompatible types: got "Pointer" expected "JSValue"

It seems like the compiler wants to loop over the properties of the JavaScript Object, not the elements of the list. But as far as I understand the documentation it should be possible this way...

My little example looks like this:

Code: Pascal  [Select]
  1. program example;
  2.  
  3. {$mode objfpc}
  4.  
  5. uses
  6.   browserconsole, JS, Classes, SysUtils, Web,
  7.   testclasses;
  8.  
  9. var
  10.   unknown, manfred: TPerson;
  11.   somePerson: Pointer;
  12.   persons: TList;
  13.  
  14. begin
  15.  
  16.   persons := TList.Create;
  17.  
  18.   unknown := TPerson.Create;
  19.   persons.Add(unknown);
  20.  
  21.   manfred := TPerson.Create('Manfred', 1802);
  22.   persons.Add(manfred);
  23.  
  24.   for somePerson in persons do
  25.   begin
  26.     console.table(TJSObject(somePerson));
  27.   end;
  28.  
  29. end.
  30.  

where TPerson is defined like this:

Code: Pascal  [Select]
  1. unit testclasses;
  2.  
  3. {$mode objfpc}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils;
  9.  
  10. type
  11.   TPerson = class
  12.   private
  13.     FName: string;
  14.     FYearOfBirth: smallint;
  15.   public
  16.     constructor Create;
  17.     constructor Create(aName: string; aYearOfBirth: smallint);
  18.     property Name: string read FName write FName;
  19.     property YearOfBirth: smallint read FYearOfBirth write FYearOfBirth;
  20.   end;
  21.  
  22. implementation
  23.  
  24. constructor TPerson.Create;
  25. begin
  26.   Name := 'Unknown';
  27.   FYearOfBirth := 1970;
  28. end;
  29.  
  30. constructor TPerson.Create(aName: string; aYearOfBirth: smallint);
  31. begin
  32.   Name := aName;
  33.   YearOfBirth := aYearOfBirth;
  34. end;
  35.  
  36. end.
  37.  

Thanks in advance,
Marc
« Last Edit: December 14, 2018, 03:24:24 pm by dubst3pp4 »
Jabber: xmpp:marc.hanisch@member.fsf.org -- Support the Free Software Foundation: https://my.fsf.org/donate

HeavyUser

  • Sr. Member
  • ****
  • Posts: 262
Re: Problem with for in loop and TList
« Reply #1 on: December 14, 2018, 04:05:48 pm »
Hello,

I'm playing around with pas2js and am stuck when iterating over a TList instance. In the documentation I read that it is possible to iterate over a TList instance with the for in loop, see https://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/utils/pas2js/docs/translation.html?view=co#enumerators.

But iterating over the TList gives me the following compiler error:

Quote
example.lpr(24,7) Error: Incompatible types: got "Pointer" expected "JSValue"

It seems like the compiler wants to loop over the properties of the JavaScript Object, not the elements of the list. But as far as I understand the documentation it should be possible this way...

My little example looks like this:

Code: Pascal  [Select]
  1. program example;
  2.  
  3. {$mode objfpc}
  4.  
  5. uses
  6.   browserconsole, JS, Classes, SysUtils, Web,
  7.   testclasses;
  8.  
  9. var
  10.   unknown, manfred: TPerson;
  11.   somePerson: Pointer;
  12.   persons: TList;
  13.  
  14. begin
  15.  
  16.   persons := TList.Create;
  17.  
  18.   unknown := TPerson.Create;
  19.   persons.Add(unknown);
  20.  
  21.   manfred := TPerson.Create('Manfred', 1802);
  22.   persons.Add(manfred);
  23.  
  24.   for somePerson in persons do
  25.   begin
  26.     console.table(TJSObject(somePerson));
  27.   end;
  28.  
  29. end.
  30.  

where TPerson is defined like this:

Code: Pascal  [Select]
  1. unit testclasses;
  2.  
  3. {$mode objfpc}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils;
  9.  
  10. type
  11.   TPerson = class
  12.   private
  13.     FName: string;
  14.     FYearOfBirth: smallint;
  15.   public
  16.     constructor Create;
  17.     constructor Create(aName: string; aYearOfBirth: smallint);
  18.     property Name: string read FName write FName;
  19.     property YearOfBirth: smallint read FYearOfBirth write FYearOfBirth;
  20.   end;
  21.  
  22. implementation
  23.  
  24. constructor TPerson.Create;
  25. begin
  26.   Name := 'Unknown';
  27.   FYearOfBirth := 1970;
  28. end;
  29.  
  30. constructor TPerson.Create(aName: string; aYearOfBirth: smallint);
  31. begin
  32.   Name := aName;
  33.   YearOfBirth := aYearOfBirth;
  34. end;
  35.  
  36. end.
  37.  

Thanks in advance,
Marc

the "for in" construct requires that the developers of containers add support for it. In the rtl all containers have a getEnumerator method that returns an enumerator object with with very specific requirements (for more info take a closer look for your self). One of those is the property current with a type that must much the type of the variable in the for part of the for in construct. the Tlist enumerator returns a pointer you need to define a new class and a new getEnumerator method that will return an enumerator with the correct type of for the current property and you need to do that for all the different types of lists you might need.

dubst3pp4

  • Jr. Member
  • **
  • Posts: 80
  • Retro computing ~ GNU/Linux
    • me on Mastodon
Re: Problem with for in loop and TList
« Reply #2 on: December 14, 2018, 04:28:24 pm »
Ah, okay, this makes sense. So I was mislead by the example... I will try to implement the needed methods during the weekend. Thanks for the hint!
Jabber: xmpp:marc.hanisch@member.fsf.org -- Support the Free Software Foundation: https://my.fsf.org/donate

Akira1364

  • Hero Member
  • *****
  • Posts: 530
Re: Problem with for in loop and TList
« Reply #3 on: December 15, 2018, 06:41:57 am »
You don't need to implement anything.

HeavyUser clearly thinks you're talking about how the TList enumerator works in actual FPC, not Pas2JS. "Pointer" is a valid keyword in Pas2JS but is used for essentially nothing because it's obviously meaningless in that context. More specifically, the TList in Pas2JS stores JSValues, not Pointers, as is the case for almost everything in Pas2JS.

Literally all you need to do to fix your example is change SomePerson to be a variable of type JSValue instead of type Pointer.

Also, on a broader note: TFPList, which is a drop-in replacement for TList in both real FPC and Pas2JS since it uses the same function names, is objectively a better way to go 100% of the time unless you specifically need the Notify capability of TList, as the fact that TFPList doesn't have Notify and its associated checks makes it quite a bit faster.

In actual FPC though I'd highly recommend just using Generics.Collections or at the very least FGL, as then you can simply do stuff like:

Code: Pascal  [Select]
  1. type
  2.   TClassOneList = specialize TObjectList<TClassOne>;
  3.   TClassTwoList = specialize TObjectList<TClassTwo>;
  4.   TClassThreeList = specialize TObjectList<TClassThree>;

Which gives you three strongly-typed fully-implemented list classes instantly, with no ugly typecasting of void pointers required at any point.
« Last Edit: December 15, 2018, 05:25:32 pm by Akira1364 »

dubst3pp4

  • Jr. Member
  • **
  • Posts: 80
  • Retro computing ~ GNU/Linux
    • me on Mastodon
Re: Problem with for in loop and TList
« Reply #4 on: December 17, 2018, 09:53:08 am »
You don't need to implement anything.

HeavyUser clearly thinks you're talking about how the TList enumerator works in actual FPC, not Pas2JS. "Pointer" is a valid keyword in Pas2JS but is used for essentially nothing because it's obviously meaningless in that context. More specifically, the TList in Pas2JS stores JSValues, not Pointers, as is the case for almost everything in Pas2JS.
Oh, that's interesting, thanks for the clarification. But what I don't like about this approach is that you thereby have to write platform specific code. I want to write some base classes that I can use on the client (JavaScript target) and on the server (x86 target). Is there a way to avoid compiler switches here?

Best regards,
Marc
Jabber: xmpp:marc.hanisch@member.fsf.org -- Support the Free Software Foundation: https://my.fsf.org/donate

Thaddy

  • Hero Member
  • *****
  • Posts: 8965
Re: Problem with for in loop and TList
« Reply #5 on: December 17, 2018, 11:09:49 am »
Yes. Write code that doesn't use any of the documented limitations of Pas2Js.
Usually that is perfectly possible.

Note: also the other way around: Pas2Js has features like e.g. anonymous methods (in trunk) that are not yet available in FPC core.
« Last Edit: December 17, 2018, 11:12:10 am by Thaddy »
Most people that want to use threading should learn to patch their jeans first: use a needle.

Akira1364

  • Hero Member
  • *****
  • Posts: 530
Re: Problem with for in loop and TList
« Reply #6 on: January 02, 2019, 05:23:20 pm »
You don't need to implement anything.

HeavyUser clearly thinks you're talking about how the TList enumerator works in actual FPC, not Pas2JS. "Pointer" is a valid keyword in Pas2JS but is used for essentially nothing because it's obviously meaningless in that context. More specifically, the TList in Pas2JS stores JSValues, not Pointers, as is the case for almost everything in Pas2JS.
Oh, that's interesting, thanks for the clarification. But what I don't like about this approach is that you thereby have to write platform specific code. I want to write some base classes that I can use on the client (JavaScript target) and on the server (x86 target). Is there a way to avoid compiler switches here?

Best regards,
Marc

I mean, you could probably get around it with something as simple as:

Code: Pascal  [Select]
  1. {$ifdef FPC}
  2. type ListItem = Pointer;
  3. {$else}
  4. type ListItem = JSValue;
  5. {$endif}

And then just use ListItem as the type specifier for variables.

mattias

  • Administrator
  • Full Member
  • *
  • Posts: 139
    • http://www.lazarus.freepascal.org
Re: Problem with for in loop and TList
« Reply #7 on: February 18, 2019, 06:33:26 pm »
At the time when TList was added to the pas2js rtl, the "pointer" type did not exist, so jsvalue was used.
If TList would be introduced today it would use pointer.
What do you think, should it be changed to pointer for compatibility?