1

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.

Ammar Husain
  • 1,789
  • 2
  • 12
  • 26
  • 3
    Possible duplicate of [How do I check for C++11 support?](http://stackoverflow.com/questions/5047971/how-do-i-check-for-c11-support) (i.e. use preprocessor and check value of `__cplusplus` macro) – davmac Apr 13 '16 at 17:50

2 Answers2

4

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.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • "It's generally not enough to just check the value of __cplusplus" - wonder if you could elaborate on this, i.e. what's the issue with this approach? – davmac Apr 13 '16 at 18:56
  • 1
    @davmac: The days when g++ just set `__plusplus` to zero are over. But compiler support for the various C++ standards, vary. Few compilers implemented C++03 `export` (as far as I know only Comeau did officially, and Intel via an undocumented option). For a long time g++'s standard library didn't implement C++11 `to_string`, with lots of SO questions about it. And so on. The `__plusplus` macro can definitively tell you that the code is being compiled as C++. But regarding which language and library features are present, it's only a rough indication. – Cheers and hth. - Alf Apr 13 '16 at 19:08
0

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.

davmac
  • 20,150
  • 1
  • 40
  • 68
  • Because it doesn't work with one of the most popular compilers. – Cheers and hth. - Alf Apr 13 '16 at 19:17
  • While this code may answer the question, providing additional context regarding why and/or how it answers the question would significantly improve its long-term value. Please [edit] your answer to add some explanation. – CodeMouse92 Apr 13 '16 at 19:36
  • @Cheersandhth.-Alf Why so mysterious - which compiler would you be referring to? – davmac Apr 13 '16 at 21:14
  • @JasonMc92 I added a paragraph at the bottom, though I thought the code should be straight-forward to understand. It does exactly what OP asked for, if I understand correctly, nothing more and nothing less. If the added comment doesn't satisfy you, could you be more specific about how it could be improved? – davmac Apr 13 '16 at 21:19
  • @davmac, Thanks for the update. That about covers it. The basic point is that anyone who finds this question probably won't have an *identical* problem, but a *similar* one. Understanding what your solution is doing and why enables them to craft the unique solution they need. – CodeMouse92 Apr 13 '16 at 21:20