xtensor-stack / xtensor-r

R bindings for xtensor
BSD 3-Clause "New" or "Revised" License
86 stars 15 forks source link

Find a way to expose ufuncs/rvectorize #11

Open wolfv opened 7 years ago

wolfv commented 7 years ago

We have the (working) rvectorize, but afaik there is no way to expose just a function pointer with RCpp (without the decorations). Maybe @eddelbuettel has a hint?

I.e. this is corresponding code for xtensor-python, where the result of xt::pyvectorize get's bound to a function name in python.

#include "pybind11/pybind11.h"
#include "xtensor-python/pyvectorize.hpp"
#include <numeric>
#include <cmath>

namespace py = pybind11;

double scalar_func(double i, double j)
{
    return std::sin(i) - std::cos(j);
}

PYBIND11_PLUGIN(xtensor_python_test)
{
    py::module m("xtensor_python_test", "Test module for xtensor python bindings");

    m.def("vectorized_func", xt::pyvectorize(scalar_func), "");

    return m.ptr();
}
eddelbuettel commented 7 years ago

Hm, not sure I really understand what you are asking but here is one attempt:

Rcpp can be used to marshall types which R itself has (in its C API); this includes Rcpp::XPtr. See for example this write-up at the Rcpp Gallery.

If I misunderstand what you're asking, never mind :) I got up on another continent today...

wolfv commented 7 years ago

I think what I am trying to say is that for // [[Rcpp::export]] to work, the following line always has to contain the signature of the function, right?

Because, with e.g. pybind11, the signature of the function is found "by the compiler" and not by preprocessing, and that is what enables the xt::pyvectorize which creates a rvectorizer struct with operator() and returns that.

So technically, it would be cool to be able to do:

double scalar_func(double i, double j)
{
    return std::sin(i) - std::cos(j);
}

// [[Rcpp::export(name=".vectorized_func"]]
rvectorize(scalar_func);

Currently what would be possible is (I guess):

double scalar_func(double i, double j)
{
    return std::sin(i) - std::cos(j);
}

static auto f = rvectorize(scalar_func);

// [[Rcpp::export(name=".vectorized_func"]]
auto vectorized_func(rarray<double> a, rarray<double> b) {
    return  f(a, b);
}

(Please consider this pseudocode as I didn't try to compile it).

Cheers,

Wolf

eddelbuettel commented 7 years ago

Yes, what you say in paragraph one is correct for what we call 'Rcpp Attributes'.

You have another option with what we call 'Rcpp Modules'. That is older, more declarative and modelled after Boost.Python. You may be able to tweak it. Its internal code is however more or less a dead end.

wolfv commented 7 years ago

Oh, I see. Rcpp Modules actually does look a lot more like Boost.Python/pybind11/Cxxwrap.jl so it would be more "familiar" for users of the full xtensor suite.

But the use of Rcpp Modules is generally discouraged?

eddelbuettel commented 7 years ago

It's complicated. It was a stroke of genius by Romain, but he himself no longer seems to like it (ie when he did his [ultimately not that successful] Rcpp11 he didn't do modules) and he no longer works on Rcpp. Nobody else has a real handle on the code.

It is still used. But the code is less well maintained than the rest of Rcpp. Waits for someone to adopt it. If you want to earn some R karma ... ;-)