refchan(3tcl)

refchan ๐Ÿ’– the 3tcl era

okay so bestie, this is the refchan page from the Tcl Built-In Commands manual (3tcl, iykyk), and it's literally the blueprint for building channels in Tcl itself. Buckle up, it's a long one, but we're getting through it together ๐Ÿซถ


โœจ NAME โœจ

refchan โ€” Command handler API for channels implemented in Tcl code ๐Ÿ’…


๐ŸŒธ SYNOPSIS ๐ŸŒธ

chan create mode cmdPrefix

cmdPrefix blocking channel mode
cmdPrefix cget channel option
cmdPrefix cgetall channel
cmdPrefix configure channel option value
cmdPrefix finalize channel
cmdPrefix initialize channel mode
cmdPrefix read channel count
cmdPrefix seek channel offset base
cmdPrefix watch channel eventspec
cmdPrefix write channel data

๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

The Tcl-level handler for a reflected channel has to be a command with subcommands โ€” we call that an ensemble, like the kind you'd get from namespace ensemble create. But bestie, do not be fooled: the implementation of handlers for reflected channel is not tied to namespace ensembles in any way at all. If you wanna see how to build an oo::class that supports this API, peek at EXAMPLE below ๐Ÿ‘€

Also, heads up: cmdPrefix is literally whatever you specified in the call to chan create, and it may consist of multiple arguments. When it gets used, that whole thing gets expanded to multiple words in place of the prefix. Slay.

Out of all the possible subcommands, your handler must support initialize, finalize, and watch. Everything else? Optional. Not mandatory. Chill. ๐Ÿ˜Œ


๐Ÿ‘‘ MANDATORY SUBCOMMANDS ๐Ÿ‘‘

cmdPrefix initialize channel mode

An invocation of this subcommand is going to be the first call the cmdPrefix receives for the specified new channel. It's your job here to set up any internal data structures you need to keep track of the channel and its state.

The return value of the method has to be a list containing the names of all subcommands supported by the cmdPrefix. This also tells the Tcl core which version of the API for reflected channels is used by this command handler. Iconic, right?

Any error thrown by the method will abort the creation of the channel and no channel will be created. The thrown error will appear as an error thrown by chan create. Any exception other than an error (e.g., break, etc.) is treated as (and converted to) an error.

Note: if the creation of the channel was aborted due to failures here, then the finalize subcommand will not be called.

The mode argument tells the handler whether the channel was opened for reading, writing, or both. It's a list containing any of the strings read or write. The list may be empty, but it will usually contain at least one element.

The subcommand must throw an error if the chosen mode is not supported by the cmdPrefix. No pretending, bestie.


cmdPrefix finalize channel

An invocation of this subcommand is going to be the last call the cmdPrefix receives for the specified channel. It gets generated just before the destruction of the data structures of the channel held by the Tcl core. The command handler must not access the channel anymore in no way. Once this subcommand is called, any internal resources allocated to this channel must be cleaned up. Bye bye, resources ๐Ÿงนโœจ

The return value of this subcommand is ignored.

If the subcommand throws an error, the command which caused its invocation (usually chan close) will appear to have thrown this error. Any exception beyond error (e.g., break, etc.) is treated as (and converted to) an error.

This subcommand is not invoked if the creation of the channel was aborted during initialize (See above). Remember that one.


cmdPrefix watch channel eventspec

This subcommand notifies the cmdPrefix that the specified channel is interested in the events listed in the eventspec. That argument is a list containing any of read and write. The list may be empty, which signals that the channel does not wish to be notified of any events. In that situation, the handler should disable event generation completely. Okay so basically: empty list = go silent ๐Ÿ”‡

Warning: Any return value of the subcommand is ignored. This includes all errors thrown by the subcommand, break, continue, and custom return codes. Like, literally all of it. Don't rely on it.

This subcommand interacts with chan postevent. Trying to post an event which was not listed in the last call to watch will cause chan postevent to throw an error. So keep your eventspec honest ๐Ÿ’€


๐ŸŒˆ OPTIONAL SUBCOMMANDS ๐ŸŒˆ

cmdPrefix read channel count

This optional subcommand is called when the user requests data from the channel channel. count specifies how many bytes have been requested. If the subcommand is not supported then it is not possible to read from the channel handled by the command.

The return value of this subcommand is taken as the requested data bytes. If the returned data contains more bytes than requested, an error will be signaled and later thrown by the command which performed the read (usually gets or read). However, returning fewer bytes than requested is acceptable. So under-delivering is fine, over-delivering is not ๐Ÿšซ

Note that returning nothing (0 bytes) is a signal to the higher layers that EOF has been reached on the channel. To signal that the channel is out of data right now, but has not yet reached EOF, it is necessary to throw the error "EAGAIN", i.e. to either

