Reputation: 11
I am not able to find a way to link c struct data type variables to tcl struct data type variables.. I want to send struct variables data from c programming to tcl.. Can anyone give suggestion on this ?
Upvotes: 0
Views: 399
Reputation: 137767
Tcl doesn't have structs as such, so you need to think what you're trying to do. The key choices have to do with how many structs are we talking about and how active these entities need to be.
If you're just talking about mapping a single struct instance, you can use Tcl_LinkVar()
to bind primitive C types (such as the members of the struct), and have the Tcl side variables all be elements of a single array. This is great for things like systems control.
struct foo_t {
int bar;
double grill;
}
// NEEDS to be at file or global scope!
// Or to something with at least the same lifetime as the Tcl interpreter.
// MUST NOT be stack-allocated or Bad Things Happen™
struct foo_t foo;
// And then to bind the elements in your Init function, and omitting some casts
Tcl_LinkVar(interp, "foo(bar)", &foo.bar, TCL_LINK_INT);
Tcl_LinkVar(interp, "foo(grill)", &foo.grill, TCL_LINK_DOUBLE);
While that is a two-way binding, it doesn't work so well if you want to bind many of them. In theory it can work somewhat but it's really messy.
If you're wanting to do the equivalent of passing around structs as values, there's three basic approaches. These are mapping them as lists, mapping them as dicts, and doing custom stuff. In all cases with these, it really helps if you make some helper functions, which I'll call NewFooObj()
and GetFooFromObj()
:
For lists:
Tcl_Obj *NewFooObj(struct foo_t *fooPtr) {
Tcl_Obj *objs[2];
objs[0] = Tcl_NewIntObj(fooPtr->bar);
objs[1] = Tcl_NewDoubleObj(fooPtr->grill);
return Tcl_NewListObj(2, objs);
}
int GetFooFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, struct foo_t *fooPtr) {
int objc;
Tcl_Obj **objv;
if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
return TCL_ERROR;
}
if (objc != 2) {
Tcl_AppendResult(interp, "foo objects need two elements", NULL);
return TCL_ERROR;
}
if (Tcl_GetIntFromObj(interp, objv[0], &fooPtr->bar) != TCL_OK) {
return TCL_ERROR;
}
if (Tcl_GetDoubleFromObj(interp, objv[0], &fooPtr->grill) != TCL_OK) {
return TCL_ERROR;
}
return TCL_OK;
}
Doing the same with dicts is an exercise for you in how to use Tcl_DictObjPut()
and Tcl_DictObjGet()
(but you'll need to be a bit careful on reading because keys are reference counted objects themselves).
Tcl_Obj *NewFooObj(struct foo_t *fooPtr) {
Tcl_Obj *objPtr = Tcl_NewDictObj();
Tcl_DictObjPut(NULL, objPtr, Tcl_NewStringObj("bar", -1),
Tcl_NewIntObj(fooPtr->bar));
Tcl_DictObjPut(NULL, objPtr, Tcl_NewStringObj("grill", -1),
Tcl_NewDoubleObj(fooPtr->grill));
return objPtr;
}
int GetFooFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, struct foo_t *fooPtr) {
Tcl_Obj *barNameObj = Tcl_NewStringObj("bar", -1);
Tcl_Obj *grillNameObj = Tcl_NewStringObj("grill", -1);
Tcl_Obj *value;
int result = TCL_ERROR;
Tcl_IncrRefCount(barNameObj);
Tcl_IncrRefCount(grillNameObj);
if (Tcl_DictObjGet(interp, objPtr, barNameObj, &value) == TCL_OK
&& Tcl_GetIntFromObj(interp, value, &foo->bar) == TCL_OK
&& Tcl_DictObjGet(interp, objPtr, grillNameObj, &value) == TCL_OK
&& Tcl_GetDoubleFromObj(interp, value, &foo->grill) == TCL_OK) {
result = TCL_OK;
}
Tcl_DecrRefCount(barNameObj);
Tcl_DecrRefCount(grillNameObj);
return result;
}
The groups of functions have conventional Tcl reference count semantics (creation functions deliver a zero refcount value, read functions don't manipulate visible reference count).
As for the third option… yes, you can also write your own custom object type that can use whatever string and internal representations you want (you'll probably want a reference to a heap-allocated struct foo_t
as the internal representation). If you go this route, you're urged to make it so that you can parse pure strings to get your type (not allowing that makes them magical and potentially fragile) and to have it so that you can round trip consistently.
If you want to have many instances and to have those instances be modifiable (and aren't just passing them through; if just passing through then you can get away with just throwing the address through the Tcl code, which is formally unsafe but very convenient, and can be made safe by handle-isation, which is in many ways how active Tcl entities like commands, variables and channels work — converting things to handles uses a hash table to map between names and pointers) then you probably have to go to something like TclOO or a system of custom commands. I think this answer is long enough already, so ask again if you need this level of complexity.
Upvotes: 3