HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
JacksonAllan
CC
C2023–2026
JacksonAllan / CC

CC

A small, ergonomic generic container library.

7Distinct people
11Mentions
0Explicit recommenders
507GitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

A small, ergonomic generic container library.

GitHub

Where it is used

No documented product use has been verified yet.

What people are saying

No recommendation phrase was found in the indexed comments.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

No other repository is repeatedly linked in the same discussions.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 3:03:16 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year11 dated mentions

Observed mentions: 2023-01-28 13:01:48 UTC to 2026-01-24 05:47:08 UTC.

02462023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 3 mentions; 2 submissions, 1 comments20232024-01-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments20242025-01-01 00:00:00 UTC to 2026-01-01 00:00:00 UTC (exclusive): 6 mentions; 2 submissions, 4 comments2026-01-01 00:00:00 UTC to 2027-01-01 00:00:00 UTC (exclusive): 1 mentions; 1 submissions, 0 comments. Incomplete calendar period.2026*

* An incomplete calendar period is marked with an outlined bar. Its count is not directly comparable with a full period.

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC321
20242024-01-01 00:00:00 UTC2025-01-01 00:00:00 UTC101
20252025-01-01 00:00:00 UTC2026-01-01 00:00:00 UTC624
2026 (partial)2026-01-01 00:00:00 UTC2027-01-01 00:00:00 UTC110

What Hacker News says

11 sources
I'd love to have some of these libraries in slimcc's test pool, but almost every one of them have hard-coded assumptions to gcc/clang/msvc/tcc in their scripts that are tiresome to undo. For example: MLIB: assumed GCC https://github.com/P-p-H-d/mlib/blob/4b9c213831624486814a483... STC: assumed -fopenmp https://github.com/stclib/STC/blob/de7313a58f748d9a6da751eb9... CC: assumed clang https://github.com/JacksonAllan/CC/blob/main/tests/run_unit_...
In Comparison of different C libraries providing generic containers capabilities
I think the idea of using a union to store the element type without any extra run-time memory cost might have some use, specifically in cases where the container struct wouldn't typically store a variable of the element type (or, more likely, a pointer to the element's type) but we want to slip that type information into the struct anyway. However, the problem that I have with this idea as a general solution for generics is that it doesn't seem to solve any of the problems posed by the most similar alternative: just having a macro that defines a struct. The example shown in the article: #define List(type) union { \ ListNode *head; \ type *payload; \ } could just as easily be: #define List(type) struct { \ type *head; \ /* Other data, such as node/element count... */ \ } (As long as our nod
Read the rest
es are maximally aligned - which they will be if they're dynamically allocated - it doesn't matter whether the pointer we store to the list head is ListNode *, type *, void *, or any other regular pointer type.) The union approach has the same drawback as the struct approach: untagged unions are not compatible with each other, so we have to typedef the container in advance in order to pass in and out of functions (as noted in the article). This is broadly similar to the drawback from which the "generic headers" approach (which I usually call the "pseudo-template" approach) suffers, namely the need for boilerplate from the user. However, the generic-headers/pseudo-template approach is guaranteed to generate the most optimized code thanks to function specialization[1], and it can be combined with another technique to provide a non-type-prefixed API, as I discuss here[2] and demonstrate in practice here[3]. I'd also like to point to my own approach to generics[4] that is similar to the one described here in that it hides extra type information in the container handle's type - information that is later extracted by the API macros and passed into the relevant functions. My approach is different in that rather than exploiting unions, it exploits functions pointers' ability to hold multiple types (i.e. the return type and argument types) in one pointer. Because function pointers are "normal" C types, this approach doesn't suffer from the aforementioned typedef/boilerplate problem (and it allows for API macros that are agnostic to both element type/s and container type). However, the cost is that the code inside the library becomes rather complex, so I usually recommend the generic-headers/pseudo-template approach as the one that most people ought to take when implementing their own generic containers. [1] https://gist.github.com/attractivechaos/6815764c213f38802227... [2] https://github.com/JacksonAllan/CC/blob/main/articles/Better... [3] https://github.com/JacksonAllan/Verstable [4] https://github.com/JacksonAllan/CC
In I write type-safe generic data structures in C

Show HN: Convenient Containers – ergonomic generics in C

Hello Hacker News :) I'd like to share my C generic data-structure library Convenient Containers (CC). The library's main advantages are summarized in the Rationale section of its README.[1] In short, using some novel techniques, the library is able to provide a range of fully typesafe data structures with a generic API agnostic to both container type and data types, without requiring the user to make any boilerplate pre-declarations for every container/data type combination. In other words, CC containers look and function much like containers in languages with native support for generics: #include <stdio.h> #include "cc.h" int main( void ) { vec( int ) our_vec; init( &our_vec ); push( &our_vec, 5 ); printf( "%d\n", *get( &our_vec, 0 ) ); cleanup( &our_vec ); map( int, float ) our_map; init( &our_
Read the rest
map ); insert( &our_map, 5, 0.5f ); printf( "%f\n", *get( &our_map, 5 ) ); cleanup( &our_map ); } CC is the only C data-structure library that offers such ergonomics, to the best of my knowledge. Of course, performance is also important. To this end, CC's hash tables (i.e. its maps and sets) have performed well in benchmarks both by me[2] and by others.[3] Its red-black trees (i.e. its ordered maps and sets) have also been benchmarked and proven to perform on par with their C++ Standard Library counterparts.[4] Some of the techniques upon which CC relies are explained briefly in its FAQ[5] and more thoroughly in a series of Reddit comments that I made back when the library was first released.[6] I am working on a series of articles to describe these techniques more systematically, the first of which I published earlier.[7] Thanks for reading! [1] https://github.com/JacksonAllan/CC#rationale [2] https://jacksonallan.github.io/c_cpp_hash_tables_benchmark/ [3] https://gist.github.com/attractivechaos/6815764c213f38802227... [4] https://github.com/JacksonAllan/CC/releases/tag/v1.3.0 [5] https://github.com/JacksonAllan/CC?tab=readme-ov-file#how-do... [6] https://www.reddit.com/r/C_Programming/comments/zvubfb/comme... [7] https://github.com/JacksonAllan/CC/blob/main/articles/Better...
2 points · Open discussion
I use something similar in a container library to warn the user if he or she supplies an argument with potential side effects to a macro that evaluates it multiple times: https://github.com/JacksonAllan/CC/blob/42a7d810274a698dff87... Specifically, if (arg)==(arg) is not a constant expression, then it could have side effects. However, this mechanism does generate some annoying false positives, as shown below: // Create a map with int keys and values that are vectors of floats: map( int, vec( float ) ) our_map; init( &our_map ); // Create a vector of floats: vec( float ) our_vec; init( &our_vec ); push( &our_vec, 1.23f ); // Insert the vector into the map. insert( &our_map, 456, our_vec ); // Generates a warning because get checks its first argument for side // effects and the compiler can't tell that the fir
Read the rest
st argument of the // outermost get has none: printf( "%f", *get( get( &our_map, 456 ), 0 ) ); // The "proper", albeit cumbersome, way to achieve the same thing without a // warning: vec( float ) *itr = get( &our_map, 456 ); printf( "%f", *get( itr, 0 ) );
In Detecting if an expression is constant in C
> The patten I like most is a struct of const function pointers, instantiated as a const global, then passed by address to whatever wants to act on that table > If you heed the "const" word above the compiler inlines through that just fine. But only when the function it self is inlined, which you quite often don't want. If you sort integers in a bunch of places, you don't really want qsort to be inlined all over the place, but rather that the compiler creates a single specialized copy of qsort just for integers. With something as simple as qsort compilers sometimes do the function specialization, but it's very brittle and you can't rely on it. If it's not specialized nor inlines then performamce is often horible. IMO an additional way to force the compiler to specialize a function based on constant arguments is needed. Something like spec
Read the rest
ifying arguments as "inline". IIRC, this library uses this style of generic programming with a very nice API: https://github.com/JacksonAllan/CC It's just unfortunate that everything needs to get inlined for good performance.
In Type-erased generic functions for C: A modest non-proposal
In my example the table stores the hash codes themselves instead of the keys (because the hash function is invertible) Oh, I see, right. If determining the home bucket is trivial, then the back-shifting method is great. The issue is just that it’s not as much of a general-purpose solution as it may initially seem. In the bad cases, it's really bad. With a different algorithm (Robin Hood or bidirectional linear probing), the load factor can be kept well over 90% with good performance, as the benchmarks in the same repo demonstrate. I’ve seen the 90% claim made several times in literature on Robin Hood hash tables. In my experience, the claim is a bit exaggerated, although I suppose it depends on what our idea of “good performance” is. See these benchmarks, which again go up to a maximum load factor of 0.95 (although boost and Absl forcibly
Read the rest
grow/rehash at 0.85-0.9): https://strong-starlight-4ea0ed.netlify.app/ Tsl, Martinus, and CC are all Robin Hood tables (https://github.com/Tessil/robin-map, https://github.com/martinus/robin-hood-hashing, and https://github.com/JacksonAllan/CC, respectively). Absl (from Google) and Boost are well-known C++ SIMD-based hash tables. Khash (https://github.com/attractivechaos/klib/blob/master/khash.h) is, I think, an ordinary open-addressing table using quadratic probing. Fastmap is a new, yet-to-be-published design that is fundamentally similar to bytell (https://www.youtube.com/watch?v=M2fKMP47slQ) but also incorporates some aspects of the aforementioned SIMD maps (in particular, it stores a 4-bit fragment of the hash code to avoid most false key comparisons). As you can see, all the Robin Hood maps spike upwards dramatically as the load factor gets high, becoming as much as 5-6 times slower at 0.95 vs 0.5 in one of the benchmarks (uint64_t key, 256-bit struct value: Total time to erase 1000 existing elements with N elements in map). Only the SIMD maps (with Boost being the clear better performer of the two) and Fastmap appear mostly immune to load factor in all benchmarks, although the SIMD maps do - I believe - use tombstones for deletion in at least some cases. I’ve only read briefly about bi-directional linear probing – never experimented with it.
In Hashmaps in Factor are faster than in Zig

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.