将字符缓冲区移植到 Rcpp

Posted

技术标签:

【中文标题】将字符缓冲区移植到 Rcpp【英文标题】:Porting character buffers into Rcpp 【发布时间】:2020-02-22 18:24:52 【问题描述】:

我正在尝试使用 Rcpp 在 R 中运行 C 代码,但不确定如何转换用于保存文件数据的缓冲区。在下面的第三行代码中,我分配了一个 unsigned char 缓冲区,我的问题是我不知道要使用什么 Rcpp 数据类型。一旦数据被读入缓冲区,我就知道如何使用 Rcpp::NumericMatrix 来保存最终结果,而不是字符缓冲区。我已经看到 Dirk Eddelbuettel 对类似问题的一些回应,他建议用 Rcpp 初始化命令替换所有“malloc”调用。我尝试使用 Rcpp::CharacterVector,但最后循环中存在类型不匹配:Rcpp::CharacterVector 不能被读取为无符号 long long int。该代码为某些 C 编译器运行,但对其他人抛出“内存损坏”错误,因此我更愿意按照 Dirk 建议的方式执行操作(使用 Rcpp 数据类型),以便无论特定编译器如何,代码都将运行。

    FILE *fp = fopen( filename, "r" );
    fseek( fp, index_data_offset, SEEK_SET );
    unsigned char* buf = (unsigned char *)malloc( 3 * number_of_index_entries * sizeof(unsigned long long int) );
    fread( buf, sizeof("unsigned long long int"), (long)(3 * number_of_index_entries), fp );
    fclose( fp );

    // Convert "buf" into a 3-column matrix.
    unsigned long long int l;
    Rcpp::NumericMatrix ToC(3, number_of_index_entries);
    for (int col=0; col<number_of_index_entries; col++ ) 
        l = 0;
        int offset = (col*3 + 0)*sizeof(unsigned long long int);
        for (int i = 0; i < 8; ++i) 
            l = l | ((unsigned long long int)buf[i+offset] << (8 * i));
        
        ToC(0,col) = l;

        l = 0;
        offset = (col*3 + 1)*sizeof(unsigned long long int);
        for (int i = 0; i < 8; ++i) 
            l = l | ((unsigned long long int)buf[i+offset] << (8 * i));
        
        ToC(1,col) = l;

        l = 0;
        offset = (col*3 + 2)*sizeof(unsigned long long int);
        for (int i = 0; i < 8; ++i) 
            l = l | ((unsigned long long int)buf[i+offset] << (8 * i));
        
        ToC(2,col) = l;
    
    return( ToC );

【问题讨论】:

【参考方案1】:

C 和 C++ 可以很可爱。如果您知道自己在做什么,那么您就可以非常直接连接到底层硬件更高级别的抽象以进行有效推理。

我建议简化并减少问题。从一个简单且已知的案例开始,例如double 的 STL 向量。我们打电话给x。用 10 或 100 个元素填充它,然后打开一个 FILE 并从

写入一个 blob
x.data(),  x.size() * sizeof(double)

关闭文件。首先分配一个相同大小的NumericVector v,然后读回字节,然后调用memcpy&amp;(v[0]),将其读入Rcpp。

应该是同一个向量。

然后你可以推广到不同的类型。 因为向量保证是连续的内存你可以直接使用这个序列化技巧。

您可以使用字符缓冲区、void* 或 ... 不要将int有效负载分配给double等等。

现在,有没有这个推荐?不,除非你追求性能并且足够了解你在做什么,在这种情况下这是合理的。否则请依赖 fantastic 现有的软件包,例如 fst 或 qs 为你做。

我希望这对您的问题有所帮助。我不完全是你问的什么。如果没有,也许你会澄清(并可能缩短/聚焦)它。

【讨论】:

【参考方案2】:

类型转换成功了:

    Rcpp::NumericVector NumVecBuf( 3 * number_of_index_entries * sizeof(unsigned long long int) );
    unsigned char* buf = (unsigned char*) &(NumVecBuf[0]);

Dirk 关于“连续记忆”的声明表明这会起作用,所以我继续将他的评论标记为答案。谢谢,德克!并且,感谢您开发和维护 Rcpp!

【讨论】:

您的答案/问题不一定是错误的,但由于 Rcpp 是 C++,我建议使用 C++ 构造来保持一致性。例如。 std::ifstream 代替 FILE 句柄,reinterpret_cast 代替 C 样式转换等。您也应该使用 sizeof(double),因为 NumericMatrix 表示浮点数据。 您还可以查看Rcpp::RawVector,它用于存储原始二进制数据。 如果您只想访问“其他”对象,甚至可以使用 XPtr

以上是关于将字符缓冲区移植到 Rcpp的主要内容,如果未能解决你的问题,请参考以下文章

我可以将标准输出重定向到某种字符串缓冲区吗?

将字符串数组作为缓冲区传递给 C++ 到 C#

将字符串的一部分复制到C中的缓冲区中

有没有办法直接在 iPhone 上渲染像素?

将具有动态字符串的结构复制到缓冲区 c

根据缓冲区长度将空终止字符数组复制到 std::string