Recent

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

MountainQ

  • Jr. Member
  • **
  • Posts: 56
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: 2478
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
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

jamie

  • Hero Member
  • *****
  • Posts: 4440
Re: assigning to Array of const
« Reply #2 on: April 11, 2021, 01:27:39 am »
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

Looks like you are doing something like have been lately..

The const array is a makeup of TVArRec, you can find that in the RTL help..

it contains data type and the item itself for the common types. with that you can Cast over the array to determine what type is sitting there when examining it.

for example

 PVarRec(@ArrayEntry[?])^.vType; and that will tell you what type is sitting there atm..

As for determining what is in the incoming text stream you need to write a function to determine the type of field it is and then use the various functions to convert it to that native type.

 So in your case, if you examine for Numbers only then its an integer. if it's numbers and a "." only then it's a float , otherwise it's a string.

 in my case, where  I am reading Iges fields I had to examine the first chars up to the letter H to determine if it was a string field  and if not, then test for a numbers only with a . ….
after you read in each segment you have functions like StrToFloatDef, StrToIntDef where as if there is no number that and just a ,, for example you can default it..

P.S.
  There is no DefaultString function fpc , I had to make one myself.
The only true wisdom is knowing you know nothing

MountainQ

  • Jr. Member
  • **
  • Posts: 56
Re: assigning to Array of const
« Reply #3 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

  • Full Member
  • ***
  • Posts: 189
Re: assigning to Array of const
« Reply #4 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. :)

Laz 2.0.10 / FPC 3.2.0 / Windows 10 (64bit)

MountainQ

  • Jr. Member
  • **
  • Posts: 56
Re: assigning to Array of const
« Reply #5 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: 2478
Re: assigning to Array of const
« Reply #6 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
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

y.ivanov

  • Jr. Member
  • **
  • Posts: 94
Re: assigning to Array of const
« Reply #7 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, 

wp

  • Hero Member
  • *****
  • Posts: 8378
Re: assigning to Array of const
« Reply #8 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');
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

MountainQ

  • Jr. Member
  • **
  • Posts: 56
Re: assigning to Array of const
« Reply #9 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: 2478
Re: assigning to Array of const
« Reply #10 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
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

jamie

  • Hero Member
  • *****
  • Posts: 4440
Re: assigning to Array of const
« Reply #11 on: April 11, 2021, 12:30:45 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.

 Variants are of type tvardAta
The only true wisdom is knowing you know nothing

MarkMLl

  • Hero Member
  • *****
  • Posts: 2478
Re: assigning to Array of const
« Reply #12 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
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

y.ivanov

  • Jr. Member
  • **
  • Posts: 94
Re: assigning to Array of const
« Reply #13 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,

MountainQ

  • Jr. Member
  • **
  • Posts: 56
Re: assigning to Array of const
« Reply #14 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

 

TinyPortal © 2005-2018