Recent

Author Topic: Using variable content as a name/command (macro substitution)  (Read 11084 times)

RAW

  • Hero Member
  • *****
  • Posts: 868
Re: Using variable content as a name/command (macro substitution)
« Reply #15 on: November 23, 2017, 04:13:52 pm »
Even the easiest ARRAY-way looks better to me than writing and extracting to/from a string....  :)

Code: Pascal  [Select][+][-]
  1. Procedure TForm1.FormClick(Sender: TObject);
  2.   Var
  3.    i: Integer;
  4.    arrGCode: Array[0..2] Of String = ('G21', 'G28', 'G30');
  5.  Begin
  6.   For i:= Low(arrGCode) To High(arrGCode)
  7.   Do GCodeExec(arrGCode[i]);
  8.  End;

If you need to call more procedures at one time....
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

Thaddy

  • Hero Member
  • *****
  • Posts: 14169
  • Probably until I exterminate Putin.
Re: Using variable content as a name/command (macro substitution)
« Reply #16 on: November 23, 2017, 05:47:48 pm »
Hey guys: it is possible to mimic a scripting language but then use RTTI..... must I explode...?
Specialize a type, not a var.

RAW

  • Hero Member
  • *****
  • Posts: 868
Re: Using variable content as a name/command (macro substitution)
« Reply #17 on: November 23, 2017, 06:48:47 pm »
Quote
.. must I explode...?
Only if you calculate it with OpenGL or DX, I like it smooth (AA, No Tearing, High Resolution) and with great effects please... Anything else hurts my eyes... (keep that in mind).
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

munair

  • Hero Member
  • *****
  • Posts: 798
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: Using variable content as a name/command (macro substitution)
« Reply #18 on: November 25, 2017, 08:20:22 pm »
@Munair: Please ask YODA if your way is "Keep it simple". I think your way is a pain and a growing pain in the future...  :)
And is that faster than a Dictionary or a HashList ???
Then your future looks VERY bleak RAW. 8-) In the meantime, go back to school. I have some homework for ya.

Do a speed test. One function loops through an array. Another function runs IF-conditions with constants. Let's do a thousand commands, from G001 to G999. Then do a 100,000 iterations searching for G999. I bet my method with constants will be no slower than about .5 seconds on an OLD computer. More importantly, you have a framework right away to define action per command. No extra procedure definition lookup required. :D :D :D

UPDATE: A speedtest revealed that the difference is not even .1 second per 100,000 iterations. I bet that in the end this approach is faster.  ;)
« Last Edit: November 26, 2017, 04:31:54 pm by Munair »
keep it simple

Nitorami

  • Sr. Member
  • ****
  • Posts: 481
Re: Using variable content as a name/command (macro substitution)
« Reply #19 on: November 26, 2017, 10:08:22 am »
I would do something along these lines. It is probably similar to what a scripting language does in the background.
The find function in the TFPHashList is rather well optimised, and fast even with very large lists.

Code: Pascal  [Select][+][-]
  1. uses sysutils, contnrs;
  2.  
  3. type NameStr = string[3];
  4.  
  5. type TMyFunc = procedure;
  6. var  ThisFunc: TMyFunc;
  7.      FuncList: TFPHashList;
  8.  
  9. procedure CallFuncByName (const fName: NameStr);
  10. begin
  11.   ThisFunc := TMyFunc(FuncList.Find(fName));
  12.   if ThisFunc <> nil then ThisFunc;  //else throw an error !
  13. end;
  14.  
  15. procedure CallManyFuncsByNames (const fNames: array of NameStr);
  16. var s: namestr;
  17. begin
  18.   for s in fNames do begin
  19.     ThisFunc := TMyFunc(FuncList.Find(s));
  20.     if ThisFunc <> nil then ThisFunc;  //else throw an error !
  21.   end;
  22. end;
  23.  
  24. //Define the required functions
  25. procedure DoG20; begin  writeln ('Execute G20'); end;
  26. procedure DoG21; begin  writeln ('Execute G21'); end;
  27. procedure DoG22; begin  writeln ('Execute G22'); end;
  28. procedure DoG23; begin  writeln ('Execute G23'); end;
  29. procedure DoG24; begin  writeln ('Execute G24'); end;
  30. procedure DoG25; begin  writeln ('Execute G25'); end;
  31.  
  32. begin
  33.   FuncList := TFPHashList.Create;
  34.  
  35. //Add all functions to the hash list
  36.   FuncList.Add ('G20',@DoG20);
  37.   FuncList.Add ('G21',@DoG21);
  38.   FuncList.Add ('G22',@DoG22);
  39.   FuncList.Add ('G23',@DoG23);
  40.   FuncList.Add ('G24',@DoG24);
  41.   FuncList.Add ('G25',@DoG25);
  42.  
  43.  
  44. //Then call the functions as required
  45.   CallFuncByName ('G25');
  46.   CallManyFuncsByNames (['G20','G23','G21']);
  47.   FuncList.Free;
  48. end.
  49.  

