Meeting C++ 2016 Trip report

Disclaimer: alles wie immer sehr subjektiv eingefaerbt.

Nachdem ich letztes Jahr ausgesetzt hatte (genauer: dem Qt World Summit den Vorzug gegeben hatte), habe ich mich dieses Jahr wieder auf den Weg nach Berlin zum Meeting C++ gemacht.

Los ging es am Freitag mit der Keynote von Bjarne Stroustrup. Leicht erkaeltet aber sichtbar gut gelaunt gab es eine Mischung aus Status Update, History und Anekdoten. Gleich im Anschluss machte Bjarne in Vertretung des erkrankten Peter Sommerlad weiter mit einer Einfuehrung in die Core Guidlines und speziell in die GSL.  Man muss Bjarne zugute halten, dass er kurzfristig einsprang, dennoch war es alles in allem ein eher schwacher Vortrag.

Dann erstmal Mittag, dazu kann ich nicht so viel sagen: ich war vom Fruehstueck noch satt.

Im Anschluss habe ich mir dann Nikos Athanasiou angehoert zum Thema “Reduce: From functional programming to C++17 Fold expressions”. Leider ist Nikos im Zeitraffer-Modus durch viel zu viele Slides mit viel zu viel Sourcecode gerauscht. Inhaltlich aber ein schoenes Thema.

Dann ging es zu Odin Holems “Ranges v3 and microcontrollers, a revolution”. Ich persoenlich denke ja, dass Ranges das “next big thing” in C++ werden (koennen). Der Vortrag selber war leider etwas schwach, Odin praesentierte zwar mit viel Humor, bei mir blieb aber der Eindruck haengen, dass von den vielen Ideen die meisten noch ihrer Umsetzung (und damit des Beweises ihrer Funktionalitaet) harren.

Und zum Abschluss noch Phil Nash, der gewohnt professionell und gut strukturiert seine Gedanken zum Thema “Functional C++ for Fun and Profit” praesentierte. Ich muss gestehen, sein string_builder-Konzept kann ich noch nicht so richtig einordnen, umso wertvoller waren seine kleinen Tipps zum funktionalen Initialisieren (IIFE – Achtung, nicht erschrecken: hier anhand von Javascript beschrieben).

Nach 10 Stunden voller Vortraege war ich schon etwas geschafft – man wird nicht juenger. Also erstmal kurz etwas frische Luft und Entspannung und dann zur Party. Die Party erfuellte alle Vorurteile, die Aussenstehende ueber Programmierer hegen: ueberall fanden sich Gruppen zusammen und diskutierten – ja genau: C++ Probleme. Ich wollte frueh ins Bett und maximal bis 10 Uhr bleiben. Als ich das erste Mal auf die Uhr schaute, war es aber schon 11pm durch – sprich: mir hats gefallen.

Samstag startete dann mit “Functional reactive programming in C++” von Ivan Cukic. Der Vortrag hinterliess bei mir einen etwas zwiespaeltigen Eindruck, irgendwie war da nichts bei, was fuer mich neu war und/oder praktischen Nutzen verspricht.

Mehr praktischen Nutzen versprach die “Clang Static Analysis” von Gabor Horvath im Anschluss. Gabor fokussierte zwar mehr auf die Techniken im Hintergrund, gab aber immer auch Hinweise auf die praktische Anwendung. Den Codechecker muss ich mir nochmal genauer anschauen.

Als letzten Vortrag gab es nochmal richtig schwere Kost: “How to test static_assert?” von Roland Bock. Kurz zusammengefasst: wie testet man die Meta-Ebene? Man schreibt Tests auf der Meta-Meta-Meta-Ebene und laesst sie auf der Meta-Meta-Ebene laufen. Ist doch klar. Praktisch anwenden werde ich das wohl eher nicht, aber als Denkuebung hat es Spass gemacht.

Alles in allem zwei schoene Tage, interessante Themen, nette und mindestens genauso interessante Leute, gute Organisation und angenehme Location. Den Rest vom Samstag (total verregnet) und den Sonntag (herrlicher Sonnenschein) habe ich dann noch fuer ein paar Touren durch Berlin genutzt.

Caching

As soon as developer finds a performance problem (or more probably someone else finds a performance problem and informs the developer), he starts thinking about caching. Caching is a great thing regarding performance, and is terrible regarding consistency. Look at the web: it is slow by design, so if you compete with desktop applications, you have to cheat use caching. Which will surely break consistency.

Lets look at the firefox (in version 39).

We use (of course) all this modern stuff like Jira and Confluence, with single-sign-on and Kerberos and all this good things. It works under Linux too. Not that easy, but it is possible to get this running (search for network.negotiate-auth.trusted-uris if you want to know more about this). So just call kinit on the command line to obtain a ticket, and you get sso with firefox. Just in case you made the mistake to start firefox first (before kinit), firefox asks for your login. So you run kinit, get your ticket and refresh this page in firefox. Bad mistake: firefox caches you credentials (in this case it caches the fact you do not have credentials). And there is no other way to clear this cache then restarting firefox.

