Recent

Author Topic: AnsiStrings. How to make code compatible with Lazarus?  (Read 3913 times)

IQuitDelphiToo

  • New member
  • *
  • Posts: 8
AnsiStrings. How to make code compatible with Lazarus?
« on: July 22, 2019, 04:08:31 pm »
I have code like this:

-------------------------------------
uses AnsiStrings;

 function  RemoveSpaces        (CONST s: string): string;                 overload;
 function  RemoveSpaces        (CONST s: Ansistring): Ansistring;     overload;   
-------------------------------------

I had to remove AnsiStrings because Lazarus don't know it. Now, when I compile, I get:
"Error: Function is already declared Public/Forward "RemoveSpaces(const AnsiString):AnsiString;"

tr_escape

  • Sr. Member
  • ****
  • Posts: 432
  • sector name toys | respect to spectre
    • Github:
Re: AnsiStrings. How to make code compatible with Lazarus?
« Reply #1 on: July 22, 2019, 04:44:49 pm »
Maybe this code can be help and you can improve them

Code: Pascal  [Select][+][-]
  1. var
  2.   s:String;
  3. .
  4. .
  5. .
  6. procedure TForm1.FormCreate(Sender: TObject);
  7. begin
  8.   s:='my_simple__string with______a_lot_of_underlines';
  9.   s:=s.Replace('__','.',[rfReplaceAll]);
  10.   Caption:=s;;
  11. end;
  12.  
  13.  

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: AnsiStrings. How to make code compatible with Lazarus?
« Reply #2 on: July 22, 2019, 05:12:17 pm »
I have code like this:

-------------------------------------
uses AnsiStrings;

 function  RemoveSpaces        (CONST s: string): string;                 overload;
 function  RemoveSpaces        (CONST s: Ansistring): Ansistring;     overload;   
-------------------------------------

I had to remove AnsiStrings because Lazarus don't know it. Now, when I compile, I get:
"Error: Function is already declared Public/Forward "RemoveSpaces(const AnsiString):AnsiString;"
In Lazarus, strings are understood as 1-byte strings, in contrast to 2-byte strings, such as WideString or UnicodeString, as used by Delphi. Therefore, the compiler has no way to distinguish both overloads. Just remove the version with Ansistring, and work with string only.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: AnsiStrings. How to make code compatible with Lazarus?
« Reply #3 on: July 22, 2019, 05:12:46 pm »
FPC/Lazarus upgraded string types differently then Delphi, so no easy answer. It depends.

To make proper choices you need to grasp the string models of Delphi (D2007- and D2009+)  and Lazarus/FPC (before 3.0, and after).

A short summary to trigger you to dig deeper:

Delphi moved to unicodestring with D2009, but Lazarus already had significant codebases (dating back to before 2005) where ansistring was utf8.

However this was always complicated by the fact that the FPC routines on Window assumed ansistring was CP_ACS (iow the local ansi codepage).

With FPC 3.0 this changed. While FPC was committed to following Delphi and change string to unicodestring like D2009+ did, the transition period was a big problem. There was a decade worth of Lazarus codebases that assumed ansistring=UTF8, and the traditional solution of overloading functions wouldn't work for classes based code, specially virtual methods (*)

So while FPC 3.0 implemented most support on the compiler level, not much changed on the library level except to make system and sysutils somewhat agnostic by overloading all functions ansistring and unicodestring, and make the exact ansistring encoding a runtime decision.

Lazarus could plug-in to that, and they promptly did. While there is a Delphi 2009 compatible mode ({$Mode delphiunicode}), any follow-up transition to make all libraries fully unicodestring hasn't happened yet.

(*) the mess with the duplicate overloading of int64 TStream.seek already showed that. Can you imagine doing that for umpteen methods?

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: AnsiStrings. How to make code compatible with Lazarus?
« Reply #4 on: July 22, 2019, 05:28:45 pm »
I have code like this:
Code: Pascal  [Select][+][-]
  1.  function  RemoveSpaces(CONST s: string): string; overload;
  2.  function  RemoveSpaces(CONST s: Ansistring): Ansistring; overload;
