The funny thing about my first programming job (I said many years later to my new wife, Jeannine, as we sipped our morning tea) was how long Bob managed to stay employed despite his being an incompetent nitwit. I remember one fine spring morning in particular....
I was hard at work, implementing a piece of new code, when Bob sauntered over to my cubicle and leaned against the cubicle wall, sipping a latte. No, I don't know why he always had a latte, he just did. So he stood there and smiled and said: "Hey, Junior."
I sat back. "Hey, Bob. What's up?"
"Y'know that code you checked in last week?" He shrugged. "Well, it just broke the build. The compiler says that you defined the function Output twice. You shouldn't do stuff like that."
"Huh?" I huhhed. "But I'm sure I had a clean build...." In my editor I quickly pulled up the file I'd been working on the week before:
//--- file trans.cpp ----------------------------- // static void Output( ostream& o ) { /*...*/ } // ... void ProcessTransaction( /*...*/ ) { // ... Output( cout ); // ... }
"There. Looks fine to me," I said and sat back, satisfied that I had caught Bob in yet another unfounded criticism.
Bob smiled. "Keep looking," he prodded.
I frowned, then did a quick grep on the source tree to see if there were any other occurrences of Output in the same module, and to my surprise, I found it in three more files:
//--- file output.h ------------------------------ void Output( std::ostream& ); //--- file output.cpp ---------------------------- #include "output.h" // ... void Output( ostream& out ) { /*...*/ } //--- file dbinit.cpp ---------------------------- // #include "output.h" // ... void InitDatabase() { // ... Output( clog ); // ... }
I mulled it over. "But that's a different Output function. Isn't it?" I asked.
Bob gestured impatiently with his latte cup. "Weren't you listening? I just told you that it broke the build."
"Which build?"
"Just the build targeting our embedded platform, or at least that's all I saw the build machine complain about. Fix it, and let me know when you've checked it in so I can get some work done." Bob sipped again, chuckled, and left.
All right, I admit it: at first I didn't believe Bob.
I tried rebuilding all the code using our usual compiler and found that it was fine. Encouraged thusly, I then switched shells and tried building the code with our other compiler that we used for the embedded target and sure enough, it broke. I sat back, bemused: my first compiler allowed the two Outputs to coexist with no problem, but the other compiler thought they were the same and complained about a duplicate definition.
I was about to give up and ask Wendy about it when I heard a rustling of pages behind me. I froze. A moment passed, then another. Then the pages rustled again, and I heard a book close. "Acolyte," said the Guru's voice behind me, "you seem to be in some difficulty. May I be of assistance?"
I turned and saw that she was smiling, so I relaxed. "Sure," I said, happy for the help. "Here's the code I've got, and here's what the compilers say. What am I doing wrong?"
She quickly glanced at the code and the compiler results. "Ah," she said, "there are two problems here. But before we delve into those, what do you think is happening? Explain it as best you can."
I took up the challenge: "Well, okay... On the one hand, inside trans.cpp, I'd expect that ProcessTransaction's call to Output would call that static version of Output."
"And...?" she prompted.
"On the other hand, inside dbinit.cpp, I'd expect that InitDatabase's call to Output would call the externally defined Output that lives inside output.cpp."
"But...?" she prompted again.
"On the gripping hand, our main compiler seems to agree with me if it hadn't, I would have noticed the problem before checking it in but our other compiler doesn't."
"You have done well," the Guru said calmly. "Now meditate on what you have seen, and you will find that there are really two problems here. The first problem is that the embedded compiler is wrong. In particular, your code is legal, and our main compiler did handle it correctly. A function declared static has internal linkage only; for so says the blessed Standard in Concepts 3:5, paragraph 3. But the other Output has external linkage, and so according to Concepts 3:5, paragraph 9, it is not the same function as the static Output internal to trans.cpp. And that is why the embedded compiler is wrong: it fails to correctly distinguish between the two Outputs. There are clearly two distinct functions here, not one."
"All right," I said, reassured. "Cool. So I was right to"
"So certain are you? The second problem," the Guru continued kindly, but firmly, "is that although your code is legal, it does not conform to our coding standards."
"Huh? Sure it does. I used the right brace placement, the right indentation, the..."
The Guru winced, eyes tightly shut, and waved me to silence. "No. No, no, no. No coding standard worth your time defines such things, nor does ours, except of course that embedded tabs are pernicious and whatever indentation you choose should be made up of spaces. No. You may have been looking at the standards Bob brought with him from his last job, but those things certainly do not apply here.
"Rather," she continued, "the problem is your use of static, for this use of static is old-fashioned, antique, discouraged, and most of all deprecated." She opened her book, which I now saw was a copy of Stroustrup's The C++ Programming Language [1], found a place near the back, and quoted: "'The use of static to indicate "local to translation unit" is deprecated in C++. ...'" She looked over her glasses at me, then down again to finish: "'... Use unnamed namespaces instead.'"
Then the light went on inside my head. "Ah, so I should do this," and I wrote:
//--- file trans.cpp ----------------------------- // <b>namespace</b> <b>{</b> void Output( ostream& o ) { /*...*/ } <b>}</b> // ... void ProcessTransaction( /*...*/ ) { // ... Output( cout ); // ... }
"Indeed, my apprentice. I do believe that even our back-released embedded compiler will understand your revised writings correctly...." The words faded away, and I turned to find that the Guru had already silently disappeared, as was her wont.
I smiled, cracked my knuckles, successfully reran the code through both compilers, successfully reran the unit tests, and checked in the code. Then I smiled, opened a new email window, and began writing a short and cryptic notice to Bob....
Reference
[1] Bjarne Stroustrup. The C++ Programming Language, 3rd Edition (Addison-Wesley, 1997).
Jim Hyslop is a senior software designer at Leitch Technology International Inc. He can be reached at [email protected].
Herb Sutter (<www.gotw.ca>) is secretary of the ISO/ANSI C++ standards committee, author of the acclaimed books Exceptional C++ and More Exceptional C++, and one of the instructors of The C++ Seminar (<www.gotw.ca/cpp_seminar>). In addition to his independent writing and consulting, he is also C++ community liaison for Microsoft.