[Rcpp-devel] A though on Rcpp::wrap

Romain Francois romain at r-enthusiasts.com
Fri Feb 8 14:53:56 CET 2013


Thank you for this detailed email, i will study it carefully. 

Romain


Le 8 févr. 2013 à 13:16, Yan Zhou <zhouyan at me.com> a écrit :

> This morning I was frustrated by a compilation error, below is a scratch of the problem
> 
> // MyClass1.h
> template <typename T>
> class MyClass {/*...*/};
> 
> #include <RcppCommon.h>
> namespace Rcpp {
> template <typename T> inline SEXP wrap(const MyClass1<T> &object) {/* ... */}
> }
> #include <Rcpp.h>
> //...
> 
> // MyClass2.h Similar to MyClass1.h
> 
> // some.cpp
> #include "MyClass1.h"
> #include "MyClass2.h"
> 
> MyClass2<T> object;
> Rcpp::List::create(Rcpp::Named("MyClass2") = object);
> 
> I believe you already see the problem. An implicit conversion happens and it implicitly call Rcpp::wrap. However because of MyClass1.h, the declaration of wrap for MyClass2 was never seen before Rcpp.h. So the dispatch never reach my wrap. This problem will always happen as long as implicit call to Rcpp::wrap (qualified name) happens and there are more than a handful headers. The reason to write libraries and use headers is that we don't need to remember what is inside. In this case. I have to remember what's there. And the requirement that the declaration happens before Rcpp.h, is sometimes just impossible. 
> 
> An easy way to fix this is, at the call site of Rcpp::wrap (which I finally tracked down),
> instead of
> 
> ::Rcpp::wrap(object);
> 
> I changed it to,
> 
> using Rcpp::wrap;
> wrap(object);
> 
> This is basically the same trick of using std::swap. Now because wrap is an unqualified dependent  name, it will have a second chance at POI (an ADL lookup). In the original implementation, Rcpp::wrap is a qualified dependent name, and at the second phase it will only look for template specialization (see the PS at the end of the email).
> 
> However a quick grep shows that there are over 200 ::Rcpp::wrap in Rcpp headers. So such change is almost impossible. There will almost certainly be some disaster introduced by such a big change.
> 
> So I though of another workaround, which you may find interesting.
> 
> Currently Rcpp::wrap looks like this,
> 
> template <typename T> 
> inline SEXP wrap(const T& object){
> 	return internal::wrap_dispatch( object, typename ::Rcpp::traits::wrap_type_traits<T>::wrap_category() ) ;
> }
> 
> Instead of having it call wrap_dispatch directly, I added another layer of indirection,
> 
> // in namespace internal
>   template <typename T>
> inline SEXP wrap_implicit(const T& object){
> 	return wrap_dispatch( object, typename ::Rcpp::traits::wrap_type_traits<T>::wrap_category() ) ;
> }
> 
> template <typename T> 
> inline SEXP wrap(const T& object){
>     using ::Rcpp::internal::wrap_implicit;
>     return wrap_implicit(object);
> }
> 
> This is basically the same as before. Now, because both wrap is a template, and thus wrap_implicit is a dependent name and according to the two phase lookup rule,  wrap_implicit(object) will be resolved at POI. All it matters is wrap_implicit was seen before POI. A side effect (benefit) is that wrap_implicit does not have to be defined in the namespace Rcpp. I can be in global namespace of in the class namespace and still be found through ADL.
> 
> Explicit call to wrap can already works this way, for example
> 
> #include <Rcpp.h>
> 
> namespace my_ns {
> class MyType {..};
> SEXP wrap (const MyType &object) {...}
> 
> }
> 
> my_ns::MyType my_object;
> using Rcpp::wrap;
> wrap(my_object); // call my_ns::wrap
> wrap(RcppTypeObject); // call Rcpp::wrap
> wrap(SomeUnknownType); // call Rcpp:wrap, possible error after all
> 
> The additional layer of wrap_implicit only affects explicit call to Rcpp::wrap, such as
> Rcpp::wrap(my_object); // Oops! no my_ns::wrap_implicit yet
> In this case, since the user explicitly request Rcpp::wrap, then of course he/she need to define it in Rcpp namespace (though still can be defined after Rcpp.h, as long as it is before POI)
> 
> In the user perspective it also affect some implicit conversion to SEXP. And thus the name wrap_implicit. Since this is more or less behind the scene, I think a little more obstructed name is better.
> 
> The bottom line is that, with such change, specialized wrap does not have to be declared before Rcpp.h
> 
> However there may be some (important) corner cases that I haven't thought of that may cause troubles. In addition, qualified name has its advantage over the using idiom, and you known Rcpp far better than I to judge this problem.
> 
> Best,
> 
> Yan Zhou
> 
> P.S. The document Rcpp-extending.pdf is not quite accurate. Section 2.3, it is not partial specialization. It is actually function templates overloading. There is no such thing as function partial specialization. A full specialization (section 2.2) only need to be seen before POI, does not have be before Rcpp.h. In Section 2.3, it is required to have the decleration before wrap.h is that it is not a specialization at all but totally another overloaded function. Without it, qualified name call ::Rcpp::wrap will always resolve to the one in wrap.h.
> _______________________________________________
> Rcpp-devel mailing list
> Rcpp-devel at lists.r-forge.r-project.org
> https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/rcpp-devel


More information about the Rcpp-devel mailing list