My advice on implementing stuff in C:
My advice on implementing stuff in C:
Posted Oct 18, 2010 16:52 UTC (Mon) by HelloWorld (guest, #56129)In reply to: My advice on implementing stuff in C: by cmccabe
Parent article: Russell: On C Library Implementation
C has a few implicit conversions that might be confusing to novices. That hardly qualifies it as "a mess."C has value-destroying conversions (double -> float, int -> char, hell, even float -> int), it has cycles in the graph of conversions (int -> char, char -> int), and the void* -> T* conversion frequently leads to bugs. enums aren't really a type but a means to define integer constants. 'a' isn't a char literal but an integer literal (check it, sizeof 'a' == 4 on most compilers). If all this isn't a mess, what is? Even Bjarne Stroustrup called C's implicit conversions "rather chaotic" (Design & Evolution of C++, page 224).
In C and C++, functions and variables that are declared "file static" can't be referenced outside that file. If you combine that with a sane policy about how to split up functionality between multiple files, it does of the things that module systems do in other languages.Actually, it's a rather lame workaround. You have to put header guards everywhere, which is just useless clutter. When a header file changes, you have to recompile every file that includes it, even if the change is totally irrelevant for some files, for example when you removed an unused function prototype or added a comment. If you forget to include a header file into its implementation file, the compiler won't check that the prototypes match the implementation. Also, the issue of name collisions isn't resolved that way. I learned about this when I worked on a project that typedefed its own fixed-size integer types (u64, u32 etc.), and one day they realized that some system header file also typedefed those names.
Any language that doesn't have eval() probably needs a macro system.I'm not so sure about that, but let's just assume its true. Then why did they build such a crappy one for C? Proper macro systems, such as those from LISP, work on the AST level, while the C preprocessor works on the token level. The result of that is that you can't write #define square(x) x*x, you have to write #define square(x) ((x)*(x)). Also, the C preprocessor doesn't really know anything about the C language. You can't do #if sizeof (int) >= 4, and you can't do stuff like declare a function only if it's not declared already.
Look at what happened with Java. In their arrogance, the designers believed that they were beyond the need for a macro system. But the language wasn't expressive enough to describe a lot of the things that people needed to do. The result was that an ugly mass of automatic code generators got written to do the things that a macro system would have done. Now, in addition to learning the core language, you have to read huge tomes describing these automatic code generators. Usually they come with clunky XML interfaces and specialized tools.The C preprocessor is just as extralinguistic as those code generators, the only difference is that it's called by the compiler driver, while other such tools aren't. Also, the C preprocessor clearly isn't powerful enough to replace code generators even for C. Just look at the numerous code generators in use every day, like flex, bison or ecpg, or those things that generate "config.h" for a C program. On the other hand, Java has annotations and annotation processors, which can replace many uses of code generators.
