Version 17 of chan mode

Updated 2007-05-06 09:43:24 by dkf

Richard Suchenwirth 2007-05-04 - MJ contributed this nice example of how to extend Tcl with a new subcommand to the chan ensemble (new from 8.5). First, write a self-contained function that implements what you want - in this case, return a list indicating whether the given channel is readable (r) and/or writable (w):

 /*
  *----------------------------------------------------------------------
  *
  * Tcl_ModeObjCmd --
  *
  *----------------------------------------------------------------------
 */

        /* ARGSUSED */
 int
 Tcl_ModeObjCmd(
    ClientData dummy,           /* Not used. */
    Tcl_Interp *interp,         /* Current interpreter. */
    int objc,                   /* Number of arguments. */
    Tcl_Obj *const objv[])      /* Argument objects. */
 {
    Tcl_Channel chan;           /* The channel to puts on. */
    const char *channelId; /* Name of channel for puts. */
    int mode;                   /* Mode in which channel is opened. */

    if (objc != 2) {
        Tcl_WrongNumArgs(interp, 1, objv, "channelId");
        return TCL_ERROR;
    }

    channelId = Tcl_GetString(objv[1]);

    chan = Tcl_GetChannel(interp, channelId, &mode);
    if (chan == (Tcl_Channel) NULL) {
        return TCL_ERROR;
    }

    if ((mode & TCL_READABLE) != 0) {
        Tcl_AppendElement(interp, "r");
    }

    if ((mode & TCL_WRITABLE) != 0) {
        Tcl_AppendElement(interp, "w");
    }

    return TCL_OK;

 }

Then, make it available as a command to Tcl:

            Tcl_CreateObjCommand(interp, "::tcl::chan::mode",
                            Tcl_ModeObjCmd, NULL, NULL);

Finally, in init.tcl, register that new command as ensemble member of chan:

 # Set up the 'chan' ensemble (TIP #208).
    namespace eval chan {
        # TIP #219. Added methods: create, postevent.
        # TIP 287.  Added method: pending.
        namespace ensemble create -command ::chan -map {
            blocked     ::tcl::chan::blocked
            close       ::tcl::chan::close
            configure   ::tcl::chan::configure
            copy        ::tcl::chan::copy
            create      ::tcl::chan::rCreate
            eof         ::tcl::chan::eof
            event       ::tcl::chan::event
            flush       ::tcl::chan::flush
            gets        ::tcl::chan::gets
            mode        {::tcl::chan::mode ;############### here}
            names       {::file channels}
            pending     ::tcl::chan::Pending
            postevent   ::tcl::chan::rPostevent
            puts        ::tcl::chan::puts
            read        ::tcl::chan::read
            seek        ::tcl::chan::seek
            tell        ::tcl::chan::tell
            truncate    ::tcl::chan::Truncate
        }
    }

NEM You shouldn't have to put the command in the tcl namespace or mess with init.tcl. The namespace ensemble mechanism is flexible enough to be extended from elsewhere. In this case, you could do:

            Tcl_CreateObjCommand(interp, "::mychan::mode",
                            Tcl_ModeObjCmd, NULL, NULL); 

and then in some script do:

 set map [namespace ensemble configure ::chan -map]
 dict set map mode ::mychan::mode
 namespace ensemble configure ::chan -map $map

DKF: I'd go further and say that what NEM suggests is the correct way to do it, since it does not rely on the current configuration of how chan has its internal subcommands. It also allows people to have other extension subcommands.


[Category Example|Category Channel]