Separation of ruby stdout/stderr from C/C++ stdout/stderr is what I want.
…
Using system or backquote, then it goes BOOM. The problem as I see it is
localized in Rubys code - not mine! Ruby spawns a child which inherits
stdout/stderr. This child should instead inherit rb_stdout/rb_stderr
instead!!! Can this issue be fixed on Ruby ???
It can’t be fixed easily; if rb_stdout were pointing at a StringIO object,
then this cannot be represented as a Unix fd.
I mean, I’m guessing you want something like ‘$deferr’ like ‘$defout’,
right? So that rb_warn sends to $deferr, and you can map this variable to
point to some other object?
OK, but what do you want to happen here?
$deferr = StringIO.new(x)
system("ls /nonexistent")
Now, the Ruby ‘system’ call is a direct analogue of the Unix ‘system’ call:
fork, exec, wait; nothing more.
What do you want instead? That Ruby should create two new pipes, pass these
to the child as stdout/stderr; then read any data generated on those pipes,
and pass them to $defout.write and $deferr.write respectively?
(Hence in the normal case where $deferr actually points to STDERR, the
message will still get written to STDERR but indirectly, by being read from
the child and then written to STDERR by the parent)
The problems I can see with this are:
- It adds overhead even for the 99% of cases where this is not needed
- It doesn’t follow the Unix API model, therefore violates POLS:
“system” should not create pipes to the child. “popen” should.
But there are more fundamental problems. Firstly, your Ruby function can
call C at any time: it’s as easy to embed C in Ruby as Ruby in C. What
happens when the C function does a write to stdout or stderr? You can’t
possibily intercept it without modifying the C. What if the C in turn calls
system() to run a child process, and the child process writes to stderr?
Hard luck, it gets written to your main program’s stderr.
Secondly, you have to modify Ruby in every case where it might generate any
output to stderr, and recode it to use $deferr. For example:
output = %x(somecommand}
would have to generate an extra pipe for reading stderr, so that it could be
written to $deferr.
Thirdly and more importantly, what are you going to do about a ‘fork’
between two Ruby processes? Consider this:
pid = fork do
sleep 5
$deferr.puts "hello"
end
$deferr = StringIO.new(x)
Process.waitpid(pid)
By your logic, the child process when it writes to its own “private” Ruby
stderr should not interfere with the parent’s stderr, right? That means that
you are requiring Ruby not to do just a ‘fork’ where I’ve written ‘fork’,
but also to set up pipes with its child process, capture its stderr, and
write it to the local $deferr object.
This is IMO nonsense. An API should not try to do magical things like this,
because it will break code which tries to use the API properly.
So:
- “fork” should just do a fork()
- “system” should just do system() [fork, exec, wait]
- “popen” should set up pipes, fork, exec, and map the pipe to an IO
object.
This is what Ruby does already, of course.
Finally, and perhaps most importantly, a Ruby program is a Unix program
(when running under Unix!). It seems to me incredibly logical that a Ruby
program should have a STDOUT and a STDERR which map directly to the Unix
concepts of STDOUT and STDERR.
C Ruby
printf(“hello,world\n”); puts “hello, world”
fprintf(stderr,“Bad value\n”); STDERR.puts “Bad value”
That’s how it should be. In C, if you wanted to write your errors to a GUI
window instead (say), you’d write a function to do that, and you would call
that function instead of fprintf. In Ruby, it’s the same: you write a method
which puts the error in the window, and call that instead of STDERR.puts
put_error(window,“Bad value”); window.put_error(“Bad value”)
In both cases, if you want to take existing code which writes directly to
stderr, without modifying it, you can run it as a child process and capture
it using a pipe. It’s easy enough to code this in either language, in the
odd case where you need it.
At the end of the day, they’re both programming languages, they both map
onto an underlying operating system in a particular and familiar and logical
way, it seems to me stupid to try and bypass this magically, and it can’t
possibly be made to work in the general case anyway.
Brian.
···
On Mon, May 12, 2003 at 07:16:51AM +0900, Simon Strandgaard wrote: