Templating in C
blog.pkh.me
blog.pkh.me
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 (instead of #include, unless you can fix IWYU).
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/me... Test: https://github.com/ambrop72/aprinter/blob/master/tests/expr_...
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.
Using IDEs since the Amiga/MS-DOS days, Devpac, all Turbo languages and so on.
Getting half-decent IDEs in UNIX has always been an uphill battle.
And no, vim and Emacs just get one half-way there, even after stacks of ELisp code.
Although nowadays there are at least KDevelop, Eclipse, Netbeans, QtCreator to pick from.
As someone that started coding in the 80's, I also don't get this attitude of doing PDP-11 style programming.
My solution is to choose projects that allow me to use modern tools.
One thing I've always dreamed of is having the compiler emit source for the assembly it generated. So you can attach the debugger and see the code as it actually is, rather than as it was when you wrote it. I love how in Java my code gets optimised by the JIT, but once I start debugging all the optimisations come off (but only once I need to see the source) and what I see is what is running.
(This is actually a bigger step forward than it might seem. You can now make good use of a few useful techniques that were previously worth avoiding because they didn't work with code completion or browsing.)
On the other hand, if you're creating a macro that consists of more than one statement, I've always thought you're much better off with templates. Even if the debugging experience isn't as good as it should be (e.g., the debugger doesn't know what template type T actually is, so you can't watch "(T *)p" and so on), you're pretty much guaranteed to be able to at least step through the code and look at the variables.
Thank you.
C++ is a strictly more powerful language than C. Play to its strengths rather than abusing C's weaker constructs.
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.
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.
They are not the same. VLA in C are on the stack vectors are allocated from dynamic memory (heap).
> Regardless, no one said anything about ABI compatibility being one of C++'s strengths.
Right but that is why in practice C++ it is not a strict superset of C. When having to interface with other parts of the systems (loading .so, .dlls) or integrating with scripting langauges, the interface still has to be wrapped in a C interface.
I didn't say that they were. I said that they were equally expressive, because the claim was that C++ is more powerful than C.
>VLA in C are on the stack vectors are allocated from dynamic memory (heap)
That is not necessarily true. It's an implementation detail, and as far as it's relevant, so is the fact that everyone supports VLAs in C++.
>Right but that is why in practice C++ it is not a strict superset of C
Nobody said that C++ was a strict superset of C. The claim was that C++ is more powerful than C, and the fact that you have to specifically request ABI compatibility is not evidence to the contrary. Rather, the fact that you can do this at all (and that you can't use C++ linkage in C) is evidence in support of it.
Unfortunately, redesigning for safety/correctness in C almost always means a loss of place-of-use conciseness. For example, you'd have to manually wrap each argument to your hypothetical printf replacement in a "struct type_wrapper." Plus, there's no way to enforce that your format string has the same number of placeholders as you have arguments.
Speaking of which, you can actually do that in C++. It would look something like
#include <cstddef>
template <std::size_t N>
static inline constexpr
std::size_t num_args (const char (&fmt) [N]) {
// Count args
}
template <std::size_t N, typename... Args>
void printf2_impl (const char* fmt, Args&&... args) {
static_assert (N == sizeof... (args), "Wrong number of arguments to printf2");
// Interleave args into fmt and print
}
#define printf2(fmt, ...) (printf2_impl <num_args (fmt)> (fmt, __VA_ARGS__))
You could even just typecheck the format string against your argument types and forward to printf. The biggest problem with this what happens when you give printf2 an empty __VA_ARGS__, but such is life when you're using the C preprocessor.And PHP is a strictly more powerful language than HTML :)
Here is an example [0], although the wrapper functions seem to be redundant in this case.
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.
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-pointe...
We can improve the macro further by removing the unnecessary if statement and the index number:
Solution in the article is overly convoluted.
In this particular case it's not exactly a problem, but when you reach a 100+ lines macro, it becomes one.
It's a bit like maintaining pixel shaders in strings, it pretty quickly becomes a huge PITA.
Now if you read carefully the post, you will notice that I'm not encouraging the latest multiple files method, it is just presented because it's a common pattern and has its use cases.
I think what is described in "Mixing full functions mechanism and macros" would be more appropriate for the example you picked. Again, you have to assume that the function is going to grow, and it needs to be editable.
0 - http://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/...
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.
Amazon link: http://www.amazon.com/Expert-Programming-Peter-van-Linden/dp...
http://www.cs.rit.edu/~ats/books/
It is complicated, but you can take parts from it and built your own system.
That said, I would recommend it as a good learning experience.
(Our implementation was not very good.)
https://github.com/halayli/lthread/blob/master/src/lthread_s...
the arguments are complete function signature