This can be very funny, as firefox caches your credentials in conjunction with the requested page. So if you are able to access jira, to run all queries, obtain all tickets except the one you have tried first in the morning – yes, that is caching.

The same thing happens with dns problems. Firefox never forgets a failed dns lookup. No way to clear the cache. Just restart the browser. Cross fingers, in most cases firefox can recover all you tabs. If not – live is hard.

Compile time sudoku

The classic approach

Solving a sudoku puzzle is not very hard. Representing the puzzle as a thin wrapper around some kind of container (a std::array in this case), it could look like this:

template<typename T, size_t N>
class SudokuT
{
private:
  std::array<T,N*N> data;
public:
  SudokuT(const std::array<T,N*N>& a) : data(a) {}
  unsigned operator()(size_t row, size_t col) const { return data[row*N+col]; }
  SudokuT<T,N> replace(size_t row, size_t col, T val) {
    SudokuT<T,N> s(*this);
    s.data[row*N+col] = val;
    return s;
  }
  size_t dimension() const { return N; }
};
// the classic 9x9 sudoku
using Sudoku = SudokuT<unsigned, 9ul>;
using Cell = std::pair<size_t, size_t>;

The Sudoku class does not much more than simulating a two-dimensional matrix
over a one dimensional container. It is just for our convenience. It is a read-only class: there is no way to modify an instance, the only way to change fields (or cells) is calling Sudoku::replace to create a new instance containing the change. This allows a clean functional style which is easier to understand (at least for me) and will make it easier to transform the “classic approach” into the compile time solution later on.
We will use the value 0 (zero) to represent the unknown value, these are the cells which should be filled by the solver.

The solving function is just a simple backtracking algorithm:

constexpr Sudoku solve(const Sudoku& s)
{
  if (!valid(s)) return s;
  else {
    Cell n{next(s)};
    if (n.first == std::numeric_limits<Cell::first_type>::max()) return s;
    for(unsigned i=1;i<=s.dimension();++i) {
      auto ns = s.replace(n.first,n.second,i);
      Sudoku res = solve(ns);
      if (finished(res) && valid(res)) return res;
    }
    return s;
  }
}

valid() is a function which checks the validity (soundness) of a given sudoku (which may still contain 0 values), finish() checks the completeness of a given puzzle. The function next() returns a Cell with the coordinates of a Cell containing 0. If next() cannot find a free cell, it will return a cell with invalid coordinates. There are other (and maybe better) ways to check this, for instance calling finished() before ensuring next() can find a result or by throwing an exception in next().
I would not say this is a good solution, but it works (in a sense that valid input produces valid output, as usual all the error checking stuff was ignored). So lets go to the interesting part:

The compile time approach

C++ offers since C++11 the constexpr keyword. It is usable since C++11 and convenient since C++14. The latter added if-statements, and loops to the list of expressions allowed in a constexpr function. This allows us to just add the constexpr keyword to our solve() function:

constexpr Sudoku solve(const Sudoku& s)
{
  if (!valid(s)) return s;
  else {
    const Cell n{next(s)};
    if (n.first == std::numeric_limits<Cell::first_type>::max()) return s;
    for(unsigned i=1;i<=s.dimension();++i) {
      const auto ns = s.replace(n.first,n.second,i);
      const Sudoku res = solve(ns);
      if (finished(res) && valid(res)) return res;
    }
    return s;
  }
}

to turn it into a constexpr-function. As we are creating (modified) copies of our Sudoku class, it has to be constexpr too:

template<typename T, size_t N>
class SudokuT
{
  std::array<T,N*N> data;
public:
  constexpr SudokuT(const std::array<T,N*N>& a) : data(a) {}
  constexpr unsigned operator()(size_t row, size_t col) const {
    return row*N+col >= N*N ? 0
                            : data[row*N+col];
  }
  constexpr SudokuT<T,N> replace(size_t row, size_t col, T val) const;
  constexpr size_t dimension() const { return N; }
};

Fortunately std::array can be constexpr-constructed and has a constexpr-[]-operator. Our access function got an additional out-of-range check, and that is it. With one exception: the replace method becomes a small problem. We have to create the modified Sudoku in one step. As our Sudoku is just a thin wrapper around std::array, the question boils down to: how do we create a copy of an existing array with a value change at one position? What we are looking for is a function like this:

template<typename T, size_t N>
constexpr std::array<T,N> replace(const std::array<T,N>&a, const size_t offset, const T val);

This function should create a copy of a, change a[offset] to val, and return the resulting array. And it should be a constexpr function. It turns out to be a non-trivial problem.

Playing with std::array

