Reputation: 6647
Let's suppose I implemented a new tcl command written in c-code that I registered using Tcl_CreateObjCommand, and inside of this c-code I call Tcl_Eval to eval a string containing code to create an Associative array and store it in a variable tmp. How can I set this tmp variable created with Tcl_eval() as the return result object from the c-function?
Example:
int MyCommand(
ClientData clientData,
Tcl_Interp* interp,
int argc,
char* argv[])
{
int rc = Tcl_Eval(interp,
"array set tmp [list {key1} {value1} {key2} {value2}]");
if (rc != TCL_OK) {
return rc;
}
//???
Tcl_SetObjResult(interp, ?? tmp variable from eval??);
return TCL_OK;
}
When I run the Tcl interpreter with the above C-extension, I would expect to see this result:
TCL> set x [MyCommand]
TCL> puts "$x(key1)"
value1 # Currently an Error and not set
TCL> puts "$x(key2)"
value2 # Currently and Error and not set
In a way the above works. Just not the way I want it to. For Example, if I type:
TCL> set x [MyCommand]
TCL> puts "$tmp(key1)"
value1 # Its Works! Except, I didn't want to set a global variable tmp
TCL> puts "$tmp(key2)"
value2 # Its Works! Except, I didn't want to set a global variable tmp
(Maybe its a "feature" to set tmp instead??) Anyways, I still want it to work the correct way by returning the value using the proc "return" mechanism.
It should be legal to call Tcl_Eval() from inside of Tcl_Eval of c-command-extension because the documentation for the "Tcl Library" States that for tcl_eval, it is legal to make nested calls to evaluate other commands. I just don't know how to copy the object result from Tcl_Eval to "return" object for c-extension procedure.
Upvotes: 0
Views: 1182
Reputation: 137787
The recommended way to set an array (given you're working with char*
values in the first place) is using calls to Tcl_SetVar2
(so named because it takes variable names as two parts).
Tcl_SetVar2(interp, "tmp", "key1", "value1", 0);
Tcl_SetVar2(interp, "tmp", "key2", "value2", 0);
Idiomatically, you'd use a name passed in as an argument to your C command implementation as an argument, so that the caller can tell you what variable to write into, and you'd want to check the results too:
int MyCommand(
ClientData clientData,
Tcl_Interp* interp,
int argc,
char* argv[])
{
// Missing: check # of arguments
if (Tcl_SetVar2(interp, argv[1], "key1", "value1", 0) == NULL)
return TCL_ERROR;
if (Tcl_SetVar2(interp, argv[1], "key2", "value2", 0) == NULL)
return TCL_ERROR;
return TCL_OK;
}
You'd then call that like this:
MyCommand x
# It has no meaningful result.
puts $x(key1)
puts $x(key2)
Upvotes: 2
Reputation: 1551
I see two problems here. You can't set the return value of a command to be the value of an array because arrays are not values. Arrays are collections of variables indexed by a string. It's a common misunderstanding. You could return the value of an element of an array. If you want a key / value map that is a proper Tcl value, consider a dictionary. Dictionaries are values and can be returned as the value of a command.
The second problem why are you using Tcl_Eval() to create an array. It is much simpler to use Tcl_SetVar() or one of its several variations to build an array.
Upvotes: 2