As I mentioned earlier, I’d like to celebrate the new year by calling out a few products I’d like to see improved in the new year.
First in line is Microsoft’s C++ compiler and IDE.
From you, what I’d like to see in 2010 is actually fairly simple (at least conceptually): rethink your IDE. The Visual Studio team as a whole is already doing this in a big way with VS10. I’m hoping you can find the time to reinvent the C++ IDE specifically as well.
For the past decade (except for the 5 years you wasted trying to eliminate native C++), you’ve been trying very hard to write the ultimate IDE for the wrong language. You’ve got something that works pretty well for C++ code anno 1993 or so, but which falls completely apart when used for more modern C++.
Why do you persist in putting so many resources into making Intellisense better, when it still has no way to deal with a simple template function? Isn’t that a hint that you should rethink your approach? Modern C++ has quite a bit in common with dynamic languages. The type of a function parameter may not be known just by looking at the function definition. Perhaps what we need is actually a kind of compile-time REPL loop, an “interactive mode”, similar to what is commonly found in dynamic languages.
For example, I could use this to immediately and interactively instantiate a template (or a complex template metaprogram), in order to inspect which types are used as parameters for each template that is instantiated as a consequence. I could use it to query the type system, for example asking whether or not the type of std::vector<int>::iterator is the same as int*, or what sizeof(std::string) is. Perhaps it could allow me to “step through” the chain of template instantiations, like we do with the debugger at runtime, instead of being limited to compiling, and then looking at the compiler errors — the metaprogramming equivalent of printf–debugging.
What I’d like to see from the MSVC IDE in the coming years is an acceptance and support of modern C++ paradigms — generic programming, template metaprogramming and all the difficulties that implies. Don’t give me an IDE that tries to provide Intellisense for a program with a completely static structure, because that’s not what a modern C++ program looks like. If you’re going to do Intellisense, make it able to handle the very flexible type system enabled by templates. take a leaf from the JavaScript support in your IDE, which supports intellisense even though the language is dynamic and types generally aren’t known until runtime. To display type information in the IDE while the code is being written, they have to be clever, and cheat and bluff and pretend. But they do a reasonable job of it.
In C++, the types aren’t known until compile-time, so from the IDE’s point of view, the problem is similar. To display type information while the code is being written (and before it is compiled), the IDE has to be clever. And at the moment, it isn’t. At the moment, Intellisense just gives up.
Let’s take a simple example. What should the IDE do about this function:
template <typename T>
typename T::return_type foo(T arg){
bar(arg);
return arg.baz();
}
If we play it by the book, and demand a “perfect” solution, there is nothing the IDE can do. It doesn’t know what T is, so it can’t help us with autocompletion, suggesting members after the dot, or anything else.
But if we’re willing to think outside the box, and accept a success rate lower than 100%, there are several strategies the IDE could use to provide meaningful Intellisense information:
- we could look at the call sites. They must logically provide types that are valid in this context. We could find one call site, and provide Intellisense on the assumption that the type
Tis whatever was passed at that call site. That wouldn’t be 100% accurate in all cases, of course, but it would give us a type that works with the function, so it would be useful. It could even look at several call sites, and compute the union of the types used. If they all provide afrobnicate()method, then the IDE could assume thatTinside the functionfooalways contains such a member. - We could look at how the type is used in the function. It must have a copy constructor (because it is passed by value), it must have some nested type
return_type, and it must have a no-argbazmember function, which returns something convertible to that type. And it must be convertible to whatever argumentsbarexpects. This probably isn’t a complete description of the type, but it would be enough to give us some limited Intellisense information at least. The compiler might be able to deduce some information about the type. We could even generate some kind of ad-hoc “concepts” implementation — perhaps not as extensive as that which was proposed in C++0x (and subsequently dropped), but a kind of helper datastructure that the IDE can attempt to map onto unknown template types. - Or we could allow the user to specify an example of a valid parameter type, and then use that to generate Intellisense information from.
- going back to the previously suggested “compile-time” REPL loop, the user could query the template, asking “if called with
T=int, would the function compile? And what would the return type be? And what would the result ofstd::is_const<T>::valuebe?”
Modern C++ has a lot in common with dynamic languages. Very little information can be reliably extracted without compiling the code, so give me the tools for optionally and temporarily compiling bits and pieces.
When I write silly template metaprograms to compute the N’th prime number, and the result is wrong, why doesn’t MSVC provide a compile-time debugger? When I get a compile error inside template code, the error message contains what is effectively a compile-time stack trace. It shows the stack of template instantiations, but as hard-to-read verbose text. Why isn’t it rendered as a stack trace? One which lets me step through the instantiation of this maze of templates, inspect the members of each, and find out where it went wrong, where it instantiated the wrong template, or where I forgot to write the specialization I intended.
In far too many ways, the C++ IDE really feels like a C IDE. Most of it doesn’t seem to know that there’s this new-fangled thing called “templates”, or that they change how people write code. The Immediate window and the debugger fail to recognize template parameter names. If I am debugging a function template <int I> void foo(), why can’t I get the debugger to tell me the value of I? It should be absolutely trivial to do. But the debugger can’t seem to do it. Intellisense can’t seem to do it. The Immediate pane can’t seem to do it. There’s a clear mismatch between the compiler, which is clearly a C++ compiler, and pretty much hasn’t bothered about the C side for close to a decade, and the IDE which still seems to be trying to be the perfect C IDE, completely disregarding every feature unique to C++.
I know you’re used to being told that you have one of the best IDE’s in existence. I beg to differ. You may have got one of the best C IDE’s, and your C# and VB IDE’s kick some serious butt. But your C++ IDE is essentially nonexistent. Your IDE does not support C++. It supports a marginally and conservatively extended C, and tries to make it look similar to your C# IDE.
So far, I’ve dealt exclusively with the IDE issues, and that’s not a coincidence. On the whole, I’m quite happy with the MSVC compiler. The performance of generated code is good; you’re making great progress on C++0x support, and overall, you’ve got a compiler I’m happy with. Of course there are still a couple of areas where the lack of standards-conformance is embarassing (never mind the export keyword, I’m more bothered about two-phase name lookup and other such relevant features), and there are some features I wish you’d borrow from GCC, and I wish you’d tighten up your warning messages a bit (some of them are nothing more than noise, or are impossible to avoid in “good” healthy code and please please please give us a sane alternative to windows.h), I have few serious complaints about the compiler. I do, however, have a few suggestions.
It seems to me that the source/header compilation mechanism could use a makeover. We can’t change the actual semantics (yet — hopefully the proposal for a module system for C++ gains traction), but the compiler can change how it actually processes the code. And yet, major compilers still process the source files in the exact same manner they did 20 years ago. Even though this is, on today’s machines, and with today’s huge codebases, ridiculously inefficient.
Ages ago, precompiled headers were invented, but I’m not really a fan of them. It’s a hackish solution which sometimes helps, but may also hurt, due to the tendency towards including everything in one single “blob” header. Even if that header is precompiled, it still means everything that includes it has to deal with these bloated monolithic symbol tables and other data structures. More importantly, it is a fragile solution, as the VC Team’s own blog shows.
But why can’t this mechanism be generalized?
Why can’t the compiler process every header in isolation, build a complete parse tree of each one, and store those on disk? And then, when the header is included, rather than reading and parsing the header again, simply load this parse tree and merge it into the rest of the compilation unit. Of course, it is easy to come up with cases where the file may have to be parsed differently depending on where it is included, but in 99.9% of all cases, the inclusion mechanism is straightforward and simple: The header is typically not included in the middle of a class definition or from inside a namespace. It usually only reacts to a few fixed macros that may be defined before the header’s inclusion. So most of the time, the header could be precompiled in isolation and reused. And for the few cases where the changed state actually matters, where the header is included in the middle of a function definition or with no include guards or where a macro (say CreateWindow, or a similarly common name, cough cough) mangles the contents of the header, in those cases, the compiler can simply fall back to the traditional source code inclusion and subsequent compilation of the translation unit. Even if these precompilation passes aren’t stored to disk in the manner of precompiled headers, they could still be kept in memory, and reused between translation units during a build. If N different .cpp files all include a certain header, it would allow that header to be compiled once, rather than N times.
Once again, we have something that feels like a leftover from C. In C, headers were mostly forward declarations and little actual code, so naive processing of headers worked fairly efficiently. in C++, it is getting more and more common to put huge amounts of code in headers, which means that the naive compilation strategy traditionally used for C becomes ridiculously slow and inefficient. Creating a truly general replacement strategy is nearly impossible, true, but it seems like it’d be possible to create a heuristic that’d enable more efficient processing of header files 99% of the time, and which could then fall back to the traditional method of copy/pasting headers into the translation unit for the last percent of cases.
And why does every translation unit have to read every file every time? Can’t their contents be kept in memory, at least for a short time? Those hundreds or thousands of file accesses are painfully slow. Windows already exposes APIs for monitoring file changes, so it should be fairly simple to determine when a source file has been modified, and only then flush it from memory.
Yes, some of these things would be challenging to implement, but like I said, I have few real complaints about the compiler, so why not imagine how it could be taken to the next level?
And of course, everyone’s favorite nitpick: Why is windows.h so absolutely horrible? Why does it have to be one monolithic header which gives us everything Windows has to offer? Why doesn’t it compile as standard C++? Why does it include so many other headers (as above, slowing down compilation)? Why does it pollute the global namespace with macros for ridiculously common names?
Well, it does, and it’d be silly to expect this to change, due to backwards compatibility concerns.
But why then, is there not a windows.hpp or similar? Why isn’t there a separate cleaned-up, C++-compatible header? One which uses function overloading instead of macros, for example? Or which just defines simple forwarding functions instead of macros? One which compiles even with the non-standard language extensions disabled? Or why isn’t there a set of these headers, allowing us to access the bits of the Windows API we’re interested in, without having to include *everything?
In short, I think the MSVC IDE could do a make-over. Out with those 12-year-old project wizards, which create complex predefined project structures accumulating every bad practice and unexpected project setting in one place. I’ve lost count of how many beginners I’ve seen choke because their tiny little projects automatically get a precompiled headers thrown in for absolutely no reason, which makes their code so fragile it breaks whenever they try to change anything.
Another addition that would really boost the usefulness of MSVC would be to provide facilities for template metaprogramming in unit tests: For example, it is common to use metaprograms to force compilation failures if a template is instantiated with a specific type. But how do we test that this works as intended? Give us the hooks and language extensions (ideally something that can be hooked into from third-party unit testing frameworks) necessary to specify that “this function is expected to fail to compile, and if it does, that’s not an error, just ignore the function and compile the remainder of the file”. Consider the compilation process a part of the language — it is something that must be inspected and debugged, (and ideally, something which should be possible to do piecewise, without compiling the entire project) and for which we may wish to write tests.
Target your IDE at Modern C++, rather than C with classes. Impress the world by being the first IDE to even think about this. Embrace, and provide support for, the changes that have happened in the C++ language, in best practices and in the mindset of the C++ community.
Out with the idea that C++ can best be presented like C#, as a static language where every piece of code can be understood in isolation. Instead, give us an IDE that treats C++ as a more dynamic language, where many types of information are just not available until the code has been compiled. Support and encourage use of templates, and accept that yes, headers are ridiculously heavy these days, and blindly recompiling them for every translation unit just doesn’t scale the way it used to. Treat compilation as an interactive process where template instantiation can be stepped through and inspected at each stage, and where interactive queries can be made statically or during debugging to inspect not just data, but also types. And face up to the fact that traditional intellisense is a lost cause. There is no way to statically produce all the information we expect from intellisense. Some can be improvised by various heuristics, or perhaps by assuming some suitable dummy values for th evarious template parameters, but others may be nearly impossible to provide useful information on until at least part of the program has been compiled. If the IDE can’t provide the information I need automatically, it could at least allow me to query for the information. Perhaps it can’t tell me anything about the template type T, but why can’t I tell it to assume that T is a std::wstring, and provide information based on this assumption. You already have a pretty good C++ compiler. It’s time to start working on a C++ IDE, and call it a day on the C IDE you’ve been polishing until now.
So dear MSVC team, in case you can’t think of anything useful to do with your time in the year 2010 (as if… I know you’ve got C++0x support to work on, and that’s infintely more important to me than IDE improvements), here’s a new year’s resolution for you: Amaze the world by showing what a C++ IDE should work like. Reinvent the role of the C++ IDE, instead of trying to force your current C-C# hybrid IDE to work for C++ as well.
Tags: c++, ide, intellisense, msvc, new-year, visual-studio