std::array is just a wrapper over the old c-style-arrays. It provides not much functionality by itself. But it turns out the be a source of much fun in conjunction with variadic templates. Consider, for instance, a function make_unique_array, which creates an array were all elements have the same value:

template<typename T, size_t N>
constexpr std::array<T,N> make_unique_array(const T val);
auto a = make_unique_array<unsigned,40ul>(42);

std::array a should now contain 40 elements and each of it with value 42. This can be easily implemented using variadic templates and a little helper function:

template<typename T, size_t... Ids>
constexpr std::array<T,sizeof...(Ids)> make_unique_array_impl(const T val, const std::index_sequence<Ids...>)
{
  return { {(static_cast<void>(Ids), val)...} };
}
template<typename T, size_t N>
constexpr std::array<T,N> make_unique_array(const T val)
{
  return make_unique_array_impl(val, std::make_index_sequence<N>());
}

std::make_index_sequence was added in C++14. It creates a variadic template (with N elements), which we use to instantiate the right version of the make_unique_array_impl function. In this function we finally use a parameter pack to create our new array: “(static_cast(Ids), val)…” will be expanded during compilation to (note that Ids in our case is the list 0, 1, 2, …, N): (static_cast(0), val), (static_cast(1), val), …, (static_cast(N), val). The cast is used to just avoid a compiler warning(unused value). The comma operator returns always the last argument, which is “val” in our case. With this little trick we get a list of N elements each with value “val”, which we use to create our array.
The same idea can be used to merge two arrays depending on the result of select-function:

template<typename T, size_t N, typename Sel, size_t... Idx>
constexpr std::array<T,N> merge(const std::array<T,N>&a, const std::array<T,N>&b, Sel sel, std::index_sequence<Idx...>)
{
  return { {sel.select(a[Idx],b[Idx],Idx)...} };
}
template<typename T, size_t N, typename Sel>
constexpr std::array<T,N> merge(const std::array<T,N>&a, const std::array<T,N>&b, Sel sel)
{
  return merge(a,b,sel,std::make_index_sequence<N>());
}

Given two arrays a and b of the same size (N) and type (T), the merge function creates a merge array c of size N and type T where each element c[i] == sel.select(a[i],b[i]).
Note that all functions above are marked as constexpr, thus it can be evaluated at compile time.

The replace

Given the functions above it is finally easy to implement the replace function:

template<typename T>
struct Sel {
  size_t idx;
  constexpr Sel(size_t i) : idx{i} {}
  constexpr T select(T a, T b, size_t index) {
    return index==idx?b:a;
  }
};
template<typename T, size_t N>
constexpr std::array<T,N> replace(const std::array<T,N>&a, const size_t offset, const T val)
{
  return merge(a, make_unique_array<T,N>(val), Sel<T>{offset});
}

Note that Sel is a constexpr class too.
Obviously replace can be simplified further:

template<typename T, size_t N, typename Sel, size_t... Idx>
constexpr std::array<T,N> replace_impl(const std::array<T,N>&a, Sel sel, std::index_sequence<Idx...>)
{
  return { {sel.select(a[Idx],Idx)...} };
}
template<typename T, size_t N, typename Sel>
constexpr std::array<T,N> replace(const std::array<T,N>&a, Sel sel)
{
  return replace_impl(a, sel, std::make_index_sequence<N>());
}
template<typename T, typename S>
struct Selector
{
  T val;
  S offset;
  constexpr Selector(const T& v, const S& o) : val(v), offset(o) {}
  constexpr T select(T a, S index) const {
    return index==offset?val:a;
  }
};

Now we can write our Sudoku-replace:

template<typename T, size_t N>
class SudokuT
{
  ...
  constexpr SudokuT<T,N> replace(size_t row, size_t col, T val) const {
    return {::replace(data,Selector<T,size_t>(val,row*N+col))};};
  }
  ...
};

The complete code is available at github.

Performance

Obviously the constexpr version runs faster, but we have to pay a price at compile time:

Version Compile time (sec) Run time (sec)
Dynamic classic 0.66 2.1
Compile time 720 0.005
Empty 0.005

The Empty version just returns immediately from solve(). This is just to show, that the runtime of the constexpr version is mainly caused by the output of the result.

Conclusion

There are two important (at least for me) results. The first one is: compile time calculation is very funny. The second one: one should think very careful if and where it is useful. It increases compile time dramatically. And there is (at least at the moment) no possibility to debug this code. Please do not forget the alternatives just because constexpr are so funny: “Write programs which write programs” (I do not remember the author). If you need some special values in your program, just write a program which calculates these values and generates an include file. Place this program in your build toolchain. Or run some initialization code at startup which fills lookup tables. All these techniques are well established, easy to maintain. Use constexpr only if you can prove, it makes sense (or if it is trivial to implement). Or if you have enough time and need some fun.