RAW

  • Hero Member
  • *****
  • Posts: 868
Re: Using variable content as a name/command (macro substitution)
« Reply #20 on: November 26, 2017, 05:26:26 pm »
Quote
...In the meantime, go back to school. I have some homework for ya.
That's exactly what it is... Move your mind from "school" to "the real world" and come up with a real comparison... at least 3 ways: 1. Method Pointer, 2. TDictionary, 3. fpHashList...  instead of this...
Quote
I bet that in the end this approach is faster.  ;)
unprofessional "bet... believe"-Thing and provide a real world test!
On the other hand... who really wanna use your method for more than 10 GCodes? As I said it's a pain to set it up and a bigger pain to change something in the future!

The easiest way is the Method-Pointer-Way, because you don't have to fill a list and you don't have to free the list and there is in the future nothing to change...no matter if you delete a proc or add a new proc ... nothing else to do. So if somebody has only 50 or 100 procedures it should be very fine on a modern computer, but without a good test it doesn't mean anything...



Quote
It is probably similar to what a scripting language does in the background.
A good point I guess... the so called direct "scripting language way" isn't some kind of magical hack!
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Using variable content as a name/command (macro substitution)
« Reply #21 on: November 26, 2017, 06:35:55 pm »
Do a speed test. One function loops through an array. Another function runs IF-conditions with constants. Let's do a thousand commands, from G001 to G999. Then do a 100,000 iterations searching for G999. I bet my method with constants will be no slower than about .5 seconds on an OLD computer. More importantly, you have a framework right away to define action per command. No extra procedure definition lookup required. :D :D :D

UPDATE: A speedtest revealed that the difference is not even .1 second per 100,000 iterations. I bet that in the end this approach is faster.  ;)
Of course but your code is difficult to maintain/expand. You use the old computer argument so you also know that there is a trade off between size and speed. The fastest way would be to use the numeric value of the G code as an index to a jump table. In comparison to your if's that is also easier to maintain.

RAW

  • Hero Member
  • *****
  • Posts: 868
Re: Using variable content as a name/command (macro substitution)
« Reply #22 on: November 26, 2017, 08:15:24 pm »
Just for fun....

Unfortunately my first test was nonsense and I couldn't see it right away...  :)
Here is my second try...

SYSTEM
=======================
Celeron M430 1,73 Ghz
RAM: 504 MB (800Mhz)
GPU: Intel 945 Mobile Express Chip
XP SP3
=======================

ARRAY OF PROCEDURE: 100.000 Iterations = 28 ms
METHOD POINTER = 280 ms
CONST (IF THEN ELSE) = 540 ms
// 0.1... ms for executing one single proc of 50 procs. No difference here...

What's missing? : TDictionary, fpHashList... etc... (maybe on another day...). On the other hand: is it possible that there is something faster than the ProcArray???

CONCLUSION (so far): Method Pointer is very easy to handle and even on an old computer very fast...

// I was a bit in a hurry so if someone recognize any mistakes please tell me...
« Last Edit: November 28, 2017, 12:57:22 am by RAW »
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

Thaddy

  • Hero Member
  • *****
  • Posts: 14169
  • Probably until I exterminate Putin.
Re: Using variable content as a name/command (macro substitution)
« Reply #23 on: November 26, 2017, 08:44:06 pm »
 No RTTI example yet?  .....
Specialize a type, not a var.

RAW

  • Hero Member
  • *****
  • Posts: 868
Re: Using variable content as a name/command (macro substitution)
« Reply #24 on: November 26, 2017, 09:14:14 pm »
No... Found a nice example but couldn't port it 1:1 to Lazarus, I guess it was from a higher Delphi version, some types are not available in LAZARUS. But it was just a very short try... Never used RTTI so far...  :)
Maybe on another boring sunday...
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

munair

  • Hero Member
  • *****
  • Posts: 798
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: Using variable content as a name/command (macro substitution)
« Reply #25 on: November 26, 2017, 10:15:48 pm »
unprofessional "bet... believe"-Thing and provide a real world test!

