Hello,
Do you think it is possible to modify the prcedure "FillChar()" to make it work correctly with the type String(xx)?
For example the code: VAR S10 : String(10); .... FillChar(S10, 10, 'A');
WriteLn(S10);
Would display: AAAAAAAAAA
I ask this because I am working on (converting to GPC) a lot of existing code, with FillChar() spread everywhere (more than 1200 SUN Pascal sources), and it works as expected on strings on SUN Pascal. However, since there is no 'Capacity' field in SUN Pascal strings, it cannot be overridden. I understand this can be a big job since calling FillChar() on a record containing Strings should work as well. I think this would be a nice feature anyway, don't you?
Also, if this cannot be done or desirable from your point of view, is there a way to override the build-in FillChar() for one of mine that would be called for the type String(xx) only?
I know this is not an elegant workaround, but this problem causes a lot of nasty bugs in the converted programs. If I was to write the code from scratch this would not be an issue, but rewriting more than 600 000 lines of Pascal code is an even bigger challenge :-(
Regards
Pascal Viandier pascal@accovia.com
Pascal Viandier wrote:
Do you think it is possible to modify the prcedure "FillChar()" to make it work correctly with the type String(xx)?
For example the code: VAR S10 : String(10); .... FillChar(S10, 10, 'A');
That's not how it's meant to work ("defined" by UCSD/BP). Do:
SetLength (S10, 10); FilLChar (S10[1], 10, 'A');
Note the `[1]'.
This has chances to work with various string formats.
Or look at StringOfChar (GPC unit).
I ask this because I am working on (converting to GPC) a lot of existing code, with FillChar() spread everywhere (more than 1200 SUN Pascal sources), and it works as expected on strings on SUN Pascal. However, since there is no 'Capacity' field in SUN Pascal strings, it cannot be overridden. I understand this can be a big job since calling FillChar() on a record containing Strings should work as well. I think this would be a nice feature anyway, don't you?
Not really, sorry ...
Also, if this cannot be done or desirable from your point of view, is there a way to override the build-in FillChar() for one of mine
FillChar is not a reserved word, just a predefined identifier. You can override it as other identifiers.
that would be called for the type String(xx) only?
Just give it a `var s: String' parameter. The built-in version will then not be available anymore, of course.
Of course, you can rename your new routine. I don't know how easy or difficult it would be -- you only mentioned the total size of 600000 lines, but that's irrelevant as most of those lines do not contain FillChar, I suppose. You could do it semi-automatically, by first renaming all occurrences to the new name, and then renaming back those that do not compile with your routine (and therefore are not strings).
Does the case with arrays/records containing strings actually occur in the program?
Which kind of strings does Sun Pascal use? Strings with a length field (and if so, does its FillChar do what you want on an array/record containing such strings)? Or simply (packed) arrays of Char, as by ISO 7185. In this case, you can do the same in GPC and FillChar will work on them.
Frank
Hello,
Thanks a lot for your suggestions.
However, by reading your answer I see no easy way to solve my problem.
In fact, there are to main patterns for using FillChar() in the code I work on: 1- Strings initialization as in the sample code bellow. For these cases, the first workaround you suggest works well and it is no big deal to change the code. 2- Records initialization. This is most of the FillChar() calls. The records (around 600 of them) are used to map on rows of tables in an Informix database. They contain various data types and strings as well. These strings are SUN Pascal Varying[n] Of Char. Internally, there are 4 bytes indicating the current string length and an Array of n Char. Since FillChar() is called with Chr(0) on them, this actually sets the current string length to 0 and fill them with zeroes. In this case, I think the best approach will be to generate procedures to do the records initialization field by field and call them instead of FillChar(). With a moderate amount of work, my translation program should be able to take care of this.
Also, when trying some ways to solve my problem, I encountered a behaviour that I don't clearly understand regarding the "Capacity" field: If I FillChar() a String with Chr(0), the Capacity field is set to 0 - as expected - that's my problem :-(. Then, if I pass this string as Var parameter to a procedure, I cannot put anything in it. There is no runtime error or warning, but the string stays empty, WriteLn() does not show anything - in the procedure or after returning from it -. But, If I re-initialize the string directly (S10 := 'AAAAA' for example): 1- the Capacity field stays 0 - that's normal 2- but I can now use WriteLn() on it and it shows what I put in it ('AAAAA').
What is exactly the role of the "Capacity" field? Is this an expected behaviour? Did I missed something?
Thanks again for your help. It is always fast and efficient.
Regards
Pascal Viandier pascal@accovia.com
Pascal Viandier wrote:
Do you think it is possible to modify the prcedure "FillChar()" to make it work correctly with the type String(xx)?
For example the code: VAR S10 : String(10); .... FillChar(S10, 10, 'A');
That's not how it's meant to work ("defined" by UCSD/BP). Do:
SetLength (S10, 10); FilLChar (S10[1], 10, 'A');
Note the `[1]'.
This has chances to work with various string formats.
Or look at StringOfChar (GPC unit).
Snip
Also, if this cannot be done or desirable from your point of view, is
there
a way to override the build-in FillChar() for one of mine
FillChar is not a reserved word, just a predefined identifier. You can override it as other identifiers.
that would be called for the type String(xx) only?
Just give it a `var s: String' parameter. The built-in version will then not be available anymore, of course.
Of course, you can rename your new routine. I don't know how easy or difficult it would be -- you only mentioned the total size of 600000 lines, but that's irrelevant as most of those lines do not contain FillChar, I suppose. You could do it semi-automatically, by first renaming all occurrences to the new name, and then renaming back those that do not compile with your routine (and therefore are not strings).
Does the case with arrays/records containing strings actually occur in the program?
Which kind of strings does Sun Pascal use? Strings with a length field (and if so, does its FillChar do what you want on an array/record containing such strings)? Or simply (packed) arrays of Char, as by ISO 7185. In this case, you can do the same in GPC and FillChar will work on them.
Frank
Pascal Viandier wrote:
Hello,
Thanks a lot for your suggestions.
However, by reading your answer I see no easy way to solve my problem.
In fact, there are to main patterns for using FillChar() in the code I work on: 1- Strings initialization as in the sample code bellow. For these cases, the first workaround you suggest works well and it is no big deal to change the code. 2- Records initialization. This is most of the FillChar() calls. The records (around 600 of them) are used to map on rows of tables in an Informix database. They contain various data types and strings as well. These strings are SUN Pascal Varying[n] Of Char. Internally, there are 4 bytes indicating the current string length and an Array of n Char. Since FillChar() is called with Chr(0) on them, this actually sets the current string length to 0 and fill them with zeroes. In this case, I think the best approach will be to generate procedures to do the records initialization field by field and call them instead of FillChar(). With a moderate amount of work, my translation program should be able to take care of this.
It seems to me that Extended Pascal's initial-state-specifier would be the ideal solution for your problem. Initial-state-specifiers are designed to exactly what you're try to accomplish here with you're hackish use of FillChar() and have the additional benefit of correctly initializing a variable of any Pascal type without needing to worry about the implementation pecularities.
You can use initial-state-specifiers in any type or variable declaration. When used in a type declaration, any variable of that type unless overridden by a initial-state-specifier in the variable declaration will be initialized to the specified initial state upon activation of the block containing the variable declaration. When used in a variable declaration, the declared variable(s) will be initialized to the specified initial state upon activation of the block containing the variable(s) declaration.
Some examples:
type PsuedoStr10 = record length: cardinal; chars: packed array [1..10] of char; end value [length: 0; chars: [1..10: chr(0)]]; var FakeStr10: PsuedoStr10; {initialized to length = 0 and all chars elements to chr(0)} AsteriskStr10: PsuedoStr10 value [length: 10; chars: [otherwise '*']]; {initialized to length = 10; all chars elements to '*'}
Note: The initial-state-specifiers "chars: [1..10: chr(0)]" and "chars: [otherwise '*']" both initialize 10 elements in this particular example. For an all array elements initial-state-specifier, "chars: [otherwise '*']" is preferrable since it will automaticly adjust to any changes to the array size in the chars field type declaration.
I believe Extended Pascal's initial-state-specifiers are supported in the latest GPC version but I haven't actual tested it.
Gale Paeper gpaeper@empirenet.com
Pascal Viandier wrote:
2- Records initialization. This is most of the FillChar() calls. The records (around 600 of them) are used to map on rows of tables in an Informix database. They contain various data types and strings as well. These strings are SUN Pascal Varying[n] Of Char. Internally, there are 4 bytes indicating the current string length and an Array of n Char. Since FillChar() is called with Chr(0) on them, this actually sets the current string length to 0 and fill them with zeroes.
If you only want to set the length to 0, you can just assign the empty string (S := ''), which is more efficient than overwriting all characters. Unless you require the (unused) characters to be Chr(0) for some particular reason ...
But if you mean by "map" that the data structure must correspond to your database, then probably an EP string won't do anyway, because of the Capacity field. You can build you own data structure, containing a length field and a character array then.
In this case, I think the best approach will be to generate procedures to do the records initialization field by field and call them instead of FillChar(). With a moderate amount of work, my translation program should be able to take care of this.
Also, when trying some ways to solve my problem, I encountered a behaviour that I don't clearly understand regarding the "Capacity" field: If I FillChar() a String with Chr(0), the Capacity field is set to 0 - as expected - that's my problem :-(. Then, if I pass this string as Var parameter to a procedure, I cannot put anything in it. There is no runtime error or warning, but the string stays empty, WriteLn() does not show anything - in the procedure or after returning from it -. But, If I re-initialize the string directly (S10 := 'AAAAA' for example): 1- the Capacity field stays 0 - that's normal 2- but I can now use WriteLn() on it and it shows what I put in it ('AAAAA').
What is exactly the role of the "Capacity" field? Is this an expected behaviour?
The behaviour is undefined, as Capacity (and other schema discriminants) must not be written to. Normally GPC prohibits that, but with low-level tricks such as FillChar you can bypass normal checks. That's why such low-level tricks must be used with care.
As for the exact behaviour you observed, it might be that for direct access to the variable (not as a paramater) the compiler uses the constant known discriminant value instead of reading the actual value. If you send a test program, I could check it, but it probably won't matter much, as it's undefined behaviour anyway.
Gale Paeper wrote:
It seems to me that Extended Pascal's initial-state-specifier would be the ideal solution for your problem. Initial-state-specifiers are designed to exactly what you're try to accomplish here with you're hackish use of FillChar() and have the additional benefit of correctly initializing a variable of any Pascal type without needing to worry about the implementation pecularities.
Yes, if you actually want to initialize the character array, this could be a rather easy solution.
I believe Extended Pascal's initial-state-specifiers are supported in the latest GPC version but I haven't actual tested it.
Mostly yes. However, the feature is rather new, so please test a bit more carefully and report any bugs found.
There's one case that doesn't fully work yet: Initializers of (structures containing) variant records. There are some hairy issues there. Some cases may work, but there are still problems. So for now, better consider them not working. I'll announce in the release notes of a future version (not the next one, though) when they will really work.
Frank
Hi,
- In fact, the rows in the database store only the string contents, not the "capacity" and "length" fields. There is some (C and Pascal) code to do the mapping between the Pascal Records and the database contents both ways.
The FillChar question occurs when the code writes Pascal Records containing strings into the database, because the full contents (value of the Capacity field) of the string is written to it.
For example, if I have a String(10) variable containing 'ABC', 10 characters are written to the database. If there is "garbage" in the remaining 7 characters, it will be written too. And when the record will by retrieved, the garbage will be part of the string because the string length is taken from the database schema.That's why I hoped FillChar() was working for strings...
At the early stages of the conversion, I tried "Array[1..x] Of Char" but I lost almost all of the GPC facilities for strings manipulation, and the automatic blank padding when comparing them generated problems. I also tried my own schemata, similar to the String but with no "Capacity" field. It was worse: I lost all string facilities and more, I was unable to assign contents directly as "MyStr := 'ABC';" (I was unable to implement a custom assignment operator ":=") so I adopted the String() type.
- Thanks for the answer to my question regarding the Capacity field after a call to FillChar. It is clearer for me now. So, do not spend your precious time to test the result of an operation which should not be done. However, I wouldn't call "a low-level trick" the call to FillChar() ;-)
- As Gale Paeper suggested I tried Extended Pascal's initial-state-specifier and since I do not use variant records in records, this works pretty well and I found no bug in my case. However I decided to generate initialization procedures automatically anyway since the existing code needs more than first-time initialization: FillChar() is potentially called many times on the same variable (between database accesses for example).
Thank a lot for your help.
Pascal Viandier pascal@accovia.com
Pascal Viandier wrote:
2- Records initialization. This is most of the FillChar() calls. The
records
(around 600 of them) are used to map on rows of tables in an Informix database. They contain various data types and strings as well. These
strings
are SUN Pascal Varying[n] Of Char. Internally, there are 4 bytes
indicating
the current string length and an Array of n Char. Since FillChar() is
called
with Chr(0) on them, this actually sets the current string length to 0 and fill them with zeroes.
If you only want to set the length to 0, you can just assign the empty string (S := ''), which is more efficient than overwriting all characters. Unless you require the (unused) characters to be Chr(0) for some particular reason ...
But if you mean by "map" that the data structure must correspond to your database, then probably an EP string won't do anyway, because of the Capacity field. You can build you own data structure, containing a length field and a character array then.
In this case, I think the best approach will be to generate procedures to
do
the records initialization field by field and call them instead of FillChar(). With a moderate amount of work, my translation program should
be
able to take care of this.
Also, when trying some ways to solve my problem, I encountered a behaviour that I don't clearly understand regarding the "Capacity" field: If I FillChar() a String with Chr(0), the Capacity field is set to 0 - as expected - that's my problem :-(. Then, if I pass this string as Var parameter to a procedure, I cannot put anything in it. There is no runtime error or warning, but the string stays empty, WriteLn() does not show anything - in the procedure or after returning from it -. But, If I re-initialize the string directly (S10 := 'AAAAA' for example): 1- the Capacity field stays 0 - that's normal 2- but I can now use WriteLn() on it and it shows what I put in it ('AAAAA').
What is exactly the role of the "Capacity" field? Is this an expected behaviour?
The behaviour is undefined, as Capacity (and other schema discriminants) must not be written to. Normally GPC prohibits that, but with low-level tricks such as FillChar you can bypass normal checks. That's why such low-level tricks must be used with care.
As for the exact behaviour you observed, it might be that for direct access to the variable (not as a paramater) the compiler uses the constant known discriminant value instead of reading the actual value. If you send a test program, I could check it, but it probably won't matter much, as it's undefined behaviour anyway.
Gale Paeper wrote:
It seems to me that Extended Pascal's initial-state-specifier would be the ideal solution for your problem. Initial-state-specifiers are designed to exactly what you're try to accomplish here with you're hackish use of FillChar() and have the additional benefit of correctly initializing a variable of any Pascal type without needing to worry about the implementation pecularities.
Yes, if you actually want to initialize the character array, this could be a rather easy solution.
I believe Extended Pascal's initial-state-specifiers are supported in the latest GPC version but I haven't actual tested it.
Mostly yes. However, the feature is rather new, so please test a bit more carefully and report any bugs found.
There's one case that doesn't fully work yet: Initializers of (structures containing) variant records. There are some hairy issues there. Some cases may work, but there are still problems. So for now, better consider them not working. I'll announce in the release notes of a future version (not the next one, though) when they will really work.
Frank
Pascal Viandier wrote:
- In fact, the rows in the database store only the string contents, not the
"capacity" and "length" fields.
There must be some way to store length info. If you do that by blank-padding, then you might consider fixed-strings, i.e., (packed) arrays of Char, as defined in ISO 7185 (it requires packed, GPC doesn't require it), which naturally behave this way and don't have Capacity and Length fields. Since in EP (and thus GPC) these are assignment-compatible to other string types, you can use them only for storing/loading data if you like, and use other string types in operations.
At the early stages of the conversion, I tried "Array[1..x] Of Char" but I lost almost all of the GPC facilities for strings manipulation, and the automatic blank padding when comparing them generated problems.
So how do you retrieve the length? If you get it differently, you could still do something like:
var Length: Integer; Foo: packed array [1 .. x] of Char; s: String (x);
begin { retrieve Foo, Length } if Length = 0 then s := '' else s := Foo[1 .. Length]; end;
Similar for storing.
I also tried my own schemata, similar to the String but with no "Capacity" field.
If you really mean a schema type, it wouldn't help. All schemas in GPC store their disciminants, as String does Capacity.
I was unable to assign contents directly as "MyStr := 'ABC';" (I was unable to implement a custom assignment operator ":=") so I adopted the String() type.
Can't comment on this, without any concrete examples.
- Thanks for the answer to my question regarding the Capacity field after a
call to FillChar. It is clearer for me now. So, do not spend your precious time to test the result of an operation which should not be done. However, I wouldn't call "a low-level trick" the call to FillChar() ;-)
It it a low-level trick, as it does direct memory manipulation, stepping around Pascal typing!
- As Gale Paeper suggested I tried Extended Pascal's initial-state-specifier
and since I do not use variant records in records, this works pretty well and I found no bug in my case. However I decided to generate initialization procedures automatically anyway since the existing code needs more than first-time initialization: FillChar() is potentially called many times on the same variable (between database accesses for example).
You could assign from a local, initialized variable:
type UninitializedMyType = record ... end; InitializedMyType = UninitializedType value [ ... ];
procedure InititalizeMyType (var a: UninitializedType); var Init: InititalizeMyType; begin a := Init end;
Frank
Hi,
<snip>
I also tried my own schemata, similar to the String but with no
"Capacity"
field.
If you really mean a schema type, it wouldn't help. All schemas in GPC store their disciminants, as String does Capacity.
I was unable to assign contents directly as "MyStr := 'ABC';" (I was unable to implement
a
custom assignment operator ":=") so I adopted the String() type.
Can't comment on this, without any concrete examples.
I created a schema that would map on SUN Pascal Varying[n] Of Char:
Type SString (Len: Integer) = Record SStr : Array [1..Len] Of Char; End Value [SStr: [Otherwise Chr(0)]]; Var Str20 : SString(20);
Then I tried to create an operator ":=" to be able to do Str20 := 'abcd'; in the program and set Str20.SStr with the value assigned. However it looks like it is not possible to create the operator ":=" (The code does not compile. It stops with a syntax error on the operator declaration).
Anyway, I modified the program so the mapping of the records with the database contents works with GNU Pascal Strings both ways.
So the FillChar and Records mapping are both solved.
Thanks for your useful remarks.
Pascal Viandier pascal@accovia.com