Recent

Author Topic: Getting a true variant parameter possibility.  (Read 861 times)

tfurnivall

  • Jr. Member
  • **
  • Posts: 85
Getting a true variant parameter possibility.
« on: November 10, 2025, 10:24:08 pm »
I am migrating some code that has its roots in a COBOL routine written over 40 years ago. The COBOL definition of the area of interest was:
Code: COBOL  [Select][+][-]
  1. 01  TRACE-LINE                                                            PIC X(114).
  2. 01  FILLER REDEFINES TRACELINE.
  3.      02 TRACE-HEADER                                                      PIC X(18).
  4.      02 TRACE-ALPHA OCCURS 8.
  5.           03 TRACE-NUM                                                    PIC Z(9)9BB.
  6.           03 TRACE-AMT REDEFINES TRACE-NUM                                PIC Z(7)9.99B.
  7.  

This gave me space (in TRACE-HEADER) to have a date & time area (only 2-digit years - this was early 1980's, and Y2K had not yet been invented), and an array of 8 areas each of which could contain a number, an amount or 12 associated characters (including dates).

By virtue of being able to MOVE SPACES to a group item, I could clear TRACE-LINE in one fell swoop. Then, I could use which ever of the areas I wanted to trace different values. It was pretty rudimentary, but I was able to pass it in and out of differrent sub-programs, and get a consistent trace result.

About 25 years ago I ported it to VBA, and extended the number of procedures available, to include conditional tracing (based on the relationship of a passed TraceLevel to a established TraceLevel, and to trace specific things for a Form Event, a Class Method and so on. This required a bit of extra effort, but again - the trace was easily available, and useful in the testing and debugging phase of developing a program. (It has shown up, unheralded, in some of my posts). Since then I've had a fairly easy conversions to other languages, and it's now time to see if I can port it to Lazarus.

I have two critical needs:

1)  The ability to trace different data -types (Integers, Amounts, Real Numbers, strings etc), and
2)  The ability to trace different numbers of these different data types.

From working with VBA I was familiar with Optional parameters, and Variant data types. However, in Lazarus, it seems that the way to have optional (ie a variable number of) parameters, is to overload procedures with differing numbers of parameters. This is ungainly (or at least, so it feels to me), but the real problem is that variable data-type can not be used with a default value. Fair enough, a variant can take any data-type, so how can we then specify a 'default' data type? Especially when there is a plethora of different meanings for the simple value 0

I hope this little exposition is clear, and understandable. What I'm trying to solve is either or both of the two presenting problems:

A)  Is there a way (short of overloading almost, ad infinitum, the various procedures that constitute the API) of allowing varying numbers of parameters to a procedure (or, of course, a function)?


B)  Is there a way that a parameter can take on a different data-type at different invocations of the called procedure? IOW can I call Trace with parameter1 being an integer one time, and then being a date/time another time?

Having revealed my dinosaur like origins, I shall now resume a somnolent posture, and hope for some smarter heads than mine to offer some ideas!

Thanks,

Tony

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12562
  • FPC developer.

tfurnivall

  • Jr. Member
  • **
  • Posts: 85
Re: Getting a true variant parameter possibility.
« Reply #2 on: November 10, 2025, 10:50:43 pm »
Thanks, markov.

I shall spend this evening reading and trying to understand the example you sent. I'll reply tomorrow!

Hartelijk dank!

Tony

440bx

  • Hero Member
  • *****
  • Posts: 5881
Re: Getting a true variant parameter possibility.
« Reply #3 on: November 10, 2025, 11:17:01 pm »
The COBOL REDEFINES facility is most closely emulated with "absolute".

if I remember my COBOL correctly, what you showed there would "translate" to:
Code: Pascal  [Select][+][-]
  1.  
  2. var
  3.   TraceLine   : array[0..pred(114)] of char;
  4.   TraceHeader : array[0..pred( 18)] of char absolute TraceLine;
  5.   TraceNum    : array[0..pred( 12)] of char absolute TraceHeader[18];
  6.   TraceAmt    : array[0..pred( 12)] of char absolute TraceHeader[18];
  7.  
  8.   { for some reason that is "unclear" the compiler rejects:
  9.  
  10.   TraceAmt    : array[0..pred( 12)] of char absolute TraceNum[0];
  11.   }
  12.  
TraceLine is just like TRACE_LINE in your code.  You can "move spaces" to it by using FillChar.
TraceHeader is at the same address as TRACE_LINE by virtue of the absolute.
TraceNum immediately follows TraceHeader by virtue of being located at the address that follows the TraceHeader array.
TraceAmt overlays TraceNum because it is "absoluted" at the same address as TraceNum.

