#ifdef GEN_INTELLISENSE_DIRECTIVES # pragma once # include "printing.hpp" #endif #pragma region Containers template struct RemoveConst { typedef TType Type; }; template struct RemoveConst { typedef TType Type; }; template struct RemoveConst { typedef TType Type[]; }; template struct RemoveConst { typedef TType Type[Size]; }; template using TRemoveConst = typename RemoveConst::Type; template struct RemovePtr { typedef TType Type; }; template struct RemovePtr { typedef TType Type; }; template using TRemovePtr = typename RemovePtr::Type; #pragma region Array #define Array(Type) Array // #define array_init(Type, ...) array_init (__VA_ARGS__) // #define array_init_reserve(Type, ...) array_init_reserve(__VA_ARGS__) struct ArrayHeader; #if GEN_COMPILER_CPP template struct Array; # define get_array_underlying_type(array) typename TRemovePtr:: DataType #endif usize array_grow_formula(ssize value); template Array array_init (AllocatorInfo allocator); template Array array_init_reserve (AllocatorInfo allocator, ssize capacity); template bool array_append_array (Array* array, Array other); template bool array_append (Array* array, Type value); template bool array_append_items (Array* array, Type* items, usize item_num); template bool array_append_at (Array* array, Type item, usize idx); template bool array_append_items_at(Array* array, Type* items, usize item_num, usize idx); template Type* array_back (Array array); template void array_clear (Array array); template bool array_fill (Array array, usize begin, usize end, Type value); template void array_free (Array* array); template bool arary_grow (Array* array, usize min_capacity); template usize array_num (Array array); template void arary_pop (Array array); template void arary_remove_at (Array array, usize idx); template bool arary_reserve (Array* array, usize new_capacity); template bool arary_resize (Array* array, usize num); template bool arary_set_capacity (Array* array, usize new_capacity); template ArrayHeader* arary_get_header (Array array); struct ArrayHeader { AllocatorInfo Allocator; usize Capacity; usize Num; }; #if GEN_COMPILER_CPP template struct Array { Type* Data; #pragma region Member Mapping forceinline static Array init(AllocatorInfo allocator) { return array_init(allocator); } forceinline static Array init_reserve(AllocatorInfo allocator, ssize capacity) { return array_init_reserve(allocator, capacity); } forceinline static usize grow_formula(ssize value) { return array_grow_formula(value); } forceinline bool append(Array other) { return array_append_array(this, other); } forceinline bool append(Type value) { return array_append(this, value); } forceinline bool append(Type* items, usize item_num) { return array_append_items(this, items, item_num); } forceinline bool append_at(Type item, usize idx) { return array_append_at(this, item, idx); } forceinline bool append_at(Type* items, usize item_num, usize idx) { return array_append_items_at(this, items, item_num, idx); } forceinline Type* back() { return array_back(* this); } forceinline void clear() { array_clear(* this); } forceinline bool fill(usize begin, usize end, Type value) { return array_fill(* this, begin, end, value); } forceinline void free() { array_free(this); } forceinline ArrayHeader* get_header() { return array_get_header(* this); } forceinline bool grow(usize min_capacity) { return array_grow(this, min_capacity); } forceinline usize num() { return array_num(*this); } forceinline void pop() { array_pop(* this); } forceinline void remove_at(usize idx) { array_remove_at(* this, idx); } forceinline bool reserve(usize new_capacity) { return array_reserve(this, new_capacity); } forceinline bool resize(usize num) { return array_resize(this, num); } forceinline bool set_capacity(usize new_capacity) { return array_set_capacity(this, new_capacity); } #pragma endregion Member Mapping forceinline operator Type*() { return Data; } forceinline operator Type const*() const { return Data; } forceinline Type* begin() { return Data; } forceinline Type* end() { return Data + get_header()->Num; } forceinline Type& operator[](ssize index) { return Data[index]; } forceinline Type const& operator[](ssize index) const { return Data[index]; } using DataType = Type; }; #endif #if GEN_COMPILER_CPP && 0 template bool append(Array& array, Array other) { return append( & array, other ); } template bool append(Array& array, Type value) { return append( & array, value ); } template bool append(Array& array, Type* items, usize item_num) { return append( & array, items, item_num ); } template bool append_at(Array& array, Type item, usize idx) { return append_at( & array, item, idx ); } template bool append_at(Array& array, Type* items, usize item_num, usize idx) { return append_at( & array, items, item_num, idx ); } template void free(Array& array) { return free( & array ); } template bool grow(Array& array, usize min_capacity) { return grow( & array, min_capacity); } template bool reserve(Array& array, usize new_capacity) { return reserve( & array, new_capacity); } template bool resize(Array& array, usize num) { return resize( & array, num); } template bool set_capacity(Array& array, usize new_capacity) { return set_capacity( & array, new_capacity); } template forceinline Type* begin(Array& array) { return array; } template forceinline Type* end(Array& array) { return array + array_get_header(array)->Num; } template forceinline Type* next(Array& array, Type* entry) { return entry + 1; } #endif template forceinline Type* array_begin(Array array) { return array; } template forceinline Type* array_end(Array array) { return array + array_get_header(array)->Num; } template forceinline Type* array_next(Array array, Type* entry) { return ++ entry; } template inline Array array_init(AllocatorInfo allocator) { return array_init_reserve(allocator, array_grow_formula(0)); } template inline Array array_init_reserve(AllocatorInfo allocator, ssize capacity) { GEN_ASSERT(capacity > 0); ArrayHeader* header = rcast(ArrayHeader*, alloc(allocator, sizeof(ArrayHeader) + sizeof(Type) * capacity)); if (header == nullptr) return {nullptr}; header->Allocator = allocator; header->Capacity = capacity; header->Num = 0; return {rcast(Type*, header + 1)}; } forceinline usize array_grow_formula(ssize value) { return 2 * value + 8; } template inline bool array_append_array(Array* array, Array other) { return array_append_items(array, (Type*)other, array_num(other)); } template inline bool array_append(Array* array, Type value) { GEN_ASSERT( array != nullptr); GEN_ASSERT(* array != nullptr); ArrayHeader* header = array_get_header(* array); if (header->Num == header->Capacity) { if ( ! array_grow(array, header->Capacity)) return false; header = array_get_header(* array); } (*array)[ header->Num] = value; header->Num++; return true; } template inline bool array_append_items(Array* array, Type* items, usize item_num) { GEN_ASSERT( array != nullptr); GEN_ASSERT(* array != nullptr); GEN_ASSERT(items != nullptr); GEN_ASSERT(item_num > 0); ArrayHeader* header = array_get_header(* array); if (header->Num + item_num > header->Capacity) { if ( ! array_grow(array, header->Capacity + item_num)) return false; header = array_get_header(* array); } mem_copy((Type*)array + header->Num, items, item_num * sizeof(Type)); header->Num += item_num; return true; } template inline bool array_append_at(Array* array, Type item, usize idx) { GEN_ASSERT( array != nullptr); GEN_ASSERT(* array != nullptr); ArrayHeader* header = array_get_header(* array); ssize slot = idx; if (slot >= (ssize)(header->Num)) slot = header->Num - 1; if (slot < 0) slot = 0; if (header->Capacity < header->Num + 1) { if ( ! array_grow(array, header->Capacity + 1)) return false; header = array_get_header(* array); } Type* target = &(*array)[slot]; mem_move(target + 1, target, (header->Num - slot) * sizeof(Type)); header->Num++; return true; } template inline bool array_append_items_at(Array* array, Type* items, usize item_num, usize idx) { GEN_ASSERT( array != nullptr); GEN_ASSERT(* array != nullptr); ArrayHeader* header = get_header(array); if (idx >= header->Num) { return array_append_items(array, items, item_num); } if (item_num > header->Capacity) { if (! grow(array, header->Capacity + item_num)) return false; header = get_header(array); } Type* target = array.Data + idx + item_num; Type* src = array.Data + idx; mem_move(target, src, (header->Num - idx) * sizeof(Type)); mem_copy(src, items, item_num * sizeof(Type)); header->Num += item_num; return true; } template inline Type* array_back(Array array) { GEN_ASSERT(array != nullptr); ArrayHeader* header = array_get_header(array); if (header->Num <= 0) return nullptr; return & (array)[header->Num - 1]; } template inline void array_clear(Array array) { GEN_ASSERT(array != nullptr); ArrayHeader* header = array_get_header(array); header->Num = 0; } template inline bool array_fill(Array array, usize begin, usize end, Type value) { GEN_ASSERT(array != nullptr); GEN_ASSERT(begin <= end); ArrayHeader* header = array_get_header(array); if (begin < 0 || end > header->Num) return false; for (ssize idx = ssize(begin); idx < ssize(end); idx++) { array[idx] = value; } return true; } template forceinline void array_free(Array* array) { GEN_ASSERT( array != nullptr); GEN_ASSERT(* array != nullptr); ArrayHeader* header = array_get_header(* array); allocator_free(header->Allocator, header); Type** Data = (Type**)array; *Data = nullptr; } template forceinline ArrayHeader* array_get_header(Array array) { GEN_ASSERT(array != nullptr); Type* Data = array; using NonConstType = TRemoveConst; return rcast(ArrayHeader*, const_cast(Data)) - 1; } template forceinline bool array_grow(Array* array, usize min_capacity) { GEN_ASSERT( array != nullptr); GEN_ASSERT(* array != nullptr); GEN_ASSERT( min_capacity > 0 ); ArrayHeader* header = array_get_header(* array); usize new_capacity = array_grow_formula(header->Capacity); if (new_capacity < min_capacity) new_capacity = min_capacity; return array_set_capacity(array, new_capacity); } template forceinline usize array_num(Array array) { GEN_ASSERT(array != nullptr); return array_get_header(array)->Num; } template forceinline void array_pop(Array array) { GEN_ASSERT(array != nullptr); ArrayHeader* header = array_get_header(array); GEN_ASSERT(header->Num > 0); header->Num--; } template inline void array_remove_at(Array array, usize idx) { GEN_ASSERT(array != nullptr); ArrayHeader* header = array_get_header(array); GEN_ASSERT(idx < header->Num); mem_move(array + idx, array + idx + 1, sizeof(Type) * (header->Num - idx - 1)); header->Num--; } template inline bool array_reserve(Array* array, usize new_capacity) { GEN_ASSERT( array != nullptr); GEN_ASSERT(* array != nullptr); ArrayHeader* header = array_get_header(array); if (header->Capacity < new_capacity) return set_capacity(array, new_capacity); return true; } template inline bool array_resize(Array* array, usize num) { GEN_ASSERT( array != nullptr); GEN_ASSERT(* array != nullptr); ArrayHeader* header = array_get_header(* array); if (header->Capacity < num) { if (! array_grow( array, num)) return false; header = array_get_header(* array); } header->Num = num; return true; } template inline bool array_set_capacity(Array* array, usize new_capacity) { GEN_ASSERT( array != nullptr); GEN_ASSERT(* array != nullptr); ArrayHeader* header = array_get_header(* array); if (new_capacity == header->Capacity) return true; if (new_capacity < header->Num) { header->Num = new_capacity; return true; } ssize size = sizeof(ArrayHeader) + sizeof(Type) * new_capacity; ArrayHeader* new_header = rcast(ArrayHeader*, alloc(header->Allocator, size)); if (new_header == nullptr) return false; mem_move(new_header, header, sizeof(ArrayHeader) + sizeof(Type) * header->Num); new_header->Capacity = new_capacity; allocator_free(header->Allocator, header); Type** Data = (Type**)array; * Data = rcast(Type*, new_header + 1); return true; } // These are intended for use in the base library of gencpp and the C-variant of the library // It provides a interoperability between the C++ and C implementation of arrays. (not letting these do any crazy substiution though) // They are undefined in gen.hpp and gen.cpp at the end of the files. // We cpp library expects the user to use the regular calls as they can resolve the type fine. #define array_init(type, allocator) array_init (allocator ) #define array_init_reserve(type, allocator, cap) array_init_reserve (allocator, cap) #define array_append_array(array, other) array_append_array < get_array_underlying_type(array) > (& array, other ) #define array_append(array, value) array_append < get_array_underlying_type(array) > (& array, value ) #define array_append_items(array, items, item_num) array_append_items < get_array_underlying_type(array) > (& array, items, item_num ) #define array_append_at(array, item, idx ) array_append_at < get_array_underlying_type(array) > (& array, item, idx ) #define array_append_at_items(array, items, item_num, idx) array_append_at_items< get_array_underlying_type(array) > (& items, item_num, idx ) #define array_back(array) array_back < get_array_underlying_type(array) > (array ) #define array_clear(array) array_clear < get_array_underlying_type(array) > (array ) #define array_fill(array, begin, end, value) array_fill < get_array_underlying_type(array) > (array, begin, end, value ) #define array_free(array) array_free < get_array_underlying_type(array) > (& array ) #define arary_grow(array, min_capacity) arary_grow < get_array_underlying_type(array) > (& array, min_capacity) #define array_num(array) array_num < get_array_underlying_type(array) > (array ) #define arary_pop(array) arary_pop < get_array_underlying_type(array) > (array ) #define arary_remove_at(array, idx) arary_remove_at < get_array_underlying_type(array) > (idx) #define arary_reserve(array, new_capacity) arary_reserve < get_array_underlying_type(array) > (& array, new_capacity ) #define arary_resize(array, num) arary_resize < get_array_underlying_type(array) > (& array, num) #define arary_set_capacity(new_capacity) arary_set_capacity < get_array_underlying_type(array) > (& array, new_capacity ) #define arary_get_header(array) arary_get_header < get_array_underlying_type(array) > (array ) #pragma endregion Array #pragma region HashTable #define HashTable(Type) HashTable template struct HashTable; #ifndef get_hashtable_underlying_type #define get_hashtable_underlying_type(table) typename TRemovePtr:: DataType #endif struct HashTableFindResult { ssize HashIndex; ssize PrevIndex; ssize EntryIndex; }; template struct HashTableEntry { u64 Key; ssize Next; Type Value; }; #define HashTableEntry(Type) HashTableEntry template HashTable hashtable_init (AllocatorInfo allocator); template HashTable hashtable_init_reserve(AllocatorInfo allocator, usize num); template void hashtable_clear (HashTable table); template void hashtable_destroy (HashTable* table); template Type* hashtable_get (HashTable table, u64 key); template void hashtable_grow (HashTable* table); template void hashtable_rehash (HashTable* table, ssize new_num); template void hashtable_rehash_fast (HashTable table); template void hashtable_remove (HashTable table, u64 key); template void hashtable_remove_entry(HashTable table, ssize idx); template void hashtable_set (HashTable* table, u64 key, Type value); template ssize hashtable_slot (HashTable table, u64 key); template void hashtable_map (HashTable table, void (*map_proc)(u64 key, Type value)); template void hashtable_map_mut (HashTable table, void (*map_proc)(u64 key, Type* value)); template ssize hashtable__add_entry (HashTable* table, u64 key); template HashTableFindResult hashtable__find (HashTable table, u64 key); template bool hashtable__full (HashTable table); static constexpr f32 HashTable_CriticalLoadScale = 0.7f; template struct HashTable { Array Hashes; Array> Entries; #if ! GEN_C_LIKE_CPP #pragma region Member Mapping forceinline static HashTable init(AllocatorInfo allocator) { return hashtable_init(allocator); } forceinline static HashTable init_reserve(AllocatorInfo allocator, usize num) { return hashtable_init_reserve(allocator, num); } forceinline void clear() { clear(*this); } forceinline void destroy() { destroy(*this); } forceinline Type* get(u64 key) { return get(*this, key); } forceinline void grow() { grow(*this); } forceinline void rehash(ssize new_num) { rehash(*this, new_num); } forceinline void rehash_fast() { rehash_fast(*this); } forceinline void remove(u64 key) { remove(*this, key); } forceinline void remove_entry(ssize idx) { remove_entry(*this, idx); } forceinline void set(u64 key, Type value) { set(*this, key, value); } forceinline ssize slot(u64 key) { return slot(*this, key); } forceinline void map(void (*proc)(u64, Type)) { map(*this, proc); } forceinline void map_mut(void (*proc)(u64, Type*)) { map_mut(*this, proc); } #pragma endregion Member Mapping #endif using DataType = Type; }; #if GEN_SUPPORT_CPP_REFERENCES template void destroy (HashTable& table) { destroy(& table); } template void grow (HashTable& table) { grow(& table); } template void rehash (HashTable& table, ssize new_num) { rehash(& table, new_num); } template void set (HashTable& table, u64 key, Type value) { set(& table, key, value); } template ssize add_entry(HashTable& table, u64 key) { add_entry(& table, key); } #endif template inline HashTable hashtable_init(AllocatorInfo allocator) { HashTable result = hashtable_init_reserve(allocator, 8); return result; } template inline HashTable hashtable_init_reserve(AllocatorInfo allocator, usize num) { HashTable result = { { nullptr }, { nullptr } }; result.Hashes = array_init_reserve(allocator, num); array_get_header(result.Hashes)->Num = num; array_resize(& result.Hashes, num); array_fill(result.Hashes, 0, num, (ssize)-1); result.Entries = array_init_reserve>(allocator, num); return result; } template forceinline void hashtable_clear(HashTable table) { GEN_ASSERT_NOT_NULL(table.Hashes); GEN_ASSERT_NOT_NULL(table.Entries); array_clear(table.Entries); array_fill(table.Hashes, 0, array_num(table.Hashes), (ssize)-1); } template forceinline void hashtable_destroy(HashTable* table) { GEN_ASSERT_NOT_NULL(table->Hashes); GEN_ASSERT_NOT_NULL(table->Entries); if (table->Hashes && array_get_header(table->Hashes)->Capacity) { array_free(table->Hashes); array_free(table->Entries); } } template forceinline Type* hashtable_get(HashTable table, u64 key) { GEN_ASSERT_NOT_NULL(table.Hashes); GEN_ASSERT_NOT_NULL(table.Entries); ssize idx = hashtable__find(table, key).EntryIndex; if (idx >= 0) return & table.Entries[idx].Value; return nullptr; } template forceinline void hashtable_map(HashTable table, void (*map_proc)(u64 key, Type value)) { GEN_ASSERT_NOT_NULL(table.Hashes); GEN_ASSERT_NOT_NULL(table.Entries); GEN_ASSERT_NOT_NULL(map_proc); for (ssize idx = 0; idx < ssize(num(table.Entries)); ++idx) { map_proc(table.Entries[idx].Key, table.Entries[idx].Value); } } template forceinline void hashtable_map_mut(HashTable table, void (*map_proc)(u64 key, Type* value)) { GEN_ASSERT_NOT_NULL(table.Hashes); GEN_ASSERT_NOT_NULL(table.Entries); GEN_ASSERT_NOT_NULL(map_proc); for (ssize idx = 0; idx < ssize(num(table.Entries)); ++idx) { map_proc(table.Entries[idx].Key, & table.Entries[idx].Value); } } template forceinline void hashtable_grow(HashTable* table) { GEN_ASSERT_NOT_NULL(table); GEN_ASSERT_NOT_NULL(table->Hashes); GEN_ASSERT_NOT_NULL(table->Entries); ssize new_num = array_grow_formula( array_num(table->Entries)); hashtable_rehash(table, new_num); } template inline void hashtable_rehash(HashTable* table, ssize new_num) { GEN_ASSERT_NOT_NULL(table); GEN_ASSERT_NOT_NULL(table->Hashes); GEN_ASSERT_NOT_NULL(table->Entries); ssize last_added_index; HashTable new_ht = hashtable_init_reserve( array_get_header(table->Hashes)->Allocator, new_num); for (ssize idx = 0; idx < ssize( array_num(table->Entries)); ++idx) { HashTableFindResult find_result; HashTableEntry& entry = table->Entries[idx]; find_result = hashtable__find(new_ht, entry.Key); last_added_index = hashtable__add_entry(& new_ht, entry.Key); if (find_result.PrevIndex < 0) new_ht.Hashes[find_result.HashIndex] = last_added_index; else new_ht.Entries[find_result.PrevIndex].Next = last_added_index; new_ht.Entries[last_added_index].Next = find_result.EntryIndex; new_ht.Entries[last_added_index].Value = entry.Value; } hashtable_destroy(table); * table = new_ht; } template inline void hashtable_rehash_fast(HashTable table) { GEN_ASSERT_NOT_NULL(table.Hashes); GEN_ASSERT_NOT_NULL(table.Entries); ssize idx; for (idx = 0; idx < ssize(num(table.Entries)); idx++) table.Entries[idx].Next = -1; for (idx = 0; idx < ssize(num(table.Hashes)); idx++) table.Hashes[idx] = -1; for (idx = 0; idx < ssize(num(table.Entries)); idx++) { HashTableEntry* entry; HashTableFindResult find_result; entry = &table.Entries[idx]; find_result = find(table, entry->Key); if (find_result.PrevIndex < 0) table.Hashes[find_result.HashIndex] = idx; else table.Entries[find_result.PrevIndex].Next = idx; } } template forceinline void hashtable_remove(HashTable table, u64 key) { GEN_ASSERT_NOT_NULL(table.Hashes); GEN_ASSERT_NOT_NULL(table.Entries); HashTableFindResult find_result = find(table, key); if (find_result.EntryIndex >= 0) { remove_at(table.Entries, find_result.EntryIndex); rehash_fast(table); } } template forceinline void hashtable_remove_entry(HashTable table, ssize idx) { GEN_ASSERT_NOT_NULL(table.Hashes); GEN_ASSERT_NOT_NULL(table.Entries); remove_at(table.Entries, idx); } template inline void hashtable_set(HashTable* table, u64 key, Type value) { GEN_ASSERT_NOT_NULL(table); GEN_ASSERT_NOT_NULL(table->Hashes); GEN_ASSERT_NOT_NULL(table->Entries); ssize idx; HashTableFindResult find_result; if (hashtable_full(* table)) hashtable_grow(table); find_result = hashtable__find(* table, key); if (find_result.EntryIndex >= 0) { idx = find_result.EntryIndex; } else { idx = hashtable__add_entry(table, key); if (find_result.PrevIndex >= 0) { table->Entries[find_result.PrevIndex].Next = idx; } else { table->Hashes[find_result.HashIndex] = idx; } } table->Entries[idx].Value = value; if (hashtable_full(* table)) hashtable_grow(table); } template forceinline ssize hashtable_slot(HashTable table, u64 key) { GEN_ASSERT_NOT_NULL(table.Hashes); GEN_ASSERT_NOT_NULL(table.Entries); for (ssize idx = 0; idx < ssize(num(table.Hashes)); ++idx) if (table.Hashes[idx] == key) return idx; return -1; } template forceinline ssize hashtable__add_entry(HashTable* table, u64 key) { GEN_ASSERT_NOT_NULL(table); GEN_ASSERT_NOT_NULL(table->Hashes); GEN_ASSERT_NOT_NULL(table->Entries); ssize idx; HashTableEntry entry = { key, -1 }; idx = array_num(table->Entries); array_append( table->Entries, entry); return idx; } template inline HashTableFindResult hashtable__find(HashTable table, u64 key) { GEN_ASSERT_NOT_NULL(table.Hashes); GEN_ASSERT_NOT_NULL(table.Entries); HashTableFindResult result = { -1, -1, -1 }; if (array_num(table.Hashes) > 0) { result.HashIndex = key % array_num(table.Hashes); result.EntryIndex = table.Hashes[result.HashIndex]; while (result.EntryIndex >= 0) { if (table.Entries[result.EntryIndex].Key == key) break; result.PrevIndex = result.EntryIndex; result.EntryIndex = table.Entries[result.EntryIndex].Next; } } return result; } template forceinline b32 hashtable_full(HashTable table) { GEN_ASSERT_NOT_NULL(table.Hashes); GEN_ASSERT_NOT_NULL(table.Entries); usize critical_load = usize(HashTable_CriticalLoadScale * f32(array_num(table.Hashes))); b32 result = array_num(table.Entries) > critical_load; return result; } #define hashtable_init(type, allocator) hashtable_init (allocator) #define hashtable_init_reserve(type, allocator, num) hashtable_init_reserve(allocator, num) #define hashtable_clear(table) hashtable_clear < get_hashtable_underlying_type(table) >(table) #define hashtable_destroy(table) hashtable_destroy < get_hashtable_underlying_type(table) >(& table) #define hashtable_get(table, key) hashtable_get < get_hashtable_underlying_type(table) >(table, key) #define hashtable_grow(table) hashtable_grow < get_hashtable_underlying_type(table) >(& table) #define hashtable_rehash(table, new_num) hashtable_rehash < get_hashtable_underlying_type(table) >(& table, new_num) #define hashtable_rehash_fast(table) hashtable_rehash_fast < get_hashtable_underlying_type(table) >(table) #define hashtable_remove(table, key) hashtable_remove < get_hashtable_underlying_type(table) >(table, key) #define hashtable_remove_entry(table, idx) hashtable_remove_entry< get_hashtable_underlying_type(table) >(table, idx) #define hashtable_set(table, key, value) hashtable_set < get_hashtable_underlying_type(table) >(& table, key, value) #define hashtable_slot(table, key) hashtable_slot < get_hashtable_underlying_type(table) >(table, key) #define hashtable_map(table, map_proc) hashtable_map < get_hashtable_underlying_type(table) >(table, map_proc) #define hashtable_map_mut(table, map_proc) hashtable_map_mut < get_hashtable_underlying_type(table) >(table, map_proc) //#define hashtable_add_entry(table, key) hashtable_add_entry < get_hashtable_underlying_type(table) >(& table, key) //#define hashtable_find(table, key) hashtable_find < get_hashtable_underlying_type(table) >(table, key) //#define hashtable_full(table) hashtable_full < get_hashtable_underlying_type(table) >(table) #pragma endregion HashTable #pragma endregion Containers