Jonathan Boccara's blog

How C++17 Benefits from Boost Libraries, Part Two

Published November 22, 2019 - 0 Comments

boost C++17

Today we have a second guest post by Bartlomiej Filipek. Bartek is a C++ programmer, blogger and author. You can find him on LinkedIn or his blog and also read his book.

Last time in our series about Boost and C++17 we covered several features: std::optional, std::variant, std::any and string_view.

This time we’ll go through more elements: std::filesystem searchers and more! We’ll also have a glimpse look on C++20.

Let’s start!

The Series

Intro

As you know, Boost libraries give us a vast set of handy algorithms, types and features that we don’t have in the Standard Library. Many functionalities were “ported” into core C++. For example, in C++11 we got std::regex, threading and smart pointers.

For C++17 we can list the following features that were adopted from Boost:

  • vocabulary types, std::variant, std::any, std::optional
  • string_view
  • searchers – Boyer Moore and Boyer Moore Horspool
  • std::filesystem
  • special math functions
  • template enhancements

We covered the first two points from the list, so let’s now focus on the remaining sections.

Searchers

As Jonathan wrote in his second part of the searchers series, Boost offers three pattern searching algorithms:

  • the Knuth-Morris-Pratt algorithm,
  • the Boyer-Moore algorithm,
  • the Boyer-Moore-Horspool algorithm.

All of the algorithms beat the naive pattern searching for large strings by using a preprocessing step. They build additional tables based on the input pattern, and the search is more efficient.

The last two of those algorithms were ported into C++17, and they are available as an additional searcher object for the std::search function.

Right now, C++17 provides a new overload for std::search:

template<class ForwardIterator, class Searcher>
ForwardIterator search( ForwardIterator first, ForwardIterator last,
                        const Searcher& searcher );

The Searcher is a template parameter (so you can even come up with your implementation!), and the library offers three types:

  • default_searcher
  • boyer_moore_searcher
  • boyer_moore_horspool_searcher

All in all you can use it like:

std::string testString = "Hello Super World";
std::string needle = "Super";
auto it = search(testString.begin(), testString.end(),
                    boyer_moore_searcher(needle.begin(), needle.end()));
if (it == testString.end())
    cout << "The string " << needle << " not found\n";

The searcher object is created once for each pattern. If you want to search the same text in different containers, then you can save a bit of preprocessing time.

On my blog, I did some performance experiments, and it looks like for larger patterns and boyer_moore we can achieve much better performance than with a default searcher. For example, when scanning inside text with 547412 characters, and looking for a 200-letter pattern, I got 8x perf speedup over the default searcher. And even 3x perf over optimised std::string::find.

If you want more about the searchers, with even some basic benchmarks you can have a look here: Speeding up Pattern Searches with Boyer-Moore Algorithm from C++17.

Filesystem

This is a massive addition to C++17 and The Standard Library. The committee took years of experience with boost::filesystem improved it, proposed a technical specification and later merged into the Standard.

As the canonical example, let’s have a look at the directory iteration from Boost:

#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;

fs::path inputPath = GetInputPath();
for (const auto& entry : fs::directory_iterator(inputPath))
    std::cout << entry.path() << '\n';

And now, the C++17’s version (with extra code that prints the type of the file):

#include <filesystem>
namespace fs = std::filesystem;

fs::path inputPath = GetInputPath();
for (const auto& entry : fs::directory_iterator(inputPath)) {
    std::cout << entry.path() << '\n';

Do you see any difference? 🙂 The code is almost the same as in Boost!

We can even extend it a bit and add more logging:

#include <filesystem>

namespace fs = std::filesystem;

for (const auto& entry : fs::directory_iterator(inputPath)) {
    const auto filenameStr = entry.path().filename().string();
    if (entry.is_directory()) {
        std::cout << "dir:  " << filenameStr << '\n';
    }
    else if (entry.is_regular_file()) {
        std::cout << "file: " << filenameStr << '\n';
    }
    else
        std::cout << "??    " << filenameStr << '\n';
}

As you can see, in the above code we can efficiently work with path objects, run the iteration over a directory (recursive or not) and print various information about the given directory entry.

The filesystem library is composed of four main parts:

  • The path object – a type that represents a path in the system. With various methods to extract the path parts, compose it, convert between formats and even from string to wide string.
  • directory_entry – holds information about the path that is inside some directory, plus cache
  • Directory iterators – two classes that allow you to scan a directory: just once or recursively.
  • Plus many supportive non-member functions:
    • getting information about the path
    • files manipulation: copy, move, create, symlinks
    • last write time
    • permissions
    • space/filesize

The library is enormous, and I hope it will be beneficial for applications that rely on file access (and which app doesn’t have to work with files?)

On my blog, I published one article by a guest author who described his process of moving from boost::filesystem into std::filesystem. Check it out if you also need to convert some of your file handling code.

Bartek’s coding blog: Converting from Boost to std::filesystem

Special Math Functions: clamp, gcd and More

The Boost libraries offer lots of algorithms and functions that help with even advanced math calculations.

For example, there’s a whole Math Toolkit 2.9.0 – 1.70.0 module with almost everything you can expect from a math library.

The C++17 Standard extended the library with a few extra functions.

We have a simple functions like clamp , gcd and lcm:

#include <iostream>
#include <algorithm>  // clamp
#include <numeric>    // for gcm, lcm

int main() {
    std::cout << std::clamp(300, 0, 255) << ', ';   
    std::cout << std::clamp(-10, 0, 255) << '\n'; 
    std::cout << std::gcd(24, 60) << ', ';
    std::cout << std::lcm(15, 50) << '\n';    
}

And, also there’s a set of special math functions: assoc_laguerre, beta, comp_ellint_1/_2/_3,  hermite, laguerre, riemann_zeta and a few others.

The full list of those special math function can be found at Mathematical special functions – @cppreference.

Template Enhancements – and, or, not

P0013 proposes to add the metafunctions and_, or_ and not_ to the standard library and cites Boost.MPL as one of the standard libraries having implemented such features for a long time. The paper was adopted in C++17 as std::conjunction, std::disjunction and std::negation.

Here’s an example, based on the code from the proposal:

template<typename... Ts>
std::enable_if_t<std::conjunction_v<std::is_same<int, Ts>...> >
PrintIntegers(Ts ... args) { 
    (std::cout << ... << args) << '\n';
}

The above function PrintIntegers works with a variable number of arguments, but they all have to be of type int.

A Glimpse of C++20

As you might already know in C++20 we’ll get Ranges and Concepts… but did you know that an earlier version was also available in Boost?

Here’s a link to the Ranges library Boost Range 2.0

And now while the Concepts in C++20 are part of the language, you can simulate them with The Boost Concept Check Library:

The library is heavily based on macros, but you could get some outline about generic programming and what we might want to achieve with Real concepts.

Summary

I hope with this blog post I gave you more incentives to start using C++17 :). The last C++ standard offers not only many language features (like if constexpr, structured bindings, fold expressions…), but also a broad set of utilities from the Standard Library. You can now use many vocabulary types: variant, optional, any. Use string views and even a significant component: std::filesystem. All without the need to reference some external library.

Your Turn

  • What are your favourite features from Boost that you use?
  • Maybe they will also be merged into the Standard?
  • Have you ported some boost code into C++17 (and its corresponding feature-set)?

Share your experience in comments.

You will also like

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin