Future proofing our C++ APIs

published at 12.08.2017 20:30 by Jens Weller

While writing my CMS, I made one experience: using both std and boost versions of various types in the same code base. Often, it is a 3rd library, in this case from boost, which I use, only supporting the old boost versions. Which is fine, but it makes maintenance and working with the code more difficult, as constantly you see a mix of boost and std.

With C++17, this is going to become an even bigger problem, as with variant, optional, any and a few others (like filesystem, string_view, ...), even more boost libraries are becoming part of the standard.

Why just use boost doesn't work that well

An obvious solution would be, to stick every where to the boost versions. Makes a good argument, if you have a large, legacy code base. But, in doing that, we deny the update and improvement which these types have gotten from standardization. This includes simple things like variadic templates, where boost even to today often uses "cutting edge" C++03 macros. Also not to mention the dependencies on old TMP libraries like mpl, which hasn't been updated. Recently, mp11, a potential replacement for mpl written in C++11 has been accepted into boost. So an alternative to mpl exists now, but its up to the maintainer of a boost library if support is added.

So using the newer standard versions is a good thing, and you should prefer them over their boost counter parts. This will improve also things like compile time, and might remove from some of your libraries the need for the external dependency of boost fully. But, this is also not a very good solution. What if you are the author of popular (boost or not) library, and have currently a dependency, which could later be replaced by the standard? As an author, you'd like to support your current user base, so using std::variant with a notice that you'll need C++17 with the next library version, might not be such a good idea. Even for new libraries I think, that authors should aim at that their libraries are being usable by a large subset of the C++ community.

Using using to future proof your C++ API

So, when writing an API, how could one support several versions of a certain API or type? While I use mostly boost and the standard as an example, one also could use other versions of variant vs. the standard variant in C++17.

One easy way is, to use an alias, which can have several versions, activated by a define on the API clients side:

#ifdef USE_STD
#include <functional>
namespace library{
using std::function;
} #else #include <boost/function.hpp> namespace library{
template<class f> using function = boost::function<f>;
} #endif

This is a very raw example, but it allows to switch between two implementations. You can use either a templated alias, or import the type into your namespace directly with using. For types not yet in the standard, but with on going standardization, one would only have to add the alias/using statement it self, with adding standard versions later. boost::asio could be a good example for this. Also, its not that easy, this has its downsides too. While under review for the standard APIs change, so that a 1:1 replacement of one API with another is not always possible. For many cases, this will be only corner cases, and these would have to be dealt with. Libraries like asio or filesystem contain many types, so fully covering these with aliases/using statements would be some work.

Also filesystem is a good example, that during standardization features can be added, that previously didn't exist. std::filesystem provides a copy function for directories, which currently does not exist in boost.

And be aware that this can introduce subtle bugs into your code. As boost is always one implementation, but the standard library implementation actually depends on the compiler you are using. For library authors, it is important that any user can overwrite your chosen defaults.

What about concepts?

Right now, concepts are part of C++20, and they offer also a solution for this. Of course, concepts are not backward compatible, but they offer a solution for the code after C++17. Maybe future standardization will only focus on the concept and its constraints in the API. Then, it is up to the user, which type is chosen as template parameter in the code.



Join the Meeting C++ patreon community!
This and other posts on Meeting C++ are enabled by my supporters on patreon!