If you have a function or method with a var parameter of type ShortString then it also passes the maximum size of the provided string variable as a hidden parameter. This is only done if the parameter is declared as ShortString, but not for any other String[<N>] even String[255]! Also this is not done for out parameters. Something like this also isn't done if the result is a ShortString.
As @ccrause already mentioned above, it really seems that, in some cases, the extra hidden "max size" parameter to manage the string size correctly is a necessity.
Yes, and these cases are
var and
out parameters (the later FPC only) with a
ShortString parameter if and only if
$P /
$OpenStrings is enabled. In that case the type of the parameter becomes in fact
OpenString (which is an internal type provided by both FPC and Delphi) which in turn leads to a hidden parameter that contains the maximum length of the string variable passed in (if you explicitly declare the parameter as
OpenString it will have the additional parameter for any modifier
except for ordinary by-value; at least in FPC, in Delphi that's only the case for
var parameters of that type).
If
$P /
$OpenStrings is
not enabled - which is the default in FPC (except for mode
Delphi since
yesterday 
) - then there simply will be no such parameter. In that case it will depend upon the setting of the
$V /
$VarStringChecks directive: if it's enabled (default in modes
Delphi and
TP) then the compiler won't allow you to pass smaller string variables to such functions. Otherwise (which is the default in the other modes) the compiler will allow you to do that and you as a developer need to make sure that you don't pass anything that could lead to a buffer overflow or something like that.
This behaviour itself is fully compatible to how Delphi handles this, only the defaults are different.
Regarding my feedback to
ccrause: it turns out that the
System unit contains an explicit
$P+ in
rtl/inc/systemh.inc which means that any of the helper functions take a
var or
out parameter of type
ShortString already contains the correct
High value of the parameter. It was simply a matter of implementing
ccrause's change correctly and
now FPC will handle the example by
dseligo better:
It is better to stick to shortstring instead of ansistring or others. On most if not all platforms this reserves just 256 byte or even less if declared properly.
E.g. if you know before hand what the maximum string length is, you can declare a shortstring type with the appropriate size:type Tmystring = string[16];
will occupy just 17 bytes on embedded targets. (one for size)
Yes, I use shortstring with appropriate size and it occupies size + 1. Problem is when strings are concatenated.
Consider this program:
program project1;
{$mode ObjFPC}
var s1: String[10];
begin
s1 := 'test';
s1 := s1 + '123';
end.
While
dseligo used AVR code I'll show the result in x86_64 code (the result will be the same on all platforms as the optimization is done at a higher level):
Before:
.globl main
main:
.globl PASCALMAIN
PASCALMAIN:
.Lc2:
# Temps allocated between rbp-256 and rbp+0
.seh_proc main
# [tshortstr.pp]
# [5] begin
pushq %rbp
.seh_pushreg %rbp
.Lc3:
movq %rsp,%rbp
.Lc4:
leaq -288(%rsp),%rsp
.seh_stackalloc 288
.seh_endprologue
call fpc_initializeunits
# [6] s1 := 'test';
leaq _$TSHORTSTR$_Ld1(%rip),%rax
leaq U_$P$TSHORTSTR_$$_S1(%rip),%rcx
movq $10,%rdx
movq %rax,%r8
call fpc_shortstr_to_shortstr
# [7] s1 := s1 + '123';
leaq _$TSHORTSTR$_Ld2(%rip),%r9
leaq U_$P$TSHORTSTR_$$_S1(%rip),%r8
leaq -256(%rbp),%rcx
movq $255,%rdx
call fpc_shortstr_concat
leaq -256(%rbp),%r8
leaq U_$P$TSHORTSTR_$$_S1(%rip),%rcx
movq $10,%rdx
call fpc_shortstr_to_shortstr
# [8] end.
call fpc_do_exit
ret
.seh_endproc
After:
.globl main
main:
.globl PASCALMAIN
PASCALMAIN:
.Lc2:
.seh_proc main
# [tshortstr.pp]
# [5] begin
pushq %rbp
.seh_pushreg %rbp
.Lc3:
movq %rsp,%rbp
.Lc4:
leaq -32(%rsp),%rsp
.seh_stackalloc 32
.seh_endprologue
call fpc_initializeunits
# [6] s1 := 'test';
leaq _$TSHORTSTR$_Ld1(%rip),%rax
leaq U_$P$TSHORTSTR_$$_S1(%rip),%rcx
movq $10,%rdx
movq %rax,%r8
call fpc_shortstr_to_shortstr
# [7] s1 := s1 + '123';
leaq _$TSHORTSTR$_Ld2(%rip),%r9
leaq U_$P$TSHORTSTR_$$_S1(%rip),%r8
leaq U_$P$TSHORTSTR_$$_S1(%rip),%rcx
movq $10,%rdx
call fpc_shortstr_concat
# [8] end.
call fpc_do_exit
ret
.seh_endproc
.Lc1:
Note the highlighted stack allocations and also the missing “Temps allocated” line in the second example.
High(s) is correct if parameter is "const {/var/out} s: OpenString;",
regardless of {$P} directive.
Please note that this is only true in FPC (also for
constref). In Delphi this will be
only true for
var parameters.