Enrico Zini: cxx11-talk-examples
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 cxx11-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
ezhash.cpp
Example usage
Simple sqlite bindings
Remarkably simple sqlite3 bindings based on lambda callbacks.
ezsqlite.h
ezsqlite.cpp
Example usage
A fast Directory object
This is a lightweight wrapper around
ezfs.cpp
Example usage
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
#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();
ezhash::Hash sha256(GCRY_MD_SHA256);
sha256.hash_buf("ciao\n");
sha256.hash_buf("foo\n");
cout << sha256.read_hex() << endl;
#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
#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);
// 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;
);
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
#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;
// 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;
);
I was unable to find a good place that describes how to create a simple
self-hosted email setup. The most surprising discovery was, how much already
works after:
The
On November 14, as a great way to say goodbye to a semester, a good friend came to my class again to present a topic to the group; a good way to sum up the contents of this talk is "everything you ever wondered about persistent storage".
As people who follow my blog know, I like inviting my friends to present selected topics in my Operating Systems class. Many subjects will stick better if presented by more than a single viewpoint, and different experiences will surely enrich the group's learning.
So, here is Rolando Cedillo A full gigabyte of him, spawning two hours (including two hiccups where my camera hit a per-file limit...).
Rolando is currently a RedHat Engineer, and in his long career, he has worked from so many trenches, it would be a crime not to have him! Of course, one day we should do a low-level hardware session with him, as his passion (and deep knowledge) for 8-bit arcades is beyond any other person I have met.
So,
A
Ever done the following?

You may have heard that Uber has been under a bit of fire lately for its
desires to
Propellor has supported docker containers for a "long" time, and it works
great. This week I've worked on adding more container support.
docker containers (revisited)
The syntax for docker containers has changed slightly. Here's how it looks
now: