Recent

Author Topic: Appending a Char onto a PChar  (Read 6345 times)

syntonica

  • Full Member
  • ***
  • Posts: 120
Appending a Char onto a PChar
« on: December 22, 2019, 11:43:39 pm »
There must be a better way!  I can find no way of conversion or coercion to do this ridiculously simple task!  >:(
Code: Pascal  [Select][+][-]
  1. var
  2.   X, TextString: PChar;
  3.   theChar: Char;
  4. begin
  5.   X := 'X';
  6.   X[0] := theChar;
  7.   strcat(textString, X)
  8. end;
  9.  
This is the only safe way I've found so far. I don't know how long my PChars are since they are managed. Unfortunately, since I'm writing what is essentially a C library under a pre-existing framework, I'm stuck with C-strings.

On the plus side, I've completed my code conversion, I can compile my library and promptly crash the host program! :D

Using XCode, it's multithreaded compile of the C++ code takes ~10sec. Compilation by FPC takes under 2 seconds. And with 3 CPU cores tied behind its back!

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12857
  • FPC developer.
Re: Appending a Char onto a PChar
« Reply #1 on: December 22, 2019, 11:59:04 pm »
Code: Pascal  [Select][+][-]
  1.  X := 'X';

you point a pointer to a literal (possibly readonly memory)

Code: Pascal  [Select][+][-]
  1.  
  2.   X[0] := theChar;
  3.  

you try to change the literal

Code: Pascal  [Select][+][-]
  1.  
  2.   strcat(textString, X)
  3.  

Then append the literal to an unitialized variable.

The better way is called "string" type :-)

If you really want pchar, study the examples in the strings unit (from which strcat hails) thoroughly.

And then there are some overloads in sysutils.

Note you can pass an ansistring to a C routine by simply typecasting to pchar. As long as the c routine doesn't change length, and only writes to the already used chars, it will be fine.

then just do

