• Reference counting

    From Alan Grunwald@[email protected] to comp.lang.tcl on Thu Oct 1 13:19:04 2026
    From Newsgroup: comp.lang.tcl

    Hi,

    I'm cracking on with my first Tcl extension. It wraps a set of C++ code
    with a function that returns a vector of strings. I want to expose that
    to the Tcl user, so I have something like

    int myExtension_nameCmd (ClientData clientData, TclInterp *interp,
    int objc, Tcl_Obj *const objv[])
    {
    if (objc != 2) {
    Tcl_WrongNumArgs (interp, 2, objv, nullptr);
    return TCL_ERROR;
    }

    Tcl_Obj *rslt (Tcl_NewListObj (0, nullptr));
    for (auto name : names ()) {
    if (Tcl_ListObjAppendElement (interp, rslt,
    Tcl_NewStringObj (name.c_str (),
    -1))
    != TCL_OK) {
    return TCL_ERROR;
    }
    }

    Tcl_SetObjResult (interp, rslt);
    return TCL_OK;
    }

    This works Just Fine (tm) normally - in the sense that the Tcl user gets
    the list that he should do, but is it "correct" in the way it handles reference counting?

    I imagine that if all is well, then the result list and all the objects
    it contains get handed over to a Tcl interpreter when I call
    Tcl_SetObjResult and that interpreter looks after freeing up the
    objects' memory at the appropriate moment.

    I'm worried what happens if there's an error constructing, say the third object to add to the list. Do I need to free the memory used by the list
    and the first two objects and if so how?

    I've tried reading the documentation in various directions (including
    from beginning to end, in that order) and I'm not really any the wiser.
    Does it answer my question, and if so where?

    Many thanks.

    Alan
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Harald Oehlmann@[email protected] to comp.lang.tcl on Thu Oct 1 15:04:47 2026
    From Newsgroup: comp.lang.tcl

    Am 01.10.2026 um 14:19 schrieb Alan Grunwald:
    Hi,

    I'm cracking on with my first Tcl extension. It wraps a set of C++ code
    with a function that returns a vector of strings. I want to expose that
    to the Tcl user, so I have something like

    int myExtension_nameCmd (ClientData clientData, TclInterp *interp,
                             int objc, Tcl_Obj *const objv[])
    {
        if (objc != 2) {
            Tcl_WrongNumArgs (interp, 2, objv, nullptr);
            return TCL_ERROR;
        }

        Tcl_Obj *rslt (Tcl_NewListObj (0, nullptr));
        for (auto name : names ()) {
            if (Tcl_ListObjAppendElement (interp, rslt,
                                          Tcl_NewStringObj (name.c_str (),
                                                            -1))
                   != TCL_OK) {
               return TCL_ERROR;
            }
        }

        Tcl_SetObjResult (interp, rslt);
        return TCL_OK;
    }

    This works Just Fine (tm) normally - in the sense that the Tcl user gets
    the list that he should do, but is it "correct" in the way it handles reference counting?

    I imagine that if all is well, then the result list and all the objects
    it contains get handed over to a Tcl interpreter when I call Tcl_SetObjResult and that interpreter looks after freeing up the
    objects' memory at the appropriate moment.

    I'm worried what happens if there's an error constructing, say the third object to add to the list. Do I need to free the memory used by the list
    and the first two objects and if so how?

    I've tried reading the documentation in various directions (including
    from beginning to end, in that order) and I'm not really any the wiser.
    Does it answer my question, and if so where?

    Many thanks.

    Alan


    Yes, reference counting is an issue and it is also -- genious --...

    The code looks correct to me.

    All Tcl_New_.. commands return an object with reference count 0.
    Any usage function increments it.

    The answer to your question about the error:
    - yes, the current code memory leaks the created objects

    You need:

    Tcl_IncrRefCount(rslt)
    after creation and decrement in the error case and after the
    Tcl_SetObjResult

    The answer to your question: is this code correct:
    Yes, it is correct. The list append error will never fire.
    It only fires, if rslt is not a propper list and this may not happen.
    So, removing the error check would be ok.

    Keep asking, it is complex !

    Harald
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From abu@[email protected] to comp.lang.tcl on Thu Oct 1 13:27:17 2026
    From Newsgroup: comp.lang.tcl


    Simply add
    Tcl_DecrRefCount(rslt) before returning TCL_ERROR;



    Tcl_Obj *rslt (Tcl_NewListObj (0, nullptr));
    for (auto name : names ()) {
    if (Tcl_ListObjAppendElement (interp, rslt,
    Tcl_NewStringObj (name.c_str (),
    -1))
    != TCL_OK) {
    return TCL_ERROR;
    }
    }

    Tcl_SetObjResult (interp, rslt);
    return TCL_OK;
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From abu@[email protected] to comp.lang.tcl on Thu Oct 1 13:33:42 2026
    From Newsgroup: comp.lang.tcl


    .. sorry, I clicked before finishing ...

    Simply add
    Tcl_DecrRefCount(rslt)
    before returning TCL_ERROR;
    ....

    Tcl_Obj *rslt (Tcl_NewListObj (0, nullptr));
    for (auto name : names ()) {
    if (Tcl_ListObjAppendElement (interp, rslt,
    Tcl_NewStringObj (name.c_str (),
    -1))
    != TCL_OK) {
    // ----------------------------------
    Tcl_DecrRefCount(rslt); // the list and (recursively all its elements are destroyed)
    // optional: inform about the error
    Tcl_SetObjResult(interp, Tcl_NewStringObj("something wrong..",-1); // ----------------------------------
    return TCL_ERROR;
    }
    }

    Tcl_SetObjResult (interp, rslt);
    return TCL_OK;
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Olivier@[email protected] to comp.lang.tcl on Thu Oct 1 15:47:23 2026
    From Newsgroup: comp.lang.tcl


    Harald Oehlmann <[email protected]> posted:


    Keep asking, it is complex !


    Bonjour,

    Since you propose it ... Since memory management is complex, have you an an idea why
    the Tcl command "memory" is not systematically available ? Can it be a security problem ? More surprising, it is probably the only Tcl command which is not described in Ashok's books !
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Colin Macleod@[email protected] to comp.lang.tcl on Thu Oct 1 16:17:04 2026
    From Newsgroup: comp.lang.tcl

    Olivier <[email protected]d> posted:

    Bonjour,

    Since you propose it ... Since memory management is complex, have you an an idea why
    the Tcl command "memory" is not systematically available ? Can it be a security
    problem ? More surprising, it is probably the only Tcl command which is not described in Ashok's books !

    The extra checks that `memory` does have a high runtime cost in processing
    time and extra memory allocation for guard areas. So it's only enabled in special debug builds.
    --
    Colin Macleod ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ https://cmacleod.me.uk

    FEED GAZA NOW! SAVE GAZA NOW! HOUSE GAZA NOW! FEED GAZA NOW!
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Olivier@[email protected] to comp.lang.tcl on Thu Oct 1 18:26:29 2026
    From Newsgroup: comp.lang.tcl


    Colin Macleod <[email protected]d> posted:

    Olivier <[email protected]d> posted:

    Bonjour,

    Since you propose it ... Since memory management is complex, have you an an idea why
    the Tcl command "memory" is not systematically available ? Can it be a security
    problem ? More surprising, it is probably the only Tcl command which is not described in Ashok's books !

    The extra checks that `memory` does have a high runtime cost in processing time and extra memory allocation for guard areas. So it's only enabled in special debug builds.


    Thank you, I will keep in mind that it is more suited for chirurgical memory problems than abnormal memory volume usage.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Harald Oehlmann@[email protected] to comp.lang.tcl on Fri Oct 2 10:04:14 2026
    From Newsgroup: comp.lang.tcl

    Am 01.10.2026 um 15:27 schrieb abu:

    Simply add
    Tcl_DecrRefCount(rslt) before returning TCL_ERROR;
    Abu, this is a correct proposal, no criticism here.

    A bit for the records an esotheric addition:

    In Tcl 9.0, there is a new command for this:
    Tcl_BounceRefCount(rslt)

    In this use-case, it is not important.

    Nevertheless, we should take the habbit to use this function to dispose Objects with 0 ref count (like in this case).

    I don't understand all reasons but it was to fix some memory issues with abstract lists.

    https://www.tcl-lang.org/man/tcl9.1/TclLib/Object.html
    has a sort of explanation in the memory management section.

    Take care,
    Harald


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Ralf Fassel@[email protected] to comp.lang.tcl on Fri Oct 2 11:53:36 2026
    From Newsgroup: comp.lang.tcl

    * abu <[email protected]d>
    | if (Tcl_ListObjAppendElement (interp, rslt,
    | Tcl_NewStringObj (name.c_str (),
    | -1))
    | != TCL_OK) {
    | // ----------------------------------
    | Tcl_DecrRefCount(rslt); // the list and (recursively all its elements are destroyed)
    | // optional: inform about the error
    | Tcl_SetObjResult(interp, Tcl_NewStringObj("something wrong..",-1);

    Adding that trailing Tcl_SetObjResult() would replace the error message
    which probably has a cue *why* the Tcl_ListObjAppendElement() did fail
    with "something wrong.." - I'd rather have the original message.

    My €0.01
    R'
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Ralf Fassel@[email protected] to comp.lang.tcl on Fri Oct 2 12:22:01 2026
    From Newsgroup: comp.lang.tcl

    * Alan Grunwald <[email protected]>
    | I'm cracking on with my first Tcl extension. It wraps a set of C++
    | code with a function that returns a vector of strings.

    If you're using c++ anyway, why not wrap the TCL Objs in a class which
    does the refcounting for you:

    class managed_objptr {
    public:
    // CTOR from provided Tcl_Obj
    managed_objptr(Tcl_Obj *obj) : obj_(obj) {
    if (0 == obj_) throw std::invalid_argument("obj == 0");
    incr();
    }
    // DTOR
    ~managed_objptr() {
    decr();
    }
    // use managed_objptr in function calls requiring a Tcl_Obj*
    operator Tcl_Obj *() const {return obj_;}

    // code for demo only, so we just omit CCTOR and assignment
    managed_objptr(const managed_objptr&other) = delete;
    managed_objptr& operator =(const managed_objptr &other) = delete;

    private:
    void incr() {
    Tcl_IncrRefCount(obj_);
    }
    void decr() {
    Tcl_DecrRefCount(obj_);
    }
    Tcl_Obj *obj_;
    }; // managed_objptr


    // usage:
    // change: Tcl_Obj *rslt (Tcl_NewListObj (0, nullptr));
    // into:
    managed_objptr rslt (Tcl_NewListObj (0, nullptr));
    // and forget about the refcounting :-)

    => incr/decr handled in CTOR/DTOR.
    => use 'rslt' transparently in any TCL function which requires
    a Tcl_Obj* (via the operator overload):

    Tcl_SetObjResult(interp, rslt);

    In your code, the DTOR of the object will handle the refcounting when
    the function exits in either way, either normally or by "return
    TCL_ERROR" early.

    HTH
    R'
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Ralf Fassel@[email protected] to comp.lang.tcl on Fri Oct 2 12:27:07 2026
    From Newsgroup: comp.lang.tcl

    * Alan Grunwald <[email protected]>
    | Tcl_Obj *rslt (Tcl_NewListObj (0, nullptr));
    | for (auto name : names ()) {
    | if (Tcl_ListObjAppendElement (interp, rslt,
    | Tcl_NewStringObj (name.c_str (),
    | -1))
    | != TCL_OK) {
    | return TCL_ERROR;
    | }
    | }

    | Tcl_SetObjResult (interp, rslt);
    | return TCL_OK;
    | }

    | This works Just Fine (tm) normally - in the sense that the Tcl user
    | gets the list that he should do, but is it "correct" in the way it
    | handles reference counting?

    As others have said, the refcounting is incomplete in the case of early
    return inside the loop. You either add Incr once and Decr before any
    return,

    OR: If you move the
    Tcl_SetObjResult (interp, rslt);
    *before* the loop:

    Tcl_Obj *rslt (Tcl_NewListObj (0, nullptr));
    !! Tcl_SetObjResult (interp, rslt);
    for (auto name : names ()) {
    if (Tcl_ListObjAppendElement (interp, rslt,
    Tcl_NewStringObj (name.c_str (),
    -1))
    != TCL_OK) {
    return TCL_ERROR;
    }
    }

    then the rslt object is handled by TCL:
    - Tcl_SetObjResult() will incr the refcount of rslt
    - any error in Tcl_ListObjAppendElement() will set a new result in the
    interp, which does a decr in the current result first, which deletes
    rslt and all its Elements.

    HTH
    R'
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Harald Oehlmann@[email protected] to comp.lang.tcl on Fri Oct 2 12:38:30 2026
    From Newsgroup: comp.lang.tcl

    OR: If you move the
    Tcl_SetObjResult (interp, rslt);
    *before* the loop:

    Tcl_Obj *rslt (Tcl_NewListObj (0, nullptr));
    !! Tcl_SetObjResult (interp, rslt);
    for (auto name : names ()) {
    if (Tcl_ListObjAppendElement (interp, rslt,
    Tcl_NewStringObj (name.c_str (),
    -1))
    != TCL_OK) {
    return TCL_ERROR;
    }
    }

    then the rslt object is handled by TCL:
    - Tcl_SetObjResult() will incr the refcount of rslt
    - any error in Tcl_ListObjAppendElement() will set a new result in the
    interp, which does a decr in the current result first, which deletes
    rslt and all its Elements.
    Super-smart proposal, Ralph !

    But is the error case of Tcl_ListObjAppendElement a realistic case?
    To my knowledge, it may only error out, if the first argument may not be converted to a list and this may not happen here...

    I normally just use:

    Tcl_Obj *rslt (Tcl_NewListObj (0, nullptr));
    for (auto name : names ()) {
    Tcl_ListObjAppendElement (interp, rslt,
    Tcl_NewStringObj (name.c_str (),-1);
    }

    To my knowledge, any possible memory error will panic and not throw an
    error message.

    Harald
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Ralf Fassel@[email protected] to comp.lang.tcl on Fri Oct 2 14:09:14 2026
    From Newsgroup: comp.lang.tcl

    * Harald Oehlmann <[email protected]>
    | But is the error case of Tcl_ListObjAppendElement a realistic case?
    | To my knowledge, it may only error out, if the first argument may not
    | be converted to a list and this may not happen here...

    This is probably true for this particular case - here omitting the error
    check does not hurt.

    My suggestion was meant to show other possibilities to avoid adding
    incr/decr all over the place (and missing some in later changes of the
    code if the function is larger than a 10-liner).

    R'
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Harald Oehlmann@[email protected] to comp.lang.tcl on Fri Oct 2 14:22:15 2026
    From Newsgroup: comp.lang.tcl

    Am 02.10.2026 um 14:09 schrieb Ralf Fassel:
    * Harald Oehlmann <[email protected]>
    | But is the error case of Tcl_ListObjAppendElement a realistic case?
    | To my knowledge, it may only error out, if the first argument may not
    | be converted to a list and this may not happen here...

    This is probably true for this particular case - here omitting the error check does not hurt.

    My suggestion was meant to show other possibilities to avoid adding
    incr/decr all over the place (and missing some in later changes of the
    code if the function is larger than a 10-liner).
    Thanks, and sorry for the spelling of your name,
    Harald
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Christian Gollwitzer@[email protected] to comp.lang.tcl on Sat Oct 3 07:44:43 2026
    From Newsgroup: comp.lang.tcl

    Am 02.10.26 um 12:22 schrieb Ralf Fassel:
    * Alan Grunwald <[email protected]>
    | I'm cracking on with my first Tcl extension. It wraps a set of C++
    | code with a function that returns a vector of strings.

    If you're using c++ anyway, why not wrap the TCL Objs in a class which
    does the refcounting for you:

    class managed_objptr {

    I can second that - once you get the wrapper correct, you'll never have
    to worry about the refcounting again. For a project which constructs
    nested list/dict structures, I'm using this code:

    https://github.com/BessyHDFViewer/HDFpp/blob/main/generic/SWObject.hpp

    It was meant for use with SWIG, but can be used without easily as well.
    To return a list, you'd do:

    SWList mylist;
    mylist.push_back(42);
    mylist.push_back("Hello");
    mylist.push_back(37.899);
    Tcl_SetObjResult(interp, mylist,getObj());

    Refcounting is handled by C++ RAII in all cases, like early return or exceptions, and you can similarly push back other SWLists or SWDicts to
    make nested structures.

    Christian


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Alan Grunwald@[email protected] to comp.lang.tcl on Mon Oct 5 17:28:39 2026
    From Newsgroup: comp.lang.tcl

    On 01/10/2026 13:19, Alan Grunwald wrote:
    Hi,

    I'm cracking on with my first Tcl extension. It wraps a set of C++ code
    with a function that returns a vector of strings. I want to expose that
    to the Tcl user, so I have something like

    int myExtension_nameCmd (ClientData clientData, TclInterp *interp,
                             int objc, Tcl_Obj *const objv[])
    {
        if (objc != 2) {
            Tcl_WrongNumArgs (interp, 2, objv, nullptr);
            return TCL_ERROR;
        }

        Tcl_Obj *rslt (Tcl_NewListObj (0, nullptr));
        for (auto name : names ()) {
            if (Tcl_ListObjAppendElement (interp, rslt,
                                          Tcl_NewStringObj (name.c_str (),
                                                            -1))
                   != TCL_OK) {
               return TCL_ERROR;
            }
        }

        Tcl_SetObjResult (interp, rslt);
        return TCL_OK;
    }

    This works Just Fine (tm) normally - in the sense that the Tcl user gets
    the list that he should do, but is it "correct" in the way it handles reference counting?

    I imagine that if all is well, then the result list and all the objects
    it contains get handed over to a Tcl interpreter when I call Tcl_SetObjResult and that interpreter looks after freeing up the
    objects' memory at the appropriate moment.

    I'm worried what happens if there's an error constructing, say the third object to add to the list. Do I need to free the memory used by the list
    and the first two objects and if so how?

    I've tried reading the documentation in various directions (including
    from beginning to end, in that order) and I'm not really any the wiser.
    Does it answer my question, and if so where?


    Thanks everyone for your responses to this question.

    I formulated individual messages of thanks and inserted them in
    appropriate places late on Saturday night, but unfortunately sent them
    as mail messages rather than newsgroup followups - it was, doubtless,
    later than I thought :-).

    Having reviewed the messages, I don't think it's worth repeating the
    exercise but doing it correctly this time. To anyone who is interested,
    I shall almost certainly go down the "smart pointer" route as proposed
    by Ralf and seconded by Christian.

    Alan
    --- Synchronet 3.22a-Linux NewsLink 1.2