Recent

Author Topic: Anonymous Methods  (Read 30797 times)

guest60499

  • Guest
Re: Anonymous Methods
« Reply #30 on: February 01, 2017, 04:03:09 pm »
Generics are often implemented as a form of macro expansion. It would also be correct to say the generic type has parameters; there is nothing preventing those parameters from being passed along with the type in some fashion. There is distinct benefit to avoiding runtime type checking but languages often just implement that functionality elsewhere.

From a computational point of view there is no reason not to use 31 bits or 33 bits or 65 bits or 63 bits.
It is just that the numeric system chosen for most systems favors 32 bits or 64 bits, multiples of two or dividable by two if you want.  (base 2, 10 or any base. I like base 7: http://www.purplemath.com/modules/numbbase2.htm  https://en.wikipedia.org/wiki/Senary)
But multiples of three and devidability by three has distinct advantages....as can be mathematically proven.
The first! computers were based on that (three! states per "bit") principle.

Don't ever let you find yourself to believe there is only one system: there isn't. Quantum state is even more funny and closer to real life application than you might think.

It is also ridiculous to state an opinion that 32 bit software is necessarily inferior to 64 bit software.
That is simply not true. You are lacking quite a bit of knowledge.

Like that signed integers use a resolution of 31 bits or 63 bits on your home computer ?  :o
Oh, well. Mathematics. I love it  :-[ 8-)

There is a physical reason to prefer a certain bit size. Due to the reality of how computers must be implemented it's inefficient to differentiate from more than two states. This leads to binary computing and what follows from that is a preference for numbers which are a power of two and those numbers become a natural upper limit (address lines either are or are not there, they can't partially be there). Certain values, like 31 or 63, are silly. Others like 48 (6 bytes) are less so and are used preferentially when the next power of two is "too much" but are still chosen based on their underlying representation. I understand that you can indeed have a 31 bit number - but how all of the circuitry below the operation of that number interacts is what causes the preference for numbers which neatly go into two in some way.

Ternary, or more accurately balanced ternary, was chosen when most computers were implemented mechanically or electromechanically. Electrodynamic implementations are necessarily binary as any other choice is inefficient. Balanced ternary has been reimplemented in digital logic but requires a 2-bit storage medium, one position of which is wasted (an example would be certain networking hardware).

It is possible to plainly state that 64 bit architectures are a better choice. They can more efficiently implement what is asked of a modern computer. And, currently, the architectures they are implemented on are more power efficient than any corresponding 32 bit processor of the same architecture (excepting certain ARM32 devices and their ARM64 counterparts but that will soon change). And while it's a minor point for the purposes of this discussion I feel it necessary to point out that some modern instructions can't be back ported to a 32 bit equivalent, or, if they were, new addressing modes would need to be introduced - which adds complexity and power consumption to circuits.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12314
  • FPC developer.
Re: Anonymous Methods
« Reply #31 on: February 01, 2017, 07:24:06 pm »
Generics are often implemented as a form of macro expansion.

Don't forget forward defined classes. Though it is hard to come up with an example (it is probably easier in languages that have inline code in the class declaration)

maybe something like

Code: [Select]

type

    TMytype = class;
 
    TMyCollection<T> = class
                     x: T;
                   procedure something;
                  end;

    TSomething = class
                    ffield : TMyCollection<TMytype>;
                    property xxx : integer read ffield.x.bla;   
                    end;

   TMytype = class
               bla: integer;
             end;


procedure tmycollection<t>.something;
begin
end;
begin
end.

but that doesn't work now either.


Sven, can this be made to work ?  >:D

PascalDragon

  • Hero Member
  • *****
  • Posts: 6035
  • Compiler Developer
