Emmanuel Ichbiah
Emmanuel Ichbiah

Reputation: 309

How do I write a Delphi procedure that modifies a string that works for both PCHAR and string?

To take an example, lets say I would like to write a simple procedure that deletes the 'X' characters from a string.

How can I design my procedure so that it works for both string and PCHAR parameters.

If I define it as:

procedure RemoveX( source : PCHAR);

than calls to RemoveX(PCHAR(mystring)) where myString is a string will remove the 'X' but will not take care of updating the string length ... Hence a subsequent myString := myString + 'done' will leave myString unchanged. And I don't want to change the length after the call to RemoveX, I expect the RemoveX procedure to deal with everything.

If on the other hand I define it as:

procedure RemoveX( var source : string);

I don't know how to pass it a PCHAR ...

Upvotes: -2

Views: 254

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 596307

I would not suggest implementing the string version in terms of the PChar version, or vice versa. I would keep them separate so that you can tailor them independently, eg:

procedure RemoveX(source : PChar); overload;
procedure RemoveX(var source : string); overload;

procedure RemoveX(source : PChar);
var
  P: PChar;
  Len: Integer;
begin
  if source = nil then Exit;
  Len := StrLen(source);
  repeat
    P := StrScan(source, 'X');
    if P = nil then Exit;
    StrMove(P, P+1, Len - Integer(P-source));
    Dec(Len);
    source := P;
  until False;
end;

procedure RemoveX(var source : string);
begin
  source := StringReplace(source, 'X', '', [rfReplaceAll]);
end;

Update: If you really want to use a single implementation for both PChar and String inputs then you can do something like this:

function RemoveX(source : PChar; sourceLen: Integer): Integer; overload;
procedure RemoveX(source : PChar); overload;
procedure RemoveX(var source : string); overload;

function RemoveX(source : PChar; sourceLen: Integer): Integer;
var
  P: PChar;
begin
  Result := 0;
  if (source = nil) or (sourceLen = 0) then Exit;
  repeat
    P := StrScan(source, 'X');
    if P = nil then Exit;
    StrMove(P, P+1, sourceLen - Integer(P-source));
    Dec(sourceLen);
    source := P;
  until False;
  Result := sourceLen;
end;

procedure RemoveX(source : PChar);
begin
  RemoveX(source, StrLen(source));
end;

procedure RemoveX(var source : string);
begin
  UniqueString(source);
  SetLength(source, RemoveX(PChar(source), Length(source)));
end;

Upvotes: 5

David Heffernan
David Heffernan

Reputation: 612963

You cannot implement this using a single parameter. You have two different types.

You could build the string version on top of a PChar version.

procedure RemoveX(var str: string);
var
  P: PChar;
begin
  UniqueString(str);
  P := PChar(str);
  RemoveX(P);
  str := P;
end;

An alternative for final line could be:

SetLength(str, StrLen(P));

Either way, this obviously assumes that you already have a functioning overload that operates on PChar. And that the function removes characters. Clearly it cannot extend the PChar buffer.

The call to UniqueString is needed in case the string is shared (ref count greater than one) or constant. After this call the string buffer is editable and not shared.

Whether or not avoiding duplication of implementation in this way is the best approach I cannot say. It depends on your design drivers. If simplicity and clarity of code is key, then avoiding duplication makes sense. If performance is key then it may be desirable to provide two bespoke implementations.

Upvotes: 2

Related Questions