Tuesday 18 August 2020

The pain(s) of C++11


Recently I wrote a small cross-platform library of widgets and didn't think much about the C++ version to be used, as my client's primary platform was Windows plus the latest Visual Studio with C++17. As the Microsoft compiler was normally the slowest to implement new standard's features I didn't worry about problems when compiling the code with GCC on Linux and just settled on the C++17 standard (or at least the subset of it supported by the used Visual Studio version).

However.... it all came out rather differently. 
 
First, I had to back-port it to Visual Studio 2015, which theoretically supported C++17, but didn't have std::optional in its STL 😨, but this is material for another article...

If that weren't enough, it turned out that the GCC version used in our build server doesn't support C++17, so I had backport it again to C++11. OK, I thought, C++11 was the giant step for C++ so it shouldn't be that difficult to go back, all the most important language features should already be present there. 

You'll imagine my surprise, when this innocent code snippet failed to compile on the build server!
   // impl. helpers:
   template<typename Enum>
      constexpr auto enumToInt(Enum e) noexcept  // C++20: requires(std::is_enum_v<Enum>) 
   {
      return static_cast<std::underlying_type_t<Enum>>(e);
   }
You see, optimistic as I am, I was already thinking about what would be possible in C++20!

I won't show you the exact error message because it is incomprehensible for most people*. Enough to say it wouldn't compile.

So this is what I had to use instead:
   template<typename Enum>
      constexpr auto enumToInt(Enum e) noexcept        // C++20: requires(std::is_enum_v<Enum>) 
        -> typename std::underlying_type<Enum>::type   // C++11 backward compatibility :-(
   {
      return static_cast<typename std::underlying_type<Enum>::type>(e);
   }           
As we can see, I needed first to sptinkle some typename keyword's instances all over the place to help and guide the compiler in parsing of the template. Secondly, in C++11 the XXX_type_t shortcuts for the standard library types aren't available. I had also to explicitely specify the return type of the lambda.

You see - much more mindless typing in the C++11 version! In some respect it's almast as verbose as the older Java releases. And not to mention lots of time used for deciphering template error messages to find out those problems!

Conclusion:

In conclusion I must say, we should appreciate C++14/17 much more than we do, because it really saves us the time to write and later to read the code!!! Somehow I wasn't fully aware of that.

P.S.:
Imagine, at first I was even tempted to use this monster:
   template<typename Enum>
      constexpr auto enumToInt(Enum e) noexcept 
        -> decltype(static_cast<typename std::underlying_type<Enum>::type>(e)) // C++11 backward compatibility :-O     
   {
      return static_cast<typename std::underlying_type<Enum>::type>(e);
   }   
--
* Personally. I always found the complaints to be somehow exagerated - with a little practice you can find the problem in not too long a time.  But it is a quest and not an automated procedure. On the other hand, isn't it always like that with compilers? Good error messages are rather a recent invention and still more like an exception than a rule.


No comments: