27 ms·
Templating in C
- yannsionneau 12y agoVery interesting!
- noobermin 12y agoI did something similar in a personal project[0], but to avoid reimplementing things like lists and resizable buffers and such over and over again but maintain a sort of "type safety.*" It's a step down from C++ templates but it works. [0] https://github.com/noobermin/ch https://github.com/noobermin/ch
- jonhohle 12y agoYou may or may not have been aware, but most Unixes provide `sys/queue.h`[0] which contains many macros for type safe collections. 0 - http://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/queue.h http://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/...
- comex 12y agoHere is my full hash table implementation done in the same manner: https://github.com/comex/substitute/blob/master/lib/cbit/htab.h https://github.com/comex/substitute/blob/master/lib/cbit/hta... It is uglier than C++ templates, no doubt about it[1]. However, it does have two advantages over STL std::unordered_map (not applicable to a custom C++-template implementation): - It's guaranteed to have low code size overhead for each supported key type, and none for additional specializations with the same type of key but different types of value. - It is possible to explicitly instantiate said code in one .c file, rather than the standard C++ approach of forcing the compiler to redo code generation for every .cpp file and hoping the linker merges duplicates. This is mainly an advantage for compile time. C++11 has extern template, but you can't use it with the STL. (It's also easier to do 'raw' things with the table, although this is just an implementation choice with upsides and downsides rather than a flaw in the STL. And independently from the duplicate specialization, C just compiles faster than C++, and I am fanatic about compile time.) [1] At least, C++ templates implemented sanely. Certain STL implementations (GNU libstdc++) appear to have a goal of being as difficult to read as possible.
- arquivo 12y agoWhy complicate with multiple files, functions, defines and includes; here it is in one file with a single define: http://pastebin.com/uWSs7ca7 http://pastebin.com/uWSs7ca7 We can improve the macro further by removing the unnecessary if statement and the index number: http://pastebin.com/vPTTYPpe http://pastebin.com/vPTTYPpe
- alxmdev 12y agoI prefer the single file solution too. I suppose it comes down to how much having escape markers on every line of the function implementation bothers you, and if your favorite code editor does full syntax highlighting in such a case.
- arquivo 12y agoThere is also a version with a normal function: http://pastebin.com/Wesjzs1A http://pastebin.com/Wesjzs1A Solution in the article is overly convoluted.
- alxmdev 12y agoYes, but that is now a worker function that handles every if(n == x) case explicitly in the body, not something you need the preprocessor for anymore. Nothing against it, but it is a different design.
- arquivo 12y agoI copied the function from the article, they are identical. This was done intentionally to show how convoluted the article is.
- alxmdev 12y agoI see. I was thinking in the context of your second pastebin link and the evil_template.c snippet in the article, where the functions are actually generic (and in which case you can't avoid the function being declared as a macro unless you go the multiple-includes route).
- _RPM 12y agoC macros are powerful.
- silentvoice 12y agoCurious it doesn't mention C11's __Generic macro. A very nice way to achieve a lot of useful "overloading" behavior. http://en.wikipedia.org/wiki/C11_%28C_standard_revision%29 http://en.wikipedia.org/wiki/C11_%28C_standard_revision%29
- ux 12y agoI was simply not aware of it as I never really looked deeply at the features of anything posterior to C99 (I only vaguely remember the thread features that look appealing). That's interesting, I might end up mentioning it in the conclusion later. Thank you.
- ndesaulniers 12y agoThe largest issue I see with this is compiler vendors dragging there feet on C11 support, vs C++11 support which is much much more competitive.
- mbel 12y agoAnother solution, which seems to be missing is to use function pointers; process_image could receive a pointer to given function instead of n. My experiences with gcc show, that functions passed by pointer are often inlined, which is not really a surprise, since they are constant arguments of the function. Here is an example [0], although the wrapper functions seem to be redundant in this case. [0] http://pastebin.com/BAgm61S8 http://pastebin.com/BAgm61S8
- chorizos22 12y ago+1, This is actually the right way go. Macros are fun to mess around, but they are avoidable and alternatives are often better.
- silentvoice 12y agoHow is this for "performance portability?" I use this solution in C when the function is very expensive, therefore a little extra indirection really won't make any difference - but can potentially improve the reusability a lot. Is inlining calls to function via a pointer a very basic optimization that any self respecting compiler should be able to do, or is it a very advanced optimization that I can't count on working across platforms? Given the possibility of dynamic libraries I don't see how it could be inlined in all cases, therefore at least some kind of analysis must be done before trying it.
- comex 12y agoInlining a call to a function via a pointer, if the function is static and implemented in the same .c file or an included header, is a basic optimization. I'm less sure about the compiler figuring out that the user wants (in that case) process_image to be inlined into each of the wrapper functions. However, if you don't mind a bit of nonportability, it's easy to force it to do so: mark the function to be inlined __forceinline on MSVC, __attribute__((always_inline)) on GCC/Clang/ICC/etc.
- mbel 12y agoOf course, my statement if far from being complete and definitive answer, it's a mere suggestion of another method to add to OP's list. If the functions are defined in different binaries which are dynamically linked, the chances of inlining are approaching zero [0], although this method may work across binaries in contrast to all techniques from the article. To enable compiler to inline the argument-function, the compiler must have definitions of both functions. To enable this in library/library-consumer scenario the higher level function [1] can be placed in header file and marked as static, this will guarantee that they are in the same module. Inlining function pointers doesn't seem to be advanced optimization technique (it is not that different from constants propagation). GCC 4.5 does this for same module functions even in -O1 [2]. Personally, I prefer to emphasize modularity in my code and move to other solutions when something is identified as a bottle-neck (which seems to be nice rule of the thumb for all optimizations). [0] I would love to be proven wrong, by some kind of JIT-ing dynamic linker. [1] ie. function accepting other functions as arguments. [2] http://stackoverflow.com/questions/2959535/c-function-pointer-inlining http://stackoverflow.com/questions/2959535/c-function-pointe...
- deleted 12y ago[deleted]
- dan-olivier 12y ago"Nevertheless, this is still a few order of magnitude better than C++ templating." Uh... no. On a personal level, I have always found that almost any type of preprocessor magic, especially re-#including with a different set of parameters in effect is a recipe for disaster. Any use of the pre-processor seems to have a serious impact on the build-system (tracking parallel variants), the IDE (color, intellisense or auto-completion), the debugger, and apparently any tool that operates at the meta-level. On the other hand, IDEs and debuggers often also have problems with C++ templates as well. Am I crazy to think that the result has generally been better with templates? I think we would better off without any of this. We should: - discard the preprocessor, - expanding and tightening control on templating), and - migrating towards more conventional module import methods, such as http://clang.llvm.org/docs/Modules.html http://clang.llvm.org/docs/Modules.html (instead of #include, unless you can fix IWYU).
- ambrop7 12y agoIn extreme cases, I combine templates and macros to achieve the desired conciseness of code. Templates are powerful, but often a lot of verbosity/boilerplate is still needed, where macros may come in. That is usually in a basic form as described in this article (one can do much more advanced stuff with macros e.g. google X macros). Just to annoy you I'll show some code of mine. It's a compile-time expression wrapper. Code: https://github.com/ambrop72/aprinter/blob/master/aprinter/meta/Expr.h https://github.com/ambrop72/aprinter/blob/master/aprinter/me... Test: https://github.com/ambrop72/aprinter/blob/master/tests/expr_test.cpp https://github.com/ambrop72/aprinter/blob/master/tests/expr_...
- dan-olivier 12y agoBefore becoming suitably annoyed at your preprocessor magic, which I will examine shortly, I would like to point out one example I had in the back of my mind when I wrote my comment: just google lttng/tracepoint.h. It just seems to me like a terrible hack. Of course, if you want to criticize templates and meta-programming (as an alternative to pre-processor), there are many jaw-dropping examples in Boost, V8, etc. I can remember several late-night sessions wrangling with (Boost) serialization code, and the cryptic multi-level warnings that ensue. Those were bad times. I always figured that the answer would be a language/tool simple enough that a serious programmer could, without heroic effort, manage to customize to his needs over a weekend. Sadly, it appears that the language lawyers have gone insane, and the trend appears to be entirely opposite (with the recent developments in C++0x/C++11/C++14). For background: I was a big fan of Visual Studio (when working in games and finance), but my most recent posting precludes developing in Windows, and then porting to Linux for deployment. Looks like most people (at this posting) are doing printf-programming, relying on logs to see what happened (in debug builds). It's hard (within our current heterogeneous environment) to setup gdbserver + Eclipse or some other gdb client on a Windows front-end (which we need for administrativia). The point is I miss being able to just set breakpoints, launch the program and inspect program state on the fly, as a means of learning the behaviour. I have cared enough about all this, and used to eagerly await the day that MS would open-source the C++ AST/Intellisense engine, opening the door for programmers being able to perform complexe refactorings with relative ease. Maybe I should just but the whole-tomato software, or some other tool. These days, I am following Clang developments with interest and hope. FYI, I think the ultimate annoyance is some Linux fanboy who doesn't "believe" in C++ or IDEs, but who is too young to remember when GUIs were not even an option, that text mode was all you had. I am surprised at the resurgence in TUIs (and the longevity of some CLIs). Sure, C++ has its warts, but RAII is better than gotos (here, I am conflating the kernel developer's aversion to C++ with the likelyhood that he/she uses vi, or some other console-based editor). Sorry for the confused monologue. I'll check out the code later.
- motoki88 12y agoI am looking for a book or web page which describes well programming patterns in C (might be focused on OOP in C). I mostly design for embedded systems. Any recomendations?
- michaelcampbell 12y agoMore of a general (i.e., not embedded systems specific) book on C is "Deep C Secrets", by Peter Van Der Linden. Amazon link: http://www.amazon.com/Expert-Programming-Peter-van-Linden/dp/0131774298 http://www.amazon.com/Expert-Programming-Peter-van-Linden/dp...
- fit2rule 12y agoAn excellent book, and one which stands right next to K&R in my opinion, as a standard reference book on the subject of the C language. It still has much relevance today - many C projects I've seen being written by the new generation of developers could benefit from having this book become standard reference material everywhere. I can't recommend it enough.
- ndesaulniers 12y agoThis is an excellent book...but I would not recommend it to beginners. Read "Head First C" first, then maybe this book. Would also recommend "21st Century C."
- arquivo 12y agoTake a look at Object Oriented Programming With ANSI-C. http://www.cs.rit.edu/~ats/books/ http://www.cs.rit.edu/~ats/books/ It is complicated, but you can take parts from it and built your own system.
- jesionaj 12y agoI've actually used a subset of that book for on an embedded system. It ended up being scrapped, partly due to project management issues and partly because we were writing twice as much code for very little benefit, so I can't say I recommend it for embedded projects. The need for malloc was a pretty big negative as well. That said, I would recommend it as a good learning experience.
- kyberias 12y agoThe most interesting code is dark grey text on black background.
- halayli 12y agohere's a similar technique I use in lthread to generate functions: https://github.com/halayli/lthread/blob/master/src/lthread_socket.c#L311-L365 https://github.com/halayli/lthread/blob/master/src/lthread_s... the arguments are complete function signature
- bitwize 12y agoJust use C++. The lack of semantically aware, type safe templates is one of the deficiencies of C that motivated the development of C++. C++ is a strictly more powerful language than C. Play to its strengths rather than abusing C's weaker constructs.
- _RPM 12y ago"Just use C++" isn't always an option.
- rdtsc 12y ago> C++ is a strictly more powerful language than Not true. C99 has variable length arrays. Those are nice for quick temporary buffers, and I have use them pretty often. > Play to its strengths rather than abusing C's weaker constructs. How do I play to its ABI compatiblity strengths? Because I have to keep wrapping all of it in extern "C" declaration in a large enough system.
- sjolsen 12y ago>C99 has variable length arrays But they don't give C any power C++ doesn't have. In terms of expressive power, you have things like std::vector, and in terms of performance, everyone except MSVC supports VLAs in C++ anyway, and MSVC doesn't even support them in C. >How do I play to its ABI compatiblity strengths? Because I have to keep wrapping all of it in extern "C" declaration in a large enough system. That is playing to its ABI compatibility strengths. In how many other languages is binary compatibility so simple? Regardless, no one said anything about ABI compatibility being one of C++'s strengths. The point was about using C++'s template system for, of all things, exactly what it was designed to do.