Thursday, June 14, 2007

The Contract Job From Hell

It seemed easy enough. That's what I thought when I received an e-mail from the Computer Science chair at my University advertising a C++ programming opportunity involving a fairly simple API to interface with a radio receiver card. The details weren't great, so I replied and said that I'd like to get some more information. About 2 weeks later I heard a reply from the CS contact person. I was given the contact person for the job, the person actually doing the hiring. I shot off an e-mail asking a few preliminary questions such as the features he'd require, the time span, the pay, etc... The reply this time was fairly quick, and the job was sounding relatively do-able.

There were a few catches though. First, the programming would need to be done on a Windows computer and he would require some TCP communication with the data I'd be working with. I'd never programmed under Windows, and I'd certainly never done anything with Windows sockets. The other problem? They needed the entire project done in 3 weeks and they were only able to pay for 10 hours of work per-week at the University minimum wage of $5.50. Dealing with that insulting pay is one thing, but actually expecting someone to come in and write code for an arbitrary API in very little time is something completely different. On the other hand, I was a poor college student, and the actual programming didn't seem like it would be much of an issue at all so I figured I'd give it a shot.

I shot back an e-mail stating my concerns about completing the project in such a short amount of time, stating that it would probably require a number of hours that they weren't able to pay for in order to do it. About 2 weeks went by and I heard nothing. I assumed some other student had also responded to the e-mail and opted to sell their soul for $165. Then, 1 week before the original 3 week "deadline" I receive an e-mail from him saying that they can raise the hours to 15 per-week, but they need it done in 3 weeks (again). By this time, mid-terms are in full swing and I've got major programming projects for school to complete. I tell him flat out that I just don't have time now, but talk to me in May when school ends, I'll be available for the entire month. Again, I heard no reply from this message.

Fast forward an entire month. They inform me that they "finally have time to get the card programmed" and wanted to know if I was still available until June. I thought long and hard about this. I had just finished a demanding year of school, and 2 very advanced programming projects. I was burned out. I needed a break. I also needed money. By my math I'd make around $300. I took a deep breath and sent off an e-mail stating that yes, I was available until June. As was par for the course, it took him a week to reply to me. Just how important is it to get this thing done?! Honestly, their deadlines came and went as I waited around for e-mail replies from this guy.

Anyways, we got everything arranged and I met with him to pick up the equipment. This consisted of a really old piece of junk computer (the card used an ISA slot), a matching really old piece of junk monitor, and 2 antennas (high and low frequencies). Being of rational mind, I decided I'd do all of the core programming on my Linux box, use boost for the TCP communication, and then move over to my high-end Windows XP machine to add in the API-specific code. I wanted no part of using the computer they gave me for anything more than trying out the final code. I encountered a pretty serious problem right off the bat: The card had 2 inputs, a BNC and a SMA. The problem? Both antennas had BNC connectors. The project description as given would not be possible with this setup as both antennas needed to be used at once. I sent off another e-mail informing him of the issue and he promptly replied asking ME what he needed to buy, and to let him know and he'd get it. Reality check: I'm a programmer. I don't know anything about this stuff! I was brought in to write code that performs the task at hand. Troubleshooting BNC/SMA antenna connectors doesn't really fit in there anywhere. Nonetheless, I did a quick Google search and located 2 links that offered some adapters. I sent them to him and told him he needed to check into the matter further, and to let me know when he had acquired them.

One week goes by. Two weeks go by. I send him a status update on the project, pose a few other questions to him regarding his desired requirements. A third week goes by. A month goes by. At this point I'm convinced he's at the bottom of a lake somewhere. I see no other reason to ignore e-mails that only serve to accomplish a task that he needs completed. I keep chugging away at the code, making my own decisions about the questions I'd posed to him. I got to a point in which I was ready to test the code on the old crappy machine. I shit you not, the video card would not kick in. That's right, I'm given improper equipment, the contractor goes "Amelia Earhart" on me as soon as I get started, and the computer I'm supposed to develop for doesn't even WORK!

I send out a 3rd e-mail stating that I've finished the code as much as possible, and that I can't test the API-specific code due to the computer not working. I finally get a response stating that he was finishing up a trip in New York and that he'd be in town in a few days. At that point we were to meet and try to get another video card working. A few more days went by, and finally, we were set to meet. 9am, Thursday.

I arrive at about 10 till 9 and head upstairs where we met the first time. He works in a musical instruction type of building. Due to all of the expensive equipment, you can only access part of the second floor, the rest is locked off by 2 doors at opposite sides of a winding hallway. My process for handling this in the past was to knock at one door, wait, walk to the other end and knock, wait, then repeat. With each trip, my knocks get louder and louder. Now, you may be shocked as I was, but it was 9:30am and he STILL WASN'T THERE. I really should have known. Being relatively amused by the whole situation, I decided I'd get creative.

As I said, I was in a musical instruction building. This meant there were practice rooms all along the hallway, wide open. With pianos in them. There was never anyone on this floor of the building, either. I figured what better way to get someones attention in another part of the building than to start playing piano? So, I did just that. I sat down, and I just played. My basic line of thinking was that anyone that would be important enough to notice me playing piano and arrive to yell at me would also be important enough to have keys to unlock the door I needed to get into. I gave up after 5 minutes and decided to just leave. As I walked out the front door, I see the contractor walking up the sidewalk. Just a simple "Hi how are you?". I spent the rest of the day explaining all of the code and documentation to him, and never once did he mention being nearly an hour late.

