Tcl_DictObj(3)
omg bestie, welcome to Tcl_DictObj(3) from the Tcl Library Procedures β it's giving C-level dictionary manipulation, and honestly? iconic. π
This is part 1, so we're doing the intro, the synopsis, and the arguments. No cap.
β¨ NAME β¨
The squad that manipulate Tcl values as dictionaries:
Tcl_NewDictObjTcl_DictObjPutTcl_DictObjGetTcl_DictObjRemoveTcl_DictObjSizeTcl_DictObjFirstTcl_DictObjNextTcl_DictObjDoneTcl_DictObjPutKeyListTcl_DictObjRemoveKeyList
That's the whole clique. They all vibe around turning Tcl values into dictionaries and then poking at key/value pairs. π«Ά
πΈ SYNOPSIS πΈ
#include <tcl.h>
Tcl_Obj *
Tcl_NewDictObj()
int
Tcl_DictObjGet(interp, dictPtr, keyPtr, valuePtrPtr)
int
Tcl_DictObjPut(interp, dictPtr, keyPtr, valuePtr)
int
Tcl_DictObjRemove(interp, dictPtr, keyPtr)
int
Tcl_DictObjSize(interp, dictPtr, sizePtr)
int
Tcl_DictObjFirst(interp, dictPtr, searchPtr,
keyPtrPtr, valuePtrPtr, donePtr)
Tcl_DictObjNext(searchPtr, keyPtrPtr, valuePtrPtr, donePtr)
Tcl_DictObjDone(searchPtr)
int
Tcl_DictObjPutKeyList(interp, dictPtr, keyc, keyv, valuePtr)
int
Tcl_DictObjRemoveKeyList(interp, dictPtr, keyc, keyv)
π ARGUMENTS π
Okay bestie, here's the cast of parameters. Pay attention, because they're all main characters. π
Tcl_Interp *interp(in) β If an error occurs while converting a value to be a dictionary value, an error message is left in the interpreter's result value unlessinterpisNULL. So if you passNULL, no error message for you. πTcl_Obj *dictPtr(in/out) β Points to the dictionary value to be manipulated. IfdictPtrdoes not already point to a dictionary value, an attempt will be made to convert it to one. Glow-up arc, honestly. β¨Tcl_Obj *keyPtr(in) β Points to the key for the key/value pair being manipulated within the dictionary value. πTcl_Obj **keyPtrPtr(out) β Points to a variable that will have the key from a key/value pair placed within it. May beNULLto indicate that the caller is not interested in the key. Not interested? Respectfully, okay. πTcl_Obj *valuePtr(in) β Points to the value for the key/value pair being manipulated within the dictionary value (or sub-value, in the case ofTcl_DictObjPutKeyList). πTcl_Obj **valuePtrPtr(out) β Points to a variable that will have the value from a key/value pair placed within it. ForTcl_DictObjFirstandTcl_DictObjNext, this may beNULLto indicate that the caller is not interested in the value. Again, the audacity? But valid. πTcl_Size | int *sizePtr(out) β Points to a variable that will have the number of key/value pairs contained within the dictionary placed within it. May be(Tcl_Size *)NULLwhen not used. If it points to a variable which type is notTcl_Size, a compiler warning will be generated. If your extensions is compiled with-DTCL_8_API, this function will returnNULLfor dictionaries larger thanINT_MAX(which should trigger proper error-handling), otherwise expect it to crash. π¨ CRASH WARNING, bestie. Do not play with this. πTcl_DictSearch *searchPtr(in/out) β Pointer to record to use to keep track of progress in enumerating all key/value pairs in a dictionary. The contents of the record will be initialized by the call toTcl_DictObjFirst. If the enumerating is to be terminated before all values in the dictionary have been returned, the search record must be passed toTcl_DictObjDoneto enable the internal locks to be released. Internal locks said release me, periodt. πint *donePtr(out) β Points to a variable that will have a non-zero value written into it when the enumeration of the key/value pairs in a dictionary has completed, and a zero otherwise. Zero means not done, non-zero means done. We love a binary. βTcl_Size keyc(in) β Indicates the number of keys that will be supplied in thekeyvarray. π’Tcl_Obj *const *keyv(in) β Array ofkeycpointers to values thatTcl_DictObjPutKeyListandTcl_DictObjRemoveKeyListwill use to locate the key/value pair to manipulate within the sub-dictionaries of the main dictionary value passed to them. Sub-dictionaries? Nested? It's giving depth. π§
π DESCRIPTION π
okay so bestie, here's the tea on how dicts work under the hood π« Tcl dictionary values have an internal representation that does two iconic things at once: it makes mapping from keys to values super fast β¨ and it pinky-promises that the particular ordering of keys inside the dictionary stays the same β with two lil exceptions:
- keys get deleted β they get removed from the order, bye π
- keys get added β they get tacked onto the end of the order, no cutting the line π
And if you ever reinterpret a dict as a list? The values sitting at the even-valued indices are the keys, and each one is followed (at the odd-valued index) by the value that key is mapped to. it's giving alternating vibes π
The procedures on this page are the ones you use to create, modify, index, and iterate over dictionary values from C code. yes, C code. we're in the trenches fr fr π«‘
Tcl_NewDictObj creates a brand new, empty dictionary value. The string representation of the value will be invalid, and the reference count of the value will be zero. fresh out the box, zero refs, no notes πΈ
Tcl_DictObjGet looks up the given key inside the given dictionary and writes a pointer to the value associated with that key into the variable pointed to by valuePtrPtr β or a NULL if that key has no mapping in the dictionary. The result is TCL_OK, or TCL_ERROR if the dictPtr cannot be converted to a dictionary π
Tcl_DictObjPut updates the given dictionary so that the given key maps to the given value. quick reminder: any key may exist at most once in any particular dictionary, no duplicates allowed, this is not a group chat where everyone can have the same name π
ββοΈ The dictionary must not be shared, but the key and value may be. This procedure may increase the reference count of both key and value if it proves necessary to store them. Neither key nor value should be NULL. The result is TCL_OK, or TCL_ERROR if the dictPtr cannot be converted to a dictionary.
Tcl_DictObjRemove updates the given dictionary so that the given key has no mapping to any value. (ghosted. blocked. removed.) The dictionary must not be shared, but the key may be. The key actually stored in the dictionary will have its reference count decremented if it was present. And no, it is not an error if the key didn't previously exist β removing something that was never there is chill π The result is TCL_OK, or TCL_ERROR if the dictPtr cannot be converted to a dictionary.
Tcl_DictObjSize updates the given variable with the number of key/value pairs currently in the given dictionary. The result is TCL_OK, or TCL_ERROR if the dictPtr cannot be converted to a dictionary, or if sizePtr points to a variable of type int and the dict contains more than 2**31 key/value pairs. that's a lot of pairs bestie, that's the overflow zone π
Tcl_DictObjFirst commences an iteration across all the key/value pairs in the given dictionary, placing the key and value in the variables pointed to by the keyPtrPtr and valuePtrPtr arguments (which may be NULL to indicate that the caller is uninterested in the key or the value respectively). The next key/value pair in the dictionary may be retrieved with Tcl_DictObjNext.
Concurrent updates of the dictionary's internal representation will not modify the iteration processing β unless the dictionary is unshared, in which case this will trigger premature termination of the iteration instead (which Tcl scripts cannot trigger via the dict command.) β οΈ
The searchPtr argument points to a piece of context that is used to identify which particular iteration is being performed, and is initialized by the call to Tcl_DictObjFirst. The donePtr argument points to a variable that is updated to be zero if there are further key/value pairs to be iterated over, or non-zero if the iteration is complete. The order of iteration is implementation-defined (iykyk, don't count on a particular order π). If the dictPtr argument cannot be converted to a dictionary, Tcl_DictObjFirst returns TCL_ERROR and the iteration is not commenced; otherwise it returns TCL_OK.
When Tcl_DictObjFirst is called upon a dictionary, a lock is placed on the dictionary so that it can be iterated over safely without regard for whether the dictionary is modified during the iteration π Because of this, once the iteration over a dictionary's keys has finished β whether because all values have been iterated over as indicated by the variable pointed to by the donePtr argument being set to one, or because no further values are required β the Tcl_DictObjDone function must be called with the same searchPtr as was passed to Tcl_DictObjFirst, so that the internal locks can be released. no lock left behind, periodt π
Once a particular searchPtr is passed to Tcl_DictObjDone, passing it to Tcl_DictObjNext (without first initializing it with Tcl_DictObjFirst) will result in no values being produced and the variable pointed to by donePtr being set to one. It is safe to call Tcl_DictObjDone multiple times on the same searchPtr for each call to Tcl_DictObjFirst π«Ά
The procedures Tcl_DictObjPutKeyList and Tcl_DictObjRemoveKeyList are the close analogues of Tcl_DictObjPut and Tcl_DictObjRemove respectively β except instead of working with a single dictionary, they're designed to operate on a nested tree of dictionaries, with inner dictionaries stored as values inside outer dictionaries π³ The keyc and keyv arguments specify a list of keys (with outermost keys first) that acts as a path to the key/value pair to be affected.
Note that there is no corresponding operation for reading a value for a path β because that's easy to construct from repeated use of Tcl_DictObjGet π
With Tcl_DictObjPutKeyList, nested dictionaries are created for non-terminal keys where they do not already exist. With Tcl_DictObjRemoveKeyList, all non-terminal keys must exist and have dictionaries as their values. no shortcuts, no missing links π₯
π REFERENCE COUNT MANAGEMENT π
this section is the β¨ responsible adult β¨ of the page, but we're gonna make it cute.
Tcl_NewDictObj always returns a zero-reference object, much like Tcl_NewObj πΈ
Tcl_DictObjPut does not modify the reference count of its dictPtr argument, but does require that the object be unshared. If Tcl_DictObjPut returns TCL_ERROR it does not manipulate any reference counts; but if it returns TCL_OK then it definitely increments the reference count of valuePtr and may increment the reference count of keyPtr β and the latter case happens exactly when the key did not previously exist in the dictionary.
Note however that this function may set the interpreter result; if that is the only place that is holding a reference to an object, it will be deleted π
Tcl_DictObjGet only reads from its dictPtr and keyPtr arguments, and does not manipulate their reference counts at all. If the valuePtrPtr argument is not set to NULL (and the function doesn't return TCL_ERROR), it will be set to a value with a reference count of at least 1, with a reference owned by the dictionary.
Note however that this function may set the interpreter result; if that is the only place that is holding a reference to an object, it will be deleted π
Tcl_DictObjRemove does not modify the reference count of its dictPtr argument, but does require that the object be unshared. It does not manipulate the reference count of its keyPtr argument at all.
Note however that this function may set the interpreter result; if that is the only place that is holding a reference to an object, it will be deleted π
Tcl_DictObjSize does not modify the reference count of its dictPtr argument; it only reads.
Note however that this function may set the interpreter result; if that is the only place that is holding a reference to the dictionary object, it will be deleted π
Tcl_DictObjFirst does not modify the reference count of its dictPtr argument; it only reads. The variables given by the keyPtrPtr and valuePtrPtr arguments (if not NULL) will be updated to contain references to the relevant values in the dictionary; their reference counts will be at least 1 (due to the dictionary holding a reference to them). It may also manipulate internal references β these are not exposed to user code, but they require a matching Tcl_DictObjDone call π«Ά
Note however that this function may set the interpreter result; if that is the only place that is holding a reference to the dictionary object, it will be deleted π
Similarly for Tcl_DictObjNext; the variables given by the keyPtrPtr and valuePtrPtr arguments (if not NULL) will be updated to contain references to the relevant values in the dictionary; their reference counts will be at least 1 (due to the dictionary holding a reference to them) β¨
Tcl_DictObjDone does not manipulate (user-visible) reference counts.
Tcl_DictObjPutKeyList is similar to Tcl_DictObjPut; it does not modify the reference count of its dictPtr argument, but does require that the object be unshared. It may increment the reference count of any value passed in the keyv argument, and will increment the reference count of the valuePtr argument on success. It is recommended that values passed via keyv and valuePtr do not have zero reference counts π
Note however that this function may set the interpreter result; if that is the only place that is holding a reference to an object, it will be deleted π
Tcl_DictObjRemoveKeyList is similar to Tcl_DictObjRemove; it does not modify the reference count of its dictPtr argument, but does require that the object be unshared, and does not modify the reference counts of any of the values passed in the keyv argument.
Note however that this function may set the interpreter result; if that is the only place that is holding a reference to an object, it will be deleted π
β¨ EXAMPLE β¨
Using the dictionary iteration interface to search and determine if there is a key that maps to itself π (self-love fr fr):
Tcl_DictSearch search;
Tcl_Obj *key, *value;
int done;
/*
* Assume interp and objPtr are parameters. This is the
* idiomatic way to start an iteration over the dictionary; it
* sets a lock on the internal representation that ensures that
* there are no concurrent modification issues when normal
* reference count management is also used. The lock is
* released automatically when the loop is finished, but must
* be released manually when an exceptional exit from the loop
* is performed. However it is safe to try to release the lock
* even if we've finished iterating over the loop.
*/
if (Tcl_DictObjFirst(interp, objPtr, &search,
&key, &value, &done) != TCL_OK) {
return TCL_ERROR;
}
for (; !done ; Tcl_DictObjNext(&search, &key, &value, &done)) {
/*
* Note that strcmp() is not a good way of comparing
* values and is just used here for demonstration
* purposes.
*/
if (!strcmp(Tcl_GetString(key), Tcl_GetString(value))) {
break;
}
}
Tcl_DictObjDone(&search);
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(!done));
return TCL_OK;
π note that strcmp() is not a good way of comparing values β it's just there for demonstration purposes, okay? don't come for me π
π SEE ALSO π
Tcl_NewObjTcl_DecrRefCountTcl_IncrRefCountTcl_InitObjHashTable
β¨ KEYWORDS β¨
okay so if you're out here grepping the man pages, these are the lil search terms that'll get you back to this exact page bestie π
dictβ the main character, obviously πdict valueβ when you need to be a little more specificdictionaryβ the full word, for when you're feeling formal β¨dictionary valueβ the full word plus the value parthash tableβ yes it's giving hash table energy, that's the whole vibeiterationβ for when you're looping through stuff and living your best life π₯valueβ just the value, no thoughts, head empty, still correct π
and that's the whole page, bestie, you made it to the end π«Ά this one came out of Tcl 8.5, and the page itself is Tcl_DictObj(3) β so if you ever need to cite it or hunt down the original, that's the tag π now go forth and slay your dicts, no cap ππ
π¦ where this page comes from
- alpine 3.23: tcl9-doc 9.0.3-r0 Β· TCL
- alpine 3.24: tcl9-doc 9.0.3-r2 Β· TCL
- fedora 43: tcl-doc 1:9.0.2-1.fc43 Β· TCL AND GPL-3.0-or-later WITH Bison-exception-2.2 AND BSD-3-Clause
- fedora 44: tcl-doc 1:9.0.2-1.fc44 Β· TCL AND GPL-3.0-or-later WITH Bison-exception-2.2 AND BSD-3-Clause
- nixos 26.05: tcl 9.0.3
- Copyright (c) 2003 Donal K. Fellows
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms