关键部分:定义我的类型集的类型列表(mp11::mp_list,称为types)helper 元函数 num_type_from_i 和 i_form_num_type 用于在给定索引/给定类型的索引的情况下查询类型模板结构 dispatch_impl,递归使用,提供对类型列表的迭代用于终止递归的 dispatch_impl 的专用版本一个方便的函数 dispatch_type() 调用 dispatch_impl 并定义列表长度/最大递归深度示例函数对象 MatInit 和 Length 以及它们的 R 接口 mat_init() 和 length()//[[Rcpp::depends(RcppArmadillo)]]//[[Rcpp::plugins(cpp11)]]#include #include #include 命名空间 mp11 = boost::mp11;使用类型 = mp11::mp_list;模板 <std::size_t I>使用 num_type_from_i = mp11::mp_at_c;模板 使用 i_form_num_type = mp11::mp_find;模板 <typename T, std::size_t N>结构 dispatch_impl{模板<std::size_t K, 模板<typename>类 Fn,类型名 ...Ar>静态自动调用(std::size_t i, Ar&&... rg) ->decltype(Fn>()(std::forward(rg)...)){如果(我== 0){返回 Fn()(std::forward(rg)...);}别的{return dispatch_impl::template call(i - 1,std::forward(rg)...);}}};模板 struct dispatch_impl{模板<std::size_t K, 模板<typename>类 Fn,类型名 ...Ar>静态自动调用(std::size_t i, Ar&&... rg) ->decltype(Fn>()(std::forward(rg)...)){如果(我== 0){返回 Fn()(std::forward(rg)...);}别的{throw std::runtime_error("不支持的数据类型.");}}};模板类 Fn,类型名 ...Ar>auto dispatch_type(std::size_t type, Ar&&... rg) ->decltype(Fn()(std::forward(rg)...)){使用 n_types = mp11::mp_size;返回 dispatch_impl::template call(type, std::forward(rg)...);}模板 结构体初始化{SEXP operator()(arma::uword n_rows, arma::uword n_cols){auto res = new arma::Mat(n_rows, n_cols);auto ind = std::size_t{i_form_num_type::value};return Rcpp::XPtr(res, true, Rcpp::wrap(ind));}};//[[Rcpp::export]]SEXP mat_init(arma::uword n_rows, arma::uword n_cols, std::size_t data_type){return dispatch_type(data_type, n_rows, n_cols);}模板 结构长度{arma::uword operator()(SEXP x){返回 Rcpp::XPtr(x)->n_elem;}};//[[Rcpp::export]]arma::uword 长度(性别 x){std::size_t type = Rcpp::as<std::size_t>(R_ExternalPtrTag(x));return dispatch_type(type, x);}通过这种方式可以轻松修改类型列表,除了需要模板化的函数对象而不是函数模板之外,诸如 length() 之类的函数的实现相当简洁.此外,我不必在 R 和 C 之间传递数据类型索引,而是可以将索引存储在外部指针结构中.如果有人发现潜在问题,我很乐意听取他们的意见.I'm in the situation where I have an Rcpp::XPtr to an Armadillo object (e.g. arma::Mat, which may be a matrix of one of the supported data types). Now I'd like to write a function that queries the number of elements. The best I could come up with so far is the following (inspired by bigstatsr):#define DISPATCH_DATA_TYPE(CALL) \{ \ switch (data_type) \ { \ case 1: CALL(unsigned short) \ case 2: CALL(unsigned int) \ case 3: CALL(unsigned long) \ case 4: CALL(short) \ case 5: CALL(int) \ case 6: CALL(long) \ case 7: CALL(float) \ case 8: CALL(double) \ default: throw Rcpp::exception("Unsupported data type."); \ } \}template <typename T>arma::uword mat_length(SEXP mat){ Rcpp::XPtr< arma::Mat<T> > p(mat); return p->n_elem;}#define MAT_LENGTH(TYPE) return mat_length<TYPE>(mat);// [[Rcpp::export]]arma::uword mat_length(SEXP mat, int data_type){ DISPATCH_DATA_TYPE(MAT_LENGTH)}Is there a better way of doing this? I'm using this pattern for quite a few functions and the verbosity is becoming a problem. Ideally I'd have a single but concise function, like (doesn't work of course)arma::uword mat_length(SEXP mat){ Rcpp::XPtr<arma::Mat> p(mat); return p->n_elem;}instead of two functions + a macro for every single instance where I pass an XPtr like that from R to C.Bonus question: is there anything obviously wrong with the macro-based approach? Is this somehow inefficient or could lead to problems down the line?To create a reproducible example, add// [[Rcpp::depends(RcppArmadillo)]]#include <RcppArmadillo.h>// [[Rcpp::export]]SEXP setup_mat(arma::uword n_rows, arma::uword n_cols){ arma::mat* res = new arma::mat(n_rows, n_cols); return Rcpp::XPtr<arma::mat>(res);}and run Rcpp::sourceCpp() on the file in R. 解决方案 The best non-macro approach I could come up with so far (using boost::mp11) is the following:Key parts:a type list (mp11::mp_list, called types) defining my set of typeshelper metafunctions num_type_from_i and i_form_num_type to query type given an index/index given a typea templated struct dispatch_impl, used recursively, providing iteration over the type lista specialized version of dispatch_impl for terminating the recursiona convenience function dispatch_type() calling dispatch_impl and defining the list length/max recursion depthexample function objects MatInit and Length alongside their R interfaces mat_init() and length()// [[Rcpp::depends(RcppArmadillo)]]// [[Rcpp::plugins(cpp11)]]#include <RcppArmadillo.h>#include <boost/mp11/list.hpp>#include <boost/mp11/algorithm.hpp>namespace mp11 = boost::mp11;using types = mp11::mp_list<int, float, double>;template <std::size_t I>using num_type_from_i = mp11::mp_at_c<types, I>;template <typename T>using i_form_num_type = mp11::mp_find<types, T>;template <typename T, std::size_t N> struct dispatch_impl{ template <std::size_t K, template<typename> class Fn, typename ...Ar> static auto call(std::size_t i, Ar&&... rg) -> decltype(Fn<mp11::mp_at_c<T, 0>>()(std::forward<Ar>(rg)...)) { if (i == 0) { return Fn<mp11::mp_at_c<T, K>>()(std::forward<Ar>(rg)...); } else { return dispatch_impl<T, N - 1>::template call<K + 1, Fn>(i - 1, std::forward<Ar>(rg)...); } }};template <typename T> struct dispatch_impl<T, 1>{ template <std::size_t K, template<typename> class Fn, typename ...Ar> static auto call(std::size_t i, Ar&&... rg) -> decltype(Fn<mp11::mp_at_c<T, 0>>()(std::forward<Ar>(rg)...)) { if (i == 0) { return Fn<mp11::mp_at_c<T, K>>()(std::forward<Ar>(rg)...); } else { throw std::runtime_error("Unsupported data type."); } }};template <template<typename> class Fn, typename ...Ar>auto dispatch_type(std::size_t type, Ar&&... rg) -> decltype(Fn<num_type_from_i<0>>()(std::forward<Ar>(rg)...)){ using n_types = mp11::mp_size<types>; return dispatch_impl<types, std::size_t{n_types::value}>::template call<0, Fn>(type, std::forward<Ar>(rg)...);}template <typename T>struct MatInit{ SEXP operator()(arma::uword n_rows, arma::uword n_cols) { auto res = new arma::Mat<T>(n_rows, n_cols); auto ind = std::size_t{i_form_num_type<T>::value}; return Rcpp::XPtr<arma::Mat<T>>(res, true, Rcpp::wrap(ind)); }};// [[Rcpp::export]]SEXP mat_init(arma::uword n_rows, arma::uword n_cols, std::size_t data_type){ return dispatch_type<MatInit>(data_type, n_rows, n_cols);}template <typename T>struct Length{ arma::uword operator()(SEXP x) { return Rcpp::XPtr<arma::Mat<T>>(x)->n_elem; }};// [[Rcpp::export]]arma::uword length(SEXP x){ std::size_t type = Rcpp::as<std::size_t>(R_ExternalPtrTag(x)); return dispatch_type<Length>(type, x);}This way the list of types can easily be modified and apart from requiring templated function objects instead of function templates, implementation of functions such as length() is fairly succinct.Furthermore, I don't have to pass the data type index between R and C, but can store the index within the external pointer structure.If anyone sees potential issues, I'd be keen on hearing from them. 这篇关于如何处理可能具有多种类型之一的 Rcpp::XPtr的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
09-05 06:32