The University of Iowa
The College of Liberal Arts and Sciences
Department of Computer Science

Programming Languages and Tools:

CS:3210:0001

Lecture/Lab #29

Programming with C++

Lambda expressions (cont.), exceptions, namespaces

Warm up: algorithms

  • What are some notable characteristics of standard library algorithms?
  • How do we write an algorithm that appends its output to the end of another container?
std::vector<int> src = { 11, 22, 33, 44, 55, 66, 77 };
std::vector<int> dst;
std::transform( src.begin(), src.end(), ???, []( int n ) { 
    return n + 100; 
});
  • If algorithms operate on iterators only, how do algorithms like remove and remove_if work?
std::remove_if( src.begin(), src.end(), [](int n) {
    return n % 2 == 0;
});

Exercise 1

Sort the vector, erase all "Bob" occurrences and print the vector to the standard output using only standard algorithms

  • Open the exercise template

  • Write your code, press Run to test

  • When you're done, grab your Repl's link and send it as a direct message to me (agurtovoy)

  • Click on the corresponding option in the "Lab29 exercises" poll in #general

Lambda expressions (cont.)

A convenient way to define an anonymous function object at the point of use

std::vector<int> v = { 11, 22, 33, 44, 55, 66, 77 };
auto iter = std::find_if( v.begin(), v.end(), []( int n ) {
    return n > 42;
} );
  • The [] syntax actually serves a purpose and is called a capture clause.

  • The empty brackets indicate that the expression body of doesn't accesses any variables from the enclosing scope.

  • Use [=] to enable capturing all outside variables by value, [&] to capture by reference.

  • Beware of lifetime issues if/when passing/returning lambda from a function.

Error handling options in C++

  • Throwing an exception
  • Returning a value indicating failure
  • Terminating the program

Throwing an exception

int main()
{
    try {
        std::cout << "Hi there" << std::endl;
        throw std::runtime_error( "No pasarán" );
        
        std::cout << "Hi again" << std::endl;
    } 
    catch ( std::exception const& x )
    {
        std::cout << "\nUh-oh: " << x.what() << std::endl;
    }
}

List of standard exception types:

When to throw an exception

  • Rare errors / errors that should not be ignored (e.g. out of memory)
  • An error that cannot be handled by an immediate caller (e.g. network outage)
  • No suitable return path for errors codes are available (e.g. constructors).
  • The code flow is made more complicated or expensive by a need to pass both a value and an error indicator back.
  • When in doubt, prefer exceptions.

When to return an error code

  • A failure is normal and expected (e.g. opening a file).
  • An immediate caller can reasonably be expected to handle the failure.

Consider using std::optional and std::variant if choosing this route.

When to terminate

  • Unrecoverable errors (e.g. memory exhaustion)

Namespaces

namespace cs3210 {

struct point {
    int x;
    int y;
};

point add( point const& l, point const& r )
{
    return point{ l.x + r.x, l.y + r.y };
}

} // namespace cs3210 {


int main() {
    cs3210::point p1{ 1, 15 };
    cs3210::point p2{ 10, -5 };
    auto p3 = add( p1, p2 );
}

A mechanism for preventing name conflicts in large projects

Made with Slides.com