• Values being overwritten in Tcl_HashTable

    From Kevin Walzer@kw@codebykevin.com to comp.lang.tcl on Sun Nov 10 22:37:07 2024
    From Newsgroup: comp.lang.tcl

    I am trying to write mulitple key-value pairs to a Tcl_HashTable, but
    when I call Tcl_SetObjResult(ip, Tcl_GetHashValue(hPtr2)), it reads the
    last value written to the hash table - even if that value is not
    associated with the key that I am using.

    Is there a best practice I am missing here? I thought that differing
    keys would be tracked in the hash table and the Tcl_FindHashEntry call
    would return different data depending on the key.

    Tcl_ResetResult does not change the result in the interpreter, which
    tells me the issue is with the table - calls to the key for "role" and
    "name" return the value for "name" - seems like the "role" value has
    been overwritten.

    My C code is below. Any help is appreciated.

    int
    Tk_SetAccessibleRole(
    TCL_UNUSED(void *),
    Tcl_Interp *ip, /* Current interpreter. */
    int objc, /* Number of arguments. */
    Tcl_Obj *const objv[]) /* Argument objects. */
    {
    if (objc < 3) {
    Tcl_WrongNumArgs(ip, 1, objv, "window? role?");
    return TCL_ERROR;
    }

    Tk_Window win;
    char *role;
    Tcl_HashEntry *hPtr, *hPtr2;

    Tcl_HashTable *AccessibleAttributes;
    AccessibleAttributes = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
    Tcl_InitHashTable(AccessibleAttributes,TCL_STRING_KEYS);

    int isNew;
    win = Tk_NameToWindow(ip, Tcl_GetString(objv[1]), Tk_MainWindow(ip));
    if (win == NULL) {
    return TCL_ERROR;
    }

    /* Set accessible role for window. */
    hPtr=Tcl_CreateHashEntry(TkAccessibilityObject, win, &isNew);

    Tcl_SetHashValue(hPtr, AccessibleAttributes);

    hPtr2 = Tcl_CreateHashEntry(AccessibleAttributes, role, &isNew);
    if (!isNew) {
    Tcl_DecrRefCount(Tcl_GetHashValue(hPtr2));
    }
    Tcl_IncrRefCount(objv[2]);
    Tcl_SetHashValue(hPtr2, objv[2]);

    Tcl_SetObjResult(ip, Tcl_GetHashValue(hPtr2));
    return TCL_OK;
    }


    int
    Tk_SetAccessibleName(
    TCL_UNUSED(void *),
    Tcl_Interp *ip, /* Current interpreter. */
    int objc, /* Number of arguments. */
    Tcl_Obj *const objv[]) /* Argument objects. */
    {
    if (objc < 3) {
    Tcl_WrongNumArgs(ip, 1, objv, "window? name?");
    return TCL_ERROR;
    }

    Tk_Window win;
    char *name;
    Tcl_HashEntry *hPtr, *hPtr2;
    int isNew;
    Tcl_HashTable *AccessibleAttributes;

    win = Tk_NameToWindow(ip, Tcl_GetString(objv[1]), Tk_MainWindow(ip));
    if (win == NULL) {
    return TCL_ERROR;
    }

    /* Set accessible name for window. */

    hPtr=Tcl_FindHashEntry(TkAccessibilityObject, win);
    if (!hPtr) {
    Tcl_AppendResult(ip, "No table found", (char *) NULL);
    return TCL_ERROR;
    }

    AccessibleAttributes = Tcl_GetHashValue(hPtr);
    hPtr2 = Tcl_CreateHashEntry(AccessibleAttributes, name, &isNew);
    if (!isNew) {
    Tcl_DecrRefCount(Tcl_GetHashValue(hPtr2));
    }
    Tcl_IncrRefCount(objv[2]);
    Tcl_SetHashValue(hPtr2, objv[2]);

    Tcl_SetObjResult(ip, Tcl_GetHashValue(hPtr2));
    return TCL_OK;
    }

    int
    Tk_GetAccessibleRole(
    TCL_UNUSED(void *),
    Tcl_Interp *ip, /* Current interpreter. */
    int objc, /* Number of arguments. */
    Tcl_Obj *const objv[]) /* Argument objects. */
    {
    if (objc < 2) {
    Tcl_WrongNumArgs(ip, 1, objv, "window?");
    return TCL_ERROR;
    }

    Tk_Window win;
    char *role;
    Tcl_HashEntry *hPtr, *hPtr2;

    Tcl_HashTable *AccessibleAttributes;

    win = Tk_NameToWindow(ip, Tcl_GetString(objv[1]), Tk_MainWindow(ip));
    if (win == NULL) {
    return TCL_ERROR;
    }

    /* Get accessible role for window. */
    hPtr=Tcl_FindHashEntry(TkAccessibilityObject, win);
    if (!hPtr) {
    Tcl_AppendResult(ip, "No table found", (char *) NULL);
    return TCL_ERROR;
    }
    AccessibleAttributes = Tcl_GetHashValue(hPtr);
    hPtr2=Tcl_FindHashEntry(AccessibleAttributes, (char*) role);
    if (!hPtr2) {
    Tcl_AppendResult(ip, "No role found", (char *) NULL);
    return TCL_ERROR;
    }

    Tcl_SetObjResult(ip, Tcl_GetHashValue(hPtr2));
    return TCL_OK;
    }

    int
    Tk_GetAccessibleName(
    TCL_UNUSED(void *),
    Tcl_Interp *ip, /* Current interpreter. */
    int objc, /* Number of arguments. */
    Tcl_Obj *const objv[]) /* Argument objects. */
    {
    if (objc < 2) {
    Tcl_WrongNumArgs(ip, 1, objv, "window?");
    return TCL_ERROR;
    }

    Tk_Window win;
    char *name;
    Tcl_HashEntry *hPtr, *hPtr2;

    Tcl_HashTable *AccessibleAttributes;

    win = Tk_NameToWindow(ip, Tcl_GetString(objv[1]), Tk_MainWindow(ip));
    if (win == NULL) {
    return TCL_ERROR;
    }

    /* Get accessible name for window. */
    hPtr=Tcl_FindHashEntry(TkAccessibilityObject, win);
    if (!hPtr) {
    Tcl_AppendResult(ip, "No table found", (char *) NULL);
    return TCL_ERROR;
    }
    AccessibleAttributes = Tcl_GetHashValue(hPtr);
    hPtr2=Tcl_FindHashEntry(AccessibleAttributes, (char *) name);
    if (!hPtr2) {
    Tcl_AppendResult(ip, "No name found", (char *) NULL);
    return TCL_ERROR;
    }

    Tcl_SetObjResult(ip, Tcl_GetHashValue(hPtr2));
    return TCL_OK;
    }

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Francois Vogel@francois.vogel.fv.NOSPAM@gmail.com to Kevin Walzer on Mon Nov 11 23:23:20 2024
    From Newsgroup: comp.lang.tcl

    Le 11/11/2024 à 04:37, Kevin Walzer a écrit :
    int
    Tk_SetAccessibleRole(
                 TCL_UNUSED(void *),
                 Tcl_Interp *ip,        /* Current interpreter. */
                 int objc,            /* Number of arguments. */
                 Tcl_Obj *const objv[])    /* Argument objects. */
    {
      if (objc < 3) {
        Tcl_WrongNumArgs(ip, 1, objv, "window? role?");
        return TCL_ERROR;
      }

      Tk_Window win;
      char *role;
      Tcl_HashEntry *hPtr, *hPtr2;

      Tcl_HashTable *AccessibleAttributes;
      AccessibleAttributes = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
      Tcl_InitHashTable(AccessibleAttributes,TCL_STRING_KEYS);

      int isNew;
      win = Tk_NameToWindow(ip, Tcl_GetString(objv[1]), Tk_MainWindow(ip));
      if (win == NULL) {
        return TCL_ERROR;
      }

      /* Set accessible role for window.  */
      hPtr=Tcl_CreateHashEntry(TkAccessibilityObject, win, &isNew);

      Tcl_SetHashValue(hPtr, AccessibleAttributes);

      hPtr2 =  Tcl_CreateHashEntry(AccessibleAttributes, role, &isNew);
      if (!isNew) {
        Tcl_DecrRefCount(Tcl_GetHashValue(hPtr2));
      }
      Tcl_IncrRefCount(objv[2]);
      Tcl_SetHashValue(hPtr2, objv[2]);

      Tcl_SetObjResult(ip, Tcl_GetHashValue(hPtr2));
      return TCL_OK;
    }

    Without diving deep into your code, here a handful of quick observations
    that may or may not be useful to you:

    1. The canonical pattern for these kind of things is:

    int isNew;
    hPtr = Tcl_CreateHashEntry(tablePtr, key, &isNew);
    if (isNew) {
    myValue = ...; // get or create myValue from somewhere
    Tcl_SetHashValue(hPtr, myValue);
    } else {
    myValue = (myValue *)Tcl_GetHashValue(hPtr);
    }

    Perhaps you should stick at it.

    2. You are using the isNew flag twice without checking it the first time.

    3. You are calling Tcl_CreateHashEntry twice, yes, but not with the same
    hash table. Is this what is intended?

    Regards,
    Francois
    --- Synchronet 3.20a-Linux NewsLink 1.114