Join Bill Weinman for an in-depth discussion in this video Overview of BWString, part of C++: Building a String Library.
- In this lesson, we're going to take a look at the overall design of the BW String Class. Here I have a working copy of unit-tests.cpp from the Exercise files. I also have the BWString and BWUTest classes open in this project. First, I'd like to take a moment and talk about the value of unit tests. Most major development environments include a unit testing system, and both X-Code and Visual Studio include unit-testing capabilities built in. The unit tests are incredibly useful. As I developed this class, I wrote unit tests for each feature as it was implemented.
Then, as I write new features, each of the existing features continues to be tested. This has saved time and effort on several occasions as new features may interfere with, or even break, an existing feature. And so, if we look in this file, this file is basically a bunch of unit tests. And the unit tests are constructed like this. I call the test method in my unit test class, and I give it a string, what the test is called, and then I give it an expression, and this is all an expression here, which evaluates to True if the test passes, and evaluates to False if the test doesn't pass.
A lot of unit test classes work the other way around, so you need to make sure. Now, when I build and run this you'll see for each of these tests I get a little string saying what the test is and whether it passed or failed, and then at the bottom I get a summary. So, this, as you can see, is an incredibly useful way to keep track of what is working and what is not working in a complex piece of code. Now, the problem with most unit test systems, especially those included with a development environment, is that they're rarely portable.
In other words, if I write my tests on one system those same tests will not work in another system unless I modify them or rewrite them entirely. The good new is that a unit testing system is a fairly easy thing to write, so I've created my own unit testing system. And, if you look here in this cpp file, you'll notice it's just not a lot of code. That's the whole thing right there. I have a constructor, an initializer, the test method itself, and the reporting method.
And over in the .h file, you see I have all of my function signatures, and I have a very simple data structure that just includes pass, fail, and a couple of strings. So, the unit testing system itself is incredibly easy to write, and this way I have a portable one that'll work on Windows, it'll work on a Mac, it'll work on UNIX, it's worked everywhere that I've used it, and without any modification whatsoever. One feature that this one has is this summary_flag.
Here it's set to false, and so we get full reporting when I build and run this. You see we get all these descriptors. If I set the summary_flag to true, and this gets passed in here into the unit test with the summary method. Now when I build and run I only get a summary. I don't get all of those individual strings being- I just get all of them passed and none of them failed, and that's really all that I want to know. So, for most purposes I leave the summary_flag to false, and I get the full reporting, and that tells me what's going on as I go through my tests.
Now, the BW String Class itself provides a lot of functionality in a very small space. It's designed to work as a drop-in replacement for the Standard String Class for as many common operations as possible. So this is the header file, and the class starts here. It basically has two data structures. There's a Raw C-String that's used to hold the string, which is exactly the same way that the STL String Class works. It wraps around a Raw C-String, and a size_t for the length.
It also keeps this, I call it a poor man's vector. A couple of pointers for an array of string objects that'll be used when use the Split function. We'll talk about that later on in this course. The public functionality of the class is here. You can see it does a lot of things, but most of these things that it does do not actually take up a lot of code. So there's the constructors. The move constructor has a little bit of code. The Split function is probably the most complicated.
It has a few functions that actually do more than a few things, but most of it... We get down here, here's the assignment operator, here's the concatenation operator. That one it has to allocate space for a string and copy it over, but most of these, the comparison operators, they're just wrappers around standard functions. And so, they don't actually involve a lot of code. This is really not a lot of code. Trim, trim takes up a little bit of logic, but most of this is really, really simple.
And we'll go through all of this in a lot of detail as we go through the rest of this course. One thing that's worth noting here, there's a few standard functions that are missing from the Microsoft Standard Library, and I've had to replace one of them here, because I wanted to use standard code and not have different code for Microsoft and different code for UNIX and Mac, and so I simply added this function here, and it's got an ifdef around it. This is a form of the variadic printfs into a string, rather than onto the screen, and I use this for the Format Method.
There it is. You can see I cull it here for the Format Method. And then, in the header, I also have some Microsoft-specific stuff. The Microsoft Compiler is very, very prevalent, and it does have some significant differences from the standard, and so we have to handle them like this, so I have this ifdef, MSC version, and I handle some pragmas, and, again, I declare this vasprintf there. So, there are some differences for the Microsoft Compiler, and that's the way that I handle them, so you can just drop this code into the Microsoft Compiler and it compiles and runs just like it does on UNIX or on a Mac.
So, this class performs most of the functionality that I need from a string class, and it does so with relatively little code, and without pulling in any of the STL container support, which tends to take up a lot of space and slow things down.