As the comment states, for some reason the compiler rejected "absolute TraceNum[0]" which would have made it clearer that TraceAmt overlays TraceNum.  Looks like a compiler bug.

HTH.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

jamie

  • Hero Member
  • *****
  • Posts: 7379
Re: Getting a true variant parameter possibility.
« Reply #4 on: November 10, 2025, 11:29:48 pm »
A variant Record ?

Look at the Variant CASE statement within a record.

That will allow you to overlay/Overlap the same memory but using different name tags.

You can put the main ID before the Case statement with that, you can decide how to address the
record.

Jamie
The only true wisdom is knowing you know nothing

Thaddy

  • Hero Member
  • *****
  • Posts: 18475
  • Here stood a man who saw the Elbe and jumped it.
Re: Getting a true variant parameter possibility.
« Reply #5 on: November 11, 2025, 06:41:12 am »
@440bx translates to:
Code: Pascal  [Select][+][-]
  1. type
  2.   TTracelist = (TrLine, TrHeader,TrNum,TrAmt);
  3.   TTraceRecord = record
  4.   case TraceList:TTracelist of
  5.   TrLine   : (TraceLine:array[0..pred(114)] of char);  // maps
  6.   TrHeader : (TraceHeader:array[0..pred( 18)] of char);// equal
  7.   TrNum    : (TraceNum:array[0..pred( 12)] of char);   // for
  8.   TrAmt    : (TraceAmt:array[0..pred( 12)] of char);   // all
  9.   end;
But I think it is more like:
Code: Pascal  [Select][+][-]
  1. type
  2.   TTraceRecord = record
  3.   case boolean of
  4.   false: (TraceLine:array[0..pred(114)] of byte); // maps
  5.   true : (TraceHeader:array[0..pred( 18)] of byte;// equal
  6.           case boolean of                                 // followed by
  7.             false: (TraceNum:array[0..pred( 12)] of byte);// maps
  8.             true : (TraceAmt:array[0..pred( 12)] of byte);// equal
  9.           )                                               // within the space of the traceheader sub-record
  10.   end;
Not sure this early in the morning. (Pack settings may be involved.)
You may need to remap the array values to the COBOL mappings to obtain the correct offsets, but I am quite sure about the ordering.
REDEFINES is indeed close to  absolute or same offset in that COBOL code.

Also note I suspect that char should really be byte in the Pascal version. As usual.
So I took the liberty to change that.

I am a bit hampered by the fact that I don' t often mix COBOL and Pascal: I need a different mindset when doing COBOL.
I need a flow, but that is entirely my issue.


« Last Edit: November 11, 2025, 07:28:41 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

440bx

  • Hero Member
  • *****
  • Posts: 5881
Re: Getting a true variant parameter possibility.
« Reply #6 on: November 11, 2025, 08:35:00 am »
variants are not close enough to the COBOL REDEFINES because the use of variants _forces_ having to qualify the field with the record identifier.  Absolute does not require the identifier to be qualified when used.

IOW, using absolute, TRACE-NUM and TRACE-AMT can be referenced _without_ having to qualify them using TRACE-HEADER or TRACELINE, e.g, MOVE 0 TO TRACE-NUM(1) in COBOL becomes TRACE_NUM[1] := 0; in Pascal using absolute (note that TRACE_NUM is _not_ qualified with a record identifier that "contains" it since it is standalone, courtesy of "absolute".)

using Pascal variants would force having to qualify TRACE-NUM with the record name.  That's not as simple and straightforward as in COBOL.

IOW, "absolute" does enable referencing the memory without qualification just as it is done in COBOL using REDEFINES.

"absolute" is great and so is COBOL's REDEFINES (I've missed that COBOL feature, among quite a few others, very often in Pascal and C/C++.)
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 18475
  • Here stood a man who saw the Elbe and jumped it.
Re: Getting a true variant parameter possibility.
« Reply #7 on: November 11, 2025, 08:50:20 am »
That does not take away that my second approximation is much closer to the COBOL intention in the sense that it describes the memory layout much better and uses the correct type for it: byte, not char.
I am proficient in COBOL - way before I  was in Pascal - and still work with it professionally, even though "retired".
I can translate it to the correct mapping if I have enough information and context.
What I do agree with you is that in case, Pascal needs to be more verbose than COBOL where it is usually the other way around. It is however, just as capable: it can perform exact mapping of memory layout.

Plz shoot holes in my second example.
« Last Edit: November 11, 2025, 09:01:08 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

440bx

  • Hero Member
  • *****
  • Posts: 5881
Re: Getting a true variant parameter possibility.
« Reply #8 on: November 11, 2025, 09:05:26 am »
My point is simply that using absolute enables producing something that is semantically closer to the original COBOL definition than using variants.