Re: Anonymous Methods
« Reply #32 on: February 02, 2017, 07:46:07 pm »
"implements" itself is implemented, but not completely. It only works with properties having an interface as type, but not with a class. And no, it's also not "macro/meta programming" as the compiler needs to generate stubs for the interface's VMT entries that correctly adjust the self pointer and then call the specified class instance's methods. I've taken a look at it once a few years ago and decided that this is not yet amongst the features I can implement (that might have changed nowadays - I'd need to take a long again, but currently I have other problems to solve :P ).
I don't think, it's hard. When I say "macro/meta programming" - I mean simple code substitution/injection needed, i.e. this feature can be implemented via some sort of "preprocessor", that takes input source file and turns it into some output via using standard features only. Some sort of programming language inside programming language (that's, why it's called "Meta programming"). Yeah, this stubs should be optimized, but at the end they're something like that:
Code: Pascal  [Select][+][-]
  1. procedure TObject1.DoSomething(X:TSomeType);
  2. begin
  3.      Object2.DoSomething(X);
  4. end;
  5.  
Simply because Object2 should be created and therefore all we need - to redirect call to it.
In essence it comes down to this, yes, but we're talking about low level code here. An interface has a different Self parameter than its instance, so that needs to be adjusted and then the correct method needs to be put into the VMT. All that done with as less code as possible (e.g. your example would generate a full stack frame merely for calling the function while interface stubs contain the offset adjustment and a jmp instruction). But as I said, the last time I looked at that code I was far more novice regarding the compiler's internals...
Problem happens in different situations. Sometimes I add one unit test after another (3-4K lines), then at some point "Linking" becomes really slow and then starts to throw "Out of memory" errors from time to time. After adding another test - program can't be compiled. Sometimes I just add several "TGeneric.Create" (even specialized in different modules) and after 5-6 different generics (every generic adds 200-700K to size of module, implementation itself can be up to 1.5M) - "Out of memory". Sometimes it looks like some sort of bug - that's when program is compiled properly with nested generics, like TPairList<Integer, TPairList<String, TPairList<Boolean, TTargetObject>>>, but fails for TPairList<Integer, TPairList<String, TPairList<Integer, TTargetObject>>>.

Thanks God, there are ways to overcome this problems. May be it's not bad, but I have to separate generic implementations into dlls. Some are big - 5Mb for example (64bit release). 3rd version of my unit tests: helper object - 1Mb, all needed generic implementations - 1.5Mb (32bit release, 2x for 64bit versions).
Well, feel free to put FPC through its paces here  :D

He seems to have been implying that Delphi implements generics by code rewriting. His experience with the errors seems to support this.

Personally it's an extremely odd way to implement generics, but I guess it works. Coincidentally it is also one of the only times I have seen a 32 bit address space become saturated.
Generics are a form of macro and macro expansion. So yes, that is correct. It is also the ONLY KNOWN way to implement generics.
At least to people who studied compiler engineering.
I would be rather surprised if you came up with something else.... That would be less odd... maybe even even...

So he is not implying, he is correct... >:D Explicitly.

https://en.wikipedia.org/wiki/Generic_programming

In this case wiki is reliable.
If I remember correctly Delphi is not storing a token stream (like we are doing), but an abstract syntax tree (like what we're doing for inlining). That might explain 1) why they have such strict type checking while ours is relatively lax (though still better than C++'s :P ) and 2) why integer constants defined in the implementation section of the generic's unit can be used inside the generics (we can't do this due to our token stream approach; I'll need to implement a clever workaround for this).

Sven, can this be made to work ?  >:D

If you kick out the xxx property then it already works. With it it wouldn't even work without generics. Pascal is a single pass language after all...

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12314
  • FPC developer.
Re: Anonymous Methods
« Reply #33 on: February 02, 2017, 08:50:35 pm »
If you kick out the xxx property then it already works. With it it wouldn't even work without generics. Pascal is a single pass language after all...

Forward references within the same (type) block are an exception to that.

Admittedly, I was just trying to craft an exception a macroprocessor couldn't handle.

PascalDragon

  • Hero Member
  • *****
  • Posts: 6035
  • Compiler Developer
Re: Anonymous Methods
« Reply #34 on: February 02, 2017, 10:43:17 pm »
If you kick out the xxx property then it already works. With it it wouldn't even work without generics. Pascal is a single pass language after all...

Forward references within the same (type) block are an exception to that.

Admittedly, I was just trying to craft an exception a macroprocessor couldn't handle.

