为什么一个Rcpp::Function可以当boost::function使用,能在运行时自省吗?

Why can a Rcpp::Function be used as boost::function, and can it be introspected at runtime?

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

我有两个问题。首先,我不太明白这是怎么回事,因为据我所知,Rcpp::Function 不是 boost::function 的子 class。 vector如何存储这些class不相同的对象呢? (或者他们是否以某种方式共享 class?)

其次,更重要的是,我希望能够在运行时内省对象。我想迭代向量和 return 它的 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()

(请注意,输出行的顺序可能因环境缓冲输出的方式而异,因此您可能会看到它以不同的顺序出现。)

How does the vector store these objects that don't have the same class?

好吧,不是 存储此类对象的向量(直接),而是向量中新创建的 boost::function 对象将存储实例。这个对象会怎么做?

一些简单的演示 class 说明了如何 可以 实现:

// 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));

请注意,class 只是最低限度地实现(仅复制构造函数;可能还会添加移动构造函数和适当的赋值运算符),因为它仅用于演示目的。

I would like to be able to introspect the objects at runtime.

您正在寻找 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?