diff --git a/ChangeLog b/ChangeLog index 5bef16f4a..a5b48c67a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2024-05-23 Andrew Johnson + + * Added variadic templates to be used instead of the generated code + in `Rcpp/generated` and `Rcpp/module` when compiling with C++11 or + later. + 2024-05-18 Dirk Eddelbuettel * docker/ci-4.3/Dockerfile: Add rcpp/ci-4.3 container for R 4.3.* diff --git a/inst/include/Rcpp/DataFrame.h b/inst/include/Rcpp/DataFrame.h index b76facf72..053b159d3 100644 --- a/inst/include/Rcpp/DataFrame.h +++ b/inst/include/Rcpp/DataFrame.h @@ -117,8 +117,14 @@ namespace Rcpp{ static DataFrame_Impl create(){ return DataFrame_Impl() ; } - - #include + #if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + template + static DataFrame_Impl create(const T&... args) { + return DataFrame_Impl::from_list(Parent::create(args...)); + } + #else + #include + #endif private: void set__(SEXP x){ diff --git a/inst/include/Rcpp/DottedPair.h b/inst/include/Rcpp/DottedPair.h index e056e2d85..eeb6825a8 100644 --- a/inst/include/Rcpp/DottedPair.h +++ b/inst/include/Rcpp/DottedPair.h @@ -35,8 +35,14 @@ RCPP_API_CLASS(DottedPair_Impl), DottedPair_Impl(SEXP x) { Storage::set__(x) ; } - - #include + #if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + template + DottedPair_Impl(const T&... args) { + Storage::set__(pairlist(args...)); + } + #else + #include + #endif void update(SEXP){} diff --git a/inst/include/Rcpp/Function.h b/inst/include/Rcpp/Function.h index 298ba5fc7..d27bcf2db 100644 --- a/inst/include/Rcpp/Function.h +++ b/inst/include/Rcpp/Function.h @@ -82,7 +82,14 @@ namespace Rcpp{ return Rcpp_fast_eval(call, R_GlobalEnv); } - #include + #if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + template + SEXP operator()(const T&... args) const { + return invoke(pairlist(args...), R_GlobalEnv); + } + #else + #include + #endif /** * Returns the environment of this function diff --git a/inst/include/Rcpp/InternalFunction.h b/inst/include/Rcpp/InternalFunction.h index 5a5722beb..42f97f90f 100644 --- a/inst/include/Rcpp/InternalFunction.h +++ b/inst/include/Rcpp/InternalFunction.h @@ -47,9 +47,14 @@ namespace Rcpp{ ) ); } + template + InternalFunction_Impl(RESULT_TYPE (*fun)(T...)) { + set(XPtr >(new CppFunctionN(fun), true)); + } +#else + #include #endif - #include void update(SEXP){} private: diff --git a/inst/include/Rcpp/InternalFunctionWithStdFunction.h b/inst/include/Rcpp/InternalFunctionWithStdFunction.h index 966da22bf..f003b10e2 100644 --- a/inst/include/Rcpp/InternalFunctionWithStdFunction.h +++ b/inst/include/Rcpp/InternalFunctionWithStdFunction.h @@ -23,13 +23,17 @@ #ifndef Rcpp_InternalFunctionWithStdFunction_h #define Rcpp_InternalFunctionWithStdFunction_h +#if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) +#include +#endif #include namespace Rcpp { namespace InternalFunctionWithStdFunction { - + #if !defined(HAS_VARIADIC_TEMPLATES) && !defined(RCPP_USING_CXX11) #include + #endif template class CppFunctionBaseFromStdFunction : public CppFunctionBase { @@ -39,8 +43,7 @@ namespace Rcpp { SEXP operator()(SEXP* args) { BEGIN_RCPP - auto result = call(fun, args); - return Rcpp::module_wrap(result); + return call(fun, args); END_RCPP } @@ -48,22 +51,6 @@ namespace Rcpp { const std::function fun; }; - template - class CppFunctionBaseFromStdFunction : public CppFunctionBase { - public: - CppFunctionBaseFromStdFunction(const std::function &fun) : fun(fun) {} - virtual ~CppFunctionBaseFromStdFunction() {} - - SEXP operator()(SEXP* args) { - BEGIN_RCPP - call(fun, args); - END_RCPP - } - - private: - const std::function fun; - }; - } // namespace InternalFunctionWithStdFunction } // namespace Rcpp diff --git a/inst/include/Rcpp/Language.h b/inst/include/Rcpp/Language.h index 03983f5e5..a2d250a87 100644 --- a/inst/include/Rcpp/Language.h +++ b/inst/include/Rcpp/Language.h @@ -102,7 +102,19 @@ namespace Rcpp{ * 0.0 is wrapped as a numeric vector using wrap( const& double ) * ... */ - #include + #if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + template + Language_Impl(const std::string& symbol, const T&... t) { + Storage::set__(pairlist(Rf_install(symbol.c_str()), t...) ); + } + + template + Language_Impl(const Function& function, const T&... t) { + Storage::set__(pairlist(function, t...)); + } + #else + #include + #endif /** * sets the symbol of the call diff --git a/inst/include/Rcpp/Module.h b/inst/include/Rcpp/Module.h index 88d3a0765..02a242f87 100644 --- a/inst/include/Rcpp/Module.h +++ b/inst/include/Rcpp/Module.h @@ -23,6 +23,7 @@ #define Rcpp_Module_h #include +#include namespace Rcpp{ @@ -85,10 +86,57 @@ namespace Rcpp{ #include #include -#include - // templates CppFunction0, ..., CppFunction65 -#include +#if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) +namespace Rcpp { + template + inline void signature(std::string& s, const char* name) { + s.clear(); + s += get_return_type() + " " + name + "("; + int n = sizeof...(T); + int i = 0; + // Using initializer list as c++11 implementation of a fold expression + (void)std::initializer_list{ + (s += get_return_type(), s += (++i == n ? "" : ", "), 0)... }; + s += ")"; + } + + template + class CppFunctionN : public CppFunction { + public: + CppFunctionN(RESULT_TYPE (*fun)(T...), const char* docstring = 0) : CppFunction(docstring), ptr_fun(fun) {} + + SEXP operator()(SEXP* args) { + BEGIN_RCPP + return call(ptr_fun, args); + END_RCPP + } + + inline int nargs() { return sizeof...(T); } + inline void signature(std::string& s, const char* name) { Rcpp::signature(s, name); } + inline DL_FUNC get_function_ptr() { return (DL_FUNC)ptr_fun; } + + private: + RESULT_TYPE (*ptr_fun)(T...); + }; + + template + class CppFunction_WithFormalsN : public CppFunctionN { + public: + CppFunction_WithFormalsN(RESULT_TYPE (*fun)(T...), Rcpp::List formals_, const char* docstring = 0) : + CppFunctionN(fun, docstring), formals(formals_) {} + + SEXP get_formals() { return formals; } + + private: + Rcpp::List formals; + }; +} +#else + #include + // templates CppFunction0, ..., CppFunction65 + #include +#endif #include #include @@ -129,12 +177,86 @@ namespace Rcpp{ private: ParentMethod* parent_method_pointer ; } ; +#if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + template + inline void ctor_signature(std::string& s, const std::string& classname) { + s.assign(classname); + s += "("; + int n = sizeof...(T); + int i = 0; + // Using initializer list as c++11 implementation of a fold expression + (void)std::initializer_list{ + (s += get_return_type(), s += (++i == n ? "" : ", "), 0)... }; + s += ")"; + } + template + class Constructor_Base { + public: + virtual Class* get_new( SEXP* args, int nargs ) = 0 ; + virtual int nargs() = 0 ; + virtual void signature(std::string& s, const std::string& class_name) = 0 ; + } ; + + template + class Constructor: public Constructor_Base { + public: + virtual Class* get_new( SEXP* args, int nargs ){ + return get_new_impl(args, nargs, traits::make_index_sequence()); + } + virtual int nargs(){ return sizeof...(T) ; } + virtual void signature(std::string& s, const std::string& class_name ){ + ctor_signature(s, class_name) ; + } + + private: + template + Class* get_new_impl(SEXP* args, int nargs, traits::index_sequence) { + return new Class( as(args[Is])... ) ; + } + }; + + template + class Factory_Base { + public: + virtual Class* get_new( SEXP* args, int nargs ) = 0 ; + virtual int nargs() = 0 ; + virtual void signature(std::string& s, const std::string& class_name) = 0 ; + } ; + + template + class Factory : public Factory_Base { + public: + Factory( Class* (*fun)(T...) ) : ptr_fun(fun){} + virtual Class* get_new( SEXP* args, int nargs ){ + return get_new( args, traits::make_index_sequence() ) ; + } + virtual int nargs(){ return sizeof...(T) ; } + virtual void signature(std::string& s, const std::string& class_name ){ + ctor_signature(s, class_name) ; + } + private: + template + Class* get_new( SEXP* args, traits::index_sequence ){ + return ptr_fun( bare_as(args[I])... ) ; + } + Class* (*ptr_fun)(T...) ; + } ; + + inline bool yes( SEXP* /*args*/, int /* nargs */ ){ + return true ; + } + template + bool yes_arity( SEXP* /* args */ , int nargs){ + return nargs == n ; + } + +#else #include #include #include - #include +#endif typedef bool (*ValidConstructor)(SEXP*,int) ; typedef bool (*ValidMethod)(SEXP*,int) ; @@ -259,8 +381,70 @@ namespace Rcpp{ } ; -#include -#include +#if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + template + class CppMethodImplN : public CppMethod { + public: + typedef typename std::conditional::type Method; + typedef CppMethod method_class; + typedef typename Rcpp::traits::remove_const_and_reference::type CLEANED_RESULT_TYPE; + + CppMethodImplN(Method m) : method_class(), met(m) {} + SEXP operator()(Class* object, SEXP* args) { + // Can't pass pointer to member function directly to `call()`, so wrap it in a lambda + auto f = [&object, this](T... cpp_args) -> CLEANED_RESULT_TYPE { + return (object->*met)(cpp_args...); + }; + return call(f, args); + } + inline int nargs() { return sizeof...(T); } + inline bool is_void() { return std::is_void::value; } + inline bool is_const() { return IsConst; } + inline void signature(std::string& s, const char* name) { Rcpp::signature(s, name); } + private: + Method met; + }; + + template + using CppMethodN = CppMethodImplN; + + template + using const_CppMethodN = CppMethodImplN; + + template + class Pointer_CppMethodImplN : public CppMethod { + public: + typedef typename std::conditional::type Method; + typedef CppMethod method_class; + typedef typename Rcpp::traits::remove_const_and_reference::type CLEANED_RESULT_TYPE; + + Pointer_CppMethodImplN(Method m) : method_class(), met(m) {} + SEXP operator()(Class* object, SEXP* args) { + // Need to have `object` as the first argument to the function, so wrap it in a lambda + auto f = [&object, this](T... cpp_args) -> CLEANED_RESULT_TYPE { + return met(object, cpp_args...); + }; + return call(f, args); + } + inline int nargs() { return sizeof...(T); } + inline bool is_void() { return std::is_void::value;} + inline bool is_const() { return IsConst; } + inline void signature(std::string& s, const char* name) { Rcpp::signature(s, name); } + private: + Method met; + }; + + template + using Pointer_CppMethodN = Pointer_CppMethodImplN; + + template + using Const_Pointer_CppMethodN = Pointer_CppMethodImplN; +#else + #include + #include +#endif template class CppProperty { @@ -368,8 +552,28 @@ namespace Rcpp{ } ; } -// function factories -#include +#if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) +namespace Rcpp { + template + void function(const char* name_, RESULT_TYPE (*fun)(T... t), const char* docstring = 0) { + Rcpp::Module* scope = ::getCurrentScope(); + if (scope) { + scope->Add(name_, new CppFunctionN(fun, docstring)); + } + } + + template + void function(const char* name_, RESULT_TYPE (*fun)(T... t), Rcpp::List formals, const char* docstring = 0) { + Rcpp::Module* scope = ::getCurrentScope(); + if (scope) { + scope->Add(name_, new CppFunction_WithFormalsN(fun, formals, docstring)); + } + } +} +#else + // function factories + #include +#endif namespace Rcpp { diff --git a/inst/include/Rcpp/Pairlist.h b/inst/include/Rcpp/Pairlist.h index 043615efb..d06bf6f34 100644 --- a/inst/include/Rcpp/Pairlist.h +++ b/inst/include/Rcpp/Pairlist.h @@ -42,9 +42,14 @@ namespace Rcpp{ Pairlist_Impl(SEXP x){ Storage::set__(r_cast(x)) ; } - - #include - + #if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + template + Pairlist_Impl(const T&... args ){ + Storage::set__(pairlist(args... )) ; + } + #else + #include + #endif void update(SEXP x){ SET_TYPEOF( x, LISTSXP ) ; } diff --git a/inst/include/Rcpp/grow.h b/inst/include/Rcpp/grow.h index e5a36c67b..b67d05a69 100644 --- a/inst/include/Rcpp/grow.h +++ b/inst/include/Rcpp/grow.h @@ -70,7 +70,18 @@ namespace Rcpp { return grow(Rf_mkString(head), y); } - #include + #if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + template + SEXP pairlist(const T1& t1) { + return grow( t1, R_NilValue ) ; + } + template + SEXP pairlist(const T& t1, const TArgs&... args) { + return grow(t1, pairlist(args...)); + } + #else + #include + #endif } // namespace Rcpp diff --git a/inst/include/Rcpp/internal/call.h b/inst/include/Rcpp/internal/call.h new file mode 100644 index 000000000..05a22a0e9 --- /dev/null +++ b/inst/include/Rcpp/internal/call.h @@ -0,0 +1,58 @@ +#ifndef RCPP_INTERNAL_CALL_H +#define RCPP_INTERNAL_CALL_H + +#include +#include + +#if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + +namespace Rcpp { +namespace internal { +// Utility struct so that we can pass a pack of types between functions +template struct type_pack {}; + + +/** + * This specialisation is for functions that return a value, whereas the below + * is for void-returning functions. + * + * The "* = nullptr" default argument allows both templates to be well-defined + * regardless of which one is used. + */ +template ::value>::type* = nullptr> +SEXP call_impl(const F& fun, SEXP* args, type_pack, + traits::index_sequence) { + RESULT_TYPE res = fun((typename traits::input_parameter::type(args[Is]))...); + return Rcpp::module_wrap(res); +} + +template ::value>::type* = nullptr> +SEXP call_impl(const F& fun, SEXP* args, type_pack, + traits::index_sequence) { + fun((typename traits::input_parameter::type(args[Is]))...); + return R_NilValue; +} +} // namespace internal + +/** + * Helper for calling a function with an array of SEXP arguments, + * where each argument is converted to the appropriate type before being passed + * to the function. A compile-time sequence is used to index the SEXP array. + * + * The function only needs the intended types of the result and arguments, + * which allows the template to be used for function pointers, lambdas, and + * `std::function` objects. + */ +template +SEXP call(const F& fun, SEXP* args) { + return internal::call_impl(fun, args, + internal::type_pack{}, + traits::make_index_sequence{}); +} +} // namespace Rcpp + +#endif + +#endif diff --git a/inst/include/Rcpp/module/class.h b/inst/include/Rcpp/module/class.h index 815626440..4ef998962 100644 --- a/inst/include/Rcpp/module/class.h +++ b/inst/include/Rcpp/module/class.h @@ -111,8 +111,21 @@ return constructor( docstring, valid ) ; } -#include -#include +#if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + template + self& constructor( const char* docstring = 0, ValidConstructor valid = &yes_arity ){ + AddConstructor( new Constructor , valid, docstring ) ; + return *this ; + } + template + self& factory( Class* (*fun)(T...), const char* docstring = 0, ValidConstructor valid = &yes_arity ){ + AddFactory( new Factory(fun) , valid, docstring ) ; + return *this ; + } +#else + #include + #include +#endif public: @@ -253,8 +266,47 @@ return *this ; } -#include -#include +#if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + template + self& method(const char* name_, RESULT_TYPE (Class::*fun)(T...), + const char* docstring = 0, ValidMethod valid = &yes_arity) { + AddMethod( name_, new CppMethodN(fun), valid, docstring); + return *this; + } + template + self& method(const char* name_, RESULT_TYPE (Class::*fun)(T...) const, + const char* docstring = 0, ValidMethod valid = &yes_arity) { + AddMethod( name_, new const_CppMethodN(fun), valid, docstring); + return *this; + } + template + self& nonconst_method(const char* name_, RESULT_TYPE (Class::*fun)(T...), + const char* docstring = 0, ValidMethod valid = &yes_arity) { + AddMethod( name_, new CppMethodN(fun), valid, docstring); + return *this; + } + template + self& const_method(const char* name_, RESULT_TYPE (Class::*fun)(T...) const, + const char* docstring = 0, ValidMethod valid = &yes_arity) { + AddMethod( name_, new const_CppMethodN(fun), valid, docstring); + return *this; + } + template + self& method(const char* name_, RESULT_TYPE (*fun)(Class*, T...), + const char* docstring = 0, ValidMethod valid = &yes_arity) { + AddMethod( name_, new Pointer_CppMethodN(fun), valid, docstring); + return *this; + } + template + self& const_method(const char* name_, RESULT_TYPE (*fun)(const Class*, T...), + const char* docstring = 0, ValidMethod valid = &yes_arity) { + AddMethod( name_, new Const_Pointer_CppMethodN(fun), valid, docstring); + return *this; + } +#else + #include + #include +#endif bool has_method( const std::string& m){ return vec_methods.find(m) != vec_methods.end() ; diff --git a/inst/include/Rcpp/traits/index_sequence.h b/inst/include/Rcpp/traits/index_sequence.h new file mode 100644 index 000000000..9a1e54b13 --- /dev/null +++ b/inst/include/Rcpp/traits/index_sequence.h @@ -0,0 +1,27 @@ +#ifndef RCPP_TRAITS_INDEX_SEQUENCE_H +#define RCPP_TRAITS_INDEX_SEQUENCE_H + + +#if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + +namespace Rcpp { +namespace traits { + /** + * C++11 implementations for index_sequence and make_index_sequence. + * To avoid name conflicts or ambiguity when compiling with C++14 or later, + * they should always be prefaced with `Rcpp::traits::` when used. + */ + template + struct index_sequence {}; + + template + struct make_index_sequence : make_index_sequence {}; + + template + struct make_index_sequence<0, Is...> : index_sequence {}; +} +} + +#endif + +#endif diff --git a/inst/include/Rcpp/traits/named_object.h b/inst/include/Rcpp/traits/named_object.h index 60eec15c3..f3734f3de 100644 --- a/inst/include/Rcpp/traits/named_object.h +++ b/inst/include/Rcpp/traits/named_object.h @@ -22,6 +22,8 @@ #ifndef Rcpp__traits__named_object__h #define Rcpp__traits__named_object__h +#include + namespace Rcpp{ class Argument ; @@ -63,6 +65,19 @@ template struct is_named : public false_type{}; template struct is_named< named_object > : public true_type {}; template <> struct is_named< Rcpp::Argument > : public true_type {}; + +#if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + template struct is_any_named : public false_type {}; + template struct is_any_named : public is_named::type {}; + + template + struct is_any_named + : public std::conditional< + is_any_named::value, + std::true_type, + is_any_named>::type {}; +#endif + } // namespace traits } // namespace Rcpp diff --git a/inst/include/Rcpp/vector/Vector.h b/inst/include/Rcpp/vector/Vector.h index 3cac35107..01505eb02 100644 --- a/inst/include/Rcpp/vector/Vector.h +++ b/inst/include/Rcpp/vector/Vector.h @@ -1122,7 +1122,55 @@ class Vector : return Vector( 0 ) ; } - #include + #if defined(HAS_VARIADIC_TEMPLATES) || defined(RCPP_USING_CXX11) + public: + template + static Vector create(const T&... t){ + return create__dispatch( typename traits::integral_constant::value + >::type(), t... ) ; + } + + private: + template + static Vector create__dispatch(traits::false_type, const T&... t){ + Vector res(sizeof...(T)) ; + iterator it(res.begin()); + create_dispatch_impl(it, t...); + return res; + } + template + static Vector create__dispatch( traits::true_type, const T&... t) { + Vector res(sizeof...(T)) ; + Shield names(::Rf_allocVector(STRSXP, sizeof...(T))); + int index = 0; + iterator it(res.begin()); + replace_element_impl(it, names, index, t...); + res.attr("names") = names; + return res; + } + template + static void create_dispatch_impl(iterator& it, const T& t) { + *it = converter_type::get(t); + } + + template + static void create_dispatch_impl(iterator& it, const T& t, const TArgs&... args) { + *it = converter_type::get(t); + create_dispatch_impl(++it, args...); + } + template + static void replace_element_impl(iterator& it, Shield& names, int& index, const T& t) { + replace_element(it, names, index, t); + } + template + static void replace_element_impl(iterator& it, Shield& names, int& index, const T& t, const TArgs&... args) { + replace_element(it, names, index, t); + replace_element_impl(++it, names, ++index, args...); + } + #else + #include + #endif public: diff --git a/inst/tinytest/cpp/InternalFunction.cpp b/inst/tinytest/cpp/InternalFunction.cpp index c6d2515eb..7261da9c8 100644 --- a/inst/tinytest/cpp/InternalFunction.cpp +++ b/inst/tinytest/cpp/InternalFunction.cpp @@ -27,6 +27,15 @@ int add(int a, int b) { return a + b; } +void dummy(int a, int b) { + Rcpp::Rcout << "dummy called" << std::endl; +} + +// [[Rcpp::export]] +Rcpp::InternalFunction getDummy() { + return Rcpp::InternalFunction( &dummy ); +} + // [[Rcpp::export]] Rcpp::InternalFunction getAdd() {