RcppCore / Rcpp

Seamless R and C++ Integration
https://www.rcpp.org
GNU General Public License v2.0
742 stars 211 forks source link

Cannot sapply lambda functions #213

Closed romainfrancois closed 9 years ago

romainfrancois commented 9 years ago

With the cpp11 plugin on, Rcpp cannot sapply lambda functions.

#include <Rcpp.h>
using namespace Rcpp ;

// [[Rcpp:::plugins(cpp11)]]

// [[Rcpp::export]]
NumericVector double_up( NumericVector x ){
  return sapply( x, [](double x){ return x*x; } )
}

/*** R
  double_up( c(1,2) )
**/

I get :

$ RcppScript /tmp/double.cpp
In file included from double.cpp:1:
In file included from /Library/Frameworks/R.framework/Versions/3.1/Resources/library/Rcpp/include/Rcpp.h:27:
In file included from /Library/Frameworks/R.framework/Versions/3.1/Resources/library/Rcpp/include/RcppCommon.h:159:
In file included from /Library/Frameworks/R.framework/Versions/3.1/Resources/library/Rcpp/include/Rcpp/traits/traits.h:74:
/Library/Frameworks/R.framework/Versions/3.1/Resources/library/Rcpp/include/Rcpp/traits/result_of.h:31:22: error: no type named 'result_type' in
      '<lambda at double.cpp:8:21>'
        typedef typename T::result_type type ;
                ~~~~~~~~~~~~^~~~~~~~~~~
/Library/Frameworks/R.framework/Versions/3.1/Resources/library/Rcpp/include/Rcpp/sugar/functions/sapply.h:105:34: note: in instantiation of template class
      'Rcpp::traits::result_of<<lambda at double.cpp:8:21> >' requested here
        typename ::Rcpp::traits::result_of<Function>::type ,
                                 ^
double.cpp:8:10: note: while substituting deduced template arguments into function template 'sapply' [with RTYPE = 14, NA = true, T = Rcpp::Vector<14, PreserveStorage>,
      Function = <lambda at double.cpp:8:21>]
  return sapply( x, [](double x){ return x*x; } )
         ^
double.cpp:8:10: error: no matching function for call to 'sapply'
  return sapply( x, [](double x){ return x*x; } )
         ^~~~~~
/Library/Frameworks/R.framework/Versions/3.1/Resources/library/Rcpp/include/Rcpp/vector/ListOf.h:134:3: note: candidate template ignored: could not match 'ListOf' against
      'Vector'