Forward references themself yes, but not what you did with the property there. The compiler does not know that TMyType has a field called bla, only that TMyType is a class and thus the compiler can treat it as such. Not more, not less. That has nothing to do with a macroprocessor however...

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12314
  • FPC developer.
Re: Anonymous Methods
« Reply #35 on: February 03, 2017, 02:42:38 am »
Forward references themself yes, but not what you did with the property there.
The compiler does not know that TMyType has a field called bla, only that TMyType is a class and thus the compiler can treat it as such.

Yes, I expected that, being mostly an opague type till fully defined. So I tried to make a test, had to actually think hard to come up with this meagre case.

Quote
That has nothing to do with a macroprocessor however...

Most macroprocessors are also single pass, so a lookup-within-block would have defeated it. . In retrospect it is a silly thought experiment  since a forward reference wouldn't be needed at all if it could lookup within the block after use.

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 873
Re: Anonymous Methods
« Reply #36 on: February 03, 2017, 07:54:28 am »
In essence it comes down to this, yes, but we're talking about low level code here. An interface has a different Self parameter than its instance, so that needs to be adjusted and then the correct method needs to be put into the VMT. All that done with as less code as possible (e.g. your example would generate a full stack frame merely for calling the function while interface stubs contain the offset adjustment and a jmp instruction). But as I said, the last time I looked at that code I was far more novice regarding the compiler's internals...
In case of delegation we can't use adjustment of Self (such as "ADD Self, offset VMT - offset IInterface1VMT"), as we do, when interface is implemented in current object - we need to pass Object2 as Self. So it should be simple:
Code: Pascal  [Select][+][-]
  1. procedure TObject1.DoSomething(X:TSomeType);
  2. asm
  3.     {All, we actually need - to pass the same parameters to another routine.
  4.     This can actually be done by code optimizer.
  5.     Assembler is expected to use automatic [] brackets, as TASM do for example.}
  6.     MOV Self, Self.Object2 {Self should be register}
  7.     JMP TObject2.DoSomething {In case of static method}
  8. end;
  9.  
I don't know. It's hard to implement complex features, when every feature has to have "raw" straight through implementation. It's always better, when you can implement one feature on the top of another already existing one. In this case all you would need - to have some code templates, that would be injected into program and handled by lower layer of abstraction. As I know, Delphi uses this approach. Have you seen that bunch of "code templates" in their System module? They use simple approach: use optimized assembler templates, if possible, and if not - use pure Pascal.

For example, Object2 - is actually property. Therefore it can be both static field or getter method. Also it can be object or interface. TObject2.DoSomething - can also be virtual or even dynamic method. Too many things to take into account, especially if all this code is already implemented elsewhere. It's always better to use high level template, so existing lower level of abstraction would handle all this problems for you. Delphi simply uses some inline template routines, like CallDynaMethod.

P.S. Question about Lazarus. Is assembler code debugging possible in Lazarus? When I open Debug->CPU window in Delphi - F7/F8 buttons start to work per asm instruction, like if I would debug my program in Turbo Debugger. But in Lazarus they continue to work per source line.
« Last Edit: February 03, 2017, 10:24:13 am by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

PascalDragon

  • Hero Member
  • *****
  • Posts: 6035
  • Compiler Developer
