I am writing a C DLL that creates a new command:
Tcl_CreateObjCommand( interp, "work_on_dict", (Tcl_ObjCmdProc *)work_on_dict_cmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL );
The implementation of the command is:
int work_on_dict_cmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[] )
{
Tcl_Obj *dReturn = Tcl_DuplicateObj( objv[1] ); <------------------- duplicate
Tcl_DictObjPut( interp, dReturn, Tcl_NewStringObj("key2", -1), Tcl_NewIntObj(2) );
Tcl_SetObjResult( interp, dReturn );
return TCL_OK;
}
The Tcl code calling it looks like this:
set dDict [dict create]
dict set dDict "key1" 1
set dDict [work_on_dict $dDict] <------------------- assign back
puts "[dict get $dDict "key1"] [dict get $dDict "key2"]"
Before altering the dictionary in the C code, I have to duplicate it. Otherwise I get a "called with shared object" error. So I have to assign the returned dictionary back to the original one in the Tcl code.
I wonder if there is'nt a smarter way, allowing the C code to work directly on the original dictionary. Something like "work_on_dict dDict" in Tcl and dereferencing the parameter in the C code.
I tried a lot of things but did'nt come to a conclusion if this is after all possible.
I would be glad if someone could give me a hint, thanks.
Here is the solution - Derived from Donal's answer:
C code:
Note that you should add error checking, probably limit the search range to TCL_NAMESPACE_ONLY and call Tcl_ObjSetVar2 for correct traces as Donal states.
int work_on_dict_cmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[] )
{
Tcl_Obj *dReturn = Tcl_ObjGetVar2( interp, objv[1], NULL, 0 );
Tcl_DictObjPut( interp, dReturn, Tcl_NewStringObj("key2", -1), Tcl_NewIntObj(2) );
Tcl_SetObjResult( interp, dReturn );
return TCL_OK;
}
Tcl:
set dDict [dict create]
dict set dDict "key1" 1
work_on_dict dDict
puts "[dict get $dDict "key1"] [dict get $dDict "key2"]"