:: Session 4 ::
sqrt(-1.2);
will give you a domain error and "NaN" will be returnedErrors found by compiler.
Before generating code, compiler analyzes code to detect sytax- & type errors.
It only allows to proceed when everything conforms to the language specification.
The reports of Sytax Errors are offen cryptic.
Tip: also look at previous lines in the messages
Reports of the mismatches between the types declared for the variables, functions etc. and the types of values or expressions assigned to or passed as arguments.
For example:
int area(int length, int width) {...;} // calculate area
int x0 = arena(7); // err: undeclared function
int x1 = area(7); // err: wrong number of arguments*
int x2 = area("seven", 2); // err: 1st argument has wrong type!…
*: every function call must provide the expected number of arguments, of the right types, and in the right order
!: and it can't implicitly convert string to integer
remaining errors like:
int area(int length, int width)
{...;} // calculate area of a rectangle
int x3 = area(10, -7);
// OK. but rectangle with a width of minus 7?
int x4 = area(10.7, 9.3); // OK. but calls area(10, 9) instead
char x5 = area(100, 999); // OK. but truncates the result
Rules:
The rules hold also for all other entities of a program, such as variables & types.
Concider the following code:
int area(int length, int width) {...;} // our area() function
double area(double length, double width) {...;} // not our area()
int area(int x, int y, char unit) {...;} // not our area()
Errors found by checking in a running program.
What to do with an error caught at run time? Questions:
For example:
int area(int length, int width) {
return length*width; } // calculate area of a rectangle
constexpr int frame_width = 2;
int framed_area(int x, int y) {
return area(x-frame_width, y-frame_width);
} // calculate area within frame
int main() { int x=-1, y=2, z=4;
int area1 = area(x, y);
int area2 = framed_area(1, z);
int area3 = framed_area(y, z);
double ratio = double(area1)/area3;
//convert to double to get floating-point divsion
}
…when we can't modify the called function, e.g. in a library.
Catch runtime error: write out an error message or take other actions if error found
Protecting the call of area(x,y)
in main()
completly:
int main() { int x=-1, y=2, z=4;
if(x<=0 || y<=0 )
error("non-positive area() argument");
int area1 = area(x, y);
if(1-frame_width<=0 || z-frame_width<=0 )
error("non-positive argument for area() called by framed_area()");
int area2 = framed_area(1, z);
if(y-frame_width<=0 || z-frame_width<=0 )
error("non-positive argument for area() called by framed_area()");
int area3 = framed_area(y, z); }
Check for valid arguments within the called functions
Arguments-hecking code is in one place, and that one place is exactly where the arguments are to be used
int area(int length, int width) { // calculate area of a rectangle
if(length<=0 || width<=0 ) error("non-positive area() argument");
return length*width; }
int framed_area(int x, int y) {
constexpr int frame_width = 2;
if(x-frame_width<=0 || y-frame_width<=0 )
error("non-positive argument for area() called by framed_area()");
return area(x-frame_width, y-frame_width); } // calculate area within frame
int main() {
int x=-1, y=2, z=4;
int area1 = area(x, y);
int area2 = framed_area(1, z);
int area3 = framed_area(y, z); }
To separate detection of an arror (should be done in a called function, aka callee) from the handling of an error (should be done in calling side of function).
Callee side:
class Bad_area {}; // a type specifically for reporting errors from area()
int area(int length, int width) {
if(length<=0 || width<=0 ) {
throw Bad_area{}; // throw a Bad_area exception in case of bad argument
}
return length*width;
}
Caller side:
int main()
try { int x=-1, y=2, z=4;
...
int area1 = area(x, y);
int area2 = framed_area(1, z);
int area3 = framed_area(y, z);
double radio = area1/area3;
}
catch (Bad_area) {
cout<<"Oops! Bad arguments.\n";
}
"Collection of data": containers
The most common and useful standard library container is the vector
The general notation [low:high)
e.g. [0:v.size())
, means indices from "low" to "high-1", i.e. including low but not high
Trying to read v[v.size()] which is one beyond the end of the vector, is an "off-by-one" error, or a "range error"
for(int i=0; i<=v.size(); i++) {}
vector's subscript operation: vector::operator[]
reports finding an error by throwing an exception (of type: out_of_range).
int main()
try { vector<int> v;
for (int x; cin>>x;)
v.push_back(x);
for (int i=0; i<=v.size(); ++i)
cout<<"v["<<i<<"]=="<<v[i]<<'\n';
}
catch (out_of_range) {
cerr<<"Oops! Range error.\n";
return 1;
}
catch (...) { // catch all other exceptions
cerr<<"Exception: something went wrong.\n";
return 2;
}
Test if the last input operation succeeded by:
double d = 0;
cin >> d;
if(cin) {
// all is well, and we can try reading again
}
else {
// the last read didn't succeed, so we take some other action
}
We (just) want to report the error and terminate the program
double sfn() {
double d = 0;
cin >> d;
if(!cin) error("couldn't read a double in 'sfn()'");
// later we'll come back and do something more useful
}
error() can't return a value. and…
is supposed to terminate the program after getting its message written.
Another type of exceptions defined by standard library is runtime_error(s)
void error(string s) { throw runtime_error(s); }
int main()
try { // ...our program...
return 0; // 0 indicates success
}
catch (runtime_error& e) {
cerr>>"runtime error: ">>e.what()>>'\n';
keep_window_open();
return 1; // 1 indicates failure
}
e.what()
extracts the error message from the runtime_error.&
in (runtime_error& e) is an indicator of "passing the exception by reference."cerr
is like cout, except it's meant for error output. (can also diverted to a different target other than to the screen in other operating systems, e.g. to a file.error()
is commonly used to give two pieces of information passed along to describe the problem, by concatenating the strings in it:
void error(string s1, s2) { throw runtime_error(s1+s2); }
Write a function that tests and throw a runtime_error exception if an assignment or initialization would lead to a changed value. e.g.:
int x1 = narrow_cast<int> (2.9); // throws
int x2 = narrow_cast<int> (2.0); // ok.
char c1 = narrow_cast<char> (1066); // throws
char c2 = narrow_cast<char> (85); // ok.
<...>
are the same as are used for vector<...>.<...>
are used when we need to specify a type rather than a value.<...>
that corresponds to its operand value.