Hacker News new | past | comments | ask | show | jobs | submit login
Klib: A standalone and lightweight glib (github.com/attractivechaos)
70 points by luu on March 13, 2015 | hide | past | favorite | 16 comments



This is really not a "standalone and lightweight glib", this is a generic data structure library in C, and it's quite blatantly obvious they're designed for different purposes.

glib is a platform and portability library that evolved away from gtk+, with the goal being to make event driven programming simpler, both for system daemons and Gtk+ applications. Most of glib's data structures are highly unoptimized simply because they don't need to run in critical paths of whatever applications they're used in; optimizing would be mostly premature (with some caveats; GSequence was added because of the need for a higher performance list-like container , GHashTable got a lot of love a few years ago because it turned out to be awful, etc). glib is ABI stable(-ish).

klib is written for doing data analysis and serialization in C, with two releases in four years and no stability claims.

The two can co-exist quite nicely - I can easily see a glib application using klib data structures along critical paths.


I'm surprised the title hasn't been changed. The actual website says "generic lib" and at no point does it draw a direct comparison with glib. Usually these things are changed much faster.


If anyone's interested, here is a blog post I wrote a while back about a subtle pitfall I encountered while using klib:

http://blog.qoid.us/2014/04/10/klib.html

(Since then, after a few rewrites of similar generic containers using various implementation techniques, I've settled on a style that minimizes the use of macros in favor of inline functions [generated from an initial declaring macro], adding verbosity but making the whole thing much easier to get right.)


his macros are truly evil. i wish he would have stayed with nicer c++ stuffs ut on compiler challenged platforms...


Is making so much use of macros for code generation really suggested practice in C? It seems like it would be difficult to debug.


I think that macros are the only real option if you want to have an interface for multiple types and still have some semblance of type-safety. The only alternative I can think of is writing code that takes void* everywhere.


There is also the approach used for linked lists in the Linux Kernel: http://kernelnewbies.org/FAQ/LinkedLists


the third option is to write your code in some more type-safe language (heck, even c++) and add extern c wrappers around all your exposed functions.


Yes. But the "void *" everywhere code would take just a portion of the space, as for every different type, you would be duplicating the code.


There are plenty of systems where errno is #define'd to a more complex expression in order to provide per-thread errors. From glibc's bits/errno.h on Linux:

  # ifndef __ASSEMBLER__
  /* Function to get address of global `errno' variable.  */
  extern int *__errno_location (void) __THROW __attribute__ ((__const__));

  #  if !defined _LIBC || defined _LIBC_REENTRANT
  /* When using threads, errno is a per-thread value.  */
  #   define errno (*__errno_location ())
  #  endif
  # endif /* !__ASSEMBLER__ */
  #endif /* _ERRNO_H */
I wouldn't expect it to be common to see a macro with parameters being an lvalue, though. It wouldn't phase a C++ programmer, since references make it possible and even idiomatic to return lvalues. Where you normally see this is:

  x["foo"] = "bar";
is just syntactic sugar for

  // operator[] could be any "normal" function name!
  x.operator[]("foo") = "bar";


No. There are 4-5 common approaches.

- Macros, as you see.

- Writing a generic implementation that takes a void* (or suitable "any" type). This has many, many drawbacks... no type safety, poor performance, frequently requires allocations if you want to store more than sizeof(void), forces the compiler to generate poor code...

- Custom data structures for your use-case. In my experience, this is the most common for performance-critical code. Doesn't end up being a problem nearly* as often as some people think (e.g., you don't spend much time writing data structure code), as you only write the subset for what you actually need at the time (A hash-table is fairly easy if you only need get-or-insert, exists, and clear), and you don't tend to need these data structures as much as you might think. This is still not ideal for some applications that require many containers (this is the minority of all applications, in my experience!).

- Intrusive containers. This only works in some cases (lists, trees, and chained hash tables), but basically the idea is that you put the shared part of the data structure as a struct at the front (or in the middle if the implementation does some offsetof math) of your type, and then all the operations use that. This tends to be less than ideal because of both the pointer chasing and storing additional pointers, but it beats the void* approach, and is better than poor implementations of the others, assuming you can do the data structure you want this way.

- Writing code that generates the code for the container for each type you need. This isn't as common as the other three but you definitely see it. Typically after generating it once it's customized but sometimes the ability to edit the generator and re-generate it might take priority.


No, it isn't. Most C will use void* instead. Which approach is better is left as an exercise.


Someone who's used macros before extensively—

Macros are only unreadable if they're written that way. See, while C can be confusing, it's built using simple tools—you can use the c preprocessor apart from the compiler itself. By default, c without cpp is not pre-processed, so you have entire control of the output. (many OS's cc/libc implementations make extensive use of macros, e.g. assert, hence my "default" qualification).

Writing with this in mind would allow two layers of compilation—once to verify the pre-processor, once to verify the resulting c compilation. While it is annoying, it doesn't require any more skill than a write-compile-debug loop.


All I have to say is that uthash and company was a lifesaver in my must-be-written-in-C compiler course. It really, really simplified things. Of course it can't just be bolted on, you need to write your code with some crazy macros.

http://troydhanson.github.io/uthash/


Does anyone know why it's called klib? I don't know about the prefix "k", but guess it's borrowed a lot of kernel codes?


Nice library, and beautiful implementation.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: