Forum > Pas2JS

Problem with for in loop and TList

(1/2) > >>

dubst3pp4:
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"
--- End quote ---

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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---program example; {$mode objfpc} uses  browserconsole, JS, Classes, SysUtils, Web,  testclasses; var  unknown, manfred: TPerson;  somePerson: Pointer;  persons: TList; begin   persons := TList.Create;   unknown := TPerson.Create;  persons.Add(unknown);   manfred := TPerson.Create('Manfred', 1802);  persons.Add(manfred);   for somePerson in persons do  begin    console.table(TJSObject(somePerson));  end; end. 
where TPerson is defined like this:


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit testclasses; {$mode objfpc} interface uses  Classes, SysUtils; type  TPerson = class  private    FName: string;    FYearOfBirth: smallint;  public    constructor Create;    constructor Create(aName: string; aYearOfBirth: smallint);    property Name: string read FName write FName;    property YearOfBirth: smallint read FYearOfBirth write FYearOfBirth;  end; implementation constructor TPerson.Create;begin  Name := 'Unknown';  FYearOfBirth := 1970;end; constructor TPerson.Create(aName: string; aYearOfBirth: smallint);begin  Name := aName;  YearOfBirth := aYearOfBirth;end; end. 
Thanks in advance,
Marc

HeavyUser:

--- Quote from: dubst3pp4 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"
--- End quote ---

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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---program example; {$mode objfpc} uses  browserconsole, JS, Classes, SysUtils, Web,  testclasses; var  unknown, manfred: TPerson;  somePerson: Pointer;  persons: TList; begin   persons := TList.Create;   unknown := TPerson.Create;  persons.Add(unknown);   manfred := TPerson.Create('Manfred', 1802);  persons.Add(manfred);   for somePerson in persons do  begin    console.table(TJSObject(somePerson));  end; end. 
where TPerson is defined like this:


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit testclasses; {$mode objfpc} interface uses  Classes, SysUtils; type  TPerson = class  private    FName: string;    FYearOfBirth: smallint;  public    constructor Create;    constructor Create(aName: string; aYearOfBirth: smallint);    property Name: string read FName write FName;    property YearOfBirth: smallint read FYearOfBirth write FYearOfBirth;  end; implementation constructor TPerson.Create;begin  Name := 'Unknown';  FYearOfBirth := 1970;end; constructor TPerson.Create(aName: string; aYearOfBirth: smallint);begin  Name := aName;  YearOfBirth := aYearOfBirth;end; end. 
Thanks in advance,
Marc

--- End quote ---

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:
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!

Akira1364:
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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---type   TClassOneList = specialize TObjectList<TClassOne>;  TClassTwoList = specialize TObjectList<TClassTwo>;  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.

dubst3pp4:

--- Quote from: Akira1364 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.

--- End quote ---
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

Navigation

[0] Message Index

[#] Next page

Go to full version