Почему Rcpp :: Function может использоваться в качестве boost :: function, и может ли он подвергаться самоанализу во время выполнения? - PullRequest
2 голосов
/ 13 марта 2019

У меня есть 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 Ответ

2 голосов
/ 13 марта 2019

Как вектор хранит эти объекты, которые не имеют одинаковый класс?

Ну, это , а не вектор, который хранит такие объекты (напрямую), вместо этого только что созданный 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...); }
};

Неявный конструктор позволяет неявно создать новый демонстрационный объект:

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

Имейте в виду, что класс реализован минимально (только конструктор копирования; еще можно добавить конструктор перемещения и соответствующие операторы присваивания), поскольку он служит только для демонстрационных целей.

Я бы хотел иметь возможность анализировать объекты во время выполнения.

Вы ищете 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_cast (который Demo, скорее всего, будет использовать для указателя оболочки в его собственном варианте target). Почему вы хотите получить это обратно? Разве вы не хотите просто обрабатывать все функции одинаково, Rcpp или нет?

...