为什么Rcpp :: Function可以用作boost :: function,并且可以在运行时自省?

时间:2019-03-13 01:40:35

标签: c++ r boost rcpp

我有一个vector<boost::function<void(void)>>-本质上是类似函数的对象的向量。向量包含一些Rcpp::Function对象以及一些boost::function<void(void)>对象。

对此我有两个问题。首先,我不太了解它是如何工作的,因为据我所知,Rcpp::Function不是boost::function的子类。向量如何存储这些不具有相同类的对象? (或者他们以某种方式共享课程?)

第二,更重要的是,我希望能够在运行时自省对象。我想遍历向量并返回它的Rcpp::List表示形式:任何Rcpp::Function对象将被添加到列表中,而任何boost::function对象将简单地由任意字符串表示,像"C++ function"

在下面的示例中,我有一个C ++函数test,该函数将Rcpp::Function作为输入并添加到向量中。它还向向量添加boost::function<void(void)>。然后遍历列表,执行每个功能。我还没有弄清的最后一部分是如何构建List,其中添加到列表中的项目取决于每个项目的类型。这可能吗?

library(Rcpp)

cppFunction(
  includes = '
    #include <boost/function.hpp>
    #include <boost/bind.hpp>
    #include <iostream>

    void cpp_message(std::string s) {
      std::cerr << s << "\\n";
    }
  ',
  code = '
    Rcpp::List test(Rcpp::Function r_fn) {
      std::vector<boost::function0<void> > fns;

      // Add a Rcpp::Function to the vector
      fns.push_back(r_fn);

      // Add a boost::function<void(void)> to the vector
      boost::function<void(void)> cpp_fn = boost::bind(&cpp_message, "bar");
      fns.push_back(cpp_fn);

      // Execute the functions to demonstrate the vector works
      for (int i=0; i<fns.size(); i++) {
        fns[i]();
      }

      // Create the List       
      Rcpp::List result;
      for (int i=0; i<fns.size(); i++) {
        // What I would like to do is something like:
        // if (fns[i] is a Rcpp::Function) {
        //   result[i] = fns[i];
        // } else {
        //   result[i] = "C++ function";
        // }
      }

      return result;
    }
  ',
  depends = "BH"
)

test(function() message("foo"))
#> foo
#> bar
#> list()

(请注意,输出行的顺序可能会有所不同,具体取决于环境如何缓冲输出,因此您可能会看到其输出顺序不同。)

1 个答案:

答案 0 :(得分:2)

  

向量如何存储这些不具有相同类的对象?

好吧,不是直接存储此类对象的向量,而是在向量内部新创建的boost::function对象将存储实例。该对象将如何操作?

一些简单的演示类说明了如何实现此

// first need a generic template allowing the Demo<void(void)> syntax
template <typename S>
class Demo;

// now specialising (otherwise, we'd need to instantiate Demo<void, void>)
template <typename R, typename ... PP>
class Demo<R(PP...)>
{
    class Wrapper
    {
    public:
        virtual ~Wrapper() { }
        virtual R operator()(PP...) = 0;
    };

    template <typename T>
    class SpecificWrapper : public Wrapper
    {
        T t;
    public:
        SpecificWrapper(T& t) : t(t) { };
        virtual R operator()(PP...pp) { return t(pp...); }
    };

    // the trick: pointer to POLYMORPHIC type!
    Wrapper* w;

public:
    // be aware that this constructor deliberately is NOT explicit
    template <typename T>
    Demo(T& t) : w(new SpecificWrapper<T>(t)) { }

    R operator()(PP...pp) { return (*w)(pp...); }
};

非显式构造函数允许隐式创建一个新的Demo对象:

Rcpp::Function r; // simplified just for demo!
std::vector<Demo<void(void)>> v;
v.push_back(r);   // implicit call to non-explicit constructor! equivalent to:
v.push_back(Demo<void(void)>(r));

请注意,该类仅实现了最少的操作(仅是复制构造函数;可能尚未添加move构造函数和适当的赋值运算符),因为该类仅用于演示目的。

  

我希望能够在运行时自检对象。

您正在寻找std::function::target

auto l = []() { std::cout << "demo" << std::endl; };
std::function<void(void)> f(l);
auto* pl = f.target<decltype(l)>();
if(pl)
    (*pl)();

但是,这闻起来有点糟糕的设计,就像需要dynamic_castDemo最有可能在其target变体中用于包装指针)一样。您为什么要找回这个?您是否只是想像Rcpp一样处理所有功能?