C++ Standard Library: <exception>, <thread>, <regex>, <random> Ben Morgan A Whistlestop Tour of … • <regex> for Pattern Matching of Strings • <random> for Random Number Generation/Distribution • <exception> for Exception Handling •Cleaner and simpler error handling, with a catch… • <thread> and others for Concurrent Computing •Several computations executing at same time Regular Expressions With <regex> Regular Expressions • A Domain Specific Language to describe and perform Pattern Matching within sequences of characters • https://en.wikipedia.org/wiki/Regular_expression • Many use cases, but three major ones • Check if a string matches a given pattern • Find all substrings of a string that match a pattern • Replace matching substrings with another string <regex> • C++11 has the <regex> library with classes and functions for using RegExps in code, with • std::regex class for defining a RegExp • std::regex_match function(s) for matching entire string • std::regex_search function(s) for matching any part of string • std::regex_replace function(s) for replacing matches with another string std::regex #include <regex> int main(int, char**) { std::regex alnumRegex("[A-Z0-9]+"); return 0; } “Sequence of 1 or more characters in the ranges A-Z and 0-9” std::regex_match #include <regex> #include <iostream> int main(int, char**) { std::regex alnumRegex("[A-Z0-9]+"); std::string foo("TH1SMATCH3S"); std::string bar("TH!Sis Bad?"); std::cout << std::regex_match(foo, alnumRegex) << "\n"; std::cout << std::regex_match(bar, alnumRegex) << "\n"; return 0; } Should print 1 then 0 std::regex_replace #include <regex> #include <iostream> “Any character not in the ranges A-Z and 0-9” int main(int, char**) { std::regex removeRegex(“[^A-Z0-9]"); std::string foo(“EATS, SHOOT’S AND LEAVES"); std::cout << std::regex_search(foo, removeRegex, "") << "\n"; return 0; } <regex> • Refer to the cppreference documents for more of using regex: • http://en.cppreference.com/w/cpp/regex • Lots more than can be done, including • Extraction of matching substrings • Different RegExp grammars • Worth learning about Regular Expressions anyway, as they are used extensively in computing! Example https://github.com/mpags-cpp/mpags-cpp-extra Random Numbers With <random> Pseudo-Random Number Generation • Most scientific software makes use of random number generation at some level • https://en.wikipedia.org/wiki/ Random_number_generation • Generally you should use the interfaces provided by the framework of your project to guarantee consistent and reliable behaviour. • If required, C++11 does provide a reasonable set of classes in the <random> library <random> • C++11 divides random number generation into three main areas • Creating seeds (initialization) for the generator • Uniform random number generation in a range • Provides Mersenne Twister and RANLUX “engines” • Random number distributions • Uses output of “engine”, returns random numbers drawn from, e.g. Normal Distribution <random> #include <random> #include <list> #include <iostream> int main(int, char**) { std::random_device seeder; // (May) use hardware to create seed value std::mt19937 engine(seeder()); //Mersenne Twister, with seed from seeder std::normal_distribution<> gauss(1.23, 2.5); // Normal, mu=1, sigma=2.5 std::list<double> data; std::generate_n(std::back_inserter(data), 1000000, [&gauss, &engine](){return gauss(engine);}); double mu {std::accumulate(data.begin(), data.end(), 0.0)/data.size()}; std::cout << "Mean : " << mu << "\n"; return 0; } Error Handling with Exceptions Error Handling in mpags-cipher • In mpags-cipher we had several cases where we needed to handle errors • Bad command line input • Invalid Cipher Key • Errors were indicated using bool returns, but • That doesn’t provide much information on the cause • Can happily ignore the return value… throwing Exceptions • An exception is nothing exceptional - it can be any object that is Copyable or Movable. • Exceptions are created (“raised, thrown”) using the throw keyword followed by the object to “throw” int foo() { … throw true; … return 42; } int main(int, char**) { int answer {foo()}; return 0; } Exception Propagation • A throw results in quite different behaviour to a return • The thrown object is passed “up the stack” of calls until it is handled. • When handled, the stack is “unwound” with destructors of any fully created objects invoked. • If the exception is never handled, it passes out of main, resulting in an immediate termination. • In this case, whether destructors are invoked is implementation defined. Exception Propagation int bar() { BObject b {}; throw true; return 1; } int foo() { AObject a {}; bar(); return 42; } int main(int, char**) { int answer {foo()}; return 0; } Stack Before throw: BObject::BObject() bar() AObject::AObject() foo() main() On Stack Unwind, call BObject::~BObject() AObject::~AObject() catching Exceptions • To handle exceptions, we wrap code that may emit them in a try/catch block. • The catch parts specify the types of exception object this block can handle (any others propagate further) int main(int, char**) { try { somethingThatMightThrow(); } catch (bool& e) { //Catch by reference to avoid slicing std::cout << “Handling bool exception\n”; } catch (int& e) { std::cout << “Handling int exception\n”; } return 0; } <exception> • Header that provides the std::exception base class, plus several generic concrete classes, e.g. • std::logic_error, std::runtime_error • Best to implement exception types specific to the project, e.g. for mpags-cipher, could have struct CommandLineParseError{}; struct InvalidCommandLineArgument{}; struct InvalidCipherKey{}; • In effect, we use the type to decide how to handle the error. Traps and Pitfalls • Though exceptions offer an easy error handling mechanism, their use does require a bit of care because of the stack unwinding • For example, if you’ve new’d an object then throw, the object won’t be deleted (memory leak) • Using Smart Pointers helps here! • Exception Safety: ensuring that an object isn’t corrupted when one of its member functions throws. Further Reading • The two best starting points for Exceptions in C++ are the Super FAQ and Core Guidelines: • https://isocpp.org/faq • https://github.com/isocpp/CppCoreGuidelines • Also see • http://exceptionsafecode.com Example https://github.com/ mpags-cpp/mpags-cppextra Concurrent Programming in C++11 Concurrency • Can no longer rely on processor clock speed for increasing computational throughput - instead, split tasks across N>1 parallel “things” • Several levels of parallelism • SIMD or “vectorization” (on chip) • Multithread/Multicore (single machine) • Multiprocessor (multiple machine) Concurrency in Action • Even something as simple as Web Browser uses concurrency: • You download a file - this happens in a separate thread. • Means you can continue browsing while the file downloads in the background. • The browser may download updates for itself in the background (Chrome for example) Concurrency in C++11 • Prior to C++11, concurrent programming relied on the underlying OS implementation (pthreads on UNIX, CreateThread on Windows) • C++11 introduced the thread support library which provide a cross-platform API hiding the underlying implementation. • Provides all of the main abstractions of multithreading in a series of headers: • http://en.cppreference.com/w/cpp/thread std::async and std::future • C++11 provides both high and low level thread creation/management interfaces (cf new/delete vs make_shared/make_unique for memory) • We’ll only look at the high level interface: • std::async : Takes a function that will be run asynchronously, returns a std::future instance that will hold the result of the function call. • std::future : Wraps result of an asynchronous operation. Provides interface to query, wait for or get result of the operation. Example https://github.com/ mpags-cpp/mpags-cppextra Traps and Pitfalls • Concurrent programming requires more thought because data (Objects) can be shared between threads • For example, what happens if two threads try to add data into the same std::vector instance at the same time? • Since computations may be performed out of sequence, synchronisation may be needed. • The good news is that designing code for concurrency generally results in cleaner and more coherent code! Further Reading • For C++, a good reference is Anthony Wiliams’ book: • For more general guides to structuring concurrent algorithms: