Reacting to Dropbox: another take on cross-platform C++ development
Théo DELRIEU
Posted on September 4, 2019
A few weeks ago, Dropbox announced in a blog post that they had decided to drop C++ from their codebase. This article sparked off a number of reactions, especially on the C++ subreddit.
I suggest that you read that first, but here is a TL;DR:
In 2013 Dropbox started to use C++ to share mobile code between iOS and Android, the goal being to avoid having to write it twice. 6 years later, they made a U-turn because of the overhead of:
Custom frameworks and libraries
A customized development environment
Differences between Mobile platforms
Finding and keeping experienced C++ developers
These are all valid points. At Tanker, we had to face similar challenges with our cross-platform end-to-end encryption SDK. However, we took a different approach to sharing C++ code between multiple platforms efficiently, including iOS and Android; this worked well for us.
I’ll share my thoughts about the Dropbox announcement and then present the solutions that we adopted.
Remaining obstacles
It is undeniable that having cross-platform C++ code is no easy feat, and implies several kinds of overhead.
Recruiting can be a real pain, especially when requiring expertise in C++, cross-building, and mobile development. Also, internal training is costly, and having trained people leaving a few months after they have been trained is a risk you might not want to take.
Writing and maintaining language-specific bindings is challenging, having N targeted platforms usually implies having N bindings, debugging C++ code through Java can be nightmarish, not to mention the complications of cross-building.
These issues existed when Dropbox started their C++ mobile journey in 2013, and still persist today.
An evolving environment
Fortunately, many other things have improved since. The number of active open-source Github projects has globally increased. The number of projects written in C++ has increased by a factor of 7.64 between 2013 and 2018:
I disagree with Dropbox when they claim that the easiest overhead to predict with C++ is the need to build frameworks and libraries.
I do not know how many C++ JSON libraries there were when they decided to write json11, but there are around 15 listed by isocpp at present. Any recent project should consider using one of those instead of hand-rolling one.
The C++ ecosystem has evolved greatly, and we are beginning to have a wide choice of open-source libraries to solve the usual development problems.
C++ cross-build support has also improved since 2013, when companies often had to write the tools themselves:
We needed a custom build system that created libraries containing C++ code as well as Java and Objective-C wrappers and could generate targets that were understood by both Xcodebuild and Gradle.
Nowadays, both Android NDK and Xcode ship with recent C++ compilers (especially the former). A tool like CMake can generate Xcode projects, and can be invoked directly from Gradle. There is still some boilerplate to write of course, but still less than creating a whole build system.
Cross-platform development at Tanker
As I mentioned before, Tanker builds an end-to-end encryption SDK on which our other products are based. There are two versions, the first in JavaScript, the second in C++. The latter is used in our iOS/Android SDKs, and also runs on Linux/Windows/macOS. From the start, we had the same goal as Dropbox: sharing as much C++ code between platforms as possible.
Having a small team of engineers, we cannot spend time reimplementing libraries (HTTP, cryptography, formatting, etc…) ourselves, unless we really have to. This is why we use and contribute as much as possible to open-source projects (e.g. JSON for Modern C++, fmt, sqlpp11). Relying on external projects comes with the daunting specter of dependency management, which is one of the major challenges in C++.
This is less true nowadays, thanks to the emergence of C++ package managers (Conan, Hunter and Vcpkg being the most famous). We chose Conan for various reasons, mostly for its flexibility. It has become a central element of our development environment by allowing us to make cross-building a very simple (you can watch my swampUP 2018 talk about this very subject), and providing a great way to build and package our libraries and SDK for multiple platforms. Using Conan also led us to help library authors making their projects packageable.
One key point of our architecture design is to allow building our SDK for several platforms, not only just for mobiles. This implied writing bindings in several programming languages. This is why we opted for having a thin C interface, wrapping our C++ API. C is a much simpler language that a lot of other languages can bind to natively. We keep this interface as simple as possible because writing bindings is still cumbersome. This has been described by Adi Shavit as The Salami Method
Thanks to that, we could easily write an iOS binding since Objective-C can directly call C code. Android still required some work but we used JNA to avoid writing JNI code ourselves. If there is a need for a Go, Rust, or C# binding in the future, it will be way easier to bind than if we had only C++.
In conclusion
Using various open-source libraries and managing them with Conan greatly helped us to bring our SDK to Linux, macOS, Windows, iOS and Android. Dropbox’s choices may have been justified at the time they built their product; however, nowadays, C++ seems to be a very viable way of sharing code across multiple platforms.
We plan on writing more articles to cover dependency management and cross-build processes using Conan and CI scripts at Tanker.
PS: This article was originally written by Théo Delrieu and published on Tanker’s Medium. As you might not be on Medium yourself, we've reproduced it here to give you a chance to see it in your notifications feed.
Posted on September 4, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.