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

    From Nicolas Robert@21:1/5 to All on Mon Jun 5 03:44:36 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Nicolas Robert on Mon Jun 5 13:07:41 2023
    Nicolas Robert <nicolasrobert.19000@gmail.com> wrote:
    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 ?

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From clt.to.davebr@dfgh.net@21:1/5 to All on Mon Jun 5 15:06:18 2023
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Nicolas Robert@21:1/5 to All on Tue Jun 6 09:52:42 2023
    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.

    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

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Nicolas Robert on Tue Jun 6 17:00:37 2023
    Nicolas Robert <nicolasrobert.19000@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).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Nicolas Robert@21:1/5 to All on Tue Jun 6 10:15:31 2023
    Le mardi 6 juin 2023 à 19:00:41 UTC+2, Rich a écrit :
    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).

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)