Re: Anonymous Methods
« Reply #37 on: February 03, 2017, 09:17:16 pm »
I've taken a look at the current implements implementation and unlike what I had previously stated the majority of the feature is already working: One can use an interface as property type or a class and one can even use the syntax for multiple interfaces for the same property (comma separated interface list).
The only incompatibility to Delphi however is that FPC currently requires a class type property to implement the interface given in the implements clause. Delphi on the other hand uses - as a rare instance in Object Pascal - structural equality. And in addition to that a class type property does not even need to implement all methods. So the following would be valid (and indeed compiles in Delphi):
Code: Pascal  [Select][+][-]
  1. program tdelegate;
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. type
  6.   ITest = interface
  7.     procedure Bar;
  8.     procedure Foo;
  9.   end;
  10.  
  11.   TTestBar = class
  12.     procedure Bar;
  13.   end;
  14.  
  15.   TTest = class(TInterfacedObject, ITest)
  16.   private
  17.     fBarIntf: TTestBar;
  18.     property BarIntf: TTestBar read fBarIntf implements ITest;
  19.     procedure ITest.Foo = Foobar;
  20.     procedure Foobar;
  21.   public
  22.     constructor Create;
  23.   end;
  24.  
  25. procedure TTestBar.Bar;
  26. begin
  27.  
  28. end;
  29.  
  30. constructor TTest.Create;
  31. begin
  32.   fBarIntf := TTestBar.Create;
  33. end;
  34.  
  35. procedure TTest.Foobar;
  36. begin
  37.  
  38. end;
  39.  
  40. begin
  41. end.

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 873
Re: Anonymous Methods
« Reply #38 on: February 04, 2017, 06:12:39 am »
I've taken a look at the current implements implementation and unlike what I had previously stated the majority of the feature is already working: One can use an interface as property type or a class and one can even use the syntax for multiple interfaces for the same property (comma separated interface list).
The only incompatibility to Delphi however is that FPC currently requires a class type property to implement the interface given in the implements clause. Delphi on the other hand uses - as a rare instance in Object Pascal - structural equality. And in addition to that a class type property does not even need to implement all methods.
I know, that FPC supports almost all features of "implements" and I was talking exactly about case, FPC doesn't support. In case of interface property Delphi does simple thing - it somehow appends this interface to interface table. I.e. when you use GetInterface - ImplGetter is being used. In case of direct "Interface := Class;" - value of property is being returned, instead of usual interface implementation. In this case Delphi actually does very small work. In case of partial interface implementation simple thing should be done, as I already said: by default compiler should add stubs (stubs will be needed anyway), I described above, for all interface methods, but allow programmer to "override" them.

Examples:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. uses
  6.   Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  7.   Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
  8.  
  9. type
  10.   TForm1 = class(TForm)
  11.     procedure FormCreate(Sender: TObject);
  12.   private
  13.     { Private declarations }
  14.   public
  15.     { Public declarations }
  16.   end;
  17.  
  18. var
  19.   Form1: TForm1;
  20.  
  21. implementation
  22.  
  23. {$R *.dfm}
  24.  
  25. type
  26.   IMyInterface = interface
  27.     function DoSomething(X:Integer):Integer;
  28.   end;
  29.  
  30.   TMyClass = class(TInterfacedObject, IMyInterface)
  31.     public
  32.       function DoSomething(X:Integer):Integer;
  33.   end;
  34.  
  35. function TMyClass.DoSomething(X: Integer):Integer;
  36. begin
  37.   Result := X + 1;
  38. end;
  39.  
  40. type
  41.   TMyClass2 = class(TInterfacedObject, IMyInterface)
  42.     protected
  43.       FMyInterface:IMyInterface;
  44.       property MyInterface:IMyInterface read FMyInterface implements IMyInterface;
  45.     public
  46.       constructor Create;
  47.   end;
  48.  
  49. constructor TMyClass2.Create;
  50. begin
  51.   FMyInterface := TMyClass.Create;
  52. end;
  53.  
  54. procedure TForm1.FormCreate(Sender: TObject);
  55.   var MyInterface:IMyInterface;
  56. begin
  57.   MyInterface := TMyClass2.Create;
  58.   Caption := IntToStr(MyInterface.DoSomething(2));
  59. end;
  60.  
  61. end.
  62.  

Picture 1

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. uses
  6.   Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  7.   Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
  8.  
  9. type
  10.   TForm1 = class(TForm)
  11.     procedure FormCreate(Sender: TObject);
  12.   private
  13.     { Private declarations }
  14.   public
  15.     { Public declarations }
  16.   end;
  17.  
  18. var
  19.   Form1: TForm1;
  20.  
  21. implementation
  22.  
  23. {$R *.dfm}
  24.  
  25. type
  26.   IMyInterface = interface
  27.     function DoA(X:Integer):Integer;
  28.   end;
  29.  
  30.   IMyInterface2 = interface
  31.     function DoA(X:Integer):Integer;
  32.     function DoB(X:Integer):Integer;
  33.   end;
  34.  
  35.   TMyClass = class(TInterfacedObject, IMyInterface)
  36.     public
  37.       function DoA(X:Integer):Integer;
  38.   end;
  39.  
  40. function TMyClass.DoA(X: Integer):Integer;
  41. begin
  42.   Result := X + 1;
  43. end;
  44.  
  45. type
  46.   TMyClass2 = class(TInterfacedObject, IMyInterface2)
  47.     protected
  48.       FMyClass:TMyClass;
  49.       property MyInterface:TMyClass read FMyClass implements IMyInterface2;
  50.       function DoB(X:Integer):Integer;
  51.     public
  52.       constructor Create;
  53.       destructor Destroy;override;
  54.   end;
  55.  
  56. constructor TMyClass2.Create;
  57. begin
  58.   FMyClass := TMyClass.Create;
  59. end;
  60.  
  61. destructor TMyClass2.Destroy;
  62. begin
  63.   FMyClass.Free;
  64. end;
  65.  
  66. function TMyClass2.DoB(X: Integer):Integer;
  67. begin
  68.   Result := X - 1;
  69. end;
  70.  
  71. procedure TForm1.FormCreate(Sender: TObject);
  72.   var MyInterface:IMyInterface2;
  73. begin
  74.   MyInterface := TMyClass2.Create;
  75.   Caption := IntToStr(MyInterface.DoA(2)) + ' ' + IntToStr(MyInterface.DoB(2));
  76. end;
  77.  
  78. end.
  79.  

Picture 2

Actually, full interface implementation via "implements" is enough for me - I don't use partial one anyway.

P.S. I.e. what Macro/Meta programming means? It's just syntax sugar. You can write this code by yourself, but this code is exactly the same cookie cutter code every time, so it's better to do it automatically.
« Last Edit: February 04, 2017, 06:30:24 am by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1536
    • Lebeau Software
Re: Anonymous Methods
« Reply #39 on: February 08, 2017, 11:23:55 pm »
I personally need them for my projects to be ported from Delphi to FPC, cuz I want them to be Linux compatible.

A Linux compiler for Delphi is currently in the works and is scheduled to be released sometime this year.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

rvk

  • Hero Member
  • *****
  • Posts: 6802
Re: Anonymous Methods
« Reply #40 on: February 08, 2017, 11:42:15 pm »
I personally need them for my projects to be ported from Delphi to FPC, cuz I want them to be Linux compatible.
A Linux compiler for Delphi is currently in the works and is scheduled to be released sometime this year.
I gather it will only be for the enterprise+ edition. Yeah that's not going to be for me :D They are really going to set the bar high for that one. I wonder how that's going to turn out for them (read: another Kylix :) )

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12314
  • FPC developer.
Re: Anonymous Methods
« Reply #41 on: February 08, 2017, 11:45:31 pm »
I personally need them for my projects to be ported from Delphi to FPC, cuz I want them to be Linux compatible.

A Linux compiler for Delphi is currently in the works and is scheduled to be released sometime this year.

Afaik it is only nextgen, not "real"/compatible delphi.
« Last Edit: February 08, 2017, 11:47:05 pm by marcov »

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1536
    • Lebeau Software
Re: Anonymous Methods
« Reply #42 on: February 09, 2017, 12:45:29 am »
Afaik it is only nextgen, not "real"/compatible delphi.

What do you mean?  It is a real native Delphi compiler for the Linux platform.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

jacmoe

  • Full Member
  • ***
  • Posts: 249
    • Jacmoe's Cyber SoapBox
Re: Anonymous Methods
« Reply #43 on: February 09, 2017, 12:56:45 am »
What do you mean?  It is a real native Delphi compiler for the Linux platform.
Embarcardero?
Wow, that is quite expensive..
Thank God for Lazarus/FreePascal  :D
« Last Edit: February 09, 2017, 02:03:43 am by jacmoe »
more signal - less noise


 

TinyPortal © 2005-2018