Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2010-07

redirecting stdout

9 messages · 4 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Bernd

is it possible to redirect stdout so that everytime I call writeln() the output is sent to a function that accepts a string instead of stdout?

I have googled for some hours now and looked into the CRT unit but I somehow cannot figure out how this mechanism is supposed to work. I understand that somehow I have to change a few (which ones?) pointers in a textrec record to some functions that I provide but I have no clue how this all is supposed to work.

What exactly happens when I write to stdout, which functions are called and in which order and where and how can I hook into this whole thing to receive the string that has been written?

I need some holding hands now, a simple "look how crt does it" won't help because I first need to understand the concept of what is going on with this textrec variable.

Say I have a procedure foo

procedure foo(s: ansistring);
begin
   // some code to send this string somewhere else
end;

What are the absolute minimum needed things (no extra bells and whistles) that I have to do so that foo() will be called on every call to writeln() and receives the string?

Is there any documentation about this that i should have read before asking this question? I could not find anything, at least not online, maybe because it is all old pre-www turbopascal knowledge that can be only found in books, and everything the CRT unit does (i don't even know what exactly it does and what it is used for, I only know that it seems to somehow amongst other things messes around with stdout/stdin) seems to be undocumented voodoo magic to me.

Bernd

Sven Barth
to Bernd: is it possible to redirect stdout so that everytime I call writeln() the output is sent…

Hi!

You can use WriteStr to "Writeln" to a String (needs 2.4.0 and above).

Example:

var
   s: String;
begin
   WriteStr(s, 'Hello World');
end;

Also you can try unit StreamIO (in fcl-base) which allows you to use Streams as TextFiles (you can also look at it to learn how to implement your own text driver).

Example:

var
   f: TextFile;
   s: TStringStream;
begin
   s := TStringStream.Create;
   AssignStream(f, s);
   Rewrite(f);
   Writeln(f, 'Hello World');
   CloseFile(f);
   s.Free;
end.

Regards,
Sven

Sven Barth
to Bernd: is it possible to redirect stdout so that everytime I call writeln() the output is sent…

Hi again!

I forgot to mention:

If you want to redirect StdOut (or Output as the variable is called in Pascal) you need to assign a new TextFile to Output (but keep the old one around and assign it back later when your application is terminating).

Example:

var
   oldoutput, f: TextFile;
begin
   AssignFile(f, 'somefile');
   Rewrite(f);
   oldoutput := Output;
   Output := f;
   Writeln('Hello World'); // this is send to Output
   Output := oldoutput;
   CloseFile(f);
end.
Bernd
to Sven Barth: Hi! You can use WriteStr to "Writeln" to a String (needs 2.4.0 and above). Example: var…

2010/7/18 Sven Barth :

Also you can try unit StreamIO (in fcl-base) which allows you to use Streams as TextFiles (you can also look at it to learn how to implement your own text driver).

This looks more promising, streams seem to be a more high level implementation of such concepts. I'm currently looking at the source of TStream and friends and trying to figure out how to make a stream class with my own overridden write method.

But I do not understand the following:

function TStream.Write(const Buffer; Count: Longint): Longint;

what type is Buffer? How does it get away with the missing type declaration without the compiler failing to compile this at all?

Sven Barth
to Bernd

Hi!

Bernd wrote 6 lines
But I do not understand the following: function TStream.Write(const Buffer; Count: Longint): Longint; what type is Buffer? How does it get away with the missing type declaration without the compiler failing to compile this at all?

Quote from the Docs (
http://www.freepascal.org/docs-html/ref/refsu54.html#x134-14100011.4.2 ):

Variable and constant parameters can be untyped. In that case the variable has no type, and hence is incompatible with all other types. However, the address operator can be used on it, or it can be can passed to a function that has also an untyped parameter. If an untyped parameter is used in an assigment, or a value must be assigned to it, a typecast must be used.

===

You pass a variable of any type you like into such a parameter. Basically you can think of it as a Pointer where you're not allowed to pass Nil (because you're passing the location of a variable).

For an example usage see the other Write* methods of TStream: they pass the variable/parameter that contains the data and the size of the data to the Write method.

To use the write method with a simple record (that does not contain a Pointer-based type like String or a dynamic array) you can use it this way:

MyStream.Write(MyRecordVar, SizeOf(TMyRecordType));

That will write the contents of your record into the stream.

Regards,
Sven

Bernd
to Sven Barth

OK, I got it working now. Just in case somebody else is googling for something like this:

first I define a class TDebugStream that will later replace the standard output:

Type
  TDebugStream = class(TStream)
    function Write(const Buffer; Count : Longint) : Longint; override;
  end;
implementation
function TDebugStream.Write(const Buffer; Count : Longint) : Longint;
var
  msg : ansistring;
begin
  result := count;
  SetLength(msg, count);
  move(buffer, PChar(msg)[0], count);
  OutputDebugString(PChar(TrimRight(msg)));
end;

then in my application i have two global variables:

var
  f : TextFile;
  s: TDebugStream;

and a procedure to redirect the output:

procedure redirect;
begin
 s := TDebugStream.Create();
 AssignStream(f, s);
 Rewrite(f);
 output := f;
end;

and then I can use it like this:

procedure TForm1.FormCreate(Sender : TObject);
begin
  redirect;
  writeln('form created');
end;
procedure TForm1.Button1Click(Sender : TObject);
begin
  writeln('button clicked');
end;

This might seem trivial but my Pascal times have been 20 years ago with TurboPascal and I have to re-learn many things that I have forgotten.

http://imagebin.org/105806 with GDB enabled
http://imagebin.org/105807 without debugger, output sent to DebugView

Now I think I have spent ten times more time with trying to figure out how to redirect writeln to OutputDebugString() than I could ever save by using it ;-)

Michael Van Canneyt
to Bernd
Bernd wrote 39 lines
OK, I got it working now. Just in case somebody else is googling for something like this: first I define a class TDebugStream that will later replace the standard output: Type TDebugStream = class(TStream) function Write(const Buffer; Count : Longint) : Longint; override; end; implementation function TDebugStream.Write(const Buffer; Count : Longint) : Longint; var msg : ansistring; begin result := count; SetLength(msg, count); move(buffer, PChar(msg)[0], count); OutputDebugString(PChar(TrimRight(msg))); end; then in my application i have two global variables: var f : TextFile; s: TDebugStream; and a procedure to redirect the output: procedure redirect; begin s := TDebugStream.Create(); AssignStream(f, s); Rewrite(f); output := f; end;

I think that simply

AssignStream(Output,S);
Rewrite(Output);

Probably would work as well.

Michael.

Bernd
to Michael Van Canneyt

2010/7/18 Michael Van Canneyt :

Show quoted text 6 lines
I think that simply AssignStream(Output,S); Rewrite(Output); Probably would work as well.

Yes, you are of course right, this is simpler. I just forgot to simplify it after I was initially playing around and experimenting with a separate file and without redirecting Output.