Accelerated C++ Solution to Exercise 5-8

Exercise 5-8

In the hcat function from S5.8.3/95, what would happen if we defined s outside the scope of the while? Rewrite and execute the program to confirm your hypothesis.

Solution

Note: the original hcat function may be found in my Solution to Exercise 5-0 (Part 3/3). The objective of this exercise is to understand what may happen should we vary the hcat function a little bit.

In short, defining the string s outside the while loop will cause problems for certain horizontal concatenation scenarios (this will explained shortly below). It is however ok to define that string s outside the while loop, as long as we also add a one-line statement inside the while loop (this will also be explained shortly below).

Potential Problem

This is what the hcat function looks like if we simply move the string s outside (or just before) the while loop.

Acpp5p8BuggyCode

Assuming we wish to use the hcat function to concatenate the following (left and right) vectors:

Acpp5p8LeftRight

When the hcat function is invoked, the following tables summarise what happens:

Acpp5p8BuggyInitialization

Acpp5p8BuggyWhileLoop

Note that at the beginning of the second loop, because we did not re-initialise the string s, it causes the the s.size() to become larger than width1 (which is the maximum length of the longest string element within the left vector). This effectively results in negative string length when we apply the s += string(width1 – s.size(), ‘ ‘). The C++ implementation will likely bump into a string length error – highlighted in red.

Below shows what happens if we attempt to run this code as it is.

Acpp5p8TestBuggy

Acpp5p8TestBuggy2

i.e. the program crashes as a result of negative string length. This can be fixed easily however – read on!

Resolution

To fix that string length error as described above, simply add a one-line statement within the while loop to re-initialise the string s at the beginning of each loop.

Acpp5p8CorrectedCode

Again, assuming we wish to use the hcat function to concatenate the following (left and right) vectors:

Acpp5p8LeftRight

When the hcat function is invoked, the following tables summarise what happens:

Acpp5p8CorrectedInitialization

Acpp5p8CorrectedWhileLoop

Below shows what happens if we attempt to run this corrected code – it should run smoothly as expected.

Acpp5p8TestCorrect

The Project

To wrap up the source and header files that I use to test my codes.

Acpp5p8MgntTree

C++ Source File List

C++ Header File List

C++ Source Files

main.cpp

#include <iostream>  // cin, cout, endl, getline
#include <vector>    // vector
#include <string>    // string
#include "frame.h"   // width, frame
/*#include "vcat.h"    // vcat */
#include "hcat.h"    // hcat
#include "vcout.h"   // vcout

using std::cin;
using std::cout;
using std::endl;
using std::getline;
using std::string;
using std::vector;

int main()
{

    string line1;            // line
    vector<string> para1;    // paragraph
    cout << "define vector<string> para1 below..." << endl;
    // read multiple lines to make a paragraph
    while (getline(cin, line1))
        para1.push_back(line1);
    cin.clear();

    string line2;            // line
    vector<string> para2;    // paragraph
    cout << "define vector<string> para2 below..." << endl;
    // read multiple lines to make a paragraph
    while (getline(cin, line2))
        para2.push_back(line2);
    cin.clear();

    cout << "-----------------------------------------------------\n"
            "Display: hcat(para1, para2)                          \n"
            "-----------------------------------------------------\n";
    vcout(hcat(para1, para2));

    return 0;
}

frame.cpp

#include <string>      // string
#include <vector>      // vector
#include <algorithm>   // max

using std::string;
using std::vector;
using std::max;

string::size_type width(const vector<string>& v)
{
    string::size_type maxlen = 0;
    for(vector<string>::size_type i = 0; i != v.size(); ++i)
        maxlen = max(maxlen, v[i].size());
    return maxlen;
}

vector<string> frame(const vector<string>& v)
{
    vector<string> ret;
    string::size_type maxlen = width(v);
    string border(maxlen + 4, '*');

    // write the top border
    ret.push_back(border);

    // write each interior row, bordered by an asterisk and a space
    for (vector<string>::size_type i = 0; i != v.size(); ++i)
        ret.push_back("* " + v[i] + string(maxlen - v[i].size(), ' ') + " *");

    // write the bottom border
    ret.push_back(border);

    return ret;
}

hcat.cpp

#include <string>      // string
#include <vector>      // vector
#include "frame.h"     // width

using std::string;
using std::vector;

vector<string> hcat(const vector<string>& left, const vector<string>& right)
{
    vector<string> ret;

    // add 1 to leave a space between pictures
    string::size_type width1 = width(left) + 1;

    // indices to look at elements from left and right respectively
    vector<string>::size_type i = 0, j = 0;

    string s;
    // continue until we've seen all rows from both pictures
    while (i != left.size() || j != right.size())
    {
        // construct new string to hold characters from both pictures
        //string s;
        s = "";

        // copy a row from the left-hand side, if there is one
        if (i != left.size())
            s = left[i++];

        // pad to full width
        s += string(width1 - s.size(), ' ');

        // copy a row from the right-hand side, if there is one
        if (j != right.size())
            s += right[j++];

        // add s to the picture we are creating
        ret.push_back(s);
    }

    return ret;
}

vcout.cpp

#include <iostream>
#include <string>    // string
#include <vector>    // vector

using std::cout;
using std::endl;
using std::string;
using std::vector;

int vcout(const vector<string>& v)
{
    for (vector<string>::const_iterator iter = v.begin();
         iter != v.end(); ++iter)
    {
        cout << (*iter) << endl;
    }
    return 0;
}

C++ Header Files

frame.h

#ifndef GUARD_FRAME_H
#define GUARD_FRAME_H

#include <string>
#include <vector>

std::string::size_type width(const std::vector<std::string>&);
std::vector<std::string> frame(const std::vector<std::string>&);

#endif // GUARD_FRAME_H

hcat.h

#ifndef GUARD_HCAT_H
#define GUARD_HCAT_H

#include <string>
#include <vector>

std::vector<std::string> hcat(const std::vector<std::string>&, const std::vector<std::string>&);

#endif // GUARD_HCAT_H

vcout.h

#ifndef GUARD_VCOUT_H
#define GUARD_VCOUT_H

#include <string>
#include <vector>

int vcout(const std::vector<std::string>&);

#endif // GUARD_VCOUT_H

Conclusion

For the hcat function to work, we must re-initialise the string s at the beginning of each loop, as illustrated above.

Reference

Koenig, Andrew & Moo, Barbara E., Accelerated C++, Addison-Wesley, 2000

One thought on “Accelerated C++ Solution to Exercise 5-8”

  1. Johnny.

    Good analysis. Your example input table is misleading. It looks like two blank lines should be entered after the ‘bbb’ for the first picture. When that is done, both vectors will have four elements and the test will pass.

Leave a reply