TIP 613: New INDEX_NULL_OK flag for Tcl_GetIndexFromObj*()

Login
Bounty program for improvements to Tcl and certain Tcl packages.
Author:         Jan Nijtmans <[email protected]>
State:          Final
Type:           Project
Vote:           Done
Created:        09-Dec-2021
Post-History:   
Keywords:       Tcl Tcl_GetIndexFromObj() Tcl_GetIndexFromObjStruct()
Tcl-Version:    8.7
Tcl-Branch:     tip-613
Vote-Summary:   Accepted 7/0/0
Votes-For:      BG, FV, JD, JN, KW, MC, SL
Votes-Against:  none
Votes-Present:  none

Abstract

This TIP proposes a new INDEX_NULL_OK flag for Tcl_GetIndexFromObj*() as TCL_INDEX_NULL_OK, and allow other variable types (like enum, short, long, long long, both signed and unsigned) for the indexPtr variable.

It also proposes to implement the TK_OPTION_NULL_OK flag in Tk for the options TK_OPTION_BOOLEAN, TK_OPTION_JUSTIFY and TK_OPTION_ANCHOR, in the same way as already present for TK_OPTION_RELIEF, and for TK_OPTION_INT in the same way as already present for TK_OPTION_DOUBLE.

Rationale

In Tk, serveral options allow the empty string, but since Tcl_GetIndexFromObjStruct() cannot handle the empty string as input well, this results in special code in Tk to handle that. This is not always done correctly, e.g.:

$ wish8.6
% text .t
.t
% .t tag configure dummy -relief {}
% .t tag configure dummy -relief xxx
bad relief "xxx": must be flat, groove, raised, ridge, solid, or sunken
% .t tag configure dummy -wrap {}
% .t tag configure dummy -wrap foo
bad wrap "foo": must be char, none, word, or 
% 
So, the error-message doesn't even mention that "" is a valid value, or it forgets to quote the empty value.

The cause of the problem is here: the empty string is made part of a string array used by Tcl_GetIndexFromObj*().

The meaning of TCL_INDEX_NULL_OK is that Tcl_GetIndexFromObj*() no longer gives an error when indexPtr is supplied a NULL or "" argument, but it will return TCL_OK and provide the index "-1". This functionality can then be used by Tk:

$ wish8.7
% text .t
.t
% .t tag configure dummy -wrap {}
% .t tag configure dummy -wrap foo
bad wrap "foo": must be char, none, word, or ""
% 

The indexPtr parameter of Tcl_GetIndexFromObj*() always had to point to an integer variable, but this TIP changes the parameter to type void * which can point to almost anything. This is done by using a wrapper macro, which makes the sizeof() of the variable available to the function. So any scalar value, being an enum or some kind of integer (1-, 2-, 4- or 8-byte) will work. indexPtr can also be (int *)NULL, then nothing will be written to it.

For Tk, the enum's Tk_Anchor and Tk_Justify will get new members TK_ANCHOR_NULL resp. TK_JUSTIFY_NULL with value -1, equivalent with the already existing TK_RELIEF_NULL (which is not an enum for historical reasons). Without the TK_OPTION_NULL_OK flag in the TK_OPTION_JUSTIFY and TK_OPTION_ANCHOR config information, everything functions as before, but when using the TK_OPTION_NULL_OK flag, the new enum values become valid values for those configuration options.

For TK_OPTION_BOOLEAN, the new possible value, when using the TK_OPTION_NULL_OK flag, is -1. This would allow the -elide, -overstrike and -underline options for text tags, currently implemented using TK_OPTION_STRING, to be re-implemented without the need for extra error-checking in the code, since TK_OPTION_BOOLEAN already takes care of that. This rewrite is not done in the tip-613, branch in order to demonstrate that this TIP does not break current code.

For TK_OPTION_DOUBLE, using the TK_OPTION_NULL_OK flag will mean that the NULL value will be translated to the internal value NaN (Was: 0.0). The reason for this change is that this makes it possible to distinguish the empty string from 0.0, without storing the original Tcl_Obj in the widget structure.

For TK_OPTION_INT, using the TK_OPTION_NULL_OK flag means that the NULL value will be translated to internal value INT_MIN. Also, TK_OPTION_PIXEL is changed the same way as TK_OPTION_INT.

The change in TK_OPTION_DOUBLE/TK_OPTION_INT would allow the -width/-relWidth/-height/-relHeight options to be re-written such that the flags (CHILD_WIDTH et al.) are not needed any more. This rewrite is not done in the tip-613, branch in order to demonstrate that this TIP does not break current code.

Caveat

Some extensions might have set the TK_OPTION_NULL_OK flag already, even though it never worked. This might result in "" as possible option value, which was previously impossible, and what might lead to new unexpected behavior. Examples are this bug in Tk menu's and this bug in Themed Tk. Solution: the extension should no longer use the TK_OPTION_NULL_OK flag, or expect Tcl 8.7 as a minimum and take care that the value -1 is handled properly. One extension known to be broken this way is 3dcanvas.

The change in the TK_OPTION_NULL_OK for TK_OPTION_DOUBLE (using NaN as internal representation in stead of 0.0) and for TK_OPTION_INT/TK_OPTION_PIXEL (using INT_MIN as internal representation in stead of 0) is a potential incompatibility if the Tcl_Obj is not stored in the widget structure. In Tk, there is no code affected (it is used only in -width/-height/-relWidth/-relHeight for the place command).

Implementation

Available in the tip-613 branch.

There's a tip-613 branch in Tk as well, implementing the TK_OPTION_NULL_OK flag for TK_OPTION_BOOLEAN, TK_OPTION_JUSTIFY and TK_OPTION_ANCHOR, using Tcl's TCL_INDEX_NULL_OK flag.

Finally there's a tip-613-demo branch in Tk as well. This branch changes the "place" command and the implementation for the -elide, -overstrike and -underline text tags, to use the new functionality.

This branch targets 8.7.

Copyright

This document has been placed in the public domain.