Recent

Author Topic: assigning to Array of const  (Read 2330 times)

MountainQ

  • Jr. Member
  • **
  • Posts: 65
assigning to Array of const
« on: April 10, 2021, 10:47:20 pm »
Greetings;

using 'array of const' can result in compact and readable code, just consider format().
I am wondering whether there is way to reverse that functionality; thereby assigning values to various types.
Something like:
Code: Pascal  [Select][+][-]
  1. var
  2.   SA: TStringArray;
  3.   s: string;
  4.   f: float;
  5.   k: integer;
  6. begin
  7.   SA := TStringArray.Create('some', '12.3', '45');
  8.   MagicFunction(SA, [s, f, k]);
  9. end;
  10.  
Where 'MagicFunction' loops through the arguments and converts the strings according to the required type. This problem came about when reading and writing text-based files that initialize some values.
I unsuccessfully played around with 'array of const'; the name does suggest this is not possible.
Does anybody knows for sure; are there alternative ideas?

Thanks


« Last Edit: April 11, 2021, 09:46:12 am by MountainQ »

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: assigning to Array of const
« Reply #1 on: April 10, 2021, 10:52:03 pm »
Show us your attempt at MagicFunction(), and the compiler's opinion of it.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

MountainQ

  • Jr. Member
  • **
  • Posts: 65
Re: assigning to Array of const
« Reply #2 on: April 11, 2021, 09:42:26 am »
Thanks for the quick replies; my thoughts were along those lines:
Code: Pascal  [Select][+][-]
  1. procedure MagicFct(SA: TStringArray; vars: array of const);
  2. var
  3.   i: integer;
  4. begin
  5.   for i := 0 to Length(vars)-1 do
  6.     case vars[i].vtype of
  7.       vtAnsiString:
  8.           AnsiString(vars[i].VAnsiString^) := SA[i]; // raises SIGSEV
  9.       vtInteger:
  10.           vars[i].VInteger := StrToInt(Sa[i]);
  11.       vtExtended:
  12.           vars[i].VExtended^ := StrToFloat(Sa[i]);
  13.     else
  14.       raise Exception.Create('NotImplemented: '+IntToStr(vars[i].vtype));
  15.     end;
  16. end;
  17.  
  18.  
  19.  
  20. var
  21.   SA: TStringArray;
  22.   s: string;
  23.   f: double;
  24.   k: integer;
  25. begin
  26.   SA := TStringArray.Create('123', '45.6');
  27.   s := '';
  28.   k := 0;
  29.   f := 0;
  30.   writeln(Int64(@f)); // <- actually neccessary
  31.   MagicFct(SA, [k, f]);
  32.   writeln('and then ', k, f);
  33. end;                              
  34.  
I am unsing FPC 3.2.0 (Lazarus Build 2.0.12 on Windows 10).
The code does not work; (including the string raises SIGSEV). I get the type 'double' to work if I include a line (as seen) that reads the address of the variable.

@MarkMLl: the compiler is fine with all that

At this point my knowledge ends; as I do not know the inner workings of array of const; if it represents just a copy of the variables that are provided then the it is supposed to not work.
Best to all.
« Last Edit: April 11, 2021, 09:48:10 am by MountainQ »

speter

  • Sr. Member
  • ****
  • Posts: 345
Re: assigning to Array of const
« Reply #3 on: April 11, 2021, 10:00:59 am »
Do you really need the "array of const"? If you knew that you will have (for example) an integer, a single and finally a string; your task would be far simpler...

cheers
S. ;)
I climbed mighty mountains, and saw that they were actually tiny foothills. :)

MountainQ

  • Jr. Member
  • **
  • Posts: 65
Re: assigning to Array of const
« Reply #4 on: April 11, 2021, 10:13:42 am »
@speter
Of course it is not absolutely necessary to have this construct; however to me it seems to fairly elegant in certain situations.
In my case I read and write to an init file.
Being able to distribute the values to the respective variables using an array of const would make the code more readable (at least in my opinion) and less error-prone. For example I can copy that array of const to a function that reverses that operation at the end of the program to write to init file (that last part is easy to implement).

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: assigning to Array of const
« Reply #5 on: April 11, 2021, 10:20:03 am »
The code does not work; (including the string raises SIGSEV). I get the type 'double' to work if I include a line (as seen) that reads the address of the variable.

Start off with the simplest cases: distinguishing between an integer, a real and a single char, try to avoid pointers.

Without trying to duplicate your code, I think your problem isn't the  const array  so much as your attempted access to string storage... there's "magic" associated with strings and dynamic arrays which is best not interfered with.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

alpine

  • Hero Member
  • *****
  • Posts: 1038
Re: assigning to Array of const
« Reply #6 on: April 11, 2021, 10:55:20 am »
Quote
Re: assigning to Array of const

You obviously can't. Please, consult the language docs for the meaning of 'array of const'. They're introduced for a sake of having a variable-length arguments.

However, you should consider something like a reference to an array of TVarRec if it is a parameter, or return a dynamically allocated one as a result of a function.

Regards, 
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

wp

  • Hero Member
  • *****
  • Posts: 11855
Re: assigning to Array of const
« Reply #7 on: April 11, 2021, 10:57:05 am »
Code: Pascal  [Select][+][-]
  1. procedure MagicFct(SA: TStringArray; vars: array of const);
  2. var
  3.   i: integer;
  4. begin
  5.   for i := 0 to Length(vars)-1 do
  6.     case vars[i].vtype of
  7.       vtAnsiString:
  8.           AnsiString(vars[i].VAnsiString^) := SA[i]; // raises SIGSEV
  9.       vtInteger:
  10.           vars[i].VInteger := StrToInt(Sa[i]);
  11.       vtExtended:
  12.           vars[i].VExtended^ := StrToFloat(Sa[i]);
  13.     else
  14.       raise Exception.Create('NotImplemented: '+IntToStr(vars[i].vtype));
  15.     end;
  16. end;
  17.  
  18. var
  19.   SA: TStringArray;
  20.   s: string;
  21.   f: double;
  22.   k: integer;
  23. begin
  24.   SA := TStringArray.Create('123', '45.6');
  25.   s := '';
  26.   k := 0;
  27.   f := 0;
  28.   writeln(Int64(@f)); // <- actually neccessary
  29.   MagicFct(SA, [k, f]);
  30.   writeln('and then ', k, f);
  31. end;                              
  32.  

But k and f are "constants" for the MagicFct, you cannot anything to the "vars" parameter of the MagicFct. - I don't know what you want to achieve. Why don't you write it in the simple way?
Code: Pascal  [Select][+][-]
  1.   k := StrToInt('123');
  2.   f :=  StrToFloat('45.6');

MountainQ

  • Jr. Member
  • **
  • Posts: 65
Re: assigning to Array of const
« Reply #8 on: April 11, 2021, 11:21:59 am »
Thanks to all for the kind responses.
I would like to add that this is not an urgent issue as there are obvious workarounds.
The topic came up when I was getting ever-changing specs for an init-file.
Explicitly assigning the values to the variables is straightforward yet somewhattedious.
As it is easily possible to gather the values from various variables using a function that uses 'array of const', I was wondering if it is possible to do the opposite.
My guess is that it is not possible as the data in the array of const is a copy of the content of the variables; except for pointers.
Hope this clarifies my intention.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: assigning to Array of const
« Reply #9 on: April 11, 2021, 12:10:02 pm »
Thanks to all for the kind responses.
I would like to add that this is not an urgent issue as there are obvious workarounds.
The topic came up when I was getting ever-changing specs for an init-file.
Explicitly assigning the values to the variables is straightforward yet somewhattedious.
As it is easily possible to gather the values from various variables using a function that uses 'array of const', I was wondering if it is possible to do the opposite.
My guess is that it is not possible as the data in the array of const is a copy of the content of the variables; except for pointers.
Hope this clarifies my intention.

Remember that FPC has adequate handling of .ini files itself. I've built on this in the past, but not found it necessary to redo from scratch.

This doesn't work:

Code: Pascal  [Select][+][-]
  1. program test;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. var
  6.   a, b, c, x: integer;
  7.  
  8.  
  9. function f(const s: string; out z: array of const): integer;
  10.  
  11. begin
  12.   result := Length(z);
  13.   if result = 3 then begin
  14.     a := 1;
  15.     b := 2;
  16.     c := 3
  17.   end
  18. end;
  19.  
  20.  
  21. begin
  22.   x := f('Test', [a, b, c]) // <===== Expects a variable
  23. end.
  24.  

This works:

Code: Pascal  [Select][+][-]
  1. program test;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. var
  6.   a, b, c, x: integer;
  7.  
  8. type
  9.   intArray= array of integer;
  10.  
  11. var
  12.   q: intArray;
  13.  
  14.  
  15. function f(const s: string; out z: intArray): integer;
  16.  
  17. begin
  18.   result := Length(z);
  19.   if result = 3 then begin
  20.     a := 1;
  21.     b := 2;
  22.     c := 3
  23.   end
  24. end;
  25.  
  26.  
  27. begin
  28.   SetLength(q, 3);
  29.   x := f('Test', q)
  30. end.
  31.  

