2
votes

At the moment I'm converting a project written in VBA to Delphi and have stumbled upon a problem with converting some Subs with Optional arguments. Say, there is a Sub declaration (just an example, actual Subs have up to 10 optional parameters):

Sub SetMark
    (x0 As Double, y0 As Double, 
     Optional TextOffset As Integer =5,
     Optional TextBefore As String = "",
     Optional Text As String = "",
     Optional TextAfter As String = "mm",
     Optional Color As String = "FFFFFF",
     Optional ArrowPresent As Boolean = True)

That Sub subsequently can be called like this:

    Call SetMark (15, 100,,,"135")
    Call SetMark (100, 100, 8,, "My text here..", "")
    'a lot of calls here

The Optional arguments are very flexible here, you can omit any of them, and you can assign a value to any of them as well. Unlike in Delphi.

Procedure SetMark
    (x0: real; y0: real, 
            TextOffset: Integer =5;
     TextBefore: ShortString = '';
     Text: ShortString = '';
     TextAfter: ShortString = 'mm';
     Color: ShortString = 'FFFFFF';
     ArrowPresent: Boolean = True);

It seems you cannot just make a copy of VBA call:

SetMark (15, 100,,,'135');// error here 

So, the question is: is there any way to convert that Subs to Delphi procedures keeping the same flexibility in parameters? My first idea was to use default parameters, but it doesn't work. As for now it seems in Delphi I will have to pass all the parameters in the list with their values directly but that means a lot of work for reviewing and proper porting of VBA calls.

Any ideas?

2
By the way, you really should not be using ShortString. That has been deprecated for many many years. Use the native string type, string. - David Heffernan
Also, tangentially, real is a legacy type that only really exists for compatibility (it is an alias of double). Better to declare a double in the Delphi code, just as the VB code does. - J...

2 Answers

12
votes

Is there any way to convert the VBA subroutines to Delphi procedures, still keeping the same flexibility in parameters?

There is no way to achieve that – that flexibility to omit parameters, other than at the end of the list, simply does not exist.

For methods of automation objects, you can use named parameters, as described here: Named/optional parameters in Delphi? However, I very much recommend that you don't implement your classes as automation objects just to get that functionality.

Whenever you switch between languages, you will find differences that are inconvenient. That is inevitable. The best approach is to try to find the best way to solve the problem in the new language, rather than trying to force idioms from the old language into the new language.

In this case you might want to use overloaded functions or parameter objects as ways to alleviate this inconvenience.

2
votes

Just to expand on the idea of refactoring to use a parameter object, you could declare a record like:

 TSetMarkParams = record
   x0 : double; 
   y0 : double; 
   TextOffset : integer;
   TextBefore : string;
   Text : string;
   TextAfter : string;
   Color : string;
   ArrowPresent : boolean;
   constructor Create(Ax0, Ay0 : double);
 end;

And implement the constructor to populate default values as :

 constructor TSetMarkParams.Create(Ax0, Ay0 : double);
 begin
   x0 := Ax0;
   y0 := Ay0;
   TextOffset := 5;
   TextBefore := '';
   Text := '';
   TextAfter := 'mm';
   Color := 'FFFFFF';
   AllowPresent := true;
 end;

Your procedure would then have signature :

 procedure SetMark(ASetMarkParams : TSetMarkParams);

Which you could then, using your example of SetMark (15, 100,,,'135'); call as :

 var 
   LSetMarkParams : TSetMarkParams
 begin
   LSetMarkParams := TSetMarkParams.Create(15, 100);
   LSetMarkParams.Text := '135';
   SetMark(LSetMarkParams);
 end;

As collateral benefit, the above is much more readable as it saves you from going blind trying to count commas when returning to debug a troublesome method call.