czwartek, 15 kwietnia 2010

My rant on Eclipse+CDT

Eclipse is non-trivial to use for C++ and not designed for it. I am using Eclipse Galileo.
Problems:

CTD build system is a bit of a failure. It rebuilds project at wrong moments (e.g. sometimes after launching eclipse, even though the object files still exist. This makes it unusable for big projects. At some point I found out that this is caused by "use parallel builds" option, checked by default. This fact should be advertised as I am sure that it distracts many people.

In many cases CDT does not rebuild things properly which causes strange runtime errors. Somehow it assumes that programmer should know which sources to rebuild, which is a bad assumption because, if something can be automated, why not automate it? Things that confuse CDT seem to be more advanced language features, like complicated templates, together with static functions, global data, overloading, conditional compilation etc, in other words the further code is from pure C code, the more problems exist. Code::Blocks IDE always knows what to rebuild, probably because it uses make in less clever way.

Try do do "Project/Clean" without rebuilding. Guess what the eclipse shows in the console at the bottom?
Answer: "Build complete"
(The project is only cleaned, the message is confusing).

Right click an occurence of a function name. How do you go to definition?
Answer: You have to choose "Open Declaration".
Click the name in the definition and choose "Open Declaration" again. Where does it take you?
Answer: To the declaration.
Right click the declaration, choose "Open Declaration".
Answer: To the definition.

You are viewing a non-console window at the bottom, e.g. "Properties" or "Type Hierarchy". Click the launch button. What do you see at the bottom?
Still error log. The IDE should show the console since something is happening there.

When launching eclipse, the window which has the progress bar is behind applications that you already run (e.g. behind windows explorer).

Trying to launch debugger with errors in the code launches it.

After termination of an application, eclipse does not show the exit code. I have to switch to debug view to see it.

After pressing the "run" button Eclipse checks if there is anything to build, which often takes long time. I have lost many minutes of my life this way. It seems that there is no way of switching this off. It would be very convenient to just run the last built executable.

The first time you run the debugger you are asked about what kind of debugging you want. What the heck do the options mean?

When pressing SHIFT+CTRL+Right, editor selects part of the identifier, instead of the whole thing. When you double click using mouse pointer, it selects the whole thing, but there seems to be no way of doing it via keyboard. Perhaps this is good for Java, but its not for C++ where there are longer identifiers as well as different spelling conventions. Particularly, with the underscores between words, half of such partial selections are not what user wants, due to "landing" on the wrong side of the underscore. It seems sensible to have an option in editor to switch to selecting whole identifier.

When the "Project Explorer" tab looses focus (this happens frequently), Eclipse somehow does not know which is the active project, so, for example, the "build" and "launch" buttons don't know which project to build or launch. This is silly since I believe most people work on single project most of the time. This also causes possible slip mistakes when user selects things in the dialog boxes that appear when he presses the buttons.

After excluding a file from build (right click on file in Project Explorer and choosing "Exclude from Build", it is not clear how to bring it back, because there is no symmetric "Include to Build" option. An unaware programmer may get stuck in the middle of a task (as I did). The option is available after the same right click, in "Properties" menu in "C/C++ Build" section.

BTW, I am using Eclipse anyway due to rich semantic help, rich set of keyboard shortcuts, syntax analysis which points out syntax errors immediately and showing compile errors in many places in IDE.

piątek, 9 kwietnia 2010

sprytniejsze budowanie zbiorcze

Jest sobie duży projekt w C++? Jest problem z czasem kompilacji z powodu dużej liczby jednostek translacji(plików cpp)? Oto proste rozwiązanie, oparte na znanej idei budowania zbiorczego, czyli "unity build" (zwanej też "uber build" albo "bulk build"), polegającej na #includowaniu plików cpp do zbiorczego pliku cpp i kompilowaniu jego zamiast rzeczonych plików.

Cechą unikatową rozwiązania, które zaraz opiszę jest to, że:
A) Nie trzeba męczyć się z ręcznym wybieraniem plików, które mają być skompilowane. Po prostu kompilujemy wszystkie pliki cpp (w przeciwieństwie do powyżej realizacji "unity build").
B) Można dowolnie przełączać pomiędzy "unity build" a normalną kompilacją.

Cały mechanizm zadziała automatycznie, jedyną rzeczą, która sygnalizuje chęć skorzystania z unity build jest dodanie jednej opcji polecenia wywołującego linker.

Aby zaimplementować mechanizm, wystarczy zrobić 2 rzeczy:
A) W każdym pliku cpp dopisać na początku (przed poleceniami #include)
#if defined(IN_COLLECTIVE) or not defined(BUILD_COLLECTIVELY)
a na końcu
#endif
B) W każdym folderze zrobić plik zbiorczy collective.cpp, wyglądający mniej więcej tak:
#ifdef BUILD_COLLECTIVELY
#define IN_COLLECTIVE
#include"A.cpp"
#include"B.cpp"
#include"C.cpp"
#include"D.cpp"
#include"E.cpp"
#undef IN_COLLECTIVE
#endif


Oczywiście A,B,C,D,E to nazwy plików cpp w folderze, gdzie znajduje się plik z powyższym kodem.

W efekcie, jeśli dodamy do opcji linkera makrodefinicję BUILD_COLLECTIVELY, to program zbuduje nam się kilka razy szybciej. Jeżeli nie, to wszystko będzie po staremu. Podkreślam, że BUILD_COLLECTIVELY możemy dodać do wywołania linkera, np.
g++ -DBUILD_COLLECTIVELY ..., nie musimy w żadnym pliku nagłówkowym.
Nie będę tu gęsto tłumaczył dlaczego to przyspiesza kompilację, ani nie będę wymieniał wszystkich zalet i wad "unity build", najlepiej odeślę do materiałów poglądowych:
http://buffered.io/2007/12/10/the-magic-of-unity-builds/
http://stackoverflow.com/questions/861707/is-reducing-number-of-cpp-translation-units-a-good-idea
a tutaj tylko zakreślę przyczynę: chodzi głównie o to, że wszystkie includowane nagłówki są przetwarzane tylko raz, a nie tyle razy, ile jest plików cpp.

W pierwszym linku powyżej są opisywane rozwiązania polegające na wyłączeniu z kompilacji wszystkich plików cpp poza zbiorczym lub też tworzeniu osobnego projektu do budowania zbiorczego. Moje rozwiązanie stanowi ulepszenie w stosunku do nich, dlatego, że w praktyce jeżeli pliki są wyłączane z projektu w ulubionym IDE lub kod jest wyłączony kompilacją warunkową, ciężko jest zmusić IDE do włączenia w takim kodzie pomocy kontekstowej, podświetlania znaczeniowego i innych udogodnień. W moim rozwiązaniu do wygodnej pracy nad tym kodem wystarczy nie zdefiniować BUILD_COLLECTIVELY (czyli tak naprawdę nie trzeba robić nic).

Zauważ, że rozwiązanie jest całkowicie transparentne, tzn jeżeli kolega zacznie pracować nad naszym kodem i utworzy sobie do tego celu nowy projekt, to domyślnie będzie miał stary, znany mu sposób budowania, pomoc kontekstową i nie będzie musiał nic dodatkowo ustawiać, żeby w ogóle zacząć pracę.

Zauważ również, że w wymienionych wymienionych powyżej krokach nie ma nic, czego nie mógłby dopisać prosty skrypt, można więc z łatwością zaimplementować to rozwiązanie w naprawdę dużym projekcie.