8

I want to make an equivalent to boost::swap and in my environment, standard headers can, or cannot be included. Depending on project licencing and other stuff.
I'd like to make portions of the code protected by guard detectors:

Let's consider one compilation unit.
project specific, afore-written potential includes:

#include <algorithm> // (or <utility> for C++11 projects)

later in project code included from my swap utility header:

namespace MyCompany
{
  template<class T1, class T2>
  void swap(T1& left, T2& right)
  {
     #ifdef _ALGORITHM_   // you get the idea.
       std::swap(left, right);
     #else
       // fallback impl
     #endif
  }
}

I simplified because we are not talking about details of the ADL trick here, but it will get included.
here for the reference of what I am talking about, but this is irrelevant to this question:
http://www.boost.org/doc/libs/1_57_0/boost/core/swap.hpp

So this question is about, how can I detect standard header inclusion ? the _ALGORITHM_ guard is present in visual studio provided header, but I read nowhere on http://www.cplusplus.com/reference/algorithm/ that it should have any macro that I can check.

(final note: this question is a little bit XY biased. What I really want is to detect the presence of the std::swap function, not the header.)

v.oddou
  • 6,476
  • 3
  • 32
  • 63
  • So you want the reverse? To use a specialized swap as a fallback? –  Mar 04 '15 at 02:27
  • let's say, as a convention, clients of our codebase will use MyCompany::swap as the default swap function, the same way boost users have widely adopted boost::swap. However, the implementation of that special swap, could use std::swap internally in case of the T1 and T2 types being swapped having a specialization of std::swap. But if the standard headers are not included at this point (because my utility will NOT include them because of respect to the stricter licensing rules), I want to detect that and never call std::swap. – v.oddou Mar 04 '15 at 02:34
  • 4
    I respect your intent but this leads to difficult-to-track-down bugs in the long run; the client's code may silently change behaviour depending whether or not they `#include ` , and you will have to deal with client support requests – M.M Mar 04 '15 at 02:58
  • 1
    possible duplicate of [how to implement is_swappable](http://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept)...? – Tony Delroy Mar 04 '15 at 03:49
  • I don't get the "licensing rules" bit. The license for `std::swap` is the same as for `operator+(int,int)`. And surely you don't want to suggest that the latter can be user-provided as well ?! – MSalters Mar 04 '15 at 12:24
  • The macro `_ALGORITHM_` is an implementation detail and you cannot rely on it. GCC's standard library defines `_GLIBCXX_ALGORITHM` instead, but there is no requirement that any macro is defined. It is also highly likely that `std::swap` gets defined by other headers e.g. for GCC's library it will be defined when you include almost **any** standard library header. – Jonathan Wakely Mar 04 '15 at 14:57
  • @MSalters I'm no specialist, but if you check the header its written: `provided that the above copyright notice * appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation` I was told it is the kind of things that got the whole game industry to ban anything non zlib-licenced, including C++ standard headers. And the publishers have softwares that can detect (from binary form) any code with license out of their recommendation. – v.oddou Mar 05 '15 at 01:28
  • @v.oddou: That's the generic GPL license. If you use GCC normally, you may apply the runtime library exception which exempts you from that rqeuirement. MSVC++ has a similar exception. Their headers may not be copied at all, but Release-mode binaries from the compiler are freely distributable. – MSalters Mar 05 '15 at 11:28

2 Answers2

5

One option you have it to make your overload a "worse match" than the alternatives. Then, only if they don't already exist will your version be selected:

#if 0
#include <algorithm>
using std::swap;
#endif

template <typename T>
struct ForceLessSpecialized {
  typedef T TYPE;
};

template <typename T>
void swap (T &, typename ForceLessSpecialized<T>::TYPE &) {
}

void bar () {
  int i;
  swap (i, i);
}  

What's happening:

When there are two candidate function template specializations, the compiler performs "Partial ordering of function templates"('03 14.5.5.2). This checks if the function template parameters of the one template can be used to specialize the other.

For each template we'll use dummy parameters T1 and T2 and we create dummy argument lists using these types:

// std::swap argument list
( T1 &  , T1 & )

// our swap argument list
( T2 &, typename ForceLessSpecialized<T2>::TYPE & )

Specializing our swap using the dummy arguments from std::swap gives:

Deduction from First parameter:  T == T1
Deduction from Second parameter:  Non Deduced Context

The deduced T is T1 and deduction has succeeded.

Specializing std::swap using the dummy arguments for our swap gives:

Deduction from First parameter:  T == T2
Deduction from Second parameter:  T == ForceLessSpecialized<T2>::TYPE

The deduced types for T are not the same, and so this is considered a deduction failure.

Therefore, the synthesized arguments of std::swap can be used to specialize our template, but the synthesized arguments of our template cannot be used to specialize std::swap. std::swap is seen as being more specialized and so wins the partial ordernig.

Richard Corden
  • 21,389
  • 8
  • 58
  • 85
  • damn, that's great work. definitely a good read to take from the standard. 14.5.5.2 refers to classes, the actual paragraph is 14.5.6.2, but there is a link to it. thank you :) this is funny because yesterday I read Herb Sutter's take on "why not specialize function templates". http://www.gotw.ca/publications/mill17.htm good read as well – v.oddou Mar 05 '15 at 01:55
  • @v.oddou: As you point out the paragraph in the more recent standards is 14.5.6.2, I was using the '03 standard which has it as 14.5.5.2. – Richard Corden Mar 06 '15 at 10:17
1

C++17 brought a new feature that now makes this possible is relevant.
https://en.cppreference.com/w/cpp/preprocessor/include

it's called __has_include
example here: #if __has_include(<algorithm>)

EDIT: it's different from the requirement of the question though, it just tells reachability.

v.oddou
  • 6,476
  • 3
  • 32
  • 63
  • 2
    But that returns whether a header exists at all, not whether it has been included already. – Quentin Oct 01 '18 at 08:19
  • damn, really ? I haven't read in depth enough then... (back after reading) omg, you are right. that's not what I wanted in the question. It's a completely different feature – v.oddou Oct 01 '18 at 08:28