Recent

Author Topic: Using callback  (Read 523 times)

LemonParty

  • Hero Member
  • *****
  • Posts: 554
Using callback
« on: July 06, 2026, 06:50:21 pm »
Hello.
Code: Pascal  [Select][+][-]
  1.   TQWordPair = array [0..1] of QWord;
  2.   TQWordPairs = array of TQWordPair;
I have a TQWordPairs. TQWordPair is a pair where the first element is a position and the second is a length. Elements of TQWordPairs points to numbers in some string. I need to increment or decrement all numbers in that string on some value. I decided to create a callback that accept inside position and length of string fragment and return the pointer to another fragment of string and length of this fragment. "Master" procedure should replace fragment of string with this new fragment that pointed inside of callback and than concatinate all fragments together. For example:
Quote
"apple 100 20 10"
should became (increasing on 100)
Quote
"apple 200 120 110"
The question is how this callback should look like? What parameters should contain?
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

jamie

  • Hero Member
  • *****
  • Posts: 7823
Re: Using callback
« Reply #1 on: July 06, 2026, 08:50:29 pm »
dealing with callbacks with large data types normally is best handled via REF types, there for its pointer accessed in the background.

Jamie
The only true wisdom is knowing you know nothing

LemonParty

  • Hero Member
  • *****
  • Posts: 554
Re: Using callback
« Reply #2 on: July 06, 2026, 09:31:18 pm »
I see a few different options of callback:
Code: Pascal  [Select][+][-]
  1. type
  2.   {S - original string; Position & Len – position of current fragment; NewValue & NewValueLen – output values that points to the new fragment}
  3.   TCallback1 = procedure(S: AnsiString; Position, Len: SizeUInt; var NewValue: AnsiChar; var NewValueLen: SizeUInt);
  4.   {Should we pass S or we can omit this parameter? What will happen if we point NewValue to managed type like string?}
  5.  
  6.   {S - original string; Position & Len – position of current fragment; Result is the new fragment}
  7.   TCallback2 = function(S: AnsiString; Position, Len: SizeUInt): AnsiString;
  8.   {Again should we pass S or we can omit this parameter?}
  9.   {The disadvantage of this callback is that it produces unnecessary strings}
What is better?
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

ASerge

  • Hero Member
  • *****
  • Posts: 2510
Re: Using callback
« Reply #3 on: July 06, 2026, 09:39:29 pm »
Why do you need a callback?

Code: Pascal  [Select][+][-]
  1. {$APPTYPE CONSOLE}
  2. {$MODE OBJFPC}
  3. {$LONGSTRINGS ON}
  4.  
  5. uses SysUtils;
  6.  
  7. type
  8.   TFragment = record
  9.     Pos, Len: QWORD;
  10.   end;
  11.  
  12.   TFragments = array of TFragment;
  13.  
  14. procedure IncNumbersInStringByFragments(const Fragments: TFragments; IncBy: Integer; var InString: string);
  15. var
  16.   Fragment: TFragment;
  17.   From: SizeInt;
  18.   FromCorection: SizeInt;
  19.   NumStrOld, NumStrNew: string;
  20.   Num: Integer;
  21. begin
  22.   FromCorection := 0;
  23.   for Fragment in Fragments do
  24.   begin
  25.     From := Fragment.Pos + FromCorection;
  26.     NumStrOld := Copy(InString, From, Fragment.Len);
  27.     if TryStrToInt(NumStrOld, Num) then
  28.     begin
  29.       Inc(Num, IncBy);
  30.       NumStrNew := IntToStr(Num);
  31.       System.Delete(InString, From, Fragment.Len);
  32.       System.Insert(NumStrNew, InString, From);
  33.       Inc(FromCorection, Length(NumStrNew) - Length(NumStrOld));
  34.     end;
  35.   end;
  36. end;
  37.  
  38. var
  39.   S: string = 'apple 200 120 110';
  40.   Fragments: TFragments = ((Pos:7; Len:3), (Pos:11; Len:3), (Pos:15; Len:3));
  41. begin
  42.   Writeln(S);
  43.   IncNumbersInStringByFragments(Fragments, 888, S);
  44.   WriteLn(S);
  45.   Readln;
  46. end.

LemonParty

  • Hero Member
  • *****
  • Posts: 554
Re: Using callback
« Reply #4 on: July 07, 2026, 12:08:38 pm »
Good code, ASerge. But what if fragments point to something else than numbers? And what if we need, for example, not increase numbers but do with them something else?
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

ASerge

  • Hero Member
  • *****
  • Posts: 2510
Re: Using callback
« Reply #5 on: July 07, 2026, 08:15:00 pm »
But what if fragments point to something else than numbers? And what if we need, for example, not increase numbers but do with them something else?
The solution satisfies the task. If the task is different, formulate it.

Warfley

  • Hero Member
  • *****
  • Posts: 2070
Re: Using callback
« Reply #6 on: July 07, 2026, 09:48:11 pm »
Compiletime or runtime?

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12513
  • Debugger - SynEdit - and more
    • wiki
Re: Using callback
« Reply #7 on: July 08, 2026, 09:06:59 am »
You may want to divide the task:
1) identify the sequence to work on
2) (maybe part of 1) provide the new sequence
3) change the string

Though if done a certain way, 3 merges with the others do.

If your string is rather long (10k or 100k) and has lots of changes, then doing delete/insert (or any way of length change) repeatedly for each change is going to be slow.

In such a case you want to alloc a new string, maybe oversize it, (if need resize it every 1000 changes) and write to the new string, and cut it at the end.  (use uniquestring on the target string, if working with pchar / SetLength replaces uniquestring)

Yes you need to copy the bits between the changes, but doing an insert/delete copies the entire rest of the string every single time.

A 2 pass solution may also work, calculate the new length then alloc and make changes...

-----------------------------------

So if you need to search for the next item, your callback needs
Code: Pascal  [Select][+][-]
  1. cb = function (ACurrentPos: PChar; ARemainingLen: integer; out ANextFound: PChar; out ANextLen: integer): boolean; // result indicates if anything was found at all
  2. // Additionally returning the new length needed may help to allocate space

Your replacement code needs a location to read, and one to write
Code: Pascal  [Select][+][-]
  1. cb = procedure(AReadPos: Pchar; AReadLen: integer; AWritePos: Phcar; AwriteMaxLen: integer; out AWrittenLen: integer);
"WritePos" could point to the new over-allocated string, or a temp buffer from where you copy it into the existing string.

You can combine the 2 methods....


LemonParty

  • Hero Member
  • *****
  • Posts: 554
Re: Using callback
« Reply #8 on: July 08, 2026, 08:50:29 pm »
Great idea about calculating the size of final string. I was thinking about the same topic.
Martin_fr, I notice in your callback you don't use string in parameters. Is this because reference count hurt the performance?
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

 

TinyPortal © 2005-2018