jacob said:
(e-mail address removed) a écrit :
Can you specify?
1) A better mapping to functionality commonly available in hardware.
I.e., high-word or widening multiply. Bit scan operations (i.e., fast
lg2, and scan for lowest.). Bit count. Endian swap. etc. Just look
through the instruction sets of the popular CPUs and see which useful
instructions are practically accessible through translation of C source
and what is not. Then add library functions that perform those and
allow the compiler vendor to support them however they wish. In this
way the most optimal paths for a given piece of hardware will be
available without resorting to non-portable inline assembly code. (And
for those platforms that do not support each function, each function
can be still be emulated which is equivalent to how such functionality
is portably delivered today.)
Just look through GMP or any similar multi-precision library. Yes it
has a portable pure C back-end, but it is utter nonsense and is only
invoked on compilers people have never heard of. But on those
compilers it is *AT LEAST* 4 times slower than the likely potential for
that hardware precisely because the language has no access to the
fastest, most functional instructions that are commonly available on
most hardware.
2) Better control flow functionality: Coroutines and alloca(). The
variable length array nonsense in C99 probably seemed like a good
cleaner substitute for alloca() at the time, but gcc has clearly shown
that its actually *harded* to implement that portably in at least their
compiler.
The whole setjmp, longjmp nonsense is precisely that. Personally I
have never gotten it to work correctly, and I have a hard time
approaching debugging of it -- I have always just found a way around
using them instead. However, it turns out that coroutines are fairly
straight forward to implement in assembly. And they incurr very little
overhead. It requires some method of allocating a new "call stack".
Although this sounds grossly exposing of platform details, if it is
abstracted correctly, it actually is a big benefit. Currently a
program itself within its source code cannot assert a requirement for
minimum of stack availability. So programs that use deep recursion can
crash easily. Besides that, coroutines represent truly new
functionality in the language that is just absolutely not duplicatable
by other means that can be considered scalable.
3) Better heap functionality. The only access we have to the heap is
through malloc/realloc/calloc/free. Its just not enough at all. The
thing is, even the best implementations of those functions put a best
amortized cost of a scalable implementation at around 50+ clocks per
allocation. That means memory allocation is something that you
necessarily try to push out of your inner loops. That means that
throwing in more functionality and more overhead into those calls will
not affect real world performance of applications that care about
performance, since such call will not be sitting in inner loops at all.
Now you can see from here in CLC itself, that there is a great need for
heaps with built-in debugging help. Simple functions that tell you the
total amount of allocated heap, or the size of a given allocation. C
is not a language that can implement garbage collection easily, but it
is very often the case that you can implement very close stand-ins that
achieve the same level of leak safety. In particular you can implement
seperate heaps, and include a "freeall()" function that will tear down
an entire heap at once rather than performing individual frees -- which
is often a lot of meaningless expense.
Another useful thing to have is an isFromAllocatedMemory() function.
The idea would be that the function would be well defined for certain
void * pointers that were from static memory, auto-memory, a pointer
allocated from heap memory and NULL. In this way you could determine
whether or not a pointer came from the heap. In practice, this would
be used primarily for debugging, but it could be used for functions
that perform "automatic freeing" of structures passed to it when they
are done with it, but *NOT* perform a free() if the structure passed to
it came from static memory.
4) A better preprocessor. Basically you want to be able to directly
compete with LISP's lambda functionality, and just make automatic code
generation more plausible in C. To be fully general, I would recommend
modelling it after the language LUA (since its such a small language,
but fully general) in terms of functionality. This kind of thing would
greatly assist things like generic programming, but also provide ways
for programmers to perform certain optimization techniques like
"constant propogation" with relative ease.
5) A universal 64bit time standard. Dave Tribble has posted his idea
and library that explains what he after. The point is to create a
universal time standard that is guaranteed to not barf in 2038 and that
lets you correctly calculate time differences in a universal way,
without being tied to the current local time (without daylight savings
messing you up). I have not looked too deeply, but obviously such a
standard would need also to include accurrate "sub-second" real-time
timer functionality which is currently not available (clock() delivers
processor tick time on UNIX systems, which is a different kind of
animal.)
The real point behind this list of features, is that they represent
true enhancements to the C language. They are enhancements that really
put the question to other languages like Java, Python, etc. Combined,
this set of features would enhance the performance, and rewiden the gap
between C and Java (or C#) for example, in ways that those two
languages could not easily catch up. These features would push the
frontiers of C precisely where other languages do not dare tread, or
else already have another approach they are locked into.
Today I can simply say to you, that if I want to use coroutines, I
cannot use the C language and am better off going to another language.
If I want multiprecision mathematics, I know that Python will deliver
it to me, and that I cannot really get a good C implementation unless
it is GMP (which is not thread-safe, BTW) or non-portable hand coded
assembly language. If I want better heap control, then I roll my own
non-portable solution, and go ahead and violate ANSI-strictness by
overriding malloc/free/realloc/calloc. On occasion I do write
auto-code generators -- but these days, I do it in Python, not the C
pre-processor or even C. And of course, I still do the occasional
in-line assembly code where I just don't otherwise have access to
certain instructions.
Strings, I don't care as much about, of course, because I have written
a portable library that anyone can use that completely solves the
problems using todays C compilers. So enhancements to C's string
functions at the standards level is actually completely useless to me.
It just goes to show how misguided that ANSI C committee is that they
are entertaining nonsense like TR 24731, which is both bad (or at best
benign), and not really necessary at the standards level.