attractivechaos / klib

A standalone and lightweight C library

Home Page:http://attractivechaos.github.io/klib/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

khash example in gh-pages documentation uses non-existing variable

rofl0r opened this issue · comments

the example uses if(!ret) even though ret isn't declared. i assume k is meant.

I had the same problem.
Could not figure out the fix due to the huge macro beyond my experience.
Is there a fix of that please? Thanks a lot.

It should be absent in example 1. If you look at the documentation for kh_put():

/*! @function
  @abstract     Insert a key to the hash table.
  @param  name  Name of the hash table [symbol]
  @param  h     Pointer to the hash table [khash_t(name)*]
  @param  k     Key [type of keys]
  @param  r     Extra return code: -1 if the operation failed;
                0 if the key is present in the hash table;
                1 if the bucket is empty (never used); 2 if the element in
				the bucket has been deleted [int*]
  @return       Iterator to the inserted element [khint_t]
 */
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)

absent == 0 tells you that the key was already present in the table.

Honestly though, line 8 should be deleted. Calling kh_del() would invalidate the k iterator so calling kh_value() with it would then be invalid.

@selavy Thanks!
To follow the original logic of the code, the variable ret should be absent, i.e. if (!absent) right?

@yifangt

The original logic of the code doesn't make sense. Hopefully this code snippet is explains why:

#include <assert.h>
#include <stdbool.h>
#include "khash.h"

enum {
    ERROR           = -1,
    ELEMENT_PRESENT = 0,
    EMPTY           = 1,
    TOMBSTONE       = 2,
};

KHASH_MAP_INIT_INT(m32, char)
int main(int argc, char **argv)
{
    int absent;
    int is_missing;
    khint_t k;

    // initialize the table
    khash_t(m32) *h = kh_init(m32);

    // insert a key of 5 (N.B. the value is NOT set)
    k = kh_put(m32, h, 5, &absent);
    assert(kh_size(h) == 1);
    assert(absent != ELEMENT_PRESENT);
    assert(absent == EMPTY);

    // delete the key we just inserted
    kh_del(m32, h, k);
    assert(kh_size(h) == 0);
    assert(kh_exist(h, k) == false); // iterator is invalid now
    assert(kh_get(m32, h, 5) == kh_end(h));

    // because kh_exist(h, k) == false, the line below is a bug:
    // kh_value(h, k) = 10;

    // re-insert a key of 5 (N.B. the value is still not set)
    k = kh_put(m32, h, 5, &absent);
    assert(kh_size(h) == 1);
    assert(absent == TOMBSTONE); // tombstone because we deleted the key
    assert(kh_get(m32, h, 5) != kh_end(h));

    // now set the value
    kh_value(h, k) = 10;
    assert(kh_value(h, kh_get(m32, h, 5)) == 10);

    return 0;
}

Thanks a lot!
I need more time to understand the huge macro, especially the logic wrapped inside.

I would recommend reading through the documentation that is in the file:

klib/khash.h

Lines 428 to 549 in f719aad

/*!
@abstract Type of the hash table.
@param name Name of the hash table [symbol]
*/
#define khash_t(name) kh_##name##_t
/*! @function
@abstract Initiate a hash table.
@param name Name of the hash table [symbol]
@return Pointer to the hash table [khash_t(name)*]
*/
#define kh_init(name) kh_init_##name()
/*! @function
@abstract Destroy a hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_destroy(name, h) kh_destroy_##name(h)
/*! @function
@abstract Reset a hash table without deallocating memory.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_clear(name, h) kh_clear_##name(h)
/*! @function
@abstract Resize a hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param s New size [khint_t]
*/
#define kh_resize(name, h, s) kh_resize_##name(h, s)
/*! @function
@abstract Insert a key to the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
@param r Extra return code: -1 if the operation failed;
0 if the key is present in the hash table;
1 if the bucket is empty (never used); 2 if the element in
the bucket has been deleted [int*]
@return Iterator to the inserted element [khint_t]
*/
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
/*! @function
@abstract Retrieve a key from the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
@return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
*/
#define kh_get(name, h, k) kh_get_##name(h, k)
/*! @function
@abstract Remove a key from the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Iterator to the element to be deleted [khint_t]
*/
#define kh_del(name, h, k) kh_del_##name(h, k)
/*! @function
@abstract Test whether a bucket contains data.
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return 1 if containing data; 0 otherwise [int]
*/
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
/*! @function
@abstract Get key given an iterator
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return Key [type of keys]
*/
#define kh_key(h, x) ((h)->keys[x])
/*! @function
@abstract Get value given an iterator
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return Value [type of values]
@discussion For hash sets, calling this results in segfault.
*/
#define kh_val(h, x) ((h)->vals[x])
/*! @function
@abstract Alias of kh_val()
*/
#define kh_value(h, x) ((h)->vals[x])
/*! @function
@abstract Get the start iterator
@param h Pointer to the hash table [khash_t(name)*]
@return The start iterator [khint_t]
*/
#define kh_begin(h) (khint_t)(0)
/*! @function
@abstract Get the end iterator
@param h Pointer to the hash table [khash_t(name)*]
@return The end iterator [khint_t]
*/
#define kh_end(h) ((h)->n_buckets)
/*! @function
@abstract Get the number of elements in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@return Number of elements in the hash table [khint_t]
*/
#define kh_size(h) ((h)->size)
/*! @function
@abstract Get the number of buckets in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@return Number of buckets in the hash table [khint_t]
*/
#define kh_n_buckets(h) ((h)->n_buckets)

I would recommend reading through the documentation that is in the file:

i would recommend opening a PR fixing the docs, but then it looks like this repo is unmaintained anyways.