Yes, it can be done with variants but, the result is not as close to the original COBOL definition.

FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

tfurnivall

  • Jr. Member
  • **
  • Posts: 85
Re: Getting a true variant parameter possibility.
« Reply #9 on: November 11, 2025, 08:54:30 pm »
Markov points me in the direction of the Variant type - and its possible step-cousin, the array of const. This seems as though it would work perfectly - later research will indicate.
Quote
This is a special case of the Open array construction, where it is allowed to pass any expression in an array to a function or procedure. The expression must have a simple result type: structures cannot be passed as an argument. This means that all ordinal, float or string types can be passed, as well as pointers, classes and interfaces (since the latter two are actually pointers).

However, the example - having said:
Quote
In code, it is possible to pass an arbitrary array of elements to this procedure
then proceeds to pass (presumably carefully) consistent values:
Code: Pascal  [Select][+][-]
  1. S:='Ansistring 1';  
  2.   T:='AnsiString 2';  
  3.   Testit ([]);  
  4.   Testit ([1,2]);  
  5.   Testit (['A','B']);  
  6.   Testit ([TRUE,FALSE,TRUE]);  
  7.   Testit (['String','Another string']);  
  8.   Testit ([S,T])  ;  
  9.   Testit ([P1,P2]);  
  10.   Testit ([@testit,Nil]);  
  11.   Testit ([ObjA,ObjB]);  
  12.   Testit ([1.234,1.234]);  
  13.   TestIt ([AClass]);
  14.  
  15.  

We have, for example, passing two integers, or two characters; three booleans or two strings.

What we don't get is an integer, and a boolean and a character and a string all at the same time!

One of the first lessons I was taught about creating demonstration (or indeed, test) data was to give my perversity full rein.
The sample code given does not do this. (Hence my whinging about the careful selection of data types to be passed in any invocation of the procedure 'TestIt'). And furthermore, given that TestIt kindly writes a newline at the end of each sample, include one of every single possiblity in a single invocation! Not that's really demonstrating the flexibility of this tool!

To further diminish the value of the example, we don't even see what it actually does when it runs. (Yes - any fool can see what the code hoped it would do, but where is the triumphant QED?)

A glimmer of hope comes with the 'printf' example (which requires some adjustment of the spectacles to read fluently) but again, there's no demonstration of the proof of concept.

Sorry about being grouchy, especially after discovering a wonderful treasure trove in the innocuous statements of the top of the documentation page - we have a Federal holiday here in the US except that we've had an enforced Federal holiday for the past 5 weeks. Bah!)

On a less grumpy mood, I do think that this is exactly what I'm looking for! I'll just have to set up my own test-bed rather than thanking the anonymous code for the example.

Thanks, anon, and thanks also, again, to Markov for pointing me in this direction!

Tony

tfurnivall

  • Jr. Member
  • **
  • Posts: 85
Re: Getting a true variant parameter possibility.
« Reply #10 on: November 11, 2025, 09:08:07 pm »
Thinking about the discussion between Thaddy and 440bx.

One difference that I (in-)conveniently forgot to mention in my example of COBOL was how the line was set up to effect the actual tracing:

COBOL:
Code: COBOL  [Select][+][-]
  1. MOVE "220-Paragraph-Name" TO TRACE-ALPHA (1)
  2. MOVE "VarName" TO TRACE-ALPHA (2)
  3. MOVE Var TO TRACE-NUM (3)
  4. PERFORM 850-TRACE.

This, of course, means that I'm doing half the work (ie setting up the contents of the actual TraceLine)  necessary for Pascal (or, indeed, any other block-structured language), where the goal is:
Code: Pascal  [Select][+][-]
  1. Trace ('220-Paragraph-Name' , 'VarName', Var);

Indeed, when using the VBA version of this, over the past couple of decades, I have blissfully burst the bounds of the default pass-by-reference of COBOL, and used forms such as:
Code: Pascal  [Select][+][-]
  1. Trace ('220-Paragraph-Name', 'VarName' & var);

How easily we forget the ties that bind us to the past!

440bx was right that the absence of any identifier qualification was an unspoken need of my preferred solution.

BTW I love it when experts chime in on my humble questions. I learn so much!

Tony

cdbc

  • Hero Member
  • *****
  • Posts: 2505
    • http://www.cdbc.dk
Re: Getting a true variant parameter possibility.
« Reply #11 on: November 11, 2025, 09:58:16 pm »
Hi Tony
Here's a little something to get you playing with 'Array of Const':
Code: Pascal  [Select][+][-]
  1. program aoc;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$ifdef win}{$AppType console}{$endif}
  5. uses
  6.   {$IFDEF UNIX}
  7.   cthreads,
  8.   {$ENDIF}
  9.   Classes, sysutils;
  10.  
  11. procedure CheckParams(Args: array of const);
  12. var li, len: cardinal; sum: cardinal = 0; res: string = '';
  13. begin
  14.   len:= Length(Args);
  15.   if len = 0 then exit;
  16.   for li:= 0 to len-1 do begin
  17.     case Args[li].VType of
  18.       vtBoolean: writeln('· Param #',li,', type = ',Args[li].VType,', ',BoolToStr(Args[li].VBoolean,true));
  19.       vtInt64: sum+= Args[li].VInt64^;
  20.       vtInteger: sum+= Args[li].VInteger;
  21.       vtQWord: sum+= Args[li].VQWord^;
  22.       vtAnsiString: res+= string(Args[li].VAnsiString) + ' ';
  23.       vtWideString: res+= UTF8Encode(widestring(Args[li].VWideString)) + ' ';
  24.       vtString: res+= Args[li].VString^ + ' ';
  25.       vtClass: writeln('· Param #',li,', type = ',Args[li].VType,', ',Args[li].VClass.ClassName);
  26.       vtObject: writeln('· Param #',li,', type = ',Args[li].VType,', ',Args[li].VObject.ToString);
  27.     end;
  28.   end;
  29.   writeln('· Sum of strings : "',trim(res),'"');
  30.   writeln('· Sum of integers: ',sum);
  31. end;
  32.  
  33. var hs: THandleStream;
  34.  
  35. begin
  36.   writeln('> > > Play with Array Of Const < < <',LineEnding);
  37.   hs:= THandleStream.Create(1);
  38.   writeln('Calling procedure CheckParams([17,''Hello'',TObject,(1+1)=2,43,''World'',false,hs,40,''Tony'']);',LineEnding);
  39.   CheckParams([17,'Hello',TObject,(1+1)=2,43,'World',false,hs,40,'Tony']);
  40.   hs.Free;
  41.   {$ifdef win}writeln('Push [Enter]'); readln;{$endif}
  42. end.
  43.  
It's a commandline program, that shows you how to handle types and values...

Here's how your output should look:
Code: Bash  [Select][+][-]
  1. bc@red array-of-const$ ./aoc
  2. > > > Play with Array Of Const < < <
  3.  
  4. Calling procedure CheckParams([17,'Hello',TObject,(1+1)=2,43,'World',false,hs,40,'Tony']);
  5.  
  6. · Param #2, type = 8, TObject
  7. · Param #3, type = 1, True
  8. · Param #6, type = 1, False
  9. · Param #7, type = 7, THandleStream
  10. · Sum of strings : "Hello World Tony"
  11. · Sum of integers: 100
  12. bc@red array-of-const$
Regards Benny
« Last Edit: November 11, 2025, 10:00:12 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

tfurnivall

  • Jr. Member
  • **
  • Posts: 85
Re: Getting a true variant parameter possibility.
« Reply #12 on: November 11, 2025, 10:39:41 pm »
I Love it!

Benny - I sit in awe, and bemusement and (just a little) ashamed of ranting!

Scanning through the code, I think you've pretty well addressed my concerns about the example data, and not seeing what's meant to happen! (I got a giggle out of passing (1+1)=2 as a parameter!)

I will skip my own flexing of the inner muscles of "array of const", and move straight to fitting into SDL/Trace.

Thanks for making the late afternoon a little more enjoyable!

Mange tak!

Tony

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11896
  • Debugger - SynEdit - and more
    • wiki
Re: Getting a true variant parameter possibility.
« Reply #13 on: November 11, 2025, 11:16:13 pm »
Quote
Code: Pascal  [Select][+][-]
  1. procedure CheckParams(Args: array of const);

If you pass the args as read only, then you may want to do
Code: Pascal  [Select][+][-]
  1. procedure CheckParams(CONST Args: array of const);

That will skip creating a copy of the entire array each time you call the procedure.

Mind:
- Copy only means the direct array content, e.g. in terms of a string/pchar that means the pointer to the text-content, but not the text-content itself.
- You will read from the memory were the caller stored the array, so that memory must not be changed (by any other code executed while you are in the call)

cdbc

  • Hero Member
  • *****
  • Posts: 2505
    • http://www.cdbc.dk
Re: Getting a true variant parameter possibility.
« Reply #14 on: November 11, 2025, 11:23:47 pm »
Hi
You're right Martin -- Thanks =^
Yup, forgot the 'const' specifier, was too busy showing Tony how to...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

 

TinyPortal © 2005-2018