Post by BartCPost by Thiago AdamsBut, think the key for real and immediate use is to be very
compatible with C. Also for general success.
I think C++ is very successful because of this.
I wonder why C doesn't just disappear then? As every C program, with
some tweaks as necessary, will compile as C++.
The ability to compile a C program as C++ is only useful if the resulting
program will run with known semantics. There are some constructs which
have defined behavior in C but have no convenient direct equivalent in C++.
Of course, the same sort of problem can exist along the path from C89
to later versions unless a compiler continues to support old semantics.
Some constructs in C89 have no practical equivalent in C99 (e.g. unless
one interprets C89 in a fashion that would not allow allocated storage to
hold objects of non-character type, it would be hard to write an efficient
function whose behavior was semantically equivalent to the following in
all cases that would be defined in C89).
void copy_as_bytes(void *dest, void *src, size_t n)
{ memmove(dest, src, n); }
The number of cases where C constructs have no C++ equivalent, however, is
larger than the number of cases where C89 constructs have no C99 equivalent.
Post by BartC#define PLUS2(X) ((X) + 2) /* Macro expanding to expression */
Inline function. (That such macros remove the need to specifier type is
something that needs thinking about, perhaps as a new language feature,
but which must under no circumstance involved introducing templates).
Macros can do some things inline functions can't. It may be practical to
give some of those abilities to inline or other functions (e.g. include a
real pass-by-reference syntax, and add some special parameter types whose
values would be generated by the compiler rather than the caller source
code). For example, given:
void inc_and_log_it(__caller_file cf, __caller_line cl,
char const *msg, unsigned &var)
{
fprintf(logfile, "%s:%u\n", msg, var);
var++;
}
a function call log_it("FRED", x); would be interpreted as though the
function had been
void log_it(char const *cf, size_t cl,
char const *msg, unsigned * restrict v);
{
fprintf(logfile, "%s:%u\n", msg, *var);
(*var)++;
}
and the call had been log_it(__FILE__, __LINE__, "FRED", &x); except that
a compiler would be entitled to treat x afterward as though its address
had not been taken.
Post by BartC#define STOR extern /* storage class specifier */
Redefining the language (wasn't this prohibited?).
A common pattern in some circles is to implement the "don't repeat yourself"
principle with external variables by having a header file like foo.h say
something like:
#ifndef FOO_C
#define FOO_EXTERN extern
#else
#define FOO_EXTERN
#endif
and then precede each declaration within the file with FOO_EXTERN. This
avoids the need to keep two sets of declarations in sync, and would be even
more useful if "extern" had priority over the presence of an initializer,
thus allowing e.g.
FOO_EXTERN const my_array[] = { 1,2,3,4,5,6 };
to be treated in files other than foo.c as a declaration of a complete
array type whose size would automatically match the number of items
therein. I've seen no way to define such things which would work as nicely
as that construct could. There are ways of achieving such functionality in
C99 by defining a macro containing all the elements and then using that
both for the initialization and for expressions that compute the size, but
nothing as clean and simple as the above.
Post by BartC#define INIT(value){ (value), 0, 0} /* braced initialiser */
IMO, also redefining the language (even if it is a new version of C). An
initialiser with multiple elements is enclosed in {...}. This gets rid
of the braces.
A macro containing a list of comma-separated items without braces is more
dubious than one which contains the braces, though the former may be needed
in some contexts where two or more arrays need to be initialized with data
which is largely identical.
Post by BartC#define CAT (PI) /* parenthesised expression */
Inline function.
#define READ_TIME_32() \
do { \
DISABLE_INTERRUPTS (); \
time_now = (uint32_t)TIMER_HI << 16; \
time_now = time_now | (uint32_t)TIMER_LO; \
ENABLE_INTERRUPTS (); \
} while (0) /* example of do-while-zero */
Inline function.
Compilers have a lot of freedom with functions declared inline. Consider
a macro to reverse all the bits in a 32-bit word using a combination of
masks and shifts. Invoking such a macro with a compile-time constant will
yield a compile-time constant result. An inline function, by contrast,
would on many compilers be likely to yield something far less efficient
(even though the function should devolve to a simple constant, many compilers
may decide for each function that it will always be inlined or never be
inlined, and since the combination of shifts and masks necessary to reverse
a non-constant value would be fairly complicated, many compilers would decide
never to inline the function).