C++11 talk examples

On 2014-11-27 I gave a talk about C++ and new features introduced with C++11: these are the examples. They are all licensed under the wtfpli version 2. See C++11 talk notes for the talk notes.

Note that the wrapper interfaces turns errors from the underlying libraries into exceptions, so the method calls just do what they should, without the need of documenting special return values for error messages, and removing the need for each library to implement yet another way of reporting errors.

Also note that all wrapper objects do RAII: you create them and they clean after themselves when they go out of scope.

The wrapper objects also have cast operators to make them behave as the pointer or handle that they are wrapping, so that they can be transparently passed to the underlying libraries.

(note: I had to add U+2063 INVISIBLE SEPARATOR to prevent noreturn statements to be misinterpreted by the blog formatter. If you copypaste the code and encounter issues, you may want to delete the noreturn statements and retype them)

A gcrypt hash class

This class is a light wrapper around gcrypt's hashing functions.

ezhash.h

#ifndef EZHASH_H
#define EZHASH_H

#include <string>
#include <gcrypt.h>

namespace ezhash {

class Hash
{
protected:
    // members can now be initialized just like this, without needing to repeat
    // their default assignment in every constructor
    gcry_md_hd_t handle = nullptr;

public:
    Hash(int algo, unsigned int flags=0);
    ~Hash();

    // Assign 'delete' to a method to tell the compiler not to generate it
    // automatically. In this case, we make the object non-copiable.
    Hash(const Hash&) = delete;
    Hash(const Hash&&) = delete;
    Hash& operator=(const Hash&) = delete;

    // Add a buffer to the hash
    void hash_buf(const std::string& buf);

    // Add the contents of a file to the hash
    void hash_file(int fd);

    // Get a string with the hexadecimal hash
    std::string read_hex(int algo=0);

    /// Pretend that we are a gcry_md_hd_t handle
    operator gcry_md_hd_t() { return handle; }
};

}

#endif

ezhash.cpp

#include "ezhash.h"
#include <unistd.h>
#include <errno.h>
#include <string>
#include <cstring>
#include <sstream>
#include <iomanip>
#include <stdexcept>

using namespace std;

namespace ezhash {

namespace {

// noreturn attribute, to tell the compiler that this function never returns
[[noreturn]] void throw_gcrypt_error(gcry_error_t err)
{
    string msg;
    msg += gcry_strsource(err);
    msg += "/";
    msg += gcry_strerror(err);
    throw runtime_error(msg);
}

string errno_str(int error)
{
    char buf[256];
#if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE
    strerror_r(errno, buf, 256);
    string res(buf);
#else
    string res(strerror_r(errno, buf, 256));
#endif
    return res;
}

[[noreturn]] void throw_libc_error(int error)
{
    throw runtime_error(errno_str(error));
}

}


Hash::Hash(int algo, unsigned int flags)
{
    gcry_error_t err = gcry_md_open(&handle, algo, flags);
    if (err) throw_gcrypt_error(err);
}

Hash::~Hash()
{
    gcry_md_close(handle);
}

void Hash::hash_buf(const std::string& buf)
{
    gcry_md_write(handle, buf.data(), buf.size());
}

void Hash::hash_file(int fd)
{
    char buf[4096];
    while (true)
    {
        ssize_t res = ::read(fd, buf, 4096);
        if (res < 0) ezfs::throw_libc_error();
        if (res == 0) break;
        gcry_md_write(handle, buf, res);
    }
}

std::string Hash::read_hex(int algo)
{
    unsigned char* res = gcry_md_read(handle, algo);

    unsigned int len = gcry_md_get_algo_dlen(
            algo == 0 ? gcry_md_get_algo(handle) : algo);

    // Format the hash into a hex digit
    stringstream hexbuf;
    hexbuf << hex << setfill('0');
    for (unsigned i = 0; i < len; ++i)
        hexbuf << setw(2) << (unsigned)res[i];

    return hexbuf.str();
}

}

