Ammar Husain
Ammar Husain

Reputation: 1849

How to choose function implementation based on C++ version

I have redefined certain C++ function objects (STL functional) to provide constexpr operator() for them. I needed these functionals to get evaluated at compile time to use for template metaprogramming. C++14 provides constexpr equivalents for the STL functional library. Currently I am compiling code with C++11, but might eventually upgrade to C++14. How do I implement these such that if I upgrade to C++14, it would automatically pick the function objects from STL, rather than my custom implementation.
Here is how I have it so far:

namespace foo {
 template <class T = void>
 struct less {
   constexpr bool operator()(T const& lhs, T const& rhs) const { 
     return lhs < rhs;
   }
 };
}

EDIT: I know this could be potentially done with __cplusplus if I just alias my namespace with std. However that would be a bad solution since I would pollute the namespace foo for all other instances.

Upvotes: 1

Views: 560

Answers (2)

davmac
davmac

Reputation: 20661

You can use the __cplusplus preprocessor-based solution without polluting the foo namespace, something along the lines of:

namespace foo {
#if __cplusplus < 201402L
 template <class T = void>
 struct less {
   constexpr bool operator()(T const& lhs, T const& rhs) const { 
     return lhs < rhs;
   }
 };
#else
  using std::less;
#endif
}

This defines a custom version of foo::less for C++ versions older than '14, and creates a an alias (still usable as foo::less) to std::less for newer C++ versions.

(The constant 201402L comes from the standard itself. Compilers conforming to C++14 are required to set __cplusplus to at least this value).

The above code might result in using your custom implementation of less in cases where the compiler has only partial support for C++14, even if that partial support includes a constexpr-qualified std::less::operator(). Apparently this is true for the latest version of Visual Studio. I suggest that this isn't even worth worrying about; calls to the function should be inlined anyway, and the moment you compile with a properly conforming C++14 compiler, you'll get the exact (compilation-level) behavior that you wanted. Alternatively, you could extend the pre-processor logic (i.e. check for compiler-specific macros) to recognize specific compiler versions known to be sufficiently conformant.

Upvotes: 0

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145419

Options, with the approach with probably least work and least impact on code, first:

  • Using the compiler's include file path to choose different versions of a header.

  • Compiler version sniffing and conditional compilation (e.g. #ifdef).

  • Wholesale code editing, possibly automated.

Compiler sniffing is pretty brittle, but is common.

It's generally not enough to just check the value of __cplusplus.


Using the compiler's include path you would for example first define a header <relops.hpp>, which just defines or names everything in terms of C++14 functionality, the default implementation, and place that in your regular header include directory.

And via your compiler's include path (e.g. CPATH for g++, and INCLUDE for Visual C++), if necessary for the particular compiler, you would direct it to first look for <relops.h> in some other directory for system-specific headers.

And this <relops.h> would define things itself, like in your presented code.


With conditional compilation you would instead determine the compiler and version based on macros such as __cplusplus, _MSC_VER, __GNUC__, something for Mac, possibly more. You would define some symbol as a result, e.g. IS_CPP14. Then you'd use #if IS_CPP14#else#endif.

Upvotes: 4

Related Questions