Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

What is the proper way to get the value of Tcl object via Tcl API

123 views
Skip to first unread message

Nicolas Robert

unread,
Jun 5, 2023, 6:44:39 AM6/5/23
to
Hello ,

I have a list like this :
set myList {1 2 3 4 ::oo::Obj10 foo 10 bar}
A method like this :
method get {} {
# Gets object value.
return $_value
}

And my C code (C experts , Close your eyes!) :
// ...
for (int i = 0; i < count; ++i) {
if (Tcl_GetDoubleFromObj(interp, sub_elements[i], &d) == TCL_OK) {
// Do something...
} else if (!strncmp(Tcl_GetString(sub_elements[i]), "::oo::Obj", 9)) {
strcpy(obj, Tcl_GetString(sub_elements[i]));
strcat(obj, " get"); // method get
if (Tcl_Eval(interp, obj) != TCL_OK) {
return TCL_ERROR;
}
Tcl_ListObjAppendElement(interp, data, Tcl_GetObjResult(interp));
} else {
// Do something...
}
}
//...

I'm using 'strncmp' combined with Tcl_Eval.
I would like to know if there is other method to get the value of my object in a list.
My code works but I don’t know if it is correct.
That's how you'd handle it, something like this ?

Thanks
Nicolas

Rich

unread,
Jun 5, 2023, 9:07:45 AM6/5/23
to
If you want the result of the 'get' call to be in the list, then one
way is to put it there when you create the list:

Create the list this way:

set myList [list 1 2 3 4 [::oo::Obj10 get] foo 10 bar]

And then you don't need to look for strings that look like oo object
names in the list to then try to create a call to that strings get
method.

clt.to...@dfgh.net

unread,
Jun 5, 2023, 11:05:22 AM6/5/23
to
You might be better off trying to avoid having to guess how to process the list objects.

However given that you are creating and executing a command from Tcl_Objs, consider using Tcl_EvalObjv, and instead of checking that the string value looks like an object name just execute the get method on the Tcl_Obj and check if there was an error.

// set up command object array (consider doing this only once in your application)
// I think this is the correct way to initalize a C array to NULLs ??
Tcl_Obj* cmd[3] = {NULL, NULL, NULL};

// second word of command is "get"
cmd[1] = Tcl_NewStringObj("get", -1);

// make sure the "get" does not disappear at an inopportune time.
Tcl_IncrRefCount(cmd[1]);

// need to decrement the reference count (Tcl_DecrRefCount(cmd[1]);)
// just before the cmd[] array goes out of scope to avoid a memory leak

...

// try executing a get method on the current list entry
// if there is a chance the sub element has a zero reference count (unlikely for a list element),
// increment it here (and remember to decrement it later to avoid a memory leak)
cmd[0] = sub_elements[i];
if (TCL_OK == Tcl_EvalObjv(interp, cmd, 0)) {
DO SOMETHING with the result in interp
} else {
TRY SOMETHING ELSE
}

I did not compile or execute this YMMV

Dave B

Nicolas Robert

unread,
Jun 6, 2023, 12:52:45 PM6/6/23
to
Le lundi 5 juin 2023 à 17:05:22 UTC+2, clt.to...@dfgh.net a écrit :
> Create the list this way:
> set myList [list 1 2 3 4 [::oo::Obj10 get] foo 10 bar]

@Rich ,
I can't, the goal here is to check each type, so I need to keep the name of the object.
@Dave

If I understand , In that way so in Tcl :
foreach obj $myList {
if {![catch {$obj get} value]} {
# Do something
}
}
Maybe I'm wrong but in pure Tcl, I would never do like that, It's maybe different in C.
That said
Below my C code :
Tcl_Obj* cmd[2];
cmd[1] = Tcl_NewStringObj ("get", -1);
Tcl_IncrRefCount(cmd[1]);

for (int i = 0; i < count; ++i) {
cmd[0] = sub_elements[i];
Tcl_IncrRefCount(cmd[0]);

if (Tcl_EvalObjv(interp, 2, cmd, 0) == TCL_OK) {
Tcl_DecrRefCount(cmd[0]);
Tcl_ListObjAppendElement(interp, data, Tcl_GetObjResult(interp));
} else {
// Do something...
}
}
Tcl_DecrRefCount(cmd[1]);

I’m not sure about myself, it works , but I tested on a list of 20000 items and my performance is bad, my C code is correct ?

Thanks
Nicolas

Rich

unread,
Jun 6, 2023, 1:00:41 PM6/6/23
to
Nicolas Robert <nicolasro...@gmail.com> wrote:
> Le lundi 5 juin 2023 à 17:05:22 UTC+2, clt.to...@dfgh.net a écrit :
>> Create the list this way:
>> set myList [list 1 2 3 4 [::oo::Obj10 get] foo 10 bar]
>
> I can't, the goal here is to check each type, so I need to keep the
> name of the object.

Then consider changing your list definition:

set myList [list val 1 val 2 val 3 val 4 obj ::oo::Obj10 val foo val 10 val bar]

Then (in Tcl) you could process something like this way:

foreach {type content} $myList {
switch -exact -- $type {
val { # do something with a value }
obj { # do a [$content get] to get the value, then do something
with the value and the object name }
}
}

A similar 'loop' in C could be crafted but I'm not going to attempt to
type that out here (it would likely contain many syntax errors).

Nicolas Robert

unread,
Jun 6, 2023, 1:15:34 PM6/6/23
to
Rich,

I do not know the type of my strings before testing them via a loop.
So my loop in Tcl is like this :
foreach content $myList {
if {[string is ...]} {...}
if {[info object isa object ...] {...}
}

I’d like to do the same in C.

Nicolas
0 new messages