将字符缓冲区移植到 Rcpp 中
Porting character buffers into Rcpp
我正在尝试 运行 使用 Rcpp 在 R 中编写 C 代码,但不确定如何转换用于保存文件数据的缓冲区。在下面的第三行代码中,我分配了一个 unsigned char 缓冲区,但我的问题是我不知道要使用哪种 Rcpp 数据类型。将数据读入缓冲区后,我想出了如何使用 Rcpp::NumericMatrix 来保存最终结果,而不是字符缓冲区。我已经看到 Dirk Eddelbuettel 对类似问题的几个回应,他建议用 Rcpp 初始化命令替换所有 'malloc' 调用。我尝试使用 Rcpp::CharacterVector,但最后循环中存在类型不匹配:Rcpp::CharacterVector 无法读取为 unsigned long long int。某些 C 编译器的代码 运行s,但对其他编译器会抛出 'memory corruption' 错误,所以我更愿意按照 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 );
C 和 C++ 可以很可爱。如果您知道自己在做什么,那么您就可以通过 非常 直达底层硬件 和 更高级别的抽象来进行有效推理。
我建议简化和减少问题。从一个简单且已知的案例开始,例如 double
的 STL 向量。让我们来电是x
。用 10 个或一百个元素填充它,然后打开一个 FILE
并从
写入一个 blob
x.data(), x.size() * sizeof(double)
关闭文件。通过首先分配一个相同大小的 NumericVector v
将其读入 Rcpp,然后读回字节,然后将 memcpy
调用到 &(v[0])
.
应该是同一个向量
然后你可以泛化到不同的类型。 因为向量保证是连续的内存你可以直接使用这个序列化技巧。
您可以使用字符缓冲区对此进行变体,或者 void*
,或者... None 重要的 只要您注意不要不匹配。 即 不要将 int
有效负载分配给 double
等等。
现在,推荐吗?不,除非你追求性能并且足够了解你在做什么,在这种情况下这是合理的。否则依赖 fantastic 现有包,如 fst or qs
为你做。
希望对您的问题有所帮助。我不完全是 你在问什么。如果没有,也许你会澄清(并可能缩短/重点)它。
类型转换成功了:
Rcpp::NumericVector NumVecBuf( 3 * number_of_index_entries * sizeof(unsigned long long int) );
unsigned char* buf = (unsigned char*) &(NumVecBuf[0]);
Dirk 关于 "contiguous memory" 的声明表明这可行,因此我继续将他的评论标记为答案。谢谢,德克!并且,感谢您开发和维护 Rcpp!
我正在尝试 运行 使用 Rcpp 在 R 中编写 C 代码,但不确定如何转换用于保存文件数据的缓冲区。在下面的第三行代码中,我分配了一个 unsigned char 缓冲区,但我的问题是我不知道要使用哪种 Rcpp 数据类型。将数据读入缓冲区后,我想出了如何使用 Rcpp::NumericMatrix 来保存最终结果,而不是字符缓冲区。我已经看到 Dirk Eddelbuettel 对类似问题的几个回应,他建议用 Rcpp 初始化命令替换所有 'malloc' 调用。我尝试使用 Rcpp::CharacterVector,但最后循环中存在类型不匹配:Rcpp::CharacterVector 无法读取为 unsigned long long int。某些 C 编译器的代码 运行s,但对其他编译器会抛出 'memory corruption' 错误,所以我更愿意按照 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 );
C 和 C++ 可以很可爱。如果您知道自己在做什么,那么您就可以通过 非常 直达底层硬件 和 更高级别的抽象来进行有效推理。
我建议简化和减少问题。从一个简单且已知的案例开始,例如 double
的 STL 向量。让我们来电是x
。用 10 个或一百个元素填充它,然后打开一个 FILE
并从
x.data(), x.size() * sizeof(double)
关闭文件。通过首先分配一个相同大小的 NumericVector v
将其读入 Rcpp,然后读回字节,然后将 memcpy
调用到 &(v[0])
.
应该是同一个向量
然后你可以泛化到不同的类型。 因为向量保证是连续的内存你可以直接使用这个序列化技巧。
您可以使用字符缓冲区对此进行变体,或者 void*
,或者... None 重要的 只要您注意不要不匹配。 即 不要将 int
有效负载分配给 double
等等。
现在,推荐吗?不,除非你追求性能并且足够了解你在做什么,在这种情况下这是合理的。否则依赖 fantastic 现有包,如 fst or qs 为你做。
希望对您的问题有所帮助。我不完全是 你在问什么。如果没有,也许你会澄清(并可能缩短/重点)它。
类型转换成功了:
Rcpp::NumericVector NumVecBuf( 3 * number_of_index_entries * sizeof(unsigned long long int) );
unsigned char* buf = (unsigned char*) &(NumVecBuf[0]);
Dirk 关于 "contiguous memory" 的声明表明这可行,因此我继续将他的评论标记为答案。谢谢,德克!并且,感谢您开发和维护 Rcpp!