One of my considerations addresses the handling of stdin/stdout/stderr. These
are available in a Delphi program as TextFile, but this type only has limited
support (no file position...). How else can the standard handles be accessed,
in a more general way also suitable for binary data?
Another point is the File I/O mode, what are the benefits and drawbacks of
using IOResult vs. using exceptions? Existing C code may be ported easier when
using IOResult, but exceptions may simplify the ported code and make it more
transparent.
DoDi
How about Windows file handles?
> One of my considerations addresses the handling of stdin/stdout/stderr. These
> are available in a Delphi program as TextFile,
The Delphi 5 help doesn't give any indication of that limitation. It
says you can pass a file variable of any type to AssignFile. Specify an
empty file name, and you'll get stdin from Reset, stdout from Rewrite.
If you want stderr, then call Rewrite, and then set the underlying file
record's handle with GetStdHandle:
AssignFile(stdout, '');
Rewrite(stdout);
TFileRec(stdout).Handle := GetStdHandle(Std_Error_Handle);
> but this type only has limited
> support (no file position...).
Do the three standard file handles have file positions, either?
> How else can the standard handles be accessed,
> in a more general way also suitable for binary data?
The WriteFile API function will write anything you give it, binary or
text. As mentioned above, get a standard handle with GetStdHandle.
> Another point is the File I/O mode, what are the benefits and drawbacks of
> using IOResult vs. using exceptions? Existing C code may be ported easier when
> using IOResult, but exceptions may simplify the ported code and make it more
> transparent.
If I my goal were a line-for-line translation of the C code to Delphi,
then I would use IOResult or GetLastError. If my goal were Delphi code
that implemented the same specification as the C code, then I'd use
exceptions and streams.
--
Rob
> In porting C code to Pascal a decision has to be made ...
I would start with deciding whether to port literally or literately
(to borrow a few words from the other Doctor). That should answer
any subsequent questions.
Groetjes,
Maarten Wiltink
there is also FILE , not just TextFile if you want to remain with the
pascal style to port over.
for example
Var
F:File; // Raw record size and RESET(F, RecordSize ?);// size could
be 1 byte of what ever., when using the BlockReads/BlockWrites it will
read/write in the recordsize*theNumberOfUNits;
F:File Of Byte; // plain old RESET(F) does it here. and you can use
Write(f,Variable); Read(F, variable); for that.
F:File of Word, Dword etc..
when using the non-text modes the FileMode is generally set to 2 which
indicates Read/write random access.
in some cases this may not want to be the desired access.
prior to RESET you can set the FileMode := 0;// read only;1, write and
2 read and write..
make sure that put the default value back to 2 after the Reset/Rewrite..
Reset opens an existing file.
one should use the Result variable after to test this.
rewrite will create or recreate the file.
P.S.
Use the {$I-} in your code because if an Pascal file error takes place
it will pop up an io error screen and interrupt your code .
with this switch off, you can test it in your own code properly.
also.
prior to any new entry of of Io code using the Pascal method one
should do this.
InOutRes := 0; to clear any old errors that my be hanging around.
once you reference the IoResult it will clear this for you.
but there maybe times where you perform another IO attempt with
a last io process generating and error that you didn't clear! if
this is the case then the next Io Pascal operation will fail because
while there is an error hanging there it won't perform any more
io's
To accomodate this, FPC adds a getfilehandle() function, to obtain the raw
filehandle in an OS independant way.
This is e.g. also useful when you have to pass something of text or file to
a C library.
> Another point is the File I/O mode, what are the benefits and drawbacks of
> using IOResult vs. using exceptions?
Exceptions are a lot heavier language construct, that you put on your most
basic RTL level this way. IOResult can be inline even.
> Existing C code may be ported easier when using IOResult, but exceptions
> may simplify the ported code and make it more transparent.
I reserve the exceptions for the higher level, mostly out of performance,
but also when you use exceptions on too many levels in the libraries,
programmers tend to create very broad catch-all except statements, to catch
all cases. This is IMHO bad programming.
>Exceptions are a lot heavier language construct, that you put on your most
>basic RTL level this way.
It depends ;-)
When I look at the many procedures checking and setting an "internal" stream
state, I really would prefer to throw exceptions and to catch these in a few
(well, not always easily determinable) subroutines.
The FPC Tstream.Error method seems to be a compromise between SEH and
individual error checks - unfortunately it doesn't integrate easily into
various C projects :-(
Hint: try to unify paszlib and bzip2lib, and make the result work also in a
Delphi environment...
BTW I found an bug in paszlib (zlib-1.1.4) that already has been cured in
zlib-1.2.2.
Thanks to all contributors here. I think that I got the technical details, what
remains to find out is the "best" overall solution.
DoDi
> BTW I found an bug in paszlib (zlib-1.1.4) that already has been cured in
> zlib-1.2.2.
You can always sent a patch/mail to core at freepascal dot org or to me.
>You can always sent a patch/mail to core at freepascal dot org or to me.
Okay, I found some more strange pieces of code, also in objects and bzip2...
The fpc streams have inspired me a lot, now I'm implementing my own TXStreams.
Errors are handled in a fpc-like Error procedure, that can raise exceptions if
desired. The base class is a (Windows) Handle stream, covering almost every
stream kind, including console, pipes etc.
The error codes of the various I/O and OS models are somewhat incompatible, so
I made all methods procedures, besides for Read and Write which return the
number of bytes transferred, for convenience. If some application wants to get
results from other methods, these can be provided by appropriate wrappers.
I'm just breaking my head on EOF and the stream position. I did not yet
understand how Tbufstream maintains the correct position. Perhaps the exact
position should not be tracked in the (buffered) streams, instead it can be
computed whenever (rarely) requested. The most important buffer information
seems to be the number of available bytes, for Read or Write. The R/W position
then has this distance from the end of the buffer. The calculation of the
stream position has to take into account the different buffer positions: for
input buffers the file position is at the end of the buffer, and for output
buffers it's at the begin of the buffer. It might be best to track the file
position of the end of the buffer, so that the stream position is that position
minus the available bytes. This model also works for partially filled input
buffers at EOF.
I'm not sure how important it is nowadays to have buffers in sync with file
positions (sectors, clusters). Reading/Writing huge data records can bypass the
file buffers, whereupon the next R/W position can be not aligned to file
entities.
For Unget I found a simple solution, years ago. The input buffer is prepended
with a couple of bytes, that serve as an underflow area for unget at the begin
of the buffer. It also will be possible to unget records this way...
DoDi