return -code error EAGAIN

or

error EAGAIN

For extensibility any error whose value is a negative integer number will cause the higher layers to set the C-level variable "errno" to the absolute value of this number, signaling a system error. However, note that the exact mapping between these error numbers and their meanings is operating system dependent. ๐Ÿ–ฅ๏ธ

For example, while on Linux both

return -code error -11

and

error -11

are equivalent to the examples above, using the more readable string "EAGAIN", this is not true for BSD, where the equivalent number is -35.

The symbolic string however is the same across systems, and internally translated to the correct number. No other error value has such a mapping to a symbolic string. Wild, but that's the vibe.

If the subcommand throws any other error, the command which caused its invocation (usually gets, or read) will appear to have thrown this error. Any exception beyond error, (e.g., break, etc.) is treated as and converted to an error.


cmdPrefix write channel data

This optional subcommand is called when the user writes data to the channel channel. The data argument contains bytes, not characters. Any type of transformation (EOL, encoding) configured for the channel has already been applied at this point. If this subcommand is not supported then it is not possible to write to the channel handled by the command.

The return value of the subcommand is taken as the number of bytes written by the channel. Anything non-numeric will cause an error to be signaled and later thrown by the command which performed the write. A negative value implies that the write failed. Returning a value greater than the number of bytes given to the handler, or zero, is forbidden and will cause the Tcl core to throw an error. So do not try it ๐Ÿซ 

To signal that the channel is not able to accept data for writing right now, it is necessary to throw the error "EAGAIN", i.e. to either

return -code error EAGAIN

or

error EAGAIN

For extensibility any error whose value is a negative integer number will cause the higher layers to set the C-level variable "errno" to the absolute value of this number, signaling a system error. However, note that the exact mapping between these error numbers and their meanings is operating system dependent.

For example, while on Linux both

return -code error -11

and

error -11

are equivalent to the examples above, using the more readable string "EAGAIN", this is not true for BSD, where the equivalent number is -35.

The symbolic string however is the same across systems, and internally translated to the correct number. No other error value has such a mapping to a symbolic string.

If the subcommand throws any other error the command which caused its invocation (usually puts) will appear to have thrown this error. Any exception beyond error (e.g., break, etc.) is treated as and converted to an error.


cmdPrefix seek channel offset base

This optional subcommand is responsible for the handling of chan seek and chan tell requests on the channel channel. If it is not supported then seeking will not be possible for the channel. ๐Ÿšถโ™€๏ธ

The base argument is the same as the equivalent argument of the builtin chan seek, namely:

  • start โ€” Seeking is relative to the beginning of the channel.
  • current โ€” Seeking is relative to the current seek position.
  • end โ€” Seeking is relative to the end of the channel.

The offset is an integer number specifying the amount of bytes to seek forward or backward. A positive number should seek forward, and a negative number should seek backward. A channel may provide only limited seeking. For example sockets can seek forward, but not backward. ๐Ÿ™ˆ

The return value of the subcommand is taken as the (new) location of the channel, counted from the start. This has to be an integer number greater than or equal to zero. If the subcommand throws an error the command which caused its invocation (usually chan seek, or chan tell) will appear to have thrown this error. Any exception beyond error (e.g., break, etc.) is treated as and converted to an error.

The offset/base combination of 0/current signals a chan tell request, i.e., seek nothing relative to the current location, making the new location identical to the current one, which is then returned. Sneaky but valid ๐Ÿ’…


cmdPrefix configure channel option value

This optional subcommand is for setting the type-specific options of channel channel. The option argument indicates the option to be written, and the value argument indicates the value to set the option to.

This subcommand will never try to update more than one option at a time; that is behavior implemented in the Tcl channel core. One at a time, no multitasking, periodt. โœ‹


...and lowkey, that same rule keeps vibing through everything below. The return value of the subcommand? ignored. Like, the driver does its thing, and whatever it hands back is just โœจnot the momentโœจ.

But bestie, the tea is in the errors: if the subcommand throws an error, then the command that did the (re)configuration or query โ€” usually fconfigure or chan configure โ€” is gonna look like it threw that error. And anything beyond a plain error (like a break, etc.?) gets treated as and converted to an error. No exceptions sneaking past, fr fr. ๐Ÿ’€

๐Ÿ’– cmdPrefix cget channel option

This one's optional, and it's what you call when you're reading a single type-specific option of channel channel. But โ€” heads up โ€” if you support this subcommand, you have to support cgetall too. Package deal, periodt. ๐Ÿซถ

It should return the value of the specified option.

