Michael
Michael

Reputation: 5939

How to form a TCL dictionary as a string to pass it around as a string variable?

I need to format a string that can be interpreted as a TCL dictionary. Basically the string is formed in C++ then passed as a string variable value into a TCL interpreter and the TCL interpreter should be able to treat that string value as a dictionary. Is that possible, and, if so, how should that string be formatted?

Upvotes: 2

Views: 1384

Answers (2)

Donal Fellows
Donal Fellows

Reputation: 137707

If you have access to Tcl's C API, use the dict operations (along with appropriate other ones).

Tcl_Obj *dict = Tcl_NewDictObj();
Tcl_IncrRefCount(dict);
Tcl_DictObjPut(NULL, dict, Tcl_NewStringObj(key1, -1), Tcl_NewStringObj(value1, -1));
// etc, to build the dictionary; you need more care if you have duplicated keys

// Now, get the string value; note that the dict object OWNS the string memory
const char *stringValue = Tcl_GetString(dict);

// and once you don't need stringValue or dict any more
Tcl_DecrRefCount(dict);

Otherwise, if you can form a Tcl list with an even number of elements, then that will be interpretable as a dictionary (the string representations are deliberately coincident). Assuming you don't want to use Tcl_Obj-based operations, there's also the low level dynamic strings (an older API that's used quite a bit internally but which isn't particularly recommended now):

Tcl_DString buffer;
Tcl_DStringInit(&buffer);
Tcl_DStringAppendElement(&buffer, key1);
Tcl_DStringAppendElement(&buffer, value1);
// ...
const char *stringValue = Tcl_DStringValue(&buffer);
// and once you're done with everything
Tcl_DStringFree(&buffer);

Before you ask, as with the Tcl_Obj, the Tcl_DString owns the memory associated with stringValue. You'll need to take a copy if you want it to persist past the freeing of the buffer. (Note that the Tcl_Obj API is potentially more efficient, except it isn't actually when all you're doing ultimately is building a string out of it.)

If you want to manage the memory allocation yourself, there's the absolute base level API. In particular, Tcl_ScanElement will let your code work out how to format a particular element string efficiently (it returns an estimate of the space required and, via an OUT parameter, the instructions on how to do the formatting) and Tcl_ConvertElement will do the formatting into the buffer you have prepared. Their use is a bit fiddly, so I'll just link to how Tcl itself uses them (that's part of the implementation of Tcl's list objects).


If you don't have the Tcl C API available at all, know that you're looking to produce a sequence of whitespace-separated words. For simple words (e.g., with just alphanumerics or most basic punctuation) you can just put the words in directly. For anything more complex (i.e., if it contains a Tcl metacharacter like [, $, \ or any whitespace), you need to either enclose them in {braces} or put backslashes in front of the offending characters. Putting braces around works in most cases (the main one it fails for is unbalanced braces; there are a few others). Putting backslashes in works in all cases but is ugly; it's the sort of thing that it is fairly easy for code to produce but really horrible to read and debug, especially with nested lists.

Those are the rules for lists. Dictionaries are format-equivalent to lists with an even number of items. There are two requirements for canonicality of dictionaries: the whitespace separating words should be single spaces (this is the canonical list form), and the keys of the dictionary (the first, third, fifth ... elements) should each be unique. Non-canonical dictionaries work, and are precisely defined (the last occurrence of a key is preferred), but might occasionally surprise when you're not thinking carefully. Tcl's own dictionary code always canonicalises a dictionary when it writes to one (because the internal model is a hash table; your C code does not need to build this).

Upvotes: 1

Shawn
Shawn

Reputation: 52529

Any string with an even number of words (As defined by tcl) is a dict.

Example tclsh session:

% set demo "a silly example string"
a silly example string
% dict get $demo a
silly
% dict get $demo example
string

If you have keys or elements that are multiple words, they should be enclosed in braces:

% set example "a {multi word value} {multi word key} test"
a {multi word value} {multi word key} test
% dict get $example a
multi word value
% dict get $example "multi word key"
test

Upvotes: 2

Related Questions