Hacker News new | ask | show | jobs
by kazinator 2377 days ago
This is really two files in one. If you have

  #define HTTPSERVER_IMPL
prior to the inclusion, then the header provides the implementation too, otherwise only the interface. Obviously, you must have this HTTPSERVER_IMPL followed by #include "httpserver.h" in only one file in your program.

If someone doesn't like it, they can split the file into two: the header proper and the "impl" part in a .c file.

Just look for the part starting with #ifdef HTTPSERVER_IMPL. Take that out through to the closing #ifdef and put it into a httpserver.c file. Then put #include "httserver.h" into the top of that file, and remove all *_IMPL preprocessor cruft from httpserver.c. Now you have a proper single-header, single C file module.

1 comments

This type of architecture makes more sense when you use it to let the user give arbitrary names and types to the functions, as with e.g. KHASH_INIT(my_t). This version with #ifdef seems a little cargo-culty since the result isn’t as polymorphic as you’d get with klib (which afaict kicked off the header-only craze) so the advantage over a traditional two-file setup is not obvious. (Of course, I don’t know why you need more polymorphism in a small http server!)

As noted above, it avoids version incompatibility by essentially forcing static linking. But most developers would statically link a small two-file library anyway, so it’s a moot point. C isn’t supposed to have training wheels.

>C isn’t supposed to have training wheels.

And then there came Arduino.

We really need to fix the lack of documentation / warnings for these generic C libraries. If you need to understand token-combining preprocessor magic (looking at you, kbtree) due to a lack of proper documentation, these newbies _will_ just try to wing it and _will_ stop poking as soon as it seems to work, regardless of why/how. Say hello to use-after-free & it's cousin, memory leaks.

Thankfully Arduino uses C++, there are no training wheels beyond taking advantage of C++ improvements over C, and a beginners friendly IDE.
For instance, I used this trick to create a shared library wrapper (something that calls dlopen, but provides stubs so the program thinks it's just calling a build-time-linked library).

Macros defined the individual functions themselves, in one place, (what are their names, arguments, and which library). A regular #include of these macros provided the declarations to the program. The shared lib wrapper module #defined the definition version of these macros prior to the #include, which expand to the invocation stubs.

It makes sense because the users who maintain it just have one place to add a new function; they don't have to do copy and paste programming to write the invocation wrapper.