10/26/2012

10-26-12 - Simple Stuff C Should Have

I'm not interested in pie-in-the-sky weirdo shit, only very simple stuff that I believe would make real world coding massively better.

1. Global variables should not be allowed without a prefix "global". (optionally). eg.


int x;  // compile failure!
static int y;
global int x; // ok

(this should be turned on as an option). I should be able to search for "global" and find them all (and remove them, because globals are pointless and horrible, especially in the modern age of multi-threading).

2. Name-hiding should require an explicit "hide" prefix, eg.


int x;

{
  int x;  // compile failure !
  hide int x; // ok

}

I hate name-hiding and think it never should have been in the language. It does nothing good and creates lots of bugs. Similarly :

3. Overloads and virtual overrides should have an explicit prefix.

This ensures that you are adding an overload or virtual override intentionally, not by accident just because the names happen to line up. The entire C overload/override method only works by coincidence; like it's a coincidence that these names are the same so they are an overload; there's no way to say "I intend this to be an overload, please be an error if it's not" (and the opposite; I intend this to be a unique function, please error if it is an overload).

Similarly, it catches the very common error that you wrote a virtual override and it all worked and then somebody changes the signature in the base class and suddenly you have code that still compiles but the virtuals no longer override.

4. C-style cast should be (optionally) an error.

I should be able to flip a switch and make C-style casts illegal. They are the devil, too easy to abuse, and impossible to search for. There should be a c_cast that provides the same action in a more verbose way.

5. Uninitialized variables should be an error. (optionally).


int x; // compile failure!
int x(0); // ok

Duh. Of course. Same thing with member variables in class constructors. It's ridiculous that it's so easy to use uninitialized memory.

6. Less undefined behavior.

Everything that's currently "undefined" should be a compile error by default. Then let me make it allowed by setting pragmas, like :


#require signed_shift

#require flat_memory_model

which then changes those usages from compile errors to clearly defined operations.

7. Standardize everything that's sort of outside the standard, such as common pragmas, warning disablement, etc.

The way to do this is to have a chunk of standard that's like "you don't have to implement this, but if you do the syntax must be like so" and then provide a way to check if it's implemented.

Just a standard syntax for warning disablement would be great (and of course the ability to do it in ranges or even C scopes).

Things like SIMD could also be added to the language in this way; just simple things like "if you have a simd type it is named this" would massively help with standardizing shit which is different on every platform/compiler for no good reason.

8. (optionally) disable generation of all automatic functions, eg. assignment, copy construct, default construct. The slight short-term convenience of these being auto-generated is vastly outweighed by the bugs they create when you use them accidentally on classes that should not be copied. Of course I know how to put non-copyable junk in classes but I shouldn't have to do that; that should be the default, and they should only be copyable when I explicitly say it's okay. And you shouldn't have to write that out either, there should be a "use default copy" directive that you put in the class when you know it's okay.

9. Trivial reflection. Just give me a way to say "on all member variables". There's no reason not to have this, it's so easy for the compiler to add, just give me a way to do :

template <typename t_op>
void Reflect( t_op & op )
{
    op(member1);
    op(member2);
    ...
}
In which the compiler generates the list of members for me, I don't have to manually do it.

7 comments:

L_(//:) said...

"Funny" that :
1,2,3,4 and to some extent 5 could be all solved by proper Find tool, some fancy based on clang or something, who understand not only text/regexp's, but language grammar. Because basically this is why You want it - it's hard as hell to find all casts/hides/overloads using current tools.

I don't really belive it will be in standard (except explicit overloads perhaps), but compiler switch for those 1-4 would be very nice.

6. No undefined behavior.
I don't really belive this is achievable. While Your examples makes sense, detecting at compile time stuff like dereferencing invalid pointers, as in:
int b=5;
int*a=&b;
a=a-42; // where 42 would be some random input, not const
*a; // undefined

is not possible in all cases.

And as a second issue - while my knowledge of C is getting rusty, I wouldn't be surprised if there would be like 50+ undefined cases (being reasonable, I really expect hundreds).
Making that many switches would be somehow... insane?

7 would be "nice", but I don't make enough multi-compiler stuff for recognizing this as an issue.



Which compilers/platforms would You be mainly interested in?
I'm thinking about doing some clang based Find tool as hobby project, but since there are so many others with ready infrastructure (basically any static analyzer like cppcheck/pvs), I'm still waiting for them making some easy to add scripts like:

take identifier X;
if (X.scope.parent.contains(X.name) && ( !X.unparsed_decorations.contains('hide')) )
make_error();

and empty #define hide in codebase

But sadly commercial solutions won't pursue this (for obvious bad "update=copy new scripts" economy issues), and I have little faith that free ones will try either.

cbloom said...

@L - you're right of course that "no undefined behavior" is a bit of a pipe dream, but I do believe it could be greatly reduced.

Tom Forsyth said...

2,3 and 5 are available as warnings on some compilers, so it is possible to avoid them with self-discipline.

ryg said...

5. I'd much prefer everything to be default-initialized, with an explicit "int arr[100] = void;" when you want to leave things uninitialized.

7. Also, the GCC insanity where there's a bunch of warnings that can only be turned on or off as a group (or even only by changing the warning level) is totally broken.

9. Easy enough for POD structs. But what to do for unions? Do base class members get enumerated? What if you inherit from the same base class in two forks? And what about (shudder) virtual base classes?

Yann Collet said...

If your talking about C, therefore without including C++, there is one feature i miss much, and i don't understand why it should be limited to C++ : Templates.

johnb said...

2. In GCC this is -Wshadow.

3. C++11 adds the override suffix for virtual methods. Of course unless you can *require* it (at least with a warning) it only does half the job. For the silently-changed-signature problem, GCC has -Woverloaded-virtual, which gives a warning if a derived class has a method with the same name, but a different signature, to a method in the base class.

4. In GCC this is -Wold-style-cast.

5. There are warnings for this of course (-Wuninitialized), but yes, it should be an error unless it's explicit.

9. I suspect even "trivial" reflection would turn out to be non-trivial, but certainly it would be useful. I'm in favour of custom preprocessing tools for this, just as a practical solution (it might be nicer if it were built into the language, but since it's not, custom tools are helpful). It can get complicated pretty quickly though. If there were a really simple, clean Python interface for libclang then it would help a lot (maybe there already is one? I haven't looked)

Unknown said...

#3 is available in current compilers, just not gcc:

http://code.google.com/searchframe#OAMlx_jo-ck/src/base/compiler_specific.h&exact_package=chromium&q=override%20compiler_specific.h&type=cs&l=130

old rants