And the error rules? Same energy: if the subcommand throws an error, the command doing the (re)configuration or query (usually fconfigure or chan configure) will appear to have thrown it. Any exception beyond error (e.g., break, etc.) is treated as and converted to an error.

๐ŸŽ€ cmdPrefix cgetall channel

Also optional, and it's the one that reads all the type-specific options of channel channel. And yes โ€” if you support this one, you gotta support cget as well. Mutual besties. ๐Ÿ’ž

It should return a list of all options and their values. And this list must have an even number of elements. Count them. Even. No cap. ๐Ÿงฎ

Error handling, same as always: if the subcommand throws an error, the command that did the (re)configuration or query (usually fconfigure or chan configure) will appear to have thrown this error. Any exception beyond error (e.g., break, etc.) is treated as and converted to an error.

๐Ÿฆ‹ cmdPrefix blocking channel mode

Optional again ๐Ÿ’… โ€” this subcommand handles changes to the blocking mode of channel channel. The mode is a boolean flag: a true value means the channel has to be set to blocking, and a false value means the channel should be non-blocking.

The return value of the subcommand is ignored. (We been knew.)

If the subcommand throws an error, the command that caused its invocation (usually fconfigure or chan configure) will appear to have thrown this error. Any exception beyond error (e.g., break, etc.) is treated as and converted to an error.

๐ŸŒธ cmdPrefix truncate channel length

Optional, and it's giving โœ‚๏ธ โ€” this subcommand handles changing the length of the underlying data stream for the channel channel. Its length gets set to length.

If the subcommand throws an error, the command that caused its invocation (usually chan truncate) will appear to have thrown this error. Any exception beyond error (e.g., break, etc.) is treated as and converted to an error.


๐Ÿ“ NOTES ๐Ÿ“

Okay so, not everything in the Tcl C interface channel toolbox made it to the Tcl-level reflected version. Some functions are just not available to channels reflected to the Tcl level. Let's go through the absences:

  • The function Tcl_DriverGetHandleProc is not supported โ€” meaning reflected channels do not have OS specific handles. Sorry bestie, no handle for you. ๐Ÿ™ˆ
  • The function Tcl_DriverHandlerProc is not supported. This driver function is only relevant for stacked channels, i.e., transformations. And reflected channels are always base channels, never transformations. Base channel main character energy only. ๐Ÿ‘‘
  • The function Tcl_DriverFlushProc is not supported. Why? Because the current generic I/O layer of Tcl does not use this function anywhere at all. So supporting it at the Tcl level makes no sense either. This may be altered in the future (through extending the API defined here and changing its version number) should the function be used at some time in the future. So like... watch this space, iykyk. ๐Ÿ‘€

โœจ EXAMPLE โœจ

Okay here's the cute part โ€” this demonstrates how to make a channel that reads from a string. Slay. ๐Ÿ“

oo::class create stringchan {
    variable data pos
    constructor {string {encoding {}}} {
        if {$encoding eq ""} {set encoding [encoding system]}
        set data [encoding convertto $encoding $string]
        set pos 0
    }

    method initialize {ch mode} {
        return "initialize finalize watch read seek"
    }
    method finalize {ch} {
        my destroy
    }
    method watch {ch events} {
        # Must be present but we ignore it because we do not
        # post any events
    }

    # Must be present on a readable channel
    method read {ch count} {
        set d [string range $data $pos [expr {$pos+$count-1}]]
        incr pos [string length $d]
        return $d
    }

    # This method is optional, but useful for the example below
    method seek {ch offset base} {
        switch $base {
            start {
                set pos $offset
            }
            current {
                incr pos $offset
            }
            end {
                set pos [string length $data]
                incr pos $offset
            }
        }
        if {$pos < 0} {
            set pos 0
        } elseif {$pos > [string length $data]} {
            set pos [string length $data]
        }
        return $pos
    }
}

# Now we create an instance...
set string "The quick brown fox jumps over the lazy dog.\n"
set ch [chan create read [stringchan new $string]]

puts [gets $ch];   # Prints the whole string

seek $ch -5 end;
puts [read $ch];   # Prints just the last word

And that's it. You just made a whole channel out of a string. Iconic behavior, truly. ๐ŸŒˆ


๐Ÿ‘€ SEE ALSO ๐Ÿ‘€

chan(3tcl), transchan(3tcl) โ€” go read those too, they're the sister docs, the trio is complete ๐Ÿ’œ

๐Ÿ”‘ KEYWORDS ๐Ÿ”‘

API, channel, ensemble, prefix, reflection


That's the whole page bestie โ€” every last crumb of it. This one's from the Tcl 8.5 era, and it lives at refchan(3tcl). Go forth and reflect some channels, you absolute legend. ๐Ÿซถโœจ๐Ÿ’…

๐Ÿ“ฆ where this page comes from