Search code examples
c++rboostrcpp

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


I have a vector<boost::function<void(void)>> -- essentially, a vector of function-like objects. The vector contains some Rcpp::Function objects, as well as some boost::function<void(void)> objects.

I have two questions about it. First off, I don’t really understand how this works, because as far as I can tell, Rcpp::Function isn’t a subclass of boost::function. How does the vector store these objects that don't have the same class? (Or do they share a class somehow?)

Second, and more important, I would like to be able to introspect the objects at runtime. I want to iterate over the vector and return a Rcpp::List representation of it: any Rcpp::Function objects will be added to the List, and any boost::function objects will simply be represented with an arbitrary string, like "C++ function".

In the example below, I have a C++ function test which takes an Rcpp::Function as input and adds to a vector. It also adds a boost::function<void(void)> to the vector. Then it iterates over the list, executing each function. The last part, which I haven't figured out, is how to build a List, where the item added to the list depends each item's type. Is this possible?

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

(Note that the order of output lines can differ depending on how the environment buffers the output, so you may see it come out in a different order.)


Solution

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

    Well, it is not the vector that stores such objects (directly), instead a newly created boost::function object inside the vector will store the instance. How would the this object do so?

    Some simple demo class illustrates how this could be implemented:

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

    The non-explicit constructor allows to implicitly create a new Demo object:

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

    Be aware that the class is just minimally implemented (solely the copy constructor; move constructor and appropriate assignment operators might yet be added), as it only serves for demonstration purposes.

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

    You're looking for 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)();
    

    But that smells a bit of bad design, just like needing dynamic_cast (which Demo most likely would use on the wrapper pointer in its own variant of target). Why would you want to get this back? Wouldn't you rather just want to handle all functions alike, whether Rcpp or not?