Skip to content

Commit

Permalink
Need modern Pascal version (anonymous functions section, fix generics…
Browse files Browse the repository at this point in the history
…_sorting for new FPC)
  • Loading branch information
michaliskambi committed Jul 1, 2024
1 parent 847994c commit dd39b9b
Show file tree
Hide file tree
Showing 13 changed files with 1,099 additions and 12 deletions.
29 changes: 21 additions & 8 deletions htdocs/doc/modern_pascal.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@
include::common.adoc[]
:description: Modern Object Pascal Introduction: units, classes, generics, memory management, exceptions and more.
:cge-social-share-image: pascal_code_sample.png
// These tags were specified in https://github.com/michaliskambi/modern-pascal-introduction/ , they don't matter anymore for CGE page.
// Michalis Kamburelis
// include::locale/attributes.adoc[]
// :toc: left
// :toclevels: 4
// :sectnums:
// :source-highlighter: coderay
// :docinfo1:

## Why

Expand Down Expand Up @@ -1915,6 +1907,27 @@ Unfortunately, you need to write ugly `@TMyClass(nil).Add` instead of just `@TMy

* A (possibly) local routine: declare with `is nested` at the end, and make sure to use `{$modeswitch nestedprocvars}` directive for the code. These go hand-in-hand with <<Local (nested) routines>>.

### Anonymous functions

Delphi and new FPC versions (>= 3.3.1) support anynomous functions.

Example:

[source,pascal]
----
include::modern_pascal_code_samples/anon_functions_list_map_foreach.dpr[]
----

More information:

- Delphi documentation: https://docwiki.embarcadero.com/RADStudio/Sydney/en/Anonymous_Methods_in_Delphi

- FPC forum post: https://forum.lazarus.freepascal.org/index.php/topic,59468.0.html

- FPC feature changelog: https://wiki.freepascal.org/FPC_New_Features_Trunk#Support_for_Function_References_and_Anonymous_Functions

To get FPC 3.3.1, we recommend to use FpcUpDeluxe: https://castle-engine.io/fpcupdeluxe .

### Generics

A powerful feature of any modern language. The definition of something (typically, of a class) can be parameterized with another type. The most typical example is when you need to create a container (a list, dictionary, tree, graph...): you can define _a list of type T_, and then _specialize_ it to instantly get _a list of integers_, _a list of strings_, _a list of TMyRecord_, and so on.
Expand Down
11 changes: 8 additions & 3 deletions htdocs/doc/modern_pascal_code_samples/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
UNIT_SOURCES:=$(wildcard *.pas)
PROGRAM_SOURCES:=$(wildcard *.lpr)
PROGRAM_BINARIES:=$(PROGRAM_SOURCES:.lpr=)
PROGRAM_SOURCES:=$(wildcard *.lpr) $(wildcard *.dpr)

PROGRAM_UNIX_BINARIES:=$(PROGRAM_SOURCES:.lpr=)
PROGRAM_UNIX_BINARIES:=$(PROGRAM_UNIX_BINARIES:.dpr=)

PROGRAM_WINDOWS_BINARIES:=$(PROGRAM_SOURCES:.lpr=.exe)
PROGRAM_WINDOWS_BINARIES:=$(PROGRAM_WINDOWS_BINARIES:.dpr=.exe)

.PHONY: all
all:
Expand All @@ -9,4 +14,4 @@ all:

.PHONY: clean
clean:
rm -Rf *.o *.ppu $(PROGRAM_BINARIES)
rm -Rf *.o *.ppu $(PROGRAM_UNIX_BINARIES) $(PROGRAM_WINDOWS_BINARIES)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{ Working example adjusted from
https://forum.lazarus.freepascal.org/index.php?topic=59468.0 }

{$ifdef FPC}
{$mode objfpc}{$H+}{$J-}
{$modeswitch functionreferences}
{$modeswitch anonymousfunctions}
{$endif}

uses Classes;

type
TFunc = function: LongInt;

var
p: TProcedure;
f: TFunc;
n: TNotifyEvent;
begin
procedure(const aArg: String)
begin
Writeln(aArg);
end('Hello World');

p := procedure
begin
Writeln('Foobar');
end;
p();

n := procedure(aSender: TObject)
begin
Writeln(HexStr(Pointer(aSender)));
end;
n(Nil);

f := function MyRes : LongInt
begin
MyRes := 42;
end;
Writeln(f());
end.
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
{ Example of Map, ForEach methods and processing list with anonymous functions. }

{$ifdef FPC}
{$mode objfpc}{$H+}{$J-}
{$modeswitch functionreferences}
{$modeswitch anonymousfunctions}
{$endif}

uses SysUtils, Generics.Collections;

type
{ Note about below TIntMapFunc and TIntMapProc definition, what to use?
For anonymous functions all 3 versions will compile.
You can assign anonymous function to any of them.
Decide of the version based on what you want to assign to them *aside*
from anonymous functions:
- The 1st version (without "of object", without "reference to")
allows to store a reference to a global function,
- The 2nd (with "of object")
allows to store a reference to a method of an object,
- The 3rd (with "reference to") is the most universal,
allows a lot of things --
see https://forum.lazarus.freepascal.org/index.php?topic=59468.0 .
}

TIntMapFunc =
//function(const Index, Item: Integer): Integer;
//function(const Index, Item: Integer): Integer of object;
reference to function(const Index, Item: Integer): Integer;
TIntMapProc =
//procedure(const Index, Item: Integer);
//procedure(const Index, Item: Integer) of object;
reference to procedure(const Index, Item: Integer);

TMyInts = class(specialize TList<Integer>)
{ Change every item in the list using AFunc. }
procedure Map(const AFunc: TIntMapFunc);
{ Call AProc for every item in the list. }
procedure ForEach(const AProc: TIntMapProc);
end;

procedure TMyInts.Map(const AFunc: TIntMapFunc);
var
Index: Integer;
begin
for Index := 0 to Count - 1 do
Items[Index] := AFunc(Index, Items[Index]);
end;

procedure TMyInts.ForEach(const AProc: TIntMapProc);
var
Index: Integer;
begin
for Index := 0 to Count - 1 do
AProc(Index, Items[Index]);
end;

var
MyList: TMyInts;
I: Integer;
F: TIntMapFunc;
begin
MyList := TMyInts.Create;
try
for I := 0 to 10 do
MyList.Add(I);

F := function(const Index, Item: Integer): Integer
begin
Result := Item + 1;
end;
// effectively this increases all numbers on the list by 3
MyList.Map(F);
MyList.Map(F);
MyList.Map(F);

// change all items to their squares
MyList.Map(function(const Index, Item: Integer): Integer
begin
Result := Item * Item;
end);

// print all items
MyList.ForEach(procedure(const Index, Item: Integer)
begin
WriteLn('Index: ', Index, ', Item: ', Item);
end);
finally FreeAndNil(MyList) end;
end.
48 changes: 48 additions & 0 deletions htdocs/doc/modern_pascal_code_samples/anonymous_functions.dpr
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{ Anonymous functions test. }

{$ifdef FPC}
{$mode objfpc}{$H+}{$J-}
{$modeswitch functionreferences}
{$modeswitch anonymousfunctions}
{$endif}
{$apptype CONSOLE}

type
TMyFunction = reference to function (const A, B: Integer): Integer;

function ProcessTheList(const F: TMyFunction): Integer;
var
I: Integer;
begin
Result := 1;
for I := 2 to 10 do
Result := F(Result, I);
end;

var
SomeFunction: TMyFunction;
begin
// Assign anonymous function to a variable
SomeFunction :=
function(const A, B: Integer): Integer
begin
Result := A + B;
end;
WriteLn('1 + 2 + 3 ... + 10 = ', ProcessTheList(SomeFunction));

// Or, simpler, just define the anonymous function inside the parameter
WriteLn('1 + 2 + 3 ... + 10 = ', ProcessTheList(
function(const A, B: Integer): Integer
begin
Result := A + B;
end
));

// Similar test, now with multiplication
WriteLn('1 * 2 * 3 ... * 10 = ', ProcessTheList(
function(const A, B: Integer): Integer
begin
Result := A * B;
end
));
end.
Loading

0 comments on commit dd39b9b

Please sign in to comment.