3

The problem asks to create a program that asks the user to enter some text and that text will be surrounded by asterisks depending on the width of the screen for example if the user inputs "Hello world" the output should be:

****************
* Hello World! *
****************

I've tried to create the functions but I'm stuck becaus of a compiler error with the shown minimal code.

Question: Why does it tell me no matching function for within_width(text, 80)?

Some of the code I have is below:

#include <iostream>
#include <string>

void display_header (std::string &header) {
    std::string text;
    header = text;
}

bool within_width (std::string& text, unsigned short int& max_width) {


}

int main() {
    std::string text;
    std::cout << "Please enter header text: ";
    std::getline(std::cin, text);

    if (within_width(text, 80)) {

        // call the display_header function and pass in the text
        // inputted by the user
    } else {
        std::cout << text;
    }
    return 0;
}
Yunnosch
  • 26,130
  • 9
  • 42
  • 54
cppnoob
  • 39
  • 6
  • 3
    I recommend taking a shot at drawing the box before asking how to draw it. You might nail it and not need to ask the question. You might only miss by a little bit and need a small correction.Whatever the outcome, you'll learn a lot more. – user4581301 Oct 09 '19 at 06:05
  • why does it tell me no matching function for within_width? – cppnoob Oct 09 '19 at 06:22
  • See? Now that is an understandable quesiton. Please [edit] your post to ask it there. – Yunnosch Oct 09 '19 at 06:23
  • 1
    @Yunnosch Pete got lucky! I shared your concerns about the indentation but I felt charitable! – Adrian Mole Oct 09 '19 at 06:23
  • Here's a hint: You can invoke `text.size()` after the `std::getline` statement to figure out how many characters to draw for the top border (+4 more for edges and margins). – selbie Oct 09 '19 at 06:24
  • I did the editing for you. It now is a clear and answereable question. The fact that it looks like a teacher template is not a problem with this updated question. – Yunnosch Oct 09 '19 at 06:32

3 Answers3

2

This declaration of the function

bool within_width (std::string& text, unsigned short int& max_width)

asks for an unsigned short int variable, because it has a reference parameter, see the second &.

To satisfy it, you need to put the value 80 into a variable and give the variable as parameter.

unsigned short int MyWidth=80;
if (within_width(text, MyWidth))

Alternatively (but I assume you are not allowed) you can use a call by value parameter

bool within_width (std::string& text, unsigned short int max_width)

Then you could call as shown.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
0

