jeudi 22 décembre 2022

Specialising Rcpp::as() for std::array

One of my projects uses C++11 std::array for fixed-sized array types, and so I'm trying to specialise Rcpp::as() for convenient conversion from SEXP to these types. Since this requires partial specialisation, I've followed the Exporter route as outlined in Rcpp-extending:

#include <RcppCommon.h>
#include <array>

namespace Rcpp {
namespace traits {

// Partial specialisation to allow as<array<T,D>>(...)
template <typename ElementType, int Dimensionality>
class Exporter<std::array<ElementType,Dimensionality>>
{
private:
    std::array<ElementType,Dimensionality> value;

public:
    Exporter (SEXP x)
    {
        std::vector<ElementType> vec = as<std::vector<ElementType>>(x);
        if (vec.size() != Dimensionality)
            throw Rcpp::exception("Array does not have the expected number of elements");
        for (int i=0; i<Dimensionality; i++)
            value[i] = vec[i];
    }
    
    std::array<ElementType,Dimensionality> get () { return value; }
};

} // traits namespace
} // Rcppnamespace

#include <Rcpp.h>

Other code within the package can then do, for example,

std::array<double,3> arr = Rcpp::as<std::array<double,3>>(vec);

I've bundled this up into a minimal package for example purposes; the "real" application is here.

The problem is that this approach compiles and works fine for me locally (macOS, clang), but gcc doesn't like it. The output from a GitHub CI action on the minimal package is below.

* installing *source* package ‘Rcpp.asArray’ ...
** using staged installation
** libs
g++ -std=gnu++11 -I"/opt/R/4.2.2/lib/R/include" -DNDEBUG  -I'/home/runner/work/_temp/Library/Rcpp/include' -I/usr/local/include   -fpic  -g -O2  -c main.cpp -o main.o
In file included from /home/runner/work/_temp/Library/Rcpp/include/Rcpp/as.h:25,
                 from /home/runner/work/_temp/Library/Rcpp/include/RcppCommon.h:169,
                 from array.h:4,
                 from main.cpp:1:
/home/runner/work/_temp/Library/Rcpp/include/Rcpp/internal/Exporter.h: In instantiation of ‘Rcpp::traits::Exporter<T>::Exporter(SEXP) [with T = std::array<double, 3>; SEXP = SEXPREC*]’:
/home/runner/work/_temp/Library/Rcpp/include/Rcpp/as.h:87:41:   required from ‘T Rcpp::internal::as(SEXP, Rcpp::traits::r_type_generic_tag) [with T = std::array<double, 3>; SEXP = SEXPREC*]’
/home/runner/work/_temp/Library/Rcpp/include/Rcpp/as.h:152:31:   required from ‘T Rcpp::as(SEXP) [with T = std::array<double, 3>; SEXP = SEXPREC*]’
main.cpp:8:62:   required from here
/home/runner/work/_temp/Library/Rcpp/include/Rcpp/internal/Exporter.h:31:42: error: no matching function for call to ‘std::array<double, 3>::array(SEXPREC*&)’
   31 |                     Exporter( SEXP x ) : t(x){}
      |                                          ^~~~
In file included from /usr/include/c++/11/tuple:39,
                 from /usr/include/c++/11/bits/hashtable_policy.h:34,
                 from /usr/include/c++/11/bits/hashtable.h:35,
                 from /usr/include/c++/11/unordered_map:46,
                 from /home/runner/work/_temp/Library/Rcpp/include/Rcpp/platform/compiler.h:153,
                 from /home/runner/work/_temp/Library/Rcpp/include/Rcpp/r/headers.h:62,
                 from /home/runner/work/_temp/Library/Rcpp/include/RcppCommon.h:30,
                 from array.h:4,
                 from main.cpp:1:
/usr/include/c++/11/array:95:12: note: candidate: ‘std::array<double, 3>::array()’
   95 |     struct array
      |            ^~~~~
/usr/include/c++/11/array:95:12: note:   candidate expects 0 arguments, 1 provided
/usr/include/c++/11/array:95:12: note: candidate: ‘constexpr std::array<double, 3>::array(const std::array<double, 3>&)’
/usr/include/c++/11/array:95:12: note:   no known conversion for argument 1 from ‘SEXP’ {aka ‘SEXPREC*’} to ‘const std::array<double, 3>&’
/usr/include/c++/11/array:95:12: note: candidate: ‘constexpr std::array<double, 3>::array(std::array<double, 3>&&)’
/usr/include/c++/11/array:95:12: note:   no known conversion for argument 1 from ‘SEXP’ {aka ‘SEXPREC*’} to ‘std::array<double, 3>&&’
make: *** [/opt/R/4.2.2/lib/R/etc/Makeconf:178: main.o] Error 1
ERROR: compilation failed for package ‘Rcpp.asArray’
* removing ‘/home/runner/work/Rcpp.asArray/Rcpp.asArray/Rcpp.asArray.Rcheck/Rcpp.asArray’

I don't know the internals of Rcpp well enough to be sure, but it looks like the default implementation of Exporter is being used in preference to the custom one, and failing because there is no std::array constructor for SEXP, which would involve intrusive modification.

Can anyone suggest a way to resolve this, please?

Aucun commentaire:

Enregistrer un commentaire