You might be able to build on that using variants or whatever you need.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: assigning to Array of const
« Reply #10 on: April 11, 2021, 12:44:16 pm »
The tvarrec was meant for internal use but still can be used externally of corse.

 At this juncture I think you should be using variants instead.

 Variants store the actual value in them and are compiler friendly so you need to directly interact with the record, basically all the overloads are already created.

@Jamie, I think you mean "DON'T need to directly...".

Apart from that I generally agree. Variants have a few rough edges (I've got into fairly deep water trying to implement an APL-style reduce on dynamic-arrays-of-dynamic-arrays-of-variants) but fewer than "const array" AKA varargs which are very much a hack to allow Object Pascal to implement printf()-like behaviour.

If you get too deep into varargs you'll hit architecture-defined behaviour, that's particularly the case if you try to define a Pascal function that looks exactly like a C function.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

alpine

  • Hero Member
  • *****
  • Posts: 1038
Re: assigning to Array of const
« Reply #11 on: April 11, 2021, 01:03:15 pm »
I would take another approach. Either way, it's about introspection, so instead of using array of const, a specs class can be defined and it's rtti used to extract the values from the ini.

Thus changes can be maintained into the published part of the class.

Goes like this:

Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. uses
  4.   SysUtils, IniFiles, TypInfo, StrUtils;
  5.  
  6. type
  7.  
  8.   { TSpecs }
  9.  
  10.   TSpecs = class
  11.   private
  12.     FIntSpec: Integer;
  13.     FStrSpec: String;
  14.   published
  15.     property IntSpec: Integer read FIntSpec write FIntSpec;
  16.     property StrSpec: String read FStrSpec write FStrSpec;
  17.     // ...
  18.   end;
  19.  
  20. procedure ReadSpec(F: TIniFile; Sect: String; Specs: TSpecs);
  21. var
  22.   C, I: Integer;
  23.   PL: PPropList;
  24.  
  25.   procedure SetPropWithText(AObject: TObject; API: PPropInfo; AText: String);
  26.   begin
  27.     case API^.PropType^.Kind of
  28.       tkChar: SetOrdProp(AObject, API, Ord(IfThen(AText <> '', AText, #0)[1]));
  29.       tkAString: SetStrProp(AObject, API, AText);
  30.       tkInteger: SetOrdProp(AObject, API, StrToIntDef(AText, 0));
  31.       tkInt64, tkQWord: SetInt64Prop(AObject, API, StrToInt64Def(AText, 0));
  32.       tkBool: SetOrdProp(AObject, API, Ord(AText <> '0'));
  33.       tkFloat: SetFloatProp(AObject, API, StrToFloatDef(AText, 0.0));
  34.       tkEnumeration, tkSet: SetOrdProp(AObject, API, StrToIntDef(AText, 0));
  35.     end;
  36.   end;
  37.  
  38. begin
  39.   C := GetPropList(Specs, PL);
  40.   try
  41.     for I := 0 to Pred(C) do
  42.       if F.ValueExists(Sect, PL^[I]^.Name) then
  43.         SetPropWithText(Specs, PL^[I], F.ReadString(Sect, PL^[I]^.Name, ''));
  44.   finally
  45.     FreeMem(PL);
  46.   end;
  47. end;
  48.  
  49. var
  50.   Ini: TIniFile;
  51.   Specs: TSpecs;
  52.  
  53. begin
  54.   Ini := TIniFile.Create('specs.ini');
  55.   Specs := TSpecs.Create;
  56.   try
  57.     ReadSpec(Ini, 'Default', Specs);
  58.     // ...
  59.     // Use Specs.IntSpec, Specs.StrSpec
  60.     // ...
  61.   finally
  62.     Ini.Free;
  63.     Specs.Free;
  64.   end;
  65. end.
  66.  

Regards,
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

MountainQ

  • Jr. Member
  • **
  • Posts: 65
Re: assigning to Array of const
« Reply #12 on: April 12, 2021, 09:23:11 am »
Thanks to all for replies; I will have another look at the problem.
Luckily, it is mostly curiosity-driven.
@y.ivanov: I agree that introspection will be required.
Currently my thoughts are along the lines of typed pointers; e.g. pinteger.
If I come up with something useful, I will post it.
Best to everyone

MortenB

  • Jr. Member
  • **
  • Posts: 59
Re: assigning to Array of const
« Reply #13 on: April 12, 2021, 09:13:47 pm »
I made myself something similar a few years back...
Just to give you an idea of something that actually work.
Array Of Const is quite useful as an unknown count of unknown parameter types to a procedure or a function.
This is just ONE of many procedures I made.
Maybe you can get some inspiration from it :)

Code: Pascal  [Select][+][-]
  1. PROCEDURE TShelves.TAdd.ColumnRow(RowData : ARRAY OF CONST; LocalType:BYTE); // No errorchecking. Only adds according to type. Also increase the size of the type's array.
  2.   VAR
  3.     ix : INTEGER ;   xx : EXTENDED;   asx : ^AnsiString;
  4.                                       wsx : ^WideString;
  5.     OldRowPointer : INTEGER;
  6.   BEGIN
  7. {
  8. AllVTypes : ARRAY[0..18] OF AnsiString =('vtInteger','vtBoolean'  ,'vtChar'      ,'vtExtended' ,'vtString'    ,'vtPointer' ,'vtPChar',
  9.                                          'vtObject' ,'vtClass'    ,'vtWideChar'  ,'vtPWideChar','vtAnsiString','vtCurrency',
  10.                                          'vtVariant','vtInterface','vtWideString','vtInt64'    ,'vtQWord'     ,'vtUniCodeString');   }
  11.     WITH pTheColumn^ DO BEGIN
  12.       OldRowPointer := RowCount;
  13.       CASE RowData[0].VType OF
  14.         0: BEGIN
  15.              ix := RowData[0].VInteger;
  16.              AddWholeNumbersToColumn(ix,LocalType);
  17.              IF ErrorMessage = '' THEN INC(RowCount);
  18.            END;
  19.         1:   {  1 } BEGIN SetLength(IfBoolean    , RowCount+1); IfBoolean    [RowCount] := RowData[0].VBoolean; INC(RowCount); END;// vtBoolean  ... Boolean
  20.         3: BEGIN
  21.              xx := RowData[0].VExtended^;  // Probably not needed, but shorter to write in below lines!
  22.              CASE LocalType OF // vtExtended ... Double, TDate, TTime, Real, Extended
  23.                 9 : BEGIN SetLength(IfDouble     , RowCount+1); IfDouble     [RowCount] := xx; INC(RowCount); END;// Double
  24.                10 : BEGIN SetLength(IfExtended   , RowCount+1); IfExtended   [RowCount] := xx; INC(RowCount); END;// Extended
  25.                12 : BEGIN SetLength(IfTime       , RowCount+1); IfTime       [RowCount] := xx; INC(RowCount); END;// TTime
  26.                13 : BEGIN SetLength(IfDate       , RowCount+1); IfDate       [RowCount] := xx; INC(RowCount); END;// TDate
  27.                14 : BEGIN SetLength(IfDateTime   , RowCount+1); IfDateTime   [RowCount] := xx; INC(RowCount); END;// TDateTime
  28.              END;
  29.            END;
  30.         4:   { 15 } BEGIN SetLength(IfShortString, RowCount+1); IfShortString[RowCount] := RowData[0].VString^; INC(RowCount); END;// vtString   ... ShortString
  31.        11:   { 16 } BEGIN
  32.                       asx := @RowData[0].VAnsiString;
  33.                       AddAnsiStringToColumn(asx^,LocalType);
  34.                       IF ErrorMessage = '' THEN INC(RowCount);
  35.                     END;
  36.        12:   { 11 } BEGIN SetLength(IfCurrency   , RowCount+1); IfCurrency   [RowCount] := RowData[0].VCurrency^; INC(RowCount); END;// vtCurrency;
  37.        15:   { 17 } BEGIN
  38.                           wsx := @RowData[0].VWideString;
  39.                           SetLength(IfWideString , RowCount+1); IfWideString [RowCount] := wsx^; INC(RowCount); // vtWideString
  40.                     END;
  41.        16:   {  8 } BEGIN SetLength(IfInt64      , RowCount+1); IfInt64      [RowCount] := RowData[0].VInt64^; INC(RowCount); END;// vtInt64
  42.        17:   {  5 } BEGIN SetLength(IfQWord      , RowCount+1); IfQWord      [RowCount] := RowData[0].VQWord^; INC(RowCount); END;// QWord
  43.       END;
  44.       IF RowCount = OldRowPointer THEN ErrorMessage :='Error! Column-Data was not added.'
  45.     END;
  46.   END;
  47.  

 

TinyPortal © 2005-2018