Al is DDJ's senior contributing editor. He can be contacted at [email protected]
An old adage holds that confession is good for the soul. This month my soul gets a healthy boost, because I have a lot to confess. Mainly about how I don't know Jack, whoever he is.
My first confession is that I'd rather play jazz piano than anything else you can do sitting down. Well, mostly. That preference is why I wrote last month's column from Zurich, Switzerland. A piano-playing assignment took me there. But while I was there, I got to visit with Erich Gamma, and Judy and I fell in love with Switzerland.
We were there in the middle of June enjoying an unseasonable warm spell more typical of August they tell me. There's a lot about Switzerland to like. First and foremost are the people. They have an undeserved reputation for being cold and distant, but that was not my experience. Case in point:
Years ago, in the preInternet era, this column distributed source code under a program I called "careware." You sent me a diskette and mailer, and I copied the source code onto the diskette and mailed it to you. I asked for a voluntary contribution of a buck, which I would donate to the local food bank. With the advent of readily downloadable files, the careware program ran its course. But in its heyday, a few readers from other countries sent me their own currency instead of a buck. A Swiss 20 franc note never made it to the bank. I always meant to convert it and pass it on, but never got around to it. So now, heading for its homeland, Judy took the 20 francs along and, once there, tried to buy our lunch with it. Well, wouldn't you know it, that note was obsolete. Several years ago, they changed the money. Jazz musician hipster lingo calls money, "jack." Needless to say, we didn't know Jack about Swiss jack.
Next time someone tells you about cold and distant people, just try passing some bad jack in New York City and see what happens. Try it in Paris. Try holding up a line of people behind you while a nonEnglish speaking, very young clerk tries to explain that she has never seen such currency and thinks you must be an international counterfeiter or something. Then tell me about cold, impersonal folks. Rather than showing impatience with the confused American tourist lady who was delaying their lunch break, the Swiss people in line who spoke English politely explained that Judy had to take the bill to a bank and exchange it. Then they waited patiently while Judy dug through her stuff to find enough real Swiss money to pay for the sandwiches. During all this, I was trying to pretend I didn't know her in case someone called the Polizei.
Switzerland has a wonderful public transportation system. We traveled extensively on my days off by train, boat, tram, and busall on a single pair of passes, mostly on the honor system. We were rarely asked to show our tickets. The trains are all electricquiet and clean. The cities are pedestrian-centric; drivers stop and politely let pedestrians cross where the crosswalks have no signals; most pedestrians actually wait for the signals where they exist. Many sidewalks have lanes marked for bicycles and the riders and pedestrians observe them. The skateboard is a means of transportation rather than a medium for juvenile exhibitionism.
Cuban cigars are legal. You can purchase beer from street vendors. Everyone has a dog, and the dogs are well behaved and welcome in most public establishments including restaurants. There seem to be no hobos or homeless people. There is graffiti, but it is usually aesthetically appealing, rarely obscene, and rarely on buildings of antiquity. They drive on the correct side of the road. The bread, cheese, and chocolate are out of this world. The Alps aren't bad either. And the women are beautiful. Is this a great country or what?
I visited with Erich Gamma, one of the gang-of-four authors of Design Patterns: Elements of Reusable Object-Oriented Software, and now the lead developer on the Eclipse project. Erich's office is on a side street in the picturesque old part of Zurich, a short walk from our hotel. Eclipse is a multiple-platform, open source, Java-based IDE. The day I visited, June 28, was the day eclipse.org officially released Version 2.0, and the office was kind of laid back with all the developers chilling out after the usual prerelease grueling test regimen. Erich gave me the 10-minute demo, a snapshot of the demo he presents in his role as Eclipse evangelist at conventions and seminars around the world.
Eclipse as delivered is a Java-based development environment. The IDE itself has many powerful features to help you build applications. The short demo is impressive and overwhelming. I can't really do it justice here because, I confess, I don't know Jack about Java and don't know enough to build a minimum application. When you build a new project with Eclipse, the default implementation includes the basic run-time libraries, but there are no default application source code files to start from. Consequently, I could not build the equivalent of a minimal Visual Studio C++ MFC application nor can I convey to you the power of this environment. But when Erich ran his demo, it was pure magic.
Eclipse is just the IDE. It implements languages with a plug-in architecture, and Java is the primary language for the project and the one distributed by default. There are C and C++ compilers being added, however. In my opinion, their success will depend on how well the implementers build into the plug-ins what the IDE needs to know about C++ syntax and program structure, and what they choose as a cross-platform application framework class library. You can learn about those developments and get in on the testing of the C/C++ part of Eclipse by going to http://www.eclipse.org/tools/ and clicking on CDT. The current emphasis is on implementing C/C++ on the Linux platform, but they are actively soliciting help in adding C/C++ to the Windows platform, too.
Eclipse is certified by the Open Source Initiative as an Open Source project. Erich commented that the open-source way of doing things often hinders a project's ability to proceed with a schedule. The participants' enthusiasm can produce an overwhelming number of source code contributions when a new feature is announced. Consequently, the project found it necessary to restrict announcements of plans and features to a limited subset of developers. Otherwise, there was an undue effort required to coordinate the unsolicited source code contributions. So, even though the Eclipse source code is open, project coordination and planning are less so.
You can download Eclipse from http://www.eclipse.org/. It's a 54-MB download, so if you aren't on a fast connection, pack a lunch. You need a Java Runtime Environment installed to use Eclipse. If you don't have one, the web site includes links to where you can download one from IBM or Sun for Windows, Linux, and Solaris platforms.
I'm doing a major revision of Quincy, the Windows-hosted C/C++ IDE that I originally wrote in 1996 to support C and C++ tutorial books. Quincy has gone through many versions over the years as the language and the compilers changed. The final version of Quincy 2000 is still available at http://www.alstevens.com/quincy2000/. The new version, Quincy 2002, is not ready for release as I write this, but an early release might be available by the time you are reading it. You will find it at http://www.alstevens.com/quincy2002/. Maybe.
There are two reasons for the revision. First, my publisher, MIS:Press, decided that it was time for a new edition of Teach Yourself C++, the book that originally featured Quincy 96 on its companion CD-ROM. Given the collapsing technology literature market, I had planned to retire from writing computer books until Debra Williams Cauley, who has nursed me through about 20 titles and who knows me as well as anyone does, called from the offices of Wiley, this year's owner of the MIS:Press imprint, and implored me to do a 7th edition. If it had been anyone but Debra, I'd have laughed loudly and asked them to add me to their "don't call" list. But the wiley Debra from Wiley knows full well how to coax me out of retirement. I think it had something to do with jack, er, money.
By now, there is much confusion about that title. I created the line of Teach Yourself... books in 1989 when MIS:Press was a small Oregon-based publisher. Since then, several other publishers ripped off, er, appropriated the catchy title, and there are Teach Yourself books about everything from everyone including several about C++. My pal Herb Schildt wrote one. If you can afford only one C++ tutorial book, please wait for my 7th edition, which ought to be out soon. Herb already has plenty of money from his runaway best-seller, Teach Yourself Notepad, and doesn't need any more, while I still have to pay for Judy's Swiss shopping sprees.
GCC and MinGW
The other reason for updating Quincy has to do with the compiler. Quincy is a typical IDE in that it hosts a compiler written by other people. The first versions of Quincy used the cygwin compiler (http://cygwin.com/), a port of the GNU Compiler Collection (GCC, http://GCC.gnu.org/) to the Win32 platform with a POSIX layer implemented in a DLL. Later, I switched to the MinGW port (http://www.mingw.org/) of GCC, which, along with its associated w32api package, implements the Win32 API for developing console and GUI applications and does not use a separate DLL other than those already accompanying the Windows OS. Both of these ports, and the GCC compiler suite itself, are in perpetual states of beta releases, never quite complete, never quite finished. The major development, however, was the release this year of GCC 3.1, which includes implementations of much of Standard C++ and which I wanted to use with Quincy. The MinGW project is in the process of porting GCC 3.1 to MinGW, and that's the compiler I want. Earlier editions of Teach Yourself C++ had to settle for less-than compliant compilers because that's all that was available.
Quincy originally had its own debugger. A debugger associates the executable program being debugged with the program's source code and, among other things, allows the programmer to step through source code lines, set breakpoints, and watch, examine, and modify variables. The executable program's binary file, when compiled to be debugged, includes tables in a passive data segment that associates relative memory locations with source code symbols such as function and variable identifiers. Quincy handles all that, or has up until now. No more. The reason is something called "stabs."
The table format that GCC uses is called stabs (http://sunsite.bilkent.edu.tr/pub/cygwin/stabs_toc.html) . Existing versions of Quincy read and parse stabs from executable files and build internal tables to support interactive, source-level debugging.
The stabs format is well defined but has anomalies because it is a language-independent data structure. GCC includes compilers for C, C++, Objective C, Java, Fortran, and Ada, and the stabs format supports identifier and scope conventions for all those languages. Stabs has more than one way to encode filenames and paths for source code files. GCC's C and C++ compilers use two different encodings. Then there is the complexity. Stabs is an intensely complex format made even more so by the strange identifier conventions that C++ applies to mangled names and instantiated templates and the programmer-defined types that programmers want to debug. Parsing stabs is a daunting task. Quincy's stabs parser includes permanent debugging code that reports the stab number in the executable file and the __LINE__ variable in Quincy's parser source file where it tries to parse something it can't decipher.
Quincy's mission is to support the example programs in my tutorials, so here's another confession. I never worried when Quincy tripped over a stabs entry that described language idioms not used by the tutorial examples. I was happy to simply let Quincy's debugger support a limited albeit substantial subset of the language. That's because, even though I read the documentation and wrote a parser, I don't know Jack about stabs.
Changing compiler ports in Quincy ought to be easy. Quincy uses a plugin architecture for the compiler. But every time I upgrade to a new release of the compiler, Quincy chokes on the stabs the compiler emits; programs that Quincy could previously compile and debug won't pass the stabs parser. The compiler guys can't help themselves from jumping around among the optional stabs formats, and Quincy's parser is not comprehensive enough to know all the optional formats. I'm always fixing that parser.
Sure enough, when I installed MinGW's port of GCC 3.1, Quincy 2000 barfed when I tried to debug a simple "hello, world" program.
There is a better way to implement a debugger. Use one that somebody else already built. I decided to completely throw out Quincy's debugger and integrate Quincy with the MinGW port of GDB, the GNU command-line source-level debugger program. You might wonder why I didn't do that back in '96. Here's why:
I did not know Jack about how to integrate an interactive command-line program with a Win32 GUI program so that the GUI program emulates the console user. The compiler is a command-line program but, beyond accepting command-line options, it is not interactive. I tried to figure out how to get the GUI program to emulate a two-way console and fool the debugger, found no helpful source of information on how to do it, and decided it would be easier to parse stabs. Not.
Now, years later, I gazed at the all-too-familiar error message that reports a misunderstood stab and decided to bite the bullet and learn how to integrate an interactive command-line tool into a Win32 GUI application.
Win32 implements an interprocess communications conduit called a "pipe." You create a pipe with the CreatePipe function, assigning to the pipe two handles that represent the two ends of the conduit. One end is for sending data and the other end is for receiving data. The protocol uses the same Win32 ReadFile and WriteFile functions that are used for file I/O to read and write pipes. By passing the read handle to another program to use as a read handle, that program can read what you write to the pipe's write handle. By passing the write handle of another pipe, the other program can write data that you can read with the read handle. It seemed to me that if I could coerce GDB into using Quincy's pipe handles for console stdin and stdout, Quincy could simulate the interactive console user. Quincy could send text commands based on GUI controls and read and translate GDB console output messages into breakpoint happenings, variable examinations, source-code line steps, and so on.
Getting GDB to use Quincy's handles ought to be simple. The Win32 CreateProcess function lets you tell the new process to use specified handles for stdin and stdout. Previous versions of Quincy use this mechanism to implement redirected I/O for programs run from within the IDE and to capture error messages from the compiler into a file that Quincy processes.
It ought to be easy, but it ain't. After several attempts at trying many variations of CreateProcess's parameters and the data members of the structures it uses, I was, I confess, ready to toss in the towel, realizing that I don't know Jack about pipes and handles. Only the spectre of a stern, disapproving Debra caused me to persevere, wimp that I am.
I went in search of someone, anyone, who had not only done what I wanted to do, but who had also published the source code. I found, of all things, Dev-C++, another open-source Win32-hosted IDE that is a front end to the MinGW compiler suite including integration with GDB. You can download Dev-C++ from http://www.bloodshed.net/. There are versions for Win32 and Linux. The Win32 version is visually appealing, but I had problems with its user interface. Getting it to step through a program is a challenge, and I haven't figured it out yet. It occurred to me that I might adapt Dev-C++ for my tutorials and abandon Quincy altogether. I'd need to modify it to integrate the tutorial exercise programs into the Teach Yourself environment, one of Quincy's more endearing features.
I downloaded the Dev-C++ source code and found two surprises. First, this C and C++ integrated development environment is written in Delphi and Pascal.
You guessed it; I don't know Jack about Pascal. But any C programmer can figure out Pascal code. Given its Pascal base, I have no desire to modify and adapt Dev-C++ to my purposes. But I ought to be able to use it to see how to get Quincy to work. Reading the Pascal code, I could not find any place where Dev-C++ launches GDB as an interactive debugger. Second surprise: The source code you download for this open-source project is over a year old. Apparently, the feature hadn't been implemented when they posted the source code. All the source code version does is launch GDB from a .BAT file and lets users debug from the command line. Heck, I could've done that. What I did find was the code that Dev-C++ uses to drive the command-line compiler, code that uses pipes. A few well-placed comments seemed to explain what I was doing wrong. Seemed to. I don't really know Jack about what I did wrong, or why what they did works, but by duplicating their Pascal code in C++, I got mine to work. It has something to do with sharing open handles between programs. You can look at the code that accompanies this column. If you understand it, great. As long as it works, I'm happy.
Consoleapp.h and consoleapp.cpp (available electronically; see "Resource Center," page 5) implement the ConsoleApp class. Eventually, Quincy will use this class to integrate GDB. For now, I've written a simple little application to exercise the interface. A GUI application that uses ConsoleApp must know the interactive command-line interface that the console application employs.
The GUI application instantiates an object of the ConsoleApp type, constructing it with the filename of the console application, in this case, gdb.exe. To run the console application, the GUI application calls ConsoleApp::Run, passing command-line arguments in a string. From this point on, the GUI application has to emulate a user by sending console input commands with ConsoleApp::WriteConsole and reading and interpreting console output messages with ConsoleApp::ReadConsole. The GUI app terminates the console application by calling ConsoleApp::Stop. These functions use the Win32 pipe API's ReadFile and WriteFile functions, which are not stdio kinds of functions. Consequently, ConsoleApp::WriteConsole appends a newline to whatever command the GUI application sends and ConsoleApp::ReadConsole reads a character at a time until the console application sends a newline.
So far, this simple ConsoleApp class is all I need to integrate Quincy with GDB. The integration is far from complete, however, and as I progress, this class might become more complicated. The real trickery will be in the code that interprets GDB output, which is meant for human interpretation. Parsing those messages and inferring their meaning might be tricky. Keep an eye on the Quincy 2002 web site for more source code that attacks this problem. If you have any questions, ask Jack, whoever he is. I don't know him.