Vitim.us
Vitim.us

Reputation: 22128

How can I create a function with an arbitrary number of parameters?

I want to create a function that receive multiples strings as parameters. Like the function printf("Hello %s",name); of C. but I don't want to pass a ready array, it wouldn't be readable.

Edit1.text:=lang('Hello');

Edit2.text:=lang('Welcome to {1} guest',place);

Edit3.text:=lang('Hi {1}, is your {2} time in {3}','Victor','first','Disney');

output should be:

Hello
Welcome to Disney guest
Hi Victor is your first time in Disney

how I create the function TForm1.lang(parameters:String):String;, I did a research, but I can't get it work.

I need to access the parameters[] and the parameters.length also.

I'm needing this to turn my App to multilang.

Upvotes: 0

Views: 1910

Answers (6)

Jerry Dodge
Jerry Dodge

Reputation: 27266

As Tony mentions above, I also recommend using a deliminated string. Except, a little more than just deliminating, but using more of a parsing technique. If I understand right, this function you're making for formatting shall NOT include an array in the parameters, but technically, that doesn't mean we can't use arrays anywhere at all (arrays are very ideal to use for this scenario for fast performance).

This method will allow virtually anything to be passed in the parameters, including the deliminator, without affecting the output. The idea is to do A) Size of parameter string, B) Deliminator between size and parameter, and C) parameter string... And repeat...

const
  MY_DELIM = '|';  //Define a deliminator

type
  TStringArray = array of String;

/////////////////////////////////

//Convert an array of string to a single parsable string
//  (Will be the first step before calling your format function)
function MakeParams(const Params: array of String): String;
var
  X: Integer;
  S: String;
begin
  Result:= '';
  for X:= 0 to Length(Params)-1 do begin
    S:= Params[X];
    Result:= Result + IntToStr(Length(S)) + MY_DELIM + S;
  end;
end;

//Convert a single parsable string to an array of string
//  (Will be called inside your format function to decode)
//  This is more or less called parsing
function ExtractParams(const Params: String): TStringArray;
var
  S: String;  //Used for temporary parsing
  T: String;  //Used for copying temporary data from string
  P: Integer; //Used for finding positions
  C: Integer; //Used for keeping track of param count
  Z: Integer; //Used for keeping track of parameter sizes
begin
  S:= Params;                   //Because we'll be using 'Delete' command
  C:= 0;                        //Set count to 0 to start
  SetLength(Result, 0);         //Prepare result array to 0 parameters
  while Length(S) > 0 do begin  //Do loop until nothing's left
    P:= Pos(MY_DELIM, S);       //Get position of next deliminator
    if P > 1 then begin         //If deliminator was found...       
      C:= C + 1;                  //We have a new parameter
      SetLength(Result, C);       //Set array length to new parameter count
      T:= Copy(S, 1, P-1);        //Get all text up to where deliminator was found
      Delete(S, 1, P);            //Delete what we just copied, including deliminator
      Z:= StrToIntDef(T, 0);      //Convert T:String to Z: Integer for size of parameter
      T:= Copy(S, 1, Z);          //Get all text up to 'Z' (size of parameter)
      Delete(S, 1, Z);            //Delete what we just copied
      Result[C-1]:= T;              //Assign the new parameter to end of array result
    end else begin              //If deliminator was NOT found...
      S:= '';                     //Clear S to exit loop (possible bad format if this happens)
    end;
  end;
end;

//Main formatting routine
function MyFormat(const Input: String; const Params: String): String;
var
  A: TStringArray;
  X: Integer;
  S: String;
  P: Integer;
  R: String;
begin
  R:= Input;
  A:= ExtractParams(Params);
  //At this point, A contains all the parameters parsed from 'Params'
  for X:= 0 to Length(A)-1 do begin
    S:= A[X];
    P:= Pos('%s', R);
    if P > 0 then begin
      Delete(R, P, 2);
      Insert(S, R, P);
    end;
  end;
  Result:= R;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Pars: String;
begin
  Pars:= MakeParams(['this', 'that', 'something else']);
  Edit1.Text:= MyFormat('%s is %s but not %s', Pars);
end;

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 595339

Delphi does not support CREATING functions withvararg-style parameters that work exactly like printf() does. It only supports CONSUMING such functions from external libraries. The closest Delphi comes to supporting the creation of functions with variable parameter lists is to use "open array" parameters, like what SysUtils.Format() uses.

Upvotes: 1

Svein Bringsli
Svein Bringsli

Reputation: 5748

All your three examples could be fixed by using SysUtils.Format:

Edit1.text := format('%s',['Hello']));
Edit1.text := format('Welcome to %s guest',[place]));
Edit1.text := format('Hi %s, is your %s time in %s',['Victor','first','Disney']));

Personally I think it's quite readable. If you can have what you need from a basic sysutils function, you should seriously consider doing that, rather than to write your own version. On the other hand, you may need more complex functionality that doesn't show in your question. If that's the case, I think paulsm4's suggestion of using a stringlist seems like a good way to go.

Upvotes: 1

Marcus Adams
Marcus Adams

Reputation: 53830

Here's an example function of how you can do this:

function TForm1.lang(s: String; params: array of String): String;
var
  i: Integer;
begin
  for i := 0 to High(params) do
  begin
    ShowMessage(params[i]);
  end;
end;

Call it like this:

lang('My format string', ['this', 'that']);

or like this:

var
  b: String;
begin
  b := 'this';
  lang('My format string', [b, 'that']);
end;

Upvotes: 4

Tony Hopkinson
Tony Hopkinson

Reputation: 20320

Not sure what you mean by not readable

DoSomething(['Param1','Param2']);

for

procedure DoSomething(args : Array of String);
Var
  Index : Integer;
Begin
  for index := Low(args) to High(args) Do
    ShowMessage(args[Index]);
End;

Seems okay to me. Course if you want to call it from outside delphi then you have an issue.

Quick fix is just to pass in a delimited string and then user TStringList to split it.

You could write a wee function to do that, don't forget to free it when you are done.

Upvotes: 3

paulsm4
paulsm4

Reputation: 121599

As you probably know, SysUtils.Format() implements "varargs" by using a set.

In your case, however, why not just pass a TStringList? The function will simply check "list.Count". Voila - you're done!

Upvotes: -1

Related Questions