Example usage

        ezhash::Hash sha256(GCRY_MD_SHA256);
        sha256.hash_buf("ciao\n");
        sha256.hash_buf("foo\n");
        cout << sha256.read_hex() << endl;

Simple sqlite bindings

Remarkably simple sqlite3 bindings based on lambda callbacks.

ezsqlite.h

#ifndef EZSQLITE_H
#define EZSQLITE_H

#include <sqlite3.h>
#include <string>
#include <functional>
#include <stdexcept>

namespace ezsqlite {

/// RAII wrapper around a sqlite3 database handle
class DB
{
protected:
    sqlite3* handle = nullptr;

public:
    // Open a connection to a SQLite database
    DB(const std::string& filename, int flags=SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
    DB(const DB&) = delete;
    DB(const DB&&) = delete;
    DB& operator=(const DB&) = delete;
    ~DB();

    /**
     * Execute a query, optionally calling 'callback' on every result row
     *
     * The arguments to callback are:
     *  1. number of columns
     *  2. text values of the columns
     *  3. names of the columns
     */
    // std::function can be used to wrap any callable thing in C++
    // see: http://en.cppreference.com/w/cpp/utility/functional/function
    void exec(const std::string& query, std::function<bool(int, char**, char**)> callback=nullptr);

    /// Pretend that we are a sqlite3 pointer
    operator sqlite3*() { return handle; }
};

}

#endif

ezsqlite.cpp

#include "ezsqlite.h"

namespace ezsqlite {

DB::DB(const std::string& filename, int flags)
{
    int res = sqlite3_open_v2(filename.c_str(), &handle, flags, nullptr);
    if (res != SQLITE_OK)
    {
        // From http://www.sqlite.org/c3ref/open.html
        // Whether or not an error occurs when it is opened, resources
        // associated with the database connection handle should be
        // released by passing it to sqlite3_close() when it is no longer
        // required.
        std::string errmsg(sqlite3_errmsg(handle));
        sqlite3_close(handle);
        throw std::runtime_error(errmsg);
    }
}

DB::~DB()
{
    sqlite3_close(handle);
}

namespace {

// Adapter to have sqlite3_exec call a std::function
int exec_callback(void* data, int columns, char** values, char** names)
{
    std::function<bool(int, char**, char**)> cb = *static_cast<std::function<bool(int, char**, char**)>*>(data);
    return cb(columns, values, names);
}

}

void DB::exec(const std::string& query, std::function<bool(int, char**, char**)> callback)
{
    char* errmsg;
    void* cb = callback ? &callback : nullptr;
    int res = sqlite3_exec(handle, query.c_str(), exec_callback, cb, &errmsg);
    if (res != SQLITE_OK && errmsg)
    {
        // http://www.sqlite.org/c3ref/exec.html
        //
        // If the 5th parameter to sqlite3_exec() is not NULL then any error
        // message is written into memory obtained from sqlite3_malloc() and
        // passed back through the 5th parameter. To avoid memory leaks, the
        // application should invoke sqlite3_free() on error message strings
        // returned through the 5th parameter of of sqlite3_exec() after the
        // error message string is no longer needed.
        std::string msg(errmsg);
        sqlite3_free(errmsg);
        throw std::runtime_error(errmsg);
    }
}

}

Example usage

    // Connect to the database
    ezsqlite::DB db("erlug.sqlite");

    // Make sure we have a table
    db.exec(R"(
        CREATE TABLE IF NOT EXISTS files (
                name TEXT NOT NULL,
                sha256sum TEXT NOT NULL
        )
    )");

    // Read the list of files that we know
    map<string, string> files;
    db.exec("SELECT name, sha256sum FROM files", [&](int columns, char** vals, char** names) {
        if (columns != 2) return false;
        files.insert(make_pair(vals[0], vals[1]));
        return true;
    });

A fast Directory object

This is a lightweight wrapper around O_PATH file descriptors for directories. I'd love to see a library of well-maintained and thin C++ bindings around libc, that do little more than turning errors into exceptions and making it also work with std::string buffers.

ezfs.h

#ifndef EZFS_H
#define EZFS_H

#include <string>
#include <functional>
#include <memory>
#include <cerrno>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>

namespace ezfs {

class Directory
{
protected:
    int handle = -1;

public:
    Directory(const std::string& pathname, int flags=0);
    ~Directory();
    Directory(const Directory&) = delete;
    Directory(const Directory&&) = delete;
    Directory& operator=(const Directory&) = delete;

    /// List the directory contents
    void ls(std::function<void(const dirent&)> callback);

    int open(const std::string& relpath, int flags, mode_t mode=0777);
};

std::string errno_str(int error=errno);
[[noreturn]] void throw_libc_error(int error=errno);

}

#endif

ezfs.cpp

#include "ezfs.h"
#include <stdexcept>
#include <memory>
#include <cstring>
#include <cstdlib>
#include <string>
#include <linux/limits.h>

using namespace std;

namespace ezfs {

string errno_str(int error)
{
    char buf[256];
#if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE
    strerror_r(errno, buf, 256);
    string res(buf);
#else
    string res(strerror_r(errno, buf, 256));
#endif
    return res;
}

[[noreturn]] void throw_libc_error(int error)
{
    throw runtime_error(errno_str(error));
}

Directory::Directory(const std::string& pathname, int flags)
{
    handle = ::open(pathname.c_str(), O_PATH | O_DIRECTORY | flags);
    if (handle < 0) throw_libc_error();
}

Directory::~Directory()
{
    ::close(handle);
}

void Directory::ls(std::function<void(const dirent&)> callback)
{
    int fd = openat(handle, ".", O_DIRECTORY);
    if (fd < 0) throw_libc_error();

    // RAII Self-cleaning DIR object
    unique_ptr<DIR, std::function<void(DIR*)>> dir(fdopendir(fd), [](DIR* dir) { if (dir) closedir(dir); });
    if (!dir)
    {
        // fdopendir(3): After a successful call to fdopendir(), fd is used
        // internally by the implementation, and should not otherwise be used
        // by the application.
        //
        // but if the fdopendir call was not successful, fd is not managed by
        // DIR, and we still need to close it, otherwise we leak a file
        // descriptor.
        //
        // However, close() may modify errno, so we take note of the errno set
        // by fdopendir and raise the exception based on that.
        int fdopendir_errno = errno;
        close(fd);
        throw_libc_error(fdopendir_errno);
    }

    // Size the dirent buffer properly
    const unsigned len = offsetof(dirent, d_name) + PATH_MAX + 1;
    unique_ptr<dirent, std::function<void(void*)>> dirbuf((dirent*)malloc(len), free);

    while (true)
    {
        dirent* res;
        int err = readdir_r(dir.get(), dirbuf.get(), &res);

        // End of directory contents
        if (err == 0)
        {
            if (res)
                callback(*res);
            else
                break;
        } else
            throw_libc_error(err);
    }
}

int Directory::open(const std::string& relpath, int flags, mode_t mode)
{
    int res = openat(handle, relpath.c_str(), flags, mode);
    if (res < 0) throw_libc_error();
    return res;
}

}

Example usage

        // This is almost the equivalent of running "sha256sum ."
        ezfs::Directory dir(".");
        dir.ls([&](const dirent& d) {
            if (d.d_type != DT_REG) return;

            ezhash::Hash sha256(GCRY_MD_SHA256);
            // I have no RAII wrapper around file handles at the moment, so
            // I'll have to use a try/catch for cleaning up after errors
            int fd = dir.open(d.d_name, O_RDONLY);
            try {
                sha256.hash_file(fd);
                close(fd);
            } catch (...) {
                close(fd);
                throw;
            }

            cout << sha256.read_hex() << "  " << d.d_name << endl;
        });