The job is done now, and I feel very relived, to say the least. I accomplished a lot, I learned a lot, and I ended up making close to $500. Despite everything, I made sure that I wrote the best code I could, and gave the most informative and helpful documentation possible. At the end of the day, I even like the guy, he's very intelligent, and I'd be "more than happy" to do some future development on this project for him. Of course, if he reads this, he'll immediately know it's about him, but I suppose that's fine. He got the best $500 custom software package imaginable.

Thursday, June 7, 2007

Careful with those stubs, mister!

This is just a tiny snippet which ties into my last post about compiler warnings.

Frequently when I'm building a class, I'll stub out a lot of the functions just to get an idea of how things will work. "Stubbing" simply means adding the function declaration/definitions, but instead of having the function actually perform it's task, it's usually either left blank, or does a print statement to show where the control is currently at. That's all fine and well...until you do something like I did.

In order to complete my stubbing, I went as far as to assign one of my class data members, an std::vector<>, to the return of the function that would be used to populate this vector. The problem? While stubbing that said function, I forgot to add a return value! This, coupled with lack of any compiler warning flags, caused a segfault when I ran some testing code.

Now, to the heart of the matter: Why was this so hard for me to track down, aside from improper (absent) warning flags? I was doing my debug print statements with std::cout. The problem? std::cout is buffered, which means things get printed when the OS decides they get printed, not exactly when you tell it to print. After I tried to initialize my vector from the function (of which returned nothing), I made a call to some other class functions, and due to the buffering of my debug statements, it seemed the segfault happened AFTER that other class function returned, but BEFORE control was given back to the constructor that called it. This clearly made no sense, and all of the code looked fine.

Using std::cerr is the optimal choice for debugging output because it gets printed exactly when you tell it to. More importantly, a simple -Wall flag to g++ would have informed me right away that: "control reaches end of non-void function" and immediately I'd know why my program was segfaulting.

So, lessons learned?

1. Enable. Compiler. Warnings.
2. Use std::cerr for debugging output
3. Complete your stubbed functions with return values and proper parameters

Note: I've been told that using std::endl will force std::cout to be printed, but for whatever reason that just wasn't happening for me. *shrug*

Attention: This is only a warning.

I tend to be pretty lazy when it comes to compiling my code. After I'm done knocking out the source, I want to make sure it compiles right away, and start testing it. However, taking the 10 seconds to add warning flags to the compile command could save me, and you, countless headaches trying to track down a bug that the compiler would have been able to point out right away if given the proper flags. The following example, unfortunately, is immune to compiler warnings, but it illustrates a basic error that compiler warnings would try to prevent you from making.

I was writing an application that dealt with unsigned integers throughout the entire program. At some point, I needed to convert those unsigned integer values into std::strings via a helper-function that I created. The values were read in via a binary file, and stored in unsigned integers. The deadline was approaching, and I didn't think about what would happen if there was a negative value in the binary file (perfectly legal, though) The result? The compiler silently converted the int that I retrieved from file to the unsigned int it was being stored in, thus mangling the sign value on my data. Luckily for me, I had been programming on a daily basis for many hours for about the past 5 days, so my brain was warmed up and I caught the error rather quickly. However, it isn't hard to imagine how something as minute as this could remain hidden for a long time, delaying the completion of your project, and driving you farther into insanity. Again, this particular mishap is unfortunately immune to warnings, but from this you can see how something of this sort could be avoided by enabling proper warnings.

There are many other error flags, please check the docs for your compiler to see what they are, but it should be a goal to not only use these flags at all times, but also to have your code compile with the strictest of warning flags in use. If you are receiving a compiler warning, there is a good bet you have a problem with your design, implementation, or maybe both. The above scenario would really be classified as design/implementation, depending on how you look at it, but really it was just a case of carelessness.

Enable compiler warnings with the needed flags. Get rid of them with better code.

Thursday, May 31, 2007

Down Periscope

Hello. You've somehow stumbled upon my programming weblog. In this area, I hope to share with you various experiences I've had writing software. For this introductory post, I'll turn you on to something I was shown yesterday evening. I was so shocked/amazed/excited when I was shown this "trick" that I exclaimed aloud numerous times...

The issue I was having in my code involved objects being duplicated (copied) as I stored them in a std::vector. Oh, by the way, I code almost entirely in C++. Anyways, the way this bit of code worked was that a line was read from file, and that line was then used to construct an object of my type. From there, those objects were added to the vector. The problem was, those original objects stuck around, so I'd have TWICE as many objects in existence than I was actually using! I asked for some assistance on IRC and I must say, my life has never been the same.

The "Trick"

Maybe I've just been living under a rock, but I had never seen this before. Apparently, you can arbitrarily create scopes anywhere in your code! I'll demonstrate it below with a basic example:



Output
$ ./scope
ctor
dtor
Only 1 Foo object exists at this point, good!
dtor

Now, instead of having 2 copies of foo in existence (the one I create, and the one that gets copied into foos), I only have the one inside of foos. Depending on your situation, this is preferable because if you need to add an object to a vector, chances are good that you'll be referencing it from within the vector for the rest of the program.

Hopefully you learned something from this, stay tuned, I'll soon be posting a very amusing and interesting story about my recent job as a contract software developer. Take care.