Overall iteration one went very well for my partner and I. We had a few small bumps along the way, but that is to be expected with almost any programming project. I want to share what went well for us and what did not work so well. Hopefully this will be a helpful resource for future 3081 students and at least interesting for current students to get any idea of how iteration one went for other groups. I have broken this blog up into a few sections on specific areas that I want to discuss. I will be discussing my observations on procrastination, test cases, design and implementation, and standard c++ libraries while working on this first step of our class project. I want to begin with the most important piece of advice I can give anyone working on a programming project.
Do not procrastinate
It seems that every teacher and professor I have ever met has said this time and time again, but procrastination is a constant struggle. I think that procrastination is particularly harmful in computer science classes. With programming assignments I have found that it is very difficult to judge exactly how long the assignment is going to take. There always seems to be a couple unexpectedly difficult steps where one small but easy to overlook error completely messes up the result of running your program or causes the program to fail to even compile. Because of this, the best course of action is to just start projects like this early instead of guessing how long it will take to complete. During this iteration, I had a couple other big projects to do so I allotted myself enough time to finish up programming iteration one without too much extra time to spare. We ended up paying for this later though when we had to stay up a lot later than both of us would have liked to the night before it was due to fix up some unexpected bugs in the program. It just so happened that one of these bugs was very difficult to find despite very thorough testing, so it took us far longer than we ever expected to find it and fix it. On a related note, always clear your vectors before you reuse them. It would have been far easier to start this project earlier and coast to the end.
Run test cases often
Running test cases all the time helped in a variety of ways. It made checking code in and out of the repository very easy. We could instantly tell before we checked code in if everything still worked and I could also immediately tell if everything I updated and merged worked as I expected. I definitely can understand the appeal of writing code as quickly as possible and then work on testing and writing test cases after the fact, but using test cases all the time is very easy and does not take much extra time. Running test cases very often greatly expedited our coding process.
Discuss the implementation
Before my partner and I did any work on the code beyond getting it set up and compiling in lab, we discussed how we wanted to implement iteration one and had a general idea of who was going to do what parts of the lab. This allowed us to work in parallel on different parts of the project that depended on each other without knowing exactly how the other part was going to be implemented. We were able to get our work done much faster this way and we were both able to work on it at the same time. We did not have to worry very about nasty merges or wasting time writing redundant code.
Explore the full capabilities of C++
C++ is incredibly powerful. There is an abundance of external libraries that can do anything and everything. Even standard libraries have a lot of functionality that can make projects like this significantly easier. Two parts of the standard library we found to be very useful were stringstream and vector. Stringstream simply provides and easy way to build strings. It works in a very similar way to cout. Below is an example of how it can be used.
char *test = "this is an example";
ss << test;
ss << test;
string test2 = ss.str();
The variable test2 now contains "th". This is a very easy way to convert char*s to strings or ints assuming the char* contains numbers. Also string stream can be cleared and reused.
We also found vectors to be very useful because they function in a similar way to arrays, but they can be dynamically allocated and give you a few nice features like a size method that tells you how many elements are in the vector. The performance cost to use a vector versus an array is that the vector requires a bit more memory to allow for dynamic allocation. Other than using slightly more memory, the performance is essentially the same. For an application like the scanner this performance difference is not important.
Because vectors can be dynamically changed and we can easily determine the size, we did not have to hardcode lengths into our code. This helped in another way. We were able to write much more manageable test cases by simply inputting a subset of the possible TokenMakers for example that we needed to implement in the final version of scanner. Although we could also do this with arrays, we found the code to do so is much simpler with vectors and far easier to edit and scale for changes in specifications like removing curly braces as we have to do in iteration two.
I learned a lot about programming with other people in a project during the course of this iteration. I think some of the new techniques and approaches will make subsequent iterations go much more smoothly and allow my partner and I to work more efficiently.