4

I have a project which is strictly written in C++14 and I want to use it in another project which is C++17. Some of the C++14 features like Dynamic Exception Specification were removed in C++17.

Is there any way to use and compile both code bases together? The project is large enough to make refactoring impractical.

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
Ghasem Ramezani
  • 2,683
  • 1
  • 13
  • 32
  • 1
    Wouldn't it be easier to just ditch the dynamic exception specifications entirely? It is after all your project, so you can change it. – Nicol Bolas Aug 19 '20 at 16:42
  • 1
    If you use no-longer-supported features only in cpp files then this is fine. Everything gets compiled into binary level after all. But you can't reuse headers that way. – freakish Aug 19 '20 at 16:44
  • Its not all about dynamic exception specifications, there is still another problems with the code which could not be compiled with C++17. – Ghasem Ramezani Aug 19 '20 at 16:57
  • To be clear, you need to link together into a single executable both C++14 and C++17 code, is that correct? Whether or not that's supported is going to be platform-specific and I can think of lots of reasons it wouldn't work. (For example, the implementation of `std::string` might be entirely different, so what happens with C++14 code passes a `std::string` to C++17 code?) – David Schwartz Aug 19 '20 at 17:19

2 Answers2

2

This will be platform specific: It is conceivable that substantially different headers are selected according to the standard specified on the command line, etc.

That said, here is an answer from Jonathan Wakely which assures you that with gcc there should be no such problem, by design, if you keep away from unstable features in old compilers.

The underlying reason according to Jonathan is that C++ standard implementations in gcc, once declared stable, do not change their ABI (i.e, outward-facing type definitions, name mangling) with the selected C++ standard, not even between compiler versions.

Because all interaction between the translation units must be restricted to the smallest common standard version there is no issue: The C++11 features do not change if you specify C++17. Newer features in the C++17 TUs cannot be used for communication with earlier-standard TUs since they were not yet available, so no issue there either. If you are able to recompile, the safest advice would be:

  1. Best to use the same std::string versions (which can be controlled from the command line at compile time).
  2. Use the same libstdc++.
  3. Use the same gcc version (and control the language standard used for each TU through the command line).

It would make sense for other compilers to follow a similar compatibility strategy.

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
  • How does this fix the problem when you need to call functions of a C++14 library from C++17 library though? You might be able to link towards it just fine, but the header includes are still going to cause compiler errors if features were used that were removed in C++17. – Jan Schultke Aug 19 '20 at 18:25
  • @J.Schultke For calls between two files compiled with different standard options you can only use the common subset of both standards. *Usually* this just means the c++14 file cannot call/use c++17 functions or types, but in cases where c++17 is not backwards compatible the opposite applies as well. Since in C++14 the exception specifications, iiuc, were not part of the function type (https://en.cppreference.com/w/cpp/language/noexcept_spec) you can probably simply wrap it in a preprocessor conditional macro which is empty in C++17. – Peter - Reinstate Monica Aug 19 '20 at 21:06
1

Yes, you can detect the C++ version and compile conditionally:

void foo() {
#if __cplusplus >= 201703L
    // code written for C++17 goes here
#else
    // code written for C++14 goes here
#endif
}

The full list of __cplusplus macros is as follows:

#if __cplusplus >= 199711L // C++98
#if __cplusplus >= 201103L // C++11
#if __cplusplus >= 201402L // C++14
#if __cplusplus >= 201703L // C++17
#if __cplusplus >= 202002L // C++20

Note that this causes various problems:

  • it's impossible to test code like this unless you compile your tests for C++14 and C++17 separately
  • your IDE might not even show warnings or errors in removed preprocessed blocks
  • you need to maintain two separate version of the same code

So if you can, come up with a solution that works in both versions or at least use as few blocks like this as possible. If you just want a single keyword or so to be removed, then consider the following solution:

#if __cplusplus >= 201703L // C++17
#define THROWS(...) noexcept(false)
#else
#define THROWS(...) throw(__VA_ARGS__)
#endif

// now you can use it like this in both C++14 and C++17:
void fun() THROWS(MyException);
Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
  • It's lead me to greater problem, I prefer to refactoring code. – Ghasem Ramezani Aug 19 '20 at 16:58
  • @GhasemRamezani not sure what to tell you then. Dynamic exception specifications are part of the function headers and when you include that into C++17 code, it won't compile. Either you need to compile conditionally like this or you need to refactor all these specifications away. You could also capture features like the exception spec in a macro, I will add that to the answer. – Jan Schultke Aug 19 '20 at 17:01