Nope, it's based on what I tested thus far. Taking the inevitable overhead into account, I know my way produces faster code. No lists, no indices, no referencing... you get the picture.

On the other hand... who really wanna use your method for more than 10 GCodes? As I said it's a pain to set it up and a bigger pain to change something in the future!

I think you're exaggerating there big time. In addition to maintaining the necessary procedures/definitions you only have consts and IFs to take care of. I set up the framework 1-999 for my test within 5 minutes by dumping the definitions to text files, which can be easily adjusted to the real situation if there's no logical sequence. I would setup the procedures like that anyway.

I'm not saying this way is better, as there's always a trade-off between faster, smaller and easier code. although I usually choose what's faster.  8).
« Last Edit: November 26, 2017, 10:19:07 pm by Munair »
keep it simple

RAW

  • Hero Member
  • *****
  • Posts: 868
Re: Using variable content as a name/command (macro substitution)
« Reply #26 on: November 28, 2017, 02:21:19 am »
Quote
I think you're exaggerating there big time.
OK OK, you can of course write your own procedure or function for this...
// something like this for example... and for the constants...
Code: Pascal  [Select][+][-]
  1. Procedure MakeCodeMemoOut(Memo: TMemo; iLines: Integer);
  2.   Var
  3.    str    : String;
  4.    i, iNum: Integer;
  5.    sl     : TStringlist;
  6.  Begin
  7.   iNum:= 1;
  8.  
  9.   sl:= TStringlist.Create;
  10.    Try
  11.     For i:= 1 To iLines
  12.     Do
  13.      Begin
  14.       str:= 'If strGCode = '+#39+'G'+IntToStr(iNum)+#39+
  15.             sLineBreak+'Then '+'G'+IntToStr(iNum)+' Else';
  16.  
  17.       sl.Add(str);
  18.       Inc(iNum);
  19.      End;
  20.  
  21.     Memo.Text:= sl.Text;
  22.    Finally
  23.     sl.Free;
  24.    End;
  25.  End;
  26.  
  27.  
  28. Procedure TForm1.FormCreate(Sender: TObject);
  29.  Begin
  30.   MakeCodeMemoOut(Memo1, 50);
  31.  End;

My test with constants or with var strings is very bad = 540 ms !!!
So tell me what you are doing different ??? Where is the bug ??? I don't see it right now...
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

SymbolicFrank

  • Hero Member
  • *****
  • Posts: 1313
Re: Using variable content as a name/command (macro substitution)
« Reply #27 on: November 28, 2017, 02:31:32 pm »
G-code is a simplified programming language itself. It's a state machine.

The best way to handle it, is to write a parser with a lexical analyzer. That can hold the states as well. There are a few written in Pascal that would work.

Otherwise, if it is to display it, you can interpret it directly and draw the vectors. In that case, I would make functions that change the state and draw the vectors like LOGO, and use a long case statement with the G-codes, that call the functions.

Something like:

Code: Pascal  [Select][+][-]
  1. type
  2.   TCNCMachine = class
  3.   public
  4.     Position: TPoint;
  5.     MaxSpeed: double;
  6.     Drill: Integer;
  7.     GProgram: TStringList;
  8.     procedure SplitLine(Line: string);
  9.     function ExtractNum(Params: string): Double;
  10.     procedure Process(GCode, Params: string);
  11.     procedure HandleG(GCode, Params: string);  
  12.     procedure HandleO(GCode, Params: string);  
  13.     procedure HandleT(GCode, Params: string);  
  14.     procedure MoveTo(GCode, Params: string); overload;
  15.     procedure MoveTo(x, y: Double); overload;
  16.   end;
  17.  
  18. procedure TCNCMachine.Process(GCode, Params: string);
  19. var
  20.   CodeType: string;
  21. begin
  22.   CodeType := Copy(GCode, 1, 1);
  23.  
  24.   case CodeType of
  25.     'G': HandleG(GCode, Params);
  26.     'O': HandleO(GCode, Params);
  27.     'T': HandleT(GCode, Params);
  28.     'X', 'Y': MoveTo(GCode, Params);
  29.   end;
  30. end;
  31.  
  32. procedure TCNCMachine.HandleG(GCode, Params: string);
  33. begin
  34.   case GCode of
  35.     'G28':
  36.     begin
  37.       Position.SetLocation(0, 0);
  38.     end;
  39.     'G50':
  40.     begin
  41.       MaxSpeed := ExtractNum(Params);
  42.     end;
  43.     else ShowMessage('Error!');
  44.   end;
  45. end;
« Last Edit: November 28, 2017, 09:23:17 pm by SymbolicFrank »

munair

  • Hero Member
  • *****
  • Posts: 798
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: Using variable content as a name/command (macro substitution)
« Reply #28 on: December 01, 2017, 11:35:55 pm »
My test with constants or with var strings is very bad = 540 ms !!!
So tell me what you are doing different ??? Where is the bug ??? I don't see it right now...
Concatenating strings makes things VERY slow. Try to avoid it as much as possible. Actually, I did the test using the FreeBASIC compiler as I was right in the middle of it, but FreePascal should give similar results. You could regard it as pseudo code if you wish:
Code: FreeBasic  [Select][+][-]
  1. #include "ifconsts.bi"
  2.  
  3. declare function Code(c as string) as integer
  4.  
  5. dim start as double
  6. dim i as integer
  7. dim c as string
  8. dim n as integer
  9.  
  10. cls
  11. c = "G999"
  12. start = timer
  13. for i = 1 to 100000
  14.         n = Code(c)
  15. next
  16. print timer - start
  17. end
  18.  
  19. function Code(c as string) as integer
  20.         static n as integer
  21.         if c < G250 then
  22.                 if c = G001 then
  23.                         n = 1
  24.                 elseif c = G002 then
  25.                         n = 2
  26.                         ...
I used three main IF blocks (250, 500, 750) which improved the test by 2 seconds. The include file simply contains the constants:
Code: FreeBasic  [Select][+][-]
  1. const G001 = "G001"
  2. const G002 = "G002"
  3. const G003 = "G003"
  4. ...
Doing the same with arrays makes the code significantly shorter but not faster:
Code: FreeBasic  [Select][+][-]
  1. #include "aconsts.bi"
  2.  
  3. declare function Code(c as string) as integer
  4.  
  5. dim start as double
  6. dim i as integer
  7. dim c as string
  8. dim n as integer
  9.  
  10. cls
  11. c = "G999"
  12. start = timer
  13. for i = 1 to 100000
  14.         n = Code(c)
  15. next
  16. print timer - start
  17. end
  18.  
  19. function Code(c as string) as integer
  20.         dim i as integer
  21.         if c < arr(250) then
  22.                 for i = 1 to 249
  23.                         if c = arr(i) then
  24.                                 return i
  25.                         end if
  26.                 next
  27.         elseif c < arr(500) then
  28.                 for i = 250 to 499
  29.                         if c = arr(i) then
  30.                                 return i
  31.                         end if
  32.                 next
  33.         elseif c < arr(750) then
  34.                 for i = 500 to 749
  35.                         if c = arr(i) then
  36.                                 return i
  37.                         end if
  38.                 next
  39.         end if
  40.         for i = 750 to 999
  41.                 if c = arr(i) then
  42.                         return i
  43.                 end if
  44.         next
  45.         return 0
  46. end function
Include file has the array definition:
Code: FreeBasic  [Select][+][-]
  1. dim shared arr(1 to 999) as string
  2.  
  3. arr(1) = "G001"
  4. arr(2) = "G002"
  5. arr(3) = "G003"
  6. ...
While the use of constants seem verbose, it is quite optimal for obvious reasons (predefined, fixed-length, etc). I'm not saying that either is the preferred way, as it is a bit more work to maintain, but it was interesting to compare speed. Yet, I would be crazy enough to do it like this if it would be faster.  :)

As I said, I dumped the definitions in a matter of minutes and the same could be done for procedures:
Code: FreeBasic  [Select][+][-]
  1. open fname for output as #handle
  2.  
  3. for i = 1 to 999
  4.         buffer = "arr(" & i & ") = " + chr(34) + "G" + format(i, "000") + chr(34)
  5.        
  6.         'buffer = format(i, "000")
  7.         'buffer = "const G" + buffer _
  8.         '+ " = " + chr(34) + "G" + buffer + chr(34)
  9.                
  10.         'buffer = "elseif c = G" + format(i, "000") + " then"
  11.         'print #handle, buffer
  12.         'buffer = "  return " & i
  13.         print #handle, buffer
  14. next
  15.  
  16. close #handle
« Last Edit: December 01, 2017, 11:50:00 pm by Munair »
keep it simple

 

TinyPortal © 2005-2018