TCL gets command with kind of -nohang option?

孤者浪人 提交于 2019-12-24 03:17:02

问题


Here is a code which just implements an interactive TCL session with command prompt MyShell >.

puts -nonewline stdout "MyShell > "
flush stdout
catch { eval [gets stdin] } got
if { $got ne "" } {
    puts stderr $got
}

This code prompts MyShell > at the terminal and waits for the enter button to be hit; while it is not hit the code does nothing. This is what the gets command does.

What I need, is some alternative to the gets command, say coolget. The coolget command should not wait for the enter button, but register some slot to be called when it is hit, and just continue the execution. The desired code should look like this:

proc evaluate { string } \
{
    catch { eval $string } got
    if { $got ne "" } {
        puts stderr $got
    }
}

puts -nonewline stdout "MyShell > "
flush stdout
coolgets stdin evaluate; # this command should not wait for the enter button
# here goes some code which is to be executed before the enter button is hit

Here is what I needed:

proc prompt { } \
{
   puts -nonewline stdout "MyShell > "
   flush stdout
}


proc process { } \
{
   catch { uplevel #0 [gets stdin] } got
   if { $got ne "" } {
       puts stderr $got
       flush stderr
   }
   prompt
}

fileevent stdin readable process

prompt
while { true } { update; after 100 }

回答1:


I think you need to look at the fileevent, fconfigure and vwait commands. Using these you can do something like the following:

proc GetData {chan} {
    if {[gets $chan line] >= 0} {
       puts -nonewline "Read data: "
       puts $line
    }
}

fconfigure stdin -blocking 0 -buffering line -translation crlf
fileevent stdin readable [list GetData stdin]

vwait x

This code registers GetData as the readable file event handler for stdin, so whenever there is data available to be read it gets called.




回答2:


Tcl applies “nohang”-like functionality to the whole channel, and it's done by configuring the channel to be non-blocking. After that, any read will return only the data that is there, gets will only return complete lines that are available without waiting, and puts (on a writable channel) will arrange for its output to be sent to the OS asynchronously. This depends on the event loop being operational.

You are recommended to use non-blocking channels with a registered file event handler. You can combine that with non-blocking to implement your coolget idea:

proc coolget {channel callback} {
    fileevent $channel readable [list apply {{ch cb} {
        if {[gets $ch line] >= 0} {
            uplevel [lappend cb $line]
        } elseif {[eof $ch]} {
            # Remove handler at EOF: important!
            fileevent $ch readable {}
        }
    }} $channel $callback]
}

That will then work just fine, except that you've got to call either vwait or update to process events (unless you've got Tk in use too; Tk is special) as Tcl won't process things magically in the background; magical background processing causes more trouble than it's worth…


If you're getting deeply tangled in asynchronous event handling, consider using Tcl 8.6's coroutines to restructure the code. In particular, code like Coronet can help a lot. However, that is very strongly dependent on Tcl 8.6, as earlier Tcl implementations can't support coroutines at all; the low-level implementation had to be rewritten from simple C calls to continuations to enable those features, and that's not backport-able with reasonable effort.



来源:https://stackoverflow.com/questions/8719899/tcl-gets-command-with-kind-of-nohang-option

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!