setlength(s,strlen(pchar(s)); to set the internal length to wherever C has put a #0 char.
« Last Edit: December 23, 2019, 12:01:53 am by marcov »

dbannon

  • Hero Member
  • *****
  • Posts: 3808
    • tomboy-ng, a rewrite of the classic Tomboy
Re: Appending a Char onto a PChar
« Reply #2 on: December 23, 2019, 12:03:08 am »
Yeah, thats the way it is really.

PChar is, really, just a pointer to some 'text' in memory. If that text is created and managed by a ANSIString, then you need to let the ANSIstring code manage it or bad things happen.

If you can manage the text pointed to by your pChar yourself, C style, allocate some memory keep within its bounds, you would not need to use the ANSIString functions but the down side is you cannot use the ANSIString functions !

FPC does have some tools to manage pure c type string ....

Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Appending a Char onto a PChar
« Reply #3 on: December 23, 2019, 12:19:58 am »
Hi !

Since some time the conversion between Pchar and String is easy:

Code: Pascal  [Select][+][-]
  1. var P : Pchar:
  2.       S : String;
  3.  
  4. s := p;
  5. // and vice versa
  6. p := pchar(s);  
  7.  
Winni

syntonica

  • Full Member
  • ***
  • Posts: 120
Re: Appending a Char onto a PChar
« Reply #4 on: December 23, 2019, 12:41:38 am »
Yeah, thats the way it is really.

PChar is, really, just a pointer to some 'text' in memory. If that text is created and managed by a ANSIString, then you need to let the ANSIstring code manage it or bad things happen.

If you can manage the text pointed to by your pChar yourself, C style, allocate some memory keep within its bounds, you would not need to use the ANSIString functions but the down side is you cannot use the ANSIString functions !

FPC does have some tools to manage pure c type string ....

Davo
Yeah, my code example was a little terse...

Thanks. I'll check out AnsiString, but the VST framework is strictly PChars handed to me. There's no way to change it even if I could. Some of my string stuff is in very time-critical routines, so in C++, I've written my own byte-twiddling routines to work directly on strings to avoid unnecessary copies. I'll probably do the same here if the AnsiString class doesn't do what I need or doesn't do it fast enough.

dbannon

  • Hero Member
  • *****
  • Posts: 3808
    • tomboy-ng, a rewrite of the classic Tomboy
Re: Appending a Char onto a PChar
« Reply #5 on: December 23, 2019, 02:20:24 am »
A lot depends on what sort of (pascal) strings you are using. If you are working with Lazarus and a GUI app, then its almost (?) certain that 'string' means ANSISTRING, if you are building something using just FPC, just what 'string' means will depend on the mode you are working in. Shortstrings are quite different !

There are a number of PChar handling functions, see https://lazarus-ccr.sourceforge.io/docs/rtl/sysutils/pcharfunctions.html

The take home message is that you must manage the memory when doing anything that changes such string.

Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

PascalDragon

  • Hero Member
  • *****
  • Posts: 6395
  • Compiler Developer
Re: Appending a Char onto a PChar
« Reply #6 on: December 23, 2019, 09:13:20 am »
Yeah, thats the way it is really.

PChar is, really, just a pointer to some 'text' in memory. If that text is created and managed by a ANSIString, then you need to let the ANSIstring code manage it or bad things happen.

If you can manage the text pointed to by your pChar yourself, C style, allocate some memory keep within its bounds, you would not need to use the ANSIString functions but the down side is you cannot use the ANSIString functions !

FPC does have some tools to manage pure c type string ....

Davo
Yeah, my code example was a little terse...

Thanks. I'll check out AnsiString, but the VST framework is strictly PChars handed to me. There's no way to change it even if I could. Some of my string stuff is in very time-critical routines, so in C++, I've written my own byte-twiddling routines to work directly on strings to avoid unnecessary copies. I'll probably do the same here if the AnsiString class doesn't do what I need or doesn't do it fast enough.

AnsiString is not a class, it's a type. A reference counted type to be precise. So as long as you only have one reference to the string changing it's characters does not result in a new string copy, otherwise a copy-on-write mechanism is used. And as others already wrote, in most cases you'll be able to simply pass an AnsiString to the C code by using PChar(YourString). And you can also retrieve an AnsiString from a PChar by using StrPas(YourPChar). The main advantage of using AnsiString is that the count is part of the string (using Length(YourString)) and one does not need to walk the whole string in search for a #0 (what StrLen does).

If you want to use PChar nevertheless you need to handle the allocations yourself. Using GetMem(Len + 1) to allocate enough of it, using the PChar routines mentioned by dbannon to work with it and FreeMem(YourPChar) to free it again. Essentially just as you'd do in C.

440bx

  • Hero Member
  • *****
  • Posts: 6490
Re: Appending a Char onto a PChar
« Reply #7 on: December 23, 2019, 09:35:20 am »
FWIW, I think someone who is used to C is probably better off using either a pchar (and do the necessary allocations) or zero based arrays of char or shortstring (while being aware that it can host a maximum of 255 characters including the null.)  That way everything is parallel to how it's done in C (except in the case of shortstring where the character count is limited.)

Of course, that decision is the OP's to make.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12857
  • FPC developer.
Re: Appending a Char onto a PChar
« Reply #8 on: December 23, 2019, 01:39:39 pm »
It depends, static types copy a lot when you start passing them around.

I would never use shortstring. It only adds limitations, and doesn't add much.  An ansistring as a field of a class works quite fine if you set length once, and only do character access to it.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1590
    • Lebeau Software
Re: Appending a Char onto a PChar
« Reply #9 on: December 23, 2019, 08:48:13 pm »
Code: Pascal  [Select][+][-]
  1. var
  2.   X, TextString: PChar;
  3.   theChar: Char;
  4. begin
  5.   X := 'X';
  6.   X[0] := theChar;
  7.   strcat(textString, X)
  8. end;
  9.  
This is the only safe way I've found so far.

That is not even a SAFE way to do it, either. strcat() expects a null-terminated input string, but your X pointer not null-terminated.  The SAFER way (assuming textString has been pre-allocated with enough memory to receive theChar at all) would look more like this instead:

Code: Pascal  [Select][+][-]
  1. var
  2.   TextString: PChar;
  3.   X: array[0..1] of Char;
  4.   theChar: Char;
  5. begin
  6.   ...
  7.   theChar := ...;
  8.   ...
  9.   // make sure textString has been adequately pre-allocated and populated first! Then...
  10.   X[0] := theChar;
  11.   X[1] := #0;
  12.   strcat(textString, X);
  13.   ...
  14. end;
  15.  

Otherwise, you can just do this instead:

Code: Pascal  [Select][+][-]
  1. var
  2.   TextString: PChar;
  3.   theChar: Char;
  4.   len: Integer;
  5. begin
  6.   ...
  7.   theChar := ...;
  8.   ...
  9.   // make sure textString has been adequately pre-allocated and populated first! Then...
  10.   len := strlen(textString);
  11.   textString[len] := theChar;
  12.   textString[len+1] := #0;
  13.   ...
  14. end;
  15.  

Or:

Code: Pascal  [Select][+][-]
  1. var
  2.   TextString, P: PChar;
  3.   theChar: Char;
  4. begin
  5.   ...
  6.   theChar := ...;
  7.   ...
  8.   // make sure textString has been adequately pre-allocated and populated first! Then...
  9.   P := @textString[strlen(textString)];
  10.   P^ := theChar;
  11.   (P+1)^ := #0;
  12.   ...
  13. end;
  14.  
« Last Edit: December 23, 2019, 08:51:26 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

syntonica

  • Full Member
  • ***
  • Posts: 120
Re: Appending a Char onto a PChar
« Reply #10 on: December 24, 2019, 12:31:48 am »
FWIW, I think someone who is used to C is probably better off using either a pchar (and do the necessary allocations) or zero based arrays of char or shortstring (while being aware that it can host a maximum of 255 characters including the null.)  That way everything is parallel to how it's done in C (except in the case of shortstring where the character count is limited.)

Of course, that decision is the OP's to make.
I actually changed one method to use String and I didn't suffer much. ;) The code came out cleaner. I'd like to use String in all the non-critical code and I'll get it all migrated eventually.  Last time I learned Pascal was in about 1975, so quite a bit has changed and frankly, a lot of it is rather ragged from keeping all the backwards compatibility. I get stuck on finding C/C++ analogs only to find later that that's not the "right" way to do things anymore.

I'm finally starting to get into the nuts and bolts of modern Pascal. I've set up all the memory management I need to make sure all my huge arrays end up on the heap where they belong. Now, I'm learning how to use LLDB from the command line. Once you have the command cheat sheet in front of you, it's quite easy! My plugin kept politely exiting with a code 217, which apparently means Bye! and No, I'm not giving you an error report! I think it's right up there with Java's "Object is not an instance of an Object." Can't anyone just say that it's a nil pointer?  ::)

syntonica

  • Full Member
  • ***
  • Posts: 120
Re: Appending a Char onto a PChar
« Reply #11 on: December 24, 2019, 12:38:59 am »
That is not even a SAFE way to do it, either. strcat() expects a null-terminated input string, but your X pointer not null-terminated.
g has been adequately pre-allocated and populated first! Then...
X should be null-terminated. Otherwise, a lot of the docs  and examples need to be updated! Assigning PChar X to a string should automatically allocate at least 2 bytes: one for the char and one for the #0.

Anyway, I'm starting to get the hang of String and such atrocities should no longer flow forth from my hand...



PascalDragon

  • Hero Member
  • *****
  • Posts: 6395
  • Compiler Developer
Re: Appending a Char onto a PChar
« Reply #12 on: December 24, 2019, 11:02:31 am »
Code: Pascal  [Select][+][-]
  1. var
  2.   X, TextString: PChar;
  3.   theChar: Char;
  4. begin
  5.   X := 'X';
  6.   X[0] := theChar;
  7.   strcat(textString, X)
  8. end;
  9.  
This is the only safe way I've found so far.

That is not even a SAFE way to do it, either. strcat() expects a null-terminated input string, but your X pointer not null-terminated.  The SAFER way (assuming textString has been pre-allocated with enough memory to receive theChar at all) would look more like this instead:

A constant string has a format that is safe to be used with both AnsiString and PChar:
Code: [Select]
.section .rodata.n__$PROGRAM$_Ld1,"d"
.balign 8
.short 0,1
.long 0
.quad -1,1
.globl _$PROGRAM$_Ld1
_$PROGRAM$_Ld1:
.ascii "X\000"
# End asmlist al_typedconsts

My plugin kept politely exiting with a code 217, which apparently means Bye! and No, I'm not giving you an error report! I think it's right up there with Java's "Object is not an instance of an Object." Can't anyone just say that it's a nil pointer?  ::)
Please note that due to compatibility reasons Pascal generates runtime errors. If you want to have better named errors you need to include the SysUtils unit to your uses clause so that the exception handling mechanism is hooked. In that case you'll then get an EAccessViolation:

Code: Pascal  [Select][+][-]
  1. program texcept;
  2.  
  3. {$ifdef usesysutils}
  4. uses
  5.   SysUtils;
  6. {$endif}
  7.  
  8. var
  9.   p: PByte = Nil;
  10.   b: Byte;
  11. begin
  12.   b := p^;
  13. end.

Code: [Select]
PS C:\fpc\git> fpc -FEtestoutput .\fpctests\texcept.pp
Free Pascal Compiler version 3.0.4 [2019/10/27] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Win64 for x64
Compiling .\fpctests\texcept.pp
texcept.pp(10,3) Note: Local variable "b" is assigned but never used
Linking testoutput\texcept.exe
13 lines compiled, 0.1 sec, 30720 bytes code, 1316 bytes data
1 note(s) issued
PS C:\fpc\git> .\testoutput\texcept.exe
Runtime error 216 at $0000000100001465
  $0000000100001465

PS C:\fpc\git> fpc -FEtestoutput .\fpctests\texcept.pp -dusesysutils
Free Pascal Compiler version 3.0.4 [2019/10/27] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Win64 for x64
Compiling .\fpctests\texcept.pp
texcept.pp(10,3) Note: Local variable "b" is assigned but never used
Linking testoutput\texcept.exe
13 lines compiled, 0.2 sec, 70896 bytes code, 4916 bytes data
1 note(s) issued
PS C:\fpc\git> .\testoutput\texcept.exe
An unhandled exception occurred at $0000000100001585:
EAccessViolation: Access violation
  $0000000100001585


Leledumbo

  • Hero Member
  • *****
  • Posts: 8836
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Appending a Char onto a PChar
« Reply #13 on: December 24, 2019, 11:08:44 am »
My plugin kept politely exiting with a code 217, which apparently means Bye! and No, I'm not giving you an error report! I think it's right up there with Java's "Object is not an instance of an Object." Can't anyone just say that it's a nil pointer?  ::)
Not always, access to uninitialized memory area will generate the same RTE. Compile with -gl to get stacktrace when that access violation happens.

Thaddy

  • Hero Member
  • *****
  • Posts: 19165
  • Glad to be alive.
Re: Appending a Char onto a PChar
« Reply #14 on: December 24, 2019, 11:39:46 am »
X should be null-terminated.
No, that is basically to prevent accidents by C programmers.
There is really no other reason to have a terminating zero in Pascal.
Pascal string types can even contain #0's in the middle of a string.
It is just that C programmers and API's do not understand that.
Code: Pascal  [Select][+][-]
  1. {$H+}
  2. var s: string = 'Merry'#0', Christmas';
  3. begin
  4.  writeln(PChar(s));
  5.  writeln(s);
  6. end.
Merry Christmas.... 8-)
« Last Edit: December 24, 2019, 11:50:25 am by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

 

TinyPortal © 2005-2018