Hello, Could someone help me.

In the Programming principles and practice using C++ book
Chapter 7 recovering from errors.
I am very confused about the exception.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Token_stream{
public:
      Token get();
      void putback(Token t);
      void ignore(char c);
private:
      bool full{false};
      Token buffer;};
void Token_stream::ignore(char c)// c represents the kind of Token
{
//first look in buffer:
if(full&&c==buffer.kind){
       full=false;
       return;}
full=false;
//now search input:
char ch=0;
while(cin>>ch)
      if(ch==c)return;
}
void calcuate()
{
   while(cin)
   try{
      cout<<prompt;
      Token t=ts.get();
      while(t.kind==print)t=ts.get();
      if(t.kind==quit)return;
      ts.putback(t);
      cout<<result<<expression()<<'\n';
}
   catch(exception&e){
    cerr<<e.what()<<'\n';
    void clean_up_mess(){
        ts.ignore(print);
}
       

They catch the exception throw to the clean_up_mess function, why still read the characters from cin:while(cin>>ch)? Are all characters in the buffers? What happen if remove the while() in the ignore()function? It still discards the c?
An exception means “something went wrong that I can’t fix”.

Imagine that you have an employee that is told to move all the files from their old cabinets and put them into new cabinets, and to organize them a new way.

Said employee trips down the stairs with a cabinet and dies.

Now you have a mess: files everywhere, with some organized in the new system in the new cabinets, some still in the old cabinets, and a whole bunch of unorganized files lying on the floor.

When the janitor comes by later and discovers the dead employee (and file mess) he reports it to the boss, saying “we’re toast”. That’s an exception.

The point is that the exception was caught (the boss was told about it) and can decide to clean up the mess. (“Alright, go pick up all the files and stick them back in the original cabinet. And... call the coroner.”)

Having caught the exception the program can perform damage control and continue running. It just didn’t finish its task.
If the exception were not caught, the program would either crash and burn or produce incorrect output.

───────────────

I have not read the book you reference. The example code posted has several errors.

  (1) You cannot declare a function in a catch block.[1]
  (2) Nothing in that code throws an exception to be caught.[2]
  (3) Use RAII to clean up.[3]

The point is that whenever you are going to do something that may fail catastrophically, but from which failure does not necessitate termination, wrap it in a try..catch.

1
2
3
4
5
6
7
8
9
10
11
try
{
  do_something_that_may_fail_horribly();
  std::cout << "success!\n";
}
catch (...)
{
  std::cout << "fooey! things did not work out.\n";
}

std::cout << "let's do more stuff now.\n";

I do not know why the cleanup is trying to read more data from the stream.
Unfortunately, with just that snippet, there is not enough context to understand what the author is trying to do.

Disclaimer: I am not a fan of Malik. I think his coding skillz are trash, yo. IMNSHO, the design behind his tokenizer smells. Since before C++ existed there have been cleaner ways of doing a lookahead tokenization, which he seems to be attempting but failing fairly spectacularly.

What RAII means is that if you have opened/accessed/whatever a resource that needs to be cleaned up when you are done with it, you should have an object whose lifetime controls that resource. File streams do this, for example. You can open the file, but you do not need to close it explicitly — let it do that automagically when the file object is destroyed at the end of its scope:

1
2
3
4
5
6
7
8
9
std::vector <std::string>
read_lines( const std::string& filename )
{
  std::vector <std::string> lines;
  std::ifstream f( filename );  // open the file, if possible
  std::string s;
  while (getline( f, s )) lines.emplace_back( s );  // get all available lines (if file is open)
  return lines;  // all done!
} // end of scope. s and f and lines are all destroyed. 

In that example, you notice that we never bother to close the file. But it happens anyway when f is destroyed at the end of the function. Convenient!

If I were to write my own file stream class I could do it like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct ifstream
{
private:
  FILE* fp;  // Here is our external resource token that we MUST manage

public:
  ifstream( const std::string& filename ): fp(nullptr)
  {
    // Here we get access to the external resource
    fp = fopen( filename.c_str(), "r" );
  }

 ~ifstream()
  {
    // And here we release the external resource (automatically for the user when )
    if (fp) fclose( fp );
  }

  // ...and lots of other stuff, like operator>> etc.
}

See how we use the object’s lifetime (in particular, its destructor) to clean up the external file resource for us? That’s RAII: acquire the resource in the constructor and dispose of it in the destructor.

───────────────
[1] You can pretend to with lambdas, but ignore that for now, as it does not apply to your situation.
[2] Unless you have turned on the evil “throw an exception on any error” iostream flag. (Which I doubt the book has done.)
[3] Yes, a horrible acronym.

Hope this helps.
That is very detail answer, thanks.
But this book does not give me the whole code that I cannot run it. So I cannot build it in the compiler just as you said the code had many errors. I collected the codes from separate parts.

I am still confused for ignore() function: the code first looks the buffer, if c there, then discarding the c. The second statement is while(cin>>ch), the code reads characters from cin until find a c, then return out the function. How the function discarding the c in the second statement? They do not have the function boy {} in there, but first statement had the processing{} for the if statement. I do not know how it works?
The ignore() simply reads characters from the stream/file/whatever until the argument c is read. The next time anything tries to read from that stream it will get the next input following that c.

There is also an apparent way to stuff a single Token into the read process, so that the next read should return that character instead of anything from the stream. This is the first thing that the function checks. No matter what is in the single Token buffer it is removed. Only if the buffer does not contain the c character does it then read from the stream.

The idea of a tokenizer is that you split input into logically-associated collections of characters. So, given the text:

int& x = y;

You can split that into the following tokens:

type        value
----------- -----
identifier  "int"
operator    "&"
identifier  "x"
operator    "="
identifier  "y"
operator    ";"

If you cannot follow the book (because it is poorly-organized and provides only unreadable code snippets like that) it would be worth your time to just forget the book and start googling around things like "C/C++ lexical analysis". You will get much better quality (and significantly more readable) examples (and some nonsense too, which you can sort past).

Good luck!
Thank you, I think this book that is very hard to self-study, although it is written by C++ creator.
Last edited on
Hmm, I don’t know where my brain was... book is not by Malik, but by Stroustrup.

That said, I’ll agree that Stroustrup is difficult to follow.
Stroustrup is hard to follow because it is a book written from his lectures.

A lot of his examples are snippets taken from a much larger source that was a chapter or more previous.
Another thing that aggravates me about his book (and his lectures). He violates one key rule he says should never be done.

Putting using namespace std; in a header file.

If it is wrong, then it is wrong. No matter who does it.
A risk every time someone says 'never'. Absolutes of artificial (legal, working stuff banned by the coder's choice) limitations are usually wrong.
Topic archived. No new replies allowed.