I have to write a wrapper for the C-API of the library GDAL to generate FreePascal bindings under Debian 12 and GDAL version 3.2.2.
One of the functions is:
void CPL_DLL OGR_G_DumpReadable( OGRGeometryH, FILE *, const char * );
that uses a FILE pointer, which:
typedef struct { ... } FILE;
stdio.h
or behind the scenes depending on the OS.Question: What is the portable equivalent of this type for FreePascal?
Advise: I know we have the the built in type File
in FreePascal, with the type related functions and procedures:
Procedure Assign(out f:File;const Name: RawByteString);
Procedure Rename(var f:File;const s : RawByteString);
Procedure Rewrite(var f:File);
Procedure Reset(var f:File);
Procedure Close(var f:File);
Procedure BlockWrite(var f:File;const Buf;Count:Int64;var Result:Int64);
Function FilePos(var f:File):Int64;
Function FileSize(var f:File):Int64;
Procedure Seek(var f:File;Pos:Int64);
Function EOF(var f:File):Boolean;
Procedure Erase(var f:File);
Procedure Truncate (var f:File);
located in the unit system
anf looking similar to fopen(...)
, fclose(...)
, etc.;
Question: Are these both types equivalent?
There is no equivalence or easy conversion between a Free Pascal file object and a C FILE*
object (or a file object of any language, for that matter, with the maybe slight exception for converting a C FILE*
to a C++ stream using non-standard functions). You will have to make a decision for your wrapper.
You can choose to make that parameter a string with the filename and then call fopen
yourself. This would be the easy way out but also the ugliest, because not all FILE*
streams are actual files (there are anonymous pipes, for example).
Another way to treat this situation is to get the raw OS handle (file descriptor in Linux) of the particular file (it's inside the TTextRec
record which you can get from a TextFile
), duplicate it using dup
and open a C FILE*
(using fdopen
) to the copy. Since the documentation of OGR_G_DumpReadable
says it wants a text file, it would be appropriate to require that from Pascal callers.
{H+}
uses
ctypes, SysUtils;
type
PFile = Pointer;
function fdopen(fd: cint; const mode: PChar): PFile; cdecl; external 'c';
function dup(fd: cint): cint; cdecl; external 'c';
function GetPFile(var F: TextFile): PFile;
var
fd: THandle;
newfd: THandle;
pf: PFile;
begin
Flush(F);
fd := TTextRec(F).Handle;
newfd := dup(fd);
if newfd = -1 then
begin
// handle error
end;
pf := fdopen(newfd, 'w');
if pf = nil then
begin
// handle error
end;
GetPFile := pf;
end;
Note, however, this approach has some caveats, thus you need to be very careful about what the library is doing with this file. If the library closes the file itself, you may want to replicate that behaviour in Pascal and Close
the Pascal file after the library function returns (but that's not required, you can leave it open).
If the library just writes and nothing more (which I believe is the case with this particular function), you need to fflush
the C file, call lseek
on the C file's descriptor to get its new location, lseek
the old file descriptor to that location and then fclose
the C file (otherwise you will leak resources).
The worst case is if the library keeps the FILE*
beyond the function's end (that is, it keeps an internal state). The Pascal file won't be aware of writes made by the C file or vice-versa. If that is the case, your best bet is to expose the C FILE to the Pascal programmer and let him/her deal with fopen
/fclose
directly, as this will allow them to have a stream that's consistent with what the library has.
Note for Windows:
You said you wanted a portable equivalent, but it seems that on Windows, the handle inside the TTextRec
record is actually a Win32 handle (at least according to Google, I haven't actually tested this). Thus, for Windows, you would want to call DuplicateHandle
instead of dup
, then call _open_osfhandle
to get a file descriptor, and finally call _fdopen
. You have to resort to conditional compilation, because the file implementation in Pascal is inherently non-portable.
Make sure to link the wrapper to the same C library that GDAL is linked to! On Linux/Unix/macOS there is usually a single system library, but on Windows, each piece of software links to whatever C library it wishes (msvcrt.dll
, one of the dozens of MSVC++ redistributables or the UCRT), and each has a different ABI.