В Rcpp, как получить строки и числа, возвращаемые с помощью шаблонов - PullRequest
0 голосов
/ 03 июля 2018

продолжение этого вопроса: В Rcpp, как получить пользовательскую структуру из C в R

Как вы получаете шаблон Rcpp для возврата строк и чисел, а не какой-то тип "Векторы"? Я пытаюсь перенести чужой C-код в R, и они используют все виды различных типов данных (char, unsigned char, short, unsigned short, int, float, long int, long unsigned int, double - нет, на самом деле, все они находятся в самом заголовке!), что мне нужно "привести" в строки и числа в R. Видя комментарий от "Ralf Stubner", я изменил ваш пример, чтобы сгенерировать MWE, показывающий проблему:

#include <RcppCommon.h>

typedef struct {
  char   firstname[128];
//  long unsigned int big_number;
} HEADER_INFO;

namespace Rcpp {
  template <>
    SEXP wrap(const HEADER_INFO& x);
}

#include <Rcpp.h>

namespace Rcpp {
  template <>
    SEXP wrap(const HEADER_INFO& x) {
      Rcpp::CharacterVector firstname(x.firstname, x.firstname + 128);
//      Rcpp::Integer big_number(x.big_number);
      return Rcpp::wrap(Rcpp::List::create(Rcpp::Named("firstname") = firstname
//                                           ,Rcpp::Named("big_number") = big_number
      ));
    };
}

//  [[Rcpp::export]]
HEADER_INFO getHeaderInfo() {
  HEADER_INFO header;
  strcpy( header.firstname, "Albert" );
//  header.big_number = 123456789012345;
  return header;
}

/*** R
getHeaderInfo()
*/

Когда вы запускаете это в R:

> getHeaderInfo()
$firstname
  [1] "65"  "108" "98"  "101" "114" "116" "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"  
 [19] "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"  
 [37] "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"  
 [55] "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"  
 [73] "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"  
 [91] "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"  
[109] "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "0"   "127" "16" 
[127] "72"  "-29"

Длинные записи без знака закомментированы, потому что я не смог найти тип данных в пространстве имен Rcpp, который будет компилироваться. Правда ли, что такие типы, как "Rcpp :: Integer" и "Rcpp :: Double" не существуют? Если мне нужно использовать IntegerVector, то как мне сказать шаблону, что я хочу ссылаться на результат в R как на целое число, а не как вектор длины 1?

1 Ответ

0 голосов
/ 04 июля 2018

Я вижу, что конструктор на основе диапазона для Rcpp::CharacterVector сбивал с толку. Это было необходимо, потому что использовался массив char *, то есть массив строк. Здесь у нас есть только одна строка (фиксированной максимальной длины). Rcpp достаточно умен, чтобы преобразовать это в CharacterVector длины один сам по себе.

Случай с большим числом интереснее. Прежде всего, неясно, что такое unsigned long int. Он определяется как минимум 32-битный, и в 32-битных системах (и 64-битных Windows IIRC) это так. В 64-битном Linux (и MacOS?) Используется 64-битное целое число. Теперь R знает только int как целочисленный тип, который обычно имеет ширину 32 бита, но со знаком и поэтому имеет небольшой размер. Однако double может представлять гораздо большие целые числа , в точности , трюк, который сам R использует в разных местах. Таким образом, в системе с 32-битной unsigned long int мы можем использовать:

#include <RcppCommon.h>

typedef struct {
  char   firstname[128];
  long unsigned int big_number;
} HEADER_INFO;

namespace Rcpp {
  template <>
  SEXP wrap(const HEADER_INFO& x);
}

#include <Rcpp.h>

namespace Rcpp {
  template <>
  SEXP wrap(const HEADER_INFO& x) {
    static_assert(sizeof(long) <= 6, "long is to large");
    double big_number = x.big_number;
    return Rcpp::wrap(Rcpp::List::create(Rcpp::Named("firstname") = x.firstname,
                     Rcpp::Named("big_number") = big_number));
  };
}

//  [[Rcpp::export]]
HEADER_INFO getHeaderInfo() {
  HEADER_INFO header;
  strcpy( header.firstname, "Albert" );
  header.big_number = 4294967295;
  return header;
}

/*** R
getHeaderInfo()
 */

Выход:

> getHeaderInfo()
$firstname
[1] "Albert"

$big_number
[1] 4294967295

Оба элемента списка на самом деле являются векторами длины один.

В системе с 64-битным unsigned long int это не скомпилируется из-за static_assert. В такой системе вы можете использовать тот же прием, что и в пакете bit64: скопируйте битовую комбинацию 64-битного целого числа в 64-битное число с плавающей запятой (т.е. double). Это может быть безопасно передано R. Однако пакет bit64 интерпретирует их как знаковые целые числа. Так что действительно большие числа будут перетекать в отрицательные числа. См., Например, integer64 и Rcpp совместимость и http://gallery.rcpp.org/articles/creating-integer64-and-nanotime-vectors/. Я не знаю простого решения для чисел без знака. Я думаю, вам придется выяснить, есть ли практический предел тому, что ожидается в этих unsigned long int.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...