I’m back home in London after C++Now 2017. Besides experimenting on personal projects and playing Hollow Knight, I think that putting together the notes I’ve scribbled down during the conference into a coherent article is a good use of my time. I hope you’ll find some of my thoughts interesting!
background
In case you’ve never heard about it before, C++Now is a gathering of C++ experts in a beautiful (and expensive!) location in Aspen, CO. In constrast to other “more mainstream” conferences like CppCon and Meeting C++, most of the content is intended for advanced and expert code wizards.
One thing I loved about this year is the theme of the keynotes: other languages. The three talks were about Rust, Haskell and D. I find it very bold to have presentations on different languages at a C++ conference, especially when they’re keynotes! This shows a level of open-mindedness, courage, and desire to make C++ and its users richer by taking inspiration from others - I feel glad to be part of this community.
I also spoke at the conference this year, with a slightly improved and lengthened version of the talk I gave at ACCU 2017: Implementing variant visitation using lambdas. The video is not yet available, but you can watch the ACCU version here on YouTube.
By the way, the social aspect of this conference is purely amazing: I felt like being part of a family reunion in a breathtaking venue. I have similar feelings when participating to other C++ conferences, but there’s something about C++Now that’s truly unique.
Enough rambling - let’s get to my thoughts on my favorite sessions.
tuesday, may 16
Rust: Hack Without Fear! (by Niko Matsakis)
After Jon Kalb’s warm welcome to the attendees and after the yearly Library in a Week planning presentation by Jeff Garland, the crowd of C++ experts was introduced to Rust by Niko Matsakis.
The keynote primarily focused on the safety of Rust, which is one of the dramatic differences from other system programming languages. Through the use of well-presented real-world examples Niko explained:
Memory safety without garbage collection: Rust achieves this by having a compile-time strong ownership model. Things like dangling references are impossible in Rust because the compiler is able to track lifetimes and ownership.
Concurrency without data races: the claim is that it is impossible to write code with a data race in Rust. This is done by preventing contemporaneous aliasing and mutability at compile-time.
Abstraction without overhead: one of the similarities between C++ and Rust is their love for “zero-cost abstractions”: Rust provides trait-based generics which are very powerful and do not introduce any unwanted run-time overhead. They kind of resemble Haskell type classes or C++0x concepts.
I had played around with Rust before the keynote and was aware of all the features Niko mentioned - hopefully it’s needless to say that I think they’re extremely useful and a step forward towards safer and saner system programming.
The keynote however didn’t cover many of Rust’s other interesting features such as macros or its great ecosystem. One of the things I’ve been thoroughly impressed by is
cargo
and its ease of use. I can literally clone any Rust project from GitHub and run it by typingin the terminal. All dependencies will be automagically downloaded and installed.
cargo
has been my most pleasant package management experience so far!While I understand that it is impossible to cover all the nice features of Rust in one hour and an half, I was disappointed by the fact that Niko didn’t mention any of the current issues with the languages and potential reasons that might drive away C++ developers.
I feel like there are many missing features that make generic programming much easier:
Overloading doesn’t exist in Rust, but it is possible to emulate it through the use of traits. This often results in a lot of boilerplate just to produce a function that behaves slightly differently for a small subset of types but whose semantics are excellently captured by a single name.
There are no variadic templates and no type-level integers. This is a huge huge step backwards from what C++ offers, but thankfully it’s being worked on as you read this blog post. The lack of these features drives me crazy because I really enjoy generic programming and metaprogramming and I feel like I’m being limited by the language here. Look at Rust’s
std::tuple
implementation: it’s copy-paste madness! This also means that implementing something like asmall_vector<N>
with heap-allocation fallback is currently impossible without macros or code generation.There is no automatic return type deduction. I’m not sure, but I feel like this is probably by-design or unfeasible due to the complexity and power of Rust’s type system… I however must admit that I’ve been spoiled by C++’s
auto
anddecltype(auto)
return type deduction and I find them really convenient when the return type is obvious from the function name and the surrounding context or when it depends on the input arguments. Thankfully something calledimpl Trait
is available in unstable versions of Rust, which greatly improves the generic programming experience.The code snippet above defines a function called
something
that takes a genericT
argument and returns anything that is anIterator
.impl Trait
is close enough to automatic return type deduction for me and I look forward to see it in stable Rust soon.The ownership/borrowing system is not perfect. There are some tasks that should be straightforward, but that are impossible without
unsafe
blocks. An example is swapping two array elements - the compiler prevents you from doing this because the array is being mutably aliased twice, even though the elements are completely separate!This code…
…will produce the following error:
error[E0499]: cannot borrow `xs[..]` as mutable more than once at a time --> <source>:3:37 | 3 | std::mem::swap(&mut xs[0], &mut xs[1]); | ----- ^^^^^- first borrow ends here | | | | | second mutable borrow occurs here | first mutable borrow occurs here
(By the way, Rust’s error messages are awesome!)
Overall I liked this keynote, but I felt like it could have gone more in depth as the audience was full of C++ experts.
Rethinking strings (by Mark Zeren)
This was a nice and interesting report on how the presenter revamped the use of strings at his company, VMWare. The key takeways for me were the following:
std::string_view
is awesome when ownership is not required and should be liberally used.The general consensus in the room for compile-time string constant definition was
constexpr std::string_view{"..."}
, as I once suggested on /r/cpp. This is great as it doesn’t have any overhead compared toconst char*
but provides a way better interface that interoperates well withstd::string
.std::string
is way too general. We would benefit in terms of readability, semantics and performance by multiple string types:unique_string
,shared_string
,fixed_string<N>
,small_string<N>
. I really like this type-based approach as it gives developers flexibility and API users clarity.Having “builders” for common operations such as
cat(a, b, c, ...)
andfmt(str, a, b, c, ...)
is a good idea as they can nowadays be implemented very efficiently thanks to variadic templates andconstexpr
. I think that it would be great to have new standard formatting and concatenation facilities that compute as much as possible during compilation.
The talk ended with Mark showing us some code from his toy
rethinking-strings
library, which can be found here on GitHub. I strongly recommend checking it out as there are many interesting ideas in there.Expression Templates Everywhere with C++14 and Yap (by Zach Laine)
Zach presented the usage and implementation of Yap, a C++14 library proposed to Boost which aims to cover the same problem space as Boost.Proto in a nicer and more efficient way thanks to features introduced in the latest standards.
In short, it’s an “expression template generator” that allows developers to easily create rich and powerful expression template trees. Zach used Boost.Hana to implement Yap and presented many interesting ideas and challenges encountered during his work.
This was overall a solid and interesting presentation and I recommend checking out the recording when its available online if you’re interested in expression templates.
I’m honestly curious whether or not it would be feasible to use Yap as a building blocks for
range-v3
…constexpr ALL the things! (by Ben Deane and Jason Turner)
This talk blew my mind! The goal was to implement fully-
constexpr
JSON literals:Ben and Jason presented the very clever design and implementation of a fully-
constexpr
JSON parser step-by-step. Starting from aconstexpr
string, they implemented a vector, a map, a mutable string, and a fully-constexpr
parser framework!By using
constexpr
“parser combinators” they managed to achieve the initial goal and optimize compilation times enough to make it actually useable.A must watch.
-
At the end of the day we had a nice picnic with friends and family. Burgers and chicken were cooked on the BBQ, and we also had ice cream!
Social events like this are great examples of why C++Now really feels like a family gathering: it is amazing to share good food and drinks in a beautiful environment while talking about hardcore C++ metaprogramming.
wednesday, may 17
Haskell taketh away: limiting side effects for parallel programming (by Ryan Newton)
The Haskell keynote woke us up after the previous night spent at the bar. Ryan defined Haskell as a “research project that escaped the lab” and explained how the languages makes it possible to define convienient and safe parallel programming abstractions.
The main idea is that Haskell is able to limit a function’s side effects through the type system - C++ is unfortunately unable to do this. By defining multiple monads that limit operations it is possible to define safer abstractions over shared memory and parallel programming.
I found the ideas and work presented during the keynote really impressive, but I honestly would have liked to see more contents/techniques that could be applied in an useful way in C++ development. Regardless, I recommend watching the recording if you’re interested in functional programming, Haskell, or parallel programming.
A vision for C++20, and std2 (part 1 of 3) (by Alisdair Meredith)
While I was only able to attend a single session of the 3-part presentation/workshop hybrid by Alisdair, I still found it really valuable and engaging. The experience I had was a prime example of something that makes C++Now really great: audience interaction.
Pretty much everybody in the audience is an expert during the conference, and presenters welcome live discussion and critique during the talks. Alisdair covered many of the upcoming major features (e.g modules, coroutines, concepts, …), mentioning their benefits and current potential issues. The audience<->presenter audience<->audience debate that kept going on during the talk excellently pointed out details/advantages/drawbacks in the upcoming features that people might not think about - hopefully the recording will do justice to this session.
One particular instance of disagreement that I care about is normal form in the Concepts TS. In the code snippet below
the arguments
a
andb
of the function must have the same type. I find this extremely counterintuitive and detrimental to the widespread adoption of normal form. I want to naturally read the above definition as: “foo
takes an argumenta
that satisfiesSomeConcept
and an argumentb
that satisfiesSomeConcept
”. Forcing the types to be equal feels weird to me and can be already done by using more verbose syntax.Alisdair proposed a disambiguation syntax which I can get behind:
While it would be useful to tersely force some arguments’ types to be the same, I still strongly believe that the default should be “any type that satisfies the concept”. I unfortunately don’t have any formal argument for this, but it bothers me (and many others) immensely and I think there should be more discussion about this before the TS makes it into the Working Draft.
The Mathematical Underpinnings of Promises in C++ (by David Sankel)
This very interactive and enjoyable talk tried to answer the following question: "what is the mathematical essence of a promise?"
David introduced the audience to the concepts of “denotational semantics” and “operational semantics”, which are formal ways of reasoning about the “meaning” of programming language semantics by the use of logical mathematical expressions. It was really interesting to see how mathematical notation can be used to express the “meaning” of promises and to reason about it.
The presenter also made sure to make the audience understand how important it is to “discover” structures such as functors, applicatives, and monads during the design of an abstraction - doing that guarantees that the abstraction is powerful and flexible enough for many use cases.
The general idea is that developers should first reason about the mathematical essence of an abstraction, implement it, and “rinse&repeat” until it is refined and poweful enough. I believe that this approach is very valuable, and David proved it in his second, more practical talk: “Promises in C++: The Universal Glue for Asynchronous Programs”. The second presentation covered his implementation of promises which looked solid and useful, making a case for the “mathematical essence” approach described above.
I highly recommend watching the recording of David’s first presentation.
Postmodern Immutable Data Structures (by Juan Pedro Bolivar Puente)
This is another presentation that I found mindblowing. Juan presented the concepts that power
immer
, a “C++ library implementing modern and efficient data immutable data structures” and demoedewig
, a text editor implemented withimmer
, built around an immutable data model.He first began praising value semantics for being easy to reason about and multithreading-friendly. Unfortunately, a program that mainly deals with values is unfeasible as it requires copying data everywhere, which becomes a big problem when the amount of data increases.
The solution to the “copy problem” is “immutable persistent data structures”.
They are immutable because they’re always
const
(i.e. adding an element to an immutable data structure produces a copy of the existing structure with the added element).They are persistent because they preserve their history. This leads to “structural sharing”:
No copies of the real data are required, as the history is always available and can be shared between instances. The history itself is compact thanks to the sharing.
Comparisons become extremely fast as it is enough to perform cheap pointer comparisons for instances of structures that are known to share their internal representation.
A very basic immutable data structure is the list… which is very bad in practice due to cache-unfriendliness and lack of random access.
Juan said that what we really want is some sort of “immutable
std::vector
”, which has all the benefits of persistent immutable data structures but the cache-friendliness of a vector. The idea is creating some sort of tree structure where the leaves are chunks of contiguous data: this is the “radix balanced tree” by P. Bagwell and T. Rompf.I found the concept really interesting and Juan’s implementation very scary!
After explaining variations of the aforementioned radix tree data structures and "transient data structures" he showed a live demo of
ewig
where he loaded a 1GB text file containing all the content of the Esperanto version of Wikpedia, selected all the test and copy-pasted it multiple times in the middle of the editor… without any kind of slowdown. I found that really impressive and realized that immutable persistent data structures are something I should learn more about and put in my multi-paradigm C++ developer toolbox.A must watch.
Type Based Template Metaprogramming is Not Dead (by Odin Holmes)
Now that Boost.Hana is here, “traditional” type-based metaprogramming is dead… right? Odin’s presentation proves that’s not really true if high performance is a requirement and heterogeneous computation are unneeded.
He’s one of the authors of
Kvasir::mpl
, the fastest metaprogramming library onmetaben.ch
!Kvasir is blazing fast because it follows the “Rule of Chiel”, created by another author of the library: Chiel Douwes. Chiel heavily benchmarked various operations on multiple compilers on machines with custom barebones kernels to figure out what are the least and most expensive metaprogramming techniques for compilers. In short:
Type aliases are very fast.
Type instantiations are kind of slow.
Template functions are very slow.
SFINAE is extremely slow.
This hierarchy of “metaprogramming technique performance” makes it obvious that instantiations should be avoided as much as possible in performance critical compile-time projects… such as type-based metaprogramming libraries. Odin showed how many existing ideas such as metafunction compositions can actually be implemented with minimal instantiations - I honestly found the ideas and the results very impressive but wished he carefully explained the techniques and code snippets more slowly and step-by-step, as I found them quite unfamililar.
This is a talk I will definitely watch again when it becomes available on YouTube as I really want to understand the amazing ideas behind Kvasir and its extremely impressive performance.
Lightning Talks (organized and moderated by Michael Caisse)
I loved this year’s lightning talk so much! There were so many different and varied talks which were extremely funny or valuable (or both!) despite their short duration. Many thanks to Michael Caisse for moderating these sessions and making them possible.
I also won “third best lightning talk” with my short presentation: “You must type it three times”. I showed the audience that building a
constexpr
/noexcept
/SFINAE-friendly library higher-order function is extremely painful even in C++17, as it is often required to manually repeat the body of the function three times to achieve the aforementioned benefits. E.g.template <typename F, typename... Ts> constexpr auto log_and_call(F&& f, Ts&&... xs) noexcept(noexcept( std::forward<F>(f)(std::forward<Ts>(xs)...) )) -> decltype( std::forward<F>(f)(std::forward<Ts>(xs)...) ) { log << "calling `f`\n"; return std::forward<F>(f)(std::forward<Ts>(xs)...); }
I encourage you to look at the slides if you’re interested in the issue, and to share your ideas for possible solutions.
is this the end?
I didn’t originally plan to split this in two parts but I found that understanding my notes, recalling interesting moments during the sessions, and packaging everything into a coherent report takes a lot of time! Hopefully you found the first part of this trip report interesting - I hope to have the second (and final) part up on my blog as soon as possible.