T sapply(const ListOf<T>& t, Function fun) {
  ^
/Library/Frameworks/R.framework/Versions/3.1/Resources/library/Rcpp/include/Rcpp/sugar/functions/sapply.h:109:1: note: candidate template ignored: substitution failure
      [with RTYPE = 14, NA = true, T = Rcpp::Vector<14, PreserveStorage>, Function = <lambda at double.cpp:8:21>]
sapply( const Rcpp::VectorBase<RTYPE,NA,T>& t, Function fun ){
^
2 errors generated.
make: *** [double.o] Error 1
clang++ -std=c++11 -I/Library/Frameworks/R.framework/Resources/include -DNDEBUG  -I/usr/local/include -I/usr/local/include/freetype2 -I/opt/X11/include  -I"/Library/Frameworks/R.framework/Versions/3.1/Resources/library/Rcpp/include"    -fPIC  -O3 -c double.cpp -o double.o
Erreur dans sourceCpp(tail(args, 1), verbose = "-v" %in% args) :
  Error 1 occurred building shared library.
Exécution arrêtée

This is mainly Rcpp::traits:: result_of's fault.

eddelbuettel commented 9 years ago

Oooof. Would be nice to have that working. Any idea of what to change / how to change it? Any chance you could cook up a PR?

romainfrancois commented 9 years ago

You need std::result_of from C++11 instead of the very simplistic Rcpp::traits::result_of.

Good luck with conditional compilation. I probably don't want to deal with that.

eddelbuettel commented 9 years ago

We have conditioned on 'do we have more than C++98' for many years within Rcpp, so there is no technical reason not to. Many other projects do as well as eg Armadillo. But yes, it is work, and as always with all of us being volunteers here this may or may not get done.

romainfrancois commented 9 years ago

I am well aware of these technicalities. I was just saying that I don't fancy being a part of it. Being free from the burden of that kind of condition compilation was one of the several reason why Rcpp11 started.

eddelbuettel commented 9 years ago

Sure. But at the same time you also cut off a lot of users.

We just saw that with RcppArmadillo where the last version tickled a bug in g++-4.4 which, however you and I may feel about a version that old, is still used in a lot of places.

MattPD commented 9 years ago

Just a heads up, in case this may be of some use: It appears that patching result_of.h is not enough.

For testing purposes, I've experimented with replacing the code defining Rcpp::traits::result_of with the following:

#include <type_traits>
namespace Rcpp { namespace traits { using std::result_of; } }

This appears to work for sapply and a (regular) standalone function, say, sapply(x, g) given double g(double x) { return x; }.

However, there are still compilation errors for the Romain's lambda example:

result_of.cpp: In function 'Rcpp::NumericVector double_up(Rcpp::NumericVector)':
result_of.cpp:23:46: error: no matching function for call to 'sapply(Rcpp::NumericVector&, double_up(Rcpp::NumericVector)::<lambda(double)>)'
result_of.cpp:23:46: note: candidates are:
[...]/R/R-current/library/Rcpp/include/Rcpp/vector/ListOf.h:134:3: note: template<class T, class Function> T Rcpp::sapply(const Rcpp::ListOf<T>&, Function)
[...]/R/R-current/library/Rcpp/include/Rcpp/sugar/functions/sapply.h:109:1: note: template<int RTYPE, bool NA, class T, class Function> Rcpp::sugar::Sapply<RTYPE, NA, T, Function, Rcpp::traits::same_type<typename std::result_of<Function>::type, typename Rcpp::traits::storage_type<Rcpp::traits::r_sexptype_traits<typename std::result_of<Function>::type>::rtype>::type>::value> Rcpp::sapply(const Rcpp::VectorBase<RTYPE, LHS_NA, LHS_T>&, Function)

Not quite sure what's the cause here -- could it be that Rcpp::traits::r_sexptype_traits or Rcpp::traits::storage_type need some further adaptation?

In any case, if making sapply work with std::result_of is possible, then conditional compilation / dispatching (depending on C++11 support) groundwork has already been done by boost::result_of:

On a side note: It's header-only and already a part of the BH package. If it's relatively self-contained, then perhaps adding a dependency for this particular case wouldn't be too troubling (compared to the current status quo, i.e., not supporting lambdas at all)? In any case, there are only trade-offs...

eddelbuettel commented 9 years ago

Thanks for the follow-up. I have at times considers whether we'd want to depend on BH and it is tempting: header-only, (mostly) stable, well-tested, widely used, ... But it would add a pretty big package to all builds so it adds quite some weight. Pulling small things out of Boost is also an option, we already have a macro processor borrowed from it.

Do you feel like working some more on this?

jjallaire commented 9 years ago

In my experience it's hard to pull only "a little" out of boost. I think once I tried to pull out "just" shared_ptr and I still ended up with several megabytes of headers. So my 2 cents would be if you want to use boost just use BH (but I still agree about being cautious about bringing BH without at least thinking through all the implications/consequences). Note that here is no end-user requirement of installing BH as it's only needed at build time.

On Sat, May 2, 2015 at 11:43 AM, Dirk Eddelbuettel <notifications@github.com

wrote:

Thanks for the follow-up. I have at times considers whether we'd want to depend on BH http://cran.rstudio.com/package=BH and it is tempting: header-only, (mostly) stable, well-tested, widely used, ... But it would add a pretty big package to all builds so it adds quite some weight. Pulling small things out of Boost is also an option, we already have a macro processor borrowed from it.

Do you feel like working some more on this?

— Reply to this email directly or view it on GitHub https://github.com/RcppCore/Rcpp/issues/213#issuecomment-98372208.

eddelbuettel commented 9 years ago

But build time is every Travis run for those working from source (rather than, say, my PPA). So BH would be expensive to some. At least until the caching gets better...

jjallaire commented 9 years ago

So they'd have to pay the BH download on every travis run. Not insubstantial but hopefully also typically not costing much more than a few seconds. I'm neutral on this overall (probably lean negative just to keep Rcpp on the leaner side, however I wouldn't oppose doing it if there is a strong user benefit).

On Sat, May 2, 2015 at 12:16 PM, Dirk Eddelbuettel <notifications@github.com

wrote:

But build time is every Travis run for those working from source (rather than, say, my PPA https://launchpad.net/%7Eedd/+archive/ubuntu/misc/+packages). So BH would be expensive to some. At least until the caching gets better...

— Reply to this email directly or view it on GitHub https://github.com/RcppCore/Rcpp/issues/213#issuecomment-98374462.

eddelbuettel commented 9 years ago

I'd word it exactly the same way. On the margin, leaner Rcpp is better but we may get to a point where depending on BH will be a good idea. So let's keep the doors open...

MattPD commented 9 years ago

@eddelbuettel: can take a look, provided someone walks me through / gives hints on the encountered issue with Rcpp::traits::r_sexptype_traits / Rcpp::traits::storage_type :-) ATM it's not quite clear to me why these fail to cooperate with std::result_of (or even whether that's indeed what makes sapply fail with the lambda example, for that matter).

@jjallaire definitely makes a good point.

While Boost has been improving its modularization (since around 2013; cf. https://svn.boost.org/trac/boost/wiki/CMakeModularizationStatus & https://isocpp.org/blog/2013/11/boost-migrating) -- resulting in independent, library-specific repositories (here: https://github.com/boostorg/utility), at the same time I can see that boost/utility/result_of.hpp depends on Boost.Preprocessor, Boost.MPL, and others...

Some dependencies on Boost.MPL seem trivial / easily removable (AFAICT, it all boils down to the equivalents of C++11's std::true_type, std::false_type, and std::conditional -- and chances are these already have analogues in the Rcpp?). Others may warrant further investigation...

That being said, I think it's an "I cross that bridge when I come to it" matter at the moment for the issue at hand -- if cooperation with std::result_of is not possible / not sufficient, then discussing the best way to conditionally depend on it may be a bit premature ;-)

kevinushey commented 9 years ago

My opinions on this are somewhat mixed. I also prefer Rcpp being lean, but am open to bringing in Boost / BH if it has a large enough benefit. I am not sure if the benefit is large enough in this case.

One thing to worry about is, if Rcpp decides to depend on boost, it could break compilation on older compilers (e.g. if the release of Boost packaged in BH is somehow incompatible with an older GCC). One of the main drives for Rcpp to stick with C++98 is to ensure it can compile with such older compilers. (I could be wrong here -- I am not sure how committed Boost is in maintaining backwards compatibility with older compilers...)

At the same time, I feel that if a user really wants C++11 features, they should just use Rcpp11 or (if @romainfrancois plans to begin work on it soon) Rcpp14.

Finally, in my opinion, allowing lambdas to work on Rcpp types isn't a strong enough reason to bring BH. You can still use lambdas just fine on STL vectors, or even on an iterator pair constructed from an Rcpp vector, e.g.

std::for_each(x.begin(), x.end(), [](double x) { ... });

Of course, the semantics aren't quite the same as sapply but I do feel like it's 'good enough', at least for Rcpp...

romainfrancois commented 9 years ago

Depending on BH means each and every package that uses Rcpp will have to have

LinkingTo: Rcpp, BH

Not a huge deal but still.

Rcpp already has some conditional compilation on c++11, that can easily be used to work around this. And lambdas are c+11 anyway. So if you do want to support this C++11 feature, the best is to actually use C++11.

That should be easy enough, and you can easily borrow from Rpp11 or Rcpp14.

jjallaire commented 9 years ago

I didn't consider the fact that depending on BH would retroactively require all of our dependent packages to do LinkingTo: Rcpp, BH. That sounds like a complete nightmare to orchestrate, definitely not worth it given what we're talking about here (and also given Romain's point about conditional compilation being another way to approach this).

eddelbuettel commented 9 years ago

I think we are all on the same page: we could deploy BH if there was sufficient need/merit; nobody had yet claimed that this issue called for it. All good...

MattPD commented 9 years ago

OK, I have a patch proposal! :-)

One thing to note is that ::Rcpp::traits::result_of is not compatible with ::std::result_of; examples:

double f(double x) { return x; }

typedef ::std::result_of<decltype(&f)(double)>::type result_type; // OK
typedef ::Rcpp::traits::result_of<decltype(&f)(double)>::type result_type; // error
typedef ::Rcpp::traits::result_of<decltype(&f)>::type result_type; // OK

Note how the std trait also needs to know the type of the argument in the above examples (this is not always the case, but it's enough to rule out the simple replacement idea). In contrast, the Rcpp trait will refuse to work (compile) when given the argument type. This explains why the aforementioned simple replacement didn't work -- these two are simply not interchangeable.

This is no big loss, however, since we can work around it -- the usage in the sapply sugar always occurs in a context where we know the argument type as well. In particular, note that sugar::Sapply always calls fun( vec[i] ): https://github.com/RcppCore/Rcpp/blob/master/inst/include/Rcpp/sugar/functions/sapply.h#L53 https://github.com/RcppCore/Rcpp/blob/master/inst/include/Rcpp/sugar/functions/sapply.h#L88

Thus, the desired result-type type is the result of the function fun application, when acting on an element of (argument-)type vec[i].

First idea (solely to check whether this form of replacement can work): typedef typename decltype(std::declval<Function>() (std::declval<SugarExpression>() [0])) type; (used SugarExpression instead of generic T for clarity, more on that in a moment). This evaluates to the type of a function -- the [0] is a quick and dirty way to get the underlying element type of vec. Lambda example compiles successfully :-) This is obviously too ugly, so the next step is to clean it up.

Making use of the following:

We arrive at (initially C++11-only, generalized below), placed in "sapply.h" (reasoning follows):

namespace Rcpp{
namespace sugar{

template <typename Function, typename SugarExpression>
struct sapply_application_result_of
{
    typedef typename ::std::result_of<Function(typename SugarExpression::stored_type)>::type type;
};

// . . . remaining content of "sapply.h" . . .

The rationale behind placing this in ::Rcpp::sugar namespace and naming this sapply_application_result_of -- it is context-specific (as in: it is result_of valid in and specifically tailored for the sapply_application context) and should not necessarily be exposed to be used in another context. That being said, it's a proposal and I'm very much looking for feedback :-)

The rationale behind the SugarExpression name -- follows from the principles documented here: https://github.com/RcppCore/Rcpp/blob/master/vignettes/Rcpp-sugar.Rnw Note how we're specifically expecting "a sugar expression, which we recognize because of the relationship with the VectorBase class template." (This is also what motivated me to initially place sapply_application_result_of in the ::Rcpp::sugar namespace, giving it this particular name.) Thus, a fully generic type name T feels somewhat inappropriate (from the clarity-vs-ambiguity POV); perhaps the remaining code can be adjusted so as to disambiguate the other uses of T, too? For now I've decided to keep the commit minimal and left these as-they-are -- but let me know if you agree that this is a good idea.

Now, we systematically replace every occurrence of ::Rcpp::traits::result_of<Function>::type with ::Rcpp::sugar::sapply_application_result_of<Function, T>::type -- 8 counts in total. (It would be shorter to use C++14-style ::Rcpp::sugar::sapply_application_result_of_t<Function, T> -- but this relies on C++11's alias templates, which are currently not supported by GCC bundled with Rtools.) Note that at each and every evaluation site we always have access to T -- hence, this is WLOG.

Now, in order to move to conditional compilation, I think we need something like the following

#if (__cplusplus >= 201103L) || defined(__GXX_EXPERIMENTAL_CXX0X__)
    #define RCPP_USE_CXX11 1
#else
    #define RCPP_USE_CXX11 0
#endif 

See: http://en.cppreference.com/w/cpp/preprocessor/replace

Note how this mirrors ARMA_USE_CXX11 used by RcppArmadillo: https://github.com/RcppCore/RcppArmadillo/blob/master/inst/include/armadillo The reason we (or ARMA_USE_CXX11) cannot simply rely on the value of __cplusplus is that the ancient GCC version which currently ships with Rtools (4.6.3 20111208 (prerelease)) ignores the standard and simply defines the macro as 1... at the same time, it also defines __GXX_EXPERIMENTAL_CXX0X__ when compiling with -std=c++0x. To my knowledge Rcpp does not define a similar macro globally -- correct me if I'm wrong.

There is a somewhat different macro RCPP_USING_CXX11, defined in the following: https://github.com/RcppCore/Rcpp/blob/master/inst/include/Rcpp/platform/compiler.h However, it is too strict for our purpose here, since it rules out the current GCC shipping with Rtools. It also doesn't appear to be used consistently; for instance, note that the following relies on directly checking __cplusplus: https://github.com/RcppCore/Rcpp/blob/master/inst/include/Rcpp/sugar/sets.h In any case, I feel that this is something that may benefit from another pair of eyes and advice. I don't feel very comfortable about placing RCPP_USE_CXX11 in this specific location. Let me know about the preferred alternatives, there must be a better solution! :-)

Finally: We can now define:

template <typename Function, typename SugarExpression>
struct sapply_application_result_of
{
#if RCPP_USE_CXX11
    typedef typename ::std::result_of<Function(typename SugarExpression::stored_type)>::type type;
#else
    typedef typename ::Rcpp::traits::result_of<Function>::type type;
#endif
} ;

Tested with freestanding functions (including passed-by-pointer and passed-by-reference), function objects, and lambdas.

Now, for C++11-and-newer compilers this brings support for lambdas -- while, at the same time, gracefully reducing to the current code for the pre-C++11 compilers :-)

jjallaire commented 9 years ago

One note to keep in mind. You referenced the Rtools compiler (4.6.3) as "ancient". Note that we also need to keep in mind that Rcpp supports GCC 4.2 (Mac Pre-Mavericks which is still ~ 25% of all Macs) and GCC 4.1 (RedHat 5) so anything we do must also compile in those configurations. Perhaps you already realize this but wanted to point it out just in case.

On Mon, May 11, 2015 at 3:20 PM, Matt notifications@github.com wrote:

OK, I have a patch proposal! :-)

One thing to note is that ::Rcpp::traits::result_of is not compatible with ::std::result_of; examples:

double f(double x) { return x; }

typedef ::std::result_of<decltype(&f)(double)>::type result_type; // OK typedef ::Rcpp::traits::result_of<decltype(&f)(double)>::type result_type; // error typedef ::Rcpp::traits::result_of<decltype(&f)>::type result_type; // OK

Note how the std trait also needs to know the type of the argument in the above examples (this is not always the case, but it's enough to rule out the simple replacement idea). In contrast, the Rcpp trait will refuse to work (compile) when given the argument type. This explains why the aforementioned simple replacement didn't work -- these two are simply not interchangeable.

This is no big loss, however, since we can work around it -- the usage in the sapply sugar always occurs in a context where we know the argument type as well. In particular, note that sugar::Sapply always calls fun( vec[i] ):

https://github.com/RcppCore/Rcpp/blob/master/inst/include/Rcpp/sugar/functions/sapply.h#L53

https://github.com/RcppCore/Rcpp/blob/master/inst/include/Rcpp/sugar/functions/sapply.h#L88

Thus, the desired result-type type is the result of the function fun application, when acting on an element of (argument-)type vec[i].

First idea (solely to check whether this form of replacement can work): typedef typename decltype(std::declval() (std::declval() [0])) type; (used SugarExpression instead of generic T for clarity, more on that in a moment). This evaluates to the type of a function -- the [0] is a quick and dirty way to get the underlying element type of vec. Lambda example compiles successfully :-) This is obviously too ugly, so the next step is to clean it up.

Making use of the following:

  • function sapply returns a function object sugar::Sapply passing t and fun to its constructor
  • the aforementioned constructor of sugar::Sapply accepts objects of type (or implicitly convertible to / derived from) const VEC &
  • VEC is a typedef to Rcpp::VectorBase
  • operator[] of Rcpp::VectorBase has return-type Rcpp::VectorBase::stored_type

We arrive at (initially C++11-only, generalized below), placed in "sapply.h" (reasoning follows):

namespace Rcpp{ namespace sugar{

template <typename Function, typename SugarExpression> struct sapply_application_result_of { typedef typename ::std::result_of<Function(typename SugarExpression::stored_type)>::type type; };

// . . . remaining content of "sapply.h" . . .

The rationale behind placing this in ::Rcpp::sugar namespace and naming this sapply_application_result_of -- it is context-specific (as in: it is result_of valid in and specifically tailored for the sapply_application context) and should not necessarily be exposed to be used in another context. That being said, it's a proposal and I'm very much looking for feedback :-)

The rationale behind the SugarExpression name -- follows from the principles documented here: https://github.com/RcppCore/Rcpp/blob/master/vignettes/Rcpp-sugar.Rnw Note how we're specifically expecting "a sugar expression, which we recognize because of the relationship with the VectorBase class template." (This is also what motivated me to initially place sapply_application_result_of in the ::Rcpp::sugar namespace, giving it this particular name.) Thus, a fully generic type name T feels somewhat inappropriate (from the clarity-vs-ambiguity POV); perhaps the remaining code can be adjusted so as to disambiguate the other uses of T, too? For now I've decided to keep the commit minimal and left these as-they-are -- but let me know if you agree that this is a good idea.

Now, we systematically replace every occurrence of ::Rcpp::traits::result_of::type with ::Rcpp::sugar::sapply_application_result_of<Function, T>::type -- 8 counts in total. (It would be shorter to use C++14-style ::Rcpp::sugar::sapply_application_result_of_t<Function, T> -- but this relies on C++11's alias templates, which are currently not supported by GCC bundled with Rtools.) Note that at each and every evaluation site we always have access to T -- hence, this is WLOG.

Now, in order to move to conditional compilation, I think we need something like the following

if (cplusplus >= 201103L) || defined(__GXX_EXPERIMENTAL_CXX0X)

#define RCPP_USE_CXX11 1

else

#define RCPP_USE_CXX11 0

endif

See: http://en.cppreference.com/w/cpp/preprocessor/replace

Note how this mirrors ARMA_USE_CXX11 used by RcppArmadillo: https://github.com/RcppCore/RcppArmadillo/blob/master/inst/include/armadillo The reason we (or ARMA_USE_CXX11) cannot simply rely on the value of cplusplus is that the ancient GCC version which currently ships with Rtools (4.6.3 20111208 (prerelease)) ignores the standard and simply defines the macro as 1... at the same time, it also defines __GXX_EXPERIMENTAL_CXX0X when compiling with -std=c++0x. To my knowledge Rcpp does not define a similar macro globally -- correct me if I'm wrong.

There is a somewhat different macro RCPP_USING_CXX11, defined in the following: https://github.com/RcppCore/Rcpp/blob/master/inst/include/Rcpp/platform/compiler.h However, it is too strict for our purpose here, since it rules out the current GCC shipping with Rtools. It also doesn't appear to be used consistently; for instance, note that the following relies on directly checking __cplusplus: https://github.com/RcppCore/Rcpp/blob/master/inst/include/Rcpp/sugar/sets.h In any case, I feel that this is something that may benefit from another pair of eyes and advice. I don't feel very comfortable about placing RCPP_USE_CXX11 in this specific location. Let me know about the preferred alternatives, there must be a better solution! :-)

Finally: We can now define:

template <typename Function, typename SugarExpression> struct sapply_application_result_of {

if RCPP_USE_CXX11

typedef typename ::std::result_of<Function(typename SugarExpression::stored_type)>::type type;

else

typedef typename ::Rcpp::traits::result_of<Function>::type type;

endif

} ;

Tested with freestanding functions (including passed-by-pointer and passed-by-reference), function objects, and lambdas.

Now, for C++11-and-newer compilers this brings support for lambdas -- while, at the same time, gracefully reducing to the current code for the pre-C++11 compilers :-)

— Reply to this email directly or view it on GitHub https://github.com/RcppCore/Rcpp/issues/213#issuecomment-101023263.

MattPD commented 9 years ago

@jjallaire: Sure! The aforementioned workarounds are very much motivated by this aspect ;-)

eddelbuettel commented 9 years ago

Minor comment re your comment about our somewhat inconsistent "is this C++11" macros: these grew over the years and sometimes out of simple needs (eg long long ...), and that area could quite possibly benefit from a 2nd (or 3rd, 4th, ...) set of eyes. But as you note the whole area is full of inconsistencies...

MattPD commented 9 years ago

@eddelbuettel: Exactly!

Ideally (read: if the supported compilers were more recent), it would probably be best to use the "Feature-testing recommendations for C++": https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations // Latest draft: http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4440.html

Or, in any case, to still use feature-level testing granularity (testing for the level of "supported standard as a whole" can generally get brittle and unreliable): https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations#expl.status

For instance, something along the following lines (or even if __has_include(<type_traits>)): https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations#detail.cpp14.n3462

However, the "more recent" in this context means either GCC 5 or Clang 3.4:

In other words, probably not applicable to Rcpp any time soon...

Incidentally (back to supporting the not-so-recent compilers), this is also the reason for the elaborate dance done by boost::result_of (involving BOOST_HAS_TR1_RESULT_OF and BOOST_NO_CXX11_DECLTYPE_N3276): http://www.boost.org/doc/libs/master/boost/utility/result_of.hpp

On a side note (and completely independently from the issue at hand): I'm wondering, given that these macros are a part of Boost.Config, how self-contained is the <boost/config.hpp> header? (Note that Boost.Config supports compilers considerably older and of a rather wi(l)der variety than those required by Rcpp, so perhaps this could be even trimmed down further.)

Perhaps rejuvenating the codebase to make use of the feature macros present there would be the closest one could get to the feature-testing recommendations in practice (including doing things right w.r.t. the granularity). This is a significant task on its own, naturally.