I had to remove AnsiStrings because Lazarus don't know it. Now, when I compile, I get:
Error: Function is already declared Public/Forward "RemoveSpaces(const AnsiString):AnsiString;"

Two posibilities: 1) your source is in {$mode delphi} or 2) it has a {$H+} (or equivalent {$longstrings}) directive.

In any of those cases string = ansistring so from the compiler's point of view you have a duplicate declaration.

If you want the function with "string" parameter to act on short strings, change the declarations to:
Code: Pascal  [Select][+][-]
  1.  function RemoveSpaces(CONST s: ShortString): ShortString; overload;
  2.  function  RemoveSpaces(CONST s: Ansistring): Ansistring; overload;

Even then, you may have problems due to type promotion (for example with constants) because both types are assignment-compatible.

ETA: Do note that dependng on what your RemoveSpaces() does, it may be safely replaced by one of the functions in StrUtils: the Trim* family, DelSpace, DelSpace1(), etc. which work on all "string" types
« Last Edit: July 22, 2019, 05:42:08 pm by lucamar »
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Thaddy

  • Hero Member
  • *****
  • Posts: 14204
  • Probably until I exterminate Putin.
Re: AnsiStrings. How to make code compatible with Lazarus?
« Reply #5 on: July 22, 2019, 06:11:35 pm »
AnsiStrings.pas is a compatibility unit introduced in D2009 (first unicode16 version).
Most of its functionality in FreePascal is in sysutils and strutils.
From its use, I conclude that a previous migration from a pre-D2009 version to a D2009+ version was likely never done correctly and/or complete.

You are now running into trouble because of it, but the cause is ^^
Specialize a type, not a var.

IQuitDelphiToo

  • New member
  • *
  • Posts: 8
Re: AnsiStrings. How to make code compatible with Lazarus?
« Reply #6 on: July 23, 2019, 03:07:16 pm »
Maybe I asked the question the wrong way. What I want to achieve is temporary compatibility between Delphi and Lazarus. Once I make all my libs compile on both Delphi/Lazarus and everything runs smooth, I will port one more time from "Delphi/Lazarus mix" to pure Lazarus.

So, for the moment it would be the best option to just comemnt out {ifdef Lazarus/Delphi} those ansistring procedures?

--------------------

> RemoveSpaces: it may be safely replaced by one of the functions in Trim family.
Maybe, I just used it as an example. I have MANY overloads for my string functions.

--------------------

> Two possibilities: 1) your source is in {$mode delphi} or 2) it has a {$H+} (or equivalent {$longstrings}) directive.

Yes. My unit was put automatically in "delphi mode" after I ran it through the DelphiToLazarus wizard (under Tools). So, what will happen if I remove the {$mode delphi} directive?


« Last Edit: July 23, 2019, 03:11:28 pm by IQuitDelphiToo »

Thaddy

  • Hero Member
  • *****
  • Posts: 14204
  • Probably until I exterminate Putin.
Re: AnsiStrings. How to make code compatible with Lazarus?
« Reply #7 on: July 23, 2019, 03:13:06 pm »
rename all strings in your old code to ansistring. that's easy with the refactoring options from either delphi or lazarus.
Specialize a type, not a var.

IQuitDelphiToo

  • New member
  • *
  • Posts: 8
Re: AnsiStrings. How to make code compatible with Lazarus?
« Reply #8 on: July 23, 2019, 03:41:04 pm »
What I want to achieve is temporary compatibility between Delphi and Lazarus.

> rename all strings in your old code to ansistring.


If I do so none of my units will work under delphi.
I want my code to run under Lazarus AND Delphi. At least until I permanently move to Lazarus.

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: AnsiStrings. How to make code compatible with Lazarus?
« Reply #9 on: July 23, 2019, 04:06:25 pm »
I say the opposite: Use "string" instead of "ansistring". 90% (a wild guess) of your code is compatible this way. Problems arise when your Delphi code calls API (use UnicodeString in Lazarus), sometimes when you access strings by pointers (use PChar in Delphi, but PWideChar in Lazarus), sometimes when you determine string length (Length() in Delphi, UTF8Length() in Lazarus) or in some other functions where the UTF8-nature of the Lazarus strings comes into play.

I converted a lot of the JVCL units to Lazarus and only rarely had problems with strings when doing it this way. However, I did not intend to keep backward compatibility to the original sources.

If you don't have it, upgrade to Laz 2.0.2 / FPC 3.0.4 - this FPC version handles the string issue in an almost transparent way.

The real problem is in the libraries. While the bulk of the LCL components is mostsly compatible with their Delphi VCL counterparts, there are sometimes subtle differences. Larger problems occur with the non-LCL controls. Many component writers (like myself) do not care much to keep backward-compatibility to Delphi. Charts, for example, do not have more in common than the component and property names. Be prepared for a rewrite of some units/forms especially for Lazarus.


lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: AnsiStrings. How to make code compatible with Lazarus?
« Reply #10 on: July 23, 2019, 04:22:39 pm »
What I want to achieve is temporary compatibility between Delphi and Lazarus.
> rename all strings in your old code to ansistring.

If I do so none of my units will work under delphi.
I want my code to run under Lazarus AND Delphi. At least until I permanently move to Lazarus.

The ideal solution would be to use the strictest type corresponding to each overload, instead of relying on "String" being some type or other. If, as seems now to be your case, there is an overload for "normal" Delphi 2009+ strings (i.e. UnicodeString) and another for "AnsiString" then change the "String" declarations to "UnicodeString". Or whatever the case may be.

As an example, if you look in FPC's RTL you'll see lots of string functions overloaded as:
Code: Pascal  [Select][+][-]
  1. procedure AStringProc(AString: RawByteString); overload;
  2. procedure AStringProc(AString: UnicodeString); overload;
that specifictiy makes those functions independent of the mode you're in, whether "string" is a short or long string, etc.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Thaddy

  • Hero Member
  • *****
  • Posts: 14204
  • Probably until I exterminate Putin.
Re: AnsiStrings. How to make code compatible with Lazarus?
« Reply #11 on: July 23, 2019, 06:37:29 pm »
I say the opposite: Use "string" instead of "ansistring". 90% (a wild guess) of your code is compatible this way. Problems arise when your Delphi code calls API (use UnicodeString in Lazarus), sometimes when you access strings by pointers (use PChar in Delphi, but PWideChar in Lazarus), sometimes when you determine string length (Length() in Delphi, UTF8Length() in Lazarus) or in some other functions where the UTF8-nature of the Lazarus strings comes into play.
I did also *a lot* of professional conversions - really huge projects from Delphi to Delphi unicode - and I stick with my original statement. Why? Every kind of string indexing or database handling (existing databases) is hopeless otherwise. Better to compile those units with the old string type and make use of automatic conversion in its use. (After all, one obviously did not need UTF8)
That goes for both Delphi, Freepascal and especially for Lazarus with its quirky UTF8 hacks.
Moving code that uses any kind of string  indexing - including length - is easier between Delphi and FPC is a lot easier than moving code between Delphi and Lazarus because of that.
(Because old code satisfies the UCS2 subset of Unicode16, thatś how Delphi did it in first instance))
The safe way is old code char -> AnsiChar, Old code string -> Ansistring. Old code Pchar ->PAnsiChar.  Old code Widestring -> Keep.
That way you know the code still works. That's also the point of the AnsiStrings unit: it is an intermediate resolution introduced for exactly this purpose.
Depending on codepage, conversions to Unicode16 is relatively easier than to UTF8. Just an observation.

Of course you can convert anything, but that is both costly and time consuming. Hence my remark: if the AnsiStrings unit is used, the conversion was never fully done.
But in my opinion that is at least where you should start with any chance of success unless you have a team of programmers dedicated to the task or too much time on hand.

That is to say: in an ideal world you should do the conversion properly, I agree, but that is what I wrote. Your advice is not always necessary, and takes a lot more effort, something that can often hardly be explained or justified in a commercial setting.
« Last Edit: July 23, 2019, 06:50:10 pm by Thaddy »
Specialize a type, not a var.

 

TinyPortal © 2005-2018