I won't give a full answer to the exercise here, just some clues.

  • the display_header() and within_width() functions need to know the string given in parameters but may not modify it ; thus the type of this parameter should be const std::string & (the const was missing).
  • the second parameter of the within_width() function is just an integer that will be compared to the length of the string ; you don't need to pass it by reference (or at least const), rather by value. Here, the (non-const) reference prevents from passing the literal constant 80.
    (it seems to be the main concern of the question after edition)
  • You need to reason step by step.
    1. all of this depends on the size of the string (12 for Hello World!) ; this information is available via size(text) (or text.size())
      (https://en.cppreference.com/w/cpp/iterator/size)
      (https://en.cppreference.com/w/cpp/string/basic_string/size)
    2. This size will have to be compared to max_width
    3. Displaying the line with header will require 4 more characters because * will be prepended and * will be appended.
    4. Thus the two surrounding lines will have the length size(header)+4 too.
    5. In order to create such a string made of *, you could use a constructor of std::string taking two parameters : the count of characters and the character to be repeated.
      (https://en.cppreference.com/w/cpp/string/basic_string/basic_string)
    6. Send all of this to std::cout in the correct order.
prog-fh
  • 13,492
  • 1
  • 15
  • 30
  • How to do the exercise is NOT the question. Otherwise I would appreciate your good way of answering a homework question. – Yunnosch Oct 09 '19 at 06:33
  • @Yunnosch You are right. When I started typing (slowly, I'm not native) the question did not state the exact problem (edited afterwards)... I should delete then. – prog-fh Oct 09 '19 at 06:38
  • The editing was by me, but I stand with it being OPs intention. No need to delete. Just edit to focus on the actual question. (I think it matches your own intention, not to help too much with the homework.) – Yunnosch Oct 09 '19 at 06:45
0

Edit: Just noticing that this answer probably goes far beyond the scope of the task you have been given (just filling in some skeleton that has been provided by your teacher).

I'll still leave it here to illustrate what could be done with arbitrary input. Maybe you want to experiment a little further than what you have been asked...

bool within_width(...)

Pretty simple: string.length() <= max – just wait a second, you need to consider asterisks and spaces at beginning and end of output, so: max - 4

But you can do better, you can split the string, best at word boundaries. That's a bit difficult more difficult, though:

std::vector<std::string> lines;

// we'll be starting with an initially empty line:
auto lineBegin = text.begin();
auto lineEnd = text.begin();

for(auto i = text.begin(); i != text.end(); ++)
// stop condition empty: we'll stop from inside the loop...
{
    // ok, we need to find next whitespace...
    // we might try using text.find_first_of("..."), but then we
    // need to know any whitespace characters ourselves, so I personally
    // would rather iterate manually and use isspace function to determine;
    // advantage: we can do other checks at the same time, too

    auto distance = std::distance(lineBegin, i);
    if(std::distance(lineBegin, i) > maxLineLength)
    {
         if(lineEnd == lineBegin)
         {
             // OK, now we have a problem: the word itself is too long
             // decide yourself, do you want to cut the word somewhere in the
             // middle (you even might implement syllable division...)
             // or just refuse to print (i. e. throw an exception you catch
             // elsewhere) - decide yourself...
         }
         else
         {
             lines.emplace_back(lineBegin, lineEnd);
             lineBegin = lineEnd; // start next line...
         }
    }

    // OK, now handle current character appropriately
    // note: no else: we need to handle the character in ANY case,
    // if we terminated the previous line or not
    if(std::isspace(static_cast<unsigned char>(*i)))
    {
         lineEnd = i;
    }
    // otherwise, we're inside a word and just go on
}
// last line hasn't been added!
lines.emplace_back(lineBegin, lineEnd);

Now you can calculate maximum length over all the strings contained. Best: Do this right when adding a new line to the vector, then you don't need a separate loop...

You might have noticed that I didn't remove whitespace at the end of the strings, so you wouldn't need to add you own one, apart, possibly, from the very last string (so you might add a lines.back() += ' ';).

The ugly part, so far, is that I left multiple subsequent whitespace. Best is removing before splitting into lines, but be aware that you need to leave at least one. So:

 auto end = text.begin();
 bool isInWord = false; // will remove leading whitespace, if there is
 for(auto c : text)
 {
     if(std::isspace(static_cast<unsigned char>(c)))
     {
         if(isInWord)
         {
             *end++ = ' '; // add a single space
             isInWord = false;
         }
     }
     else
     {
         *end++ = c;
         isInWord = true;
     }
 }

This would have moved all words towards the beginning of the string, but we yet to drop the surplus part of the string yet contained:

 text.erase(end, text.end());

Fine, the rest is pretty simple:

  • iterate over maximum length, printing a single asterisk in every loop
  • iterate over all of your strings in the vector:
    std::cout << "* " << line << "*\n";
  • repeat the initial loop to print second line of asterisks

Finally: You introduced a fix line limit of 80 characters. If console is larger, you just won't be using the entire available width, which yet might be acceptable, if it is smaller, you will get lines broken at the wrong places.

You now could (but that's optional) try to detect the width of the console – which has been asked before, so I won't go any deeper into.

Final note: The code presented above is untested, so no guarantee to be bugfree!

Aconcagua
  • 24,880
  • 4
  • 34
  • 59