Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> Standard C99

No thanks; sticking to C90.

> LTO

Violates ISO C, which says that semantic analysis is done in translation phase 7, and only linking takes place translation phase 8.



You could at least add some information about "why" for both of those :/


The "why" for the second one is: that's the model of the C language. Programs are translated in units. The semantic analysis ends when a translation unit is analyzed. All that is left is to resolve the external references when multiple units are linked; nothing is re-translated. This is very clearly stated in a few sentences in that section of the standard which describes the translation phases one by one.

Link-time optimization is a language extension which alters the concept of a translation unit. In some ways, multiple translation units become one unit (such as: they inline each other's code, or code in one is altered based on the behavior of code in the other). But in other ways they aren't one translation unit (e.g. their file scope static names are still mutually invisible.)

One reason I don't use C99 is that many of its features aren't C++ compatible. Most of C90 is. With just a little effort, a C90 program compiles as C++ and behaves the same way, and no significant features of C90 have to be wholly excluded from use.

Portability to C++ increases the exposure of the program to more compilers, with better safety and better diagnostics.

In some cases, I can use constructs that are specially targetted to C or C++ via macros. Instead of a C cast, I can have convert(type, val) macro which uses the C++ static_cast when compiled as C++, a coerce(type, val) which uses reinterpret_cast, or strip_qual(type, val) which uses const_cast. I get the diagnostics under C++, but the code still targets C. I'm also prevented from converting from void * without a cast, and there are other benefits like detecting duplicate external object names.

This way of programming in C and C++ was dubbed "Clean C" by the Harbison and Steele reference manual.

C90 is a cleaner, smaller language than C99 without extraneous "garbage features" like variable-length arrays that allocate arbitrary amounts of storage on the stack without any way to indicate failure to do so.

The useful library extensions in C99 like snprintf or newer math functions can be used from C90; you can detect them in a configure script like any library.

The overall main reason is that I simply don't have faith in the ISO C process. The last good standard that was put out was C90. After that, I don't agree with the direction it has been taking. Not all of it is bad, but we should go back to C90 and start a better fork, with better people.

   git checkout -b c90-fork c90
   git cherry-pick ... what is good from c99-branch


Aren't you just nitpicking on LTO? Who cares what the linker does if it preserves semantics? Does it actually break some real-world code?


It absolutely does. For instance, it can enforce strict aliasing across translation unit boundaries, breaking code which depends on that separation to protect it.


Here is something in C99 that seems like a great idea and improvement and is often lauded, yet turns out to be ... meh: designated initializers. Sure, these address problems like the wrong function pointer being assigned when the positional order is wrong: the programmer version of buttoning up your shirt wrong. These things are even Torvalds-approved and used in the Linux kernel (whose compilation will complain, if you mix declarations and statements, that they are not a C90 feature!!!)

But here is the thing: all the initialization comes from the initializer, not from the struct type, and the only default value you get is the good old 0 (0 integer, 0.0 floating point, null pointer). So, if you want any other default, designated initializers are basically useless.

The designers of a data type want to provide initialization mechanisms with defaults which they control from the site of the definition of the type. We can do that in C90 with macros. Macros ensure that everything is initialized. If we add a member that must be initialized with a non-default value everywhere, we add a parameter to the macro, and the compiler will catch all the places where the macro is called with the old number of parameters.

   struct foo {
     char *name;
     int bogosity;
   };

   #define foo_anon_init(bogosity) { "anon", bogosity }
   #define foo_init(name, bogosity) { name, bogosity }
You provide all the variants in one place which do all the forms of defaulting which you need, and which stick the parameters into the correct place, and then just use them everywhere else.

Even if you have designed initializers, it's still good idea to wrap initialization that way; they don't replace its benefits.

Suppose I add a frobosity member, and there is no obvious way to default it. All the places where the structure is instantiated have to be analyzed case by case to choose the appropriate value. Designated initializers (alone) won't help; we add the member to the struct and nothing happens. It defaults to zero everywhere it is not explicitly mentioned with a designated or positional initializer. With the macros we just add it as a parameter:

   struct foo {
     char *name;
     int bogosity, frobosity;
   };

   #define foo_anon_init(bogosity, frobosity) \
   { "anon", bogosity, frobosity }
   #define foo_init(name, bogosity, frobosity) \
   { name, bogosity, frobosity }
Now "make -k" and fix all the places where the macros are called with too few arguments, choosing a value for the frobosity.

With designated initializers we can of course do:

   #define foo_anon_init(bogosity, frobosity) \
   { .name = "anon", .bogosity = bogosity, \
     .frobosity = frobosity }
But that brings very little to the table when we've gathered all this stuff in one place (which alone reduces mistakes) and we are initializing every member to a nonzero value.

Languages that have keyword initializers are great when the class supplies defaults for all the ones that the constructor doesn't specify.

   (make-instance 'foo :frobosity 42) ;; 50 other slots defaulted for us
If you want to change a default, you don't want to go to all the places where the type is instantiated and update the initializer!!!




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

Search: