我用pari.h头文件写了一个cpp源代码:
#include<string>
#include<vector>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
#include<time.h>
#include<iterator> // for ostream_iterator
#include<strings.h>
#include<string.h>
#include<sstream>
#include <pari/pari.h> // for PARI/GP library
#include<Rcpp.h> // for sourceCpp to work, this line must be uncommented
// Enable C++11 via this plugin (Rcpp 0.10.3 or later)
// [[Rcpp::plugins(cpp11)]]
using namespace std;
using namespace Rcpp; // for sourceCpp to work, this line must be uncommented
// [[Rcpp::export]]
int main() {
long maxp = 1000000; // max value
pari_init(500000,2); // initiate pari
size_t rsize = 500000; // set stack size variables
size_t vsize = 100000000;
void paristack_setsize(size_t rsize, size_t vsize); // declare stack function
paristack_setsize(rsize, vsize); // set stack size
gp_allocatemem(stoi(100000000)); // allocate memory
GEN p1; // declare PARI variable
p1 = cgetg(maxp, t_VEC); // make the PARI variable a vector
long j; // declare the variable for the number to be checked. one above the vector iterator
for (long i = 0; i <= maxp; ++i) { // iterate over PARI vector
j = i + 1; // decrement index for number
gel(p1, i) = sumdiv(stoi(j)); // calculate the sum of divisors and update the vector
}
vector<long> p2(maxp); // empty vector of native type
GEN x; // declare a PARI variable to subset PARI vector
for (long i = 0; i < maxp; i++) { // for2, across vector indices
x = gel(p1, i); // subset one item of vector
p2[i] = gtolong(x); // convert PARI to native long integer and update long vector item
} // close for2
for (long z = 0; z < maxp; z++) { // for3, to iterate for stdout
cout << p2[z] << '\n'; // return the result. the vector items are printed separately
} // close for3
} // close function
(注意,可能有不必要的标题,我通常会跨源复制所有标题,但这不是问题)。没有pari.h标题的类似源文件与Rcpp编译良好,包含必要的部分(例如标题,命名空间,导出行等)。
当Rcpp相关引用被注释时,源代码编译得很好并且在使用以下标志直接使用g ++编译时没有问题:
g++ -lpari -fpermissive -Wall -Wextra -lm -fno-strict-aliasing -fomit-frame-pointer -o sumdivisors.o sumdivisors.cpp
我还将这些标志导入R:
Sys.setenv("PKG_CXXFLAGS"="-lpari -fpermissive -Wall -Wextra -lm -fno-strict-aliasing -fomit-frame-pointer")
我还在/usr/local/lib64/R/library/Rcpp/include/
创建了一个符号链接到/usr/include
下的pari目录。
然而,sourceCpp命令的输出是这样的:
> sourceCpp("sumdivisors.cpp")
In file included from /usr/local/lib64/R/library/Rcpp/include/Rcpp/r/headers.h:48:0,
from /usr/local/lib64/R/library/Rcpp/include/RcppCommon.h:29,
from /usr/local/lib64/R/library/Rcpp/include/Rcpp.h:27,
from sumdivisors.cpp:15:
/usr/local/lib64/R/library/Rcpp/include/Rcpp/platform/compiler.h:47:0: warning: "GCC_VERSION" redefined
#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
In file included from /usr/local/lib64/R/library/Rcpp/include/pari/pari.h:16:0,
from sumdivisors.cpp:14:
/usr/local/lib64/R/library/Rcpp/include/pari/paricfg.h:19:0: note: this is the location of the previous definition
#define GCC_VERSION "gcc version 6.2.1 20160830 (GCC)"
Error in dyn.load("/tmp/Rtmpc9edZe/sourceCpp-x86_64-pc-linux-gnu-0.12.8/sourcecpp_188e46b44088/sourceCpp_2.so") :
unable to load shared object '/tmp/Rtmpc9edZe/sourceCpp-x86_64-pc-linux-gnu-0.12.8/sourcecpp_188e46b44088/sourceCpp_2.so':
/tmp/Rtmpc9edZe/sourceCpp-x86_64-pc-linux-gnu-0.12.8/sourcecpp_188e46b44088/sourceCpp_2.so: undefined symbol: pari_mainstack
我复制了包括或不包含C ++ 11启用行的步骤,没有任何改变。我也改变了gcc标志,没有结果。似乎gcc版本定义和pari_mainstack的定义存在问题。
我认为问题不在于如何编写源代码。下面两个例子中,上面的cpp代码被转换为一个返回向量的函数,而且函数不是main。还提供了一个与Rcpp编译良好的类似且简单的代码:
#include<stdio.h>
#include<numeric> // for "accumulate"
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
#include<time.h>
#include<iterator> // for ostream_iterator
#include<strings.h>
#include<string.h>
#include<sstream>
#include <pari/pari.h> // for PARI/GP library
#include<Rcpp.h> // for sourceCpp to work, this line must be uncommented
// Enable C++11 via this plugin (Rcpp 0.10.3 or later)
// [[Rcpp::plugins(cpp11)]]
using namespace std;
using namespace Rcpp; // for sourceCpp to work, this line must be uncommented
// [[Rcpp::export]]
vector<long> sumdivisors() {
long maxp = 1000000; // max value
pari_init(500000,2); // initiate pari
size_t rsize = 500000; // set stack size variables
size_t vsize = 100000000;
void paristack_setsize(size_t rsize, size_t vsize); // declare stack function
paristack_setsize(rsize, vsize); // set stack size
gp_allocatemem(stoi(100000000)); // allocate memory
GEN p1; // declare PARI variable
p1 = cgetg(maxp, t_VEC); // make the PARI variable a vector
long j; // declare the variable for the number to be checked. one above the vector iterator
for (long i = 0; i <= maxp; ++i) { // iterate over PARI vector
j = i + 1; // decrement index for number
gel(p1, i) = sumdiv(stoi(j)); // calculate the sum of divisors and update the vector
}
vector<long> p2(maxp); // empty vector of native type
GEN x; // declare a PARI variable to subset PARI vector
for (long i = 0; i < maxp; i++) { // for2, across vector indices
x = gel(p1, i); // subset one item of vector
p2[i] = gtolong(x); // convert PARI to native long integer and update long vector item
} // close for2
return(p2);
/*
for (long z = 0; z < maxp; z++) { // for3, to iterate for stdout
cout << p2[z] << '\n'; // return the result. the vector items are printed separately
} // close for3
*/
} // close function
。
#include<stdio.h>
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<cmath>
#include<math.h>
#include<time.h>
#include<Rcpp.h>
using namespace std;
using namespace Rcpp;
//#include "std_lib_facilities.h"
// [[Rcpp::export]]
int pe001Cpp(int x) { // define a function pe001 with one in$teger input
int sum35 = 0; // define a scalar for the sum. start value is 0
for (int i=1; i<x; ++i) { // for 1 loop for counting up to x
if (i % 3 == 0 || i % 5 == 0) { // if 1, divisible by 3 or 5
sum35 += i; // update sum
} // close if 1
} // close for 1
return sum35; // return the final value
} // close function
// [[Rcpp::export]]
int pe001Cppb(int x) { // efficient method
int sumdivisible(int x, int y); // declare the below function in this scope
return sumdivisible(x, 3) + sumdivisible(x, 5) - sumdivisible(x, 15); // return the total sum
} // close function pe001Cppb
int sumdivisible(int x, int y) { // sum of terms divisibile by y
int ny = floor ((x-1) / y); // number of terms less than x and divisible by y
return ny * (ny + 1) / 2 * y; // return the sum
} // close function sumdivisible
执行直接编译二进制文件的过滤后的strace输出如下:
open("/usr/lib/libpari-gmp-tls.so.5", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libstdc++.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libgmp.so.10", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libnss_compat.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libnsl.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libnss_nis.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 3
正如我们从https://github.com/rstats-db/RPostgres/issues/80看到的那样,问题可能是链接库的错误版本,可以通过符号链接来解决。所以我必须知道Rcpp尝试链接哪些库文件。
更新
Scanelf输出显示有问题的符号位于/usr/lib/libpari-gmp-tls.so.2.9.1中。
[s@SS ~]$ scanelf -l -s pari_mainstack | grep pari_mainstack
ET_DYN pari_mainstack /usr/lib/libpari-gmp-tls.so.2.9.1
g ++编译文件的strace输出显示可执行文件链接到/usr/lib/libpari-gmp-tls.so.5,它本身是2.9.1版本的符号链接:
[s@SS library]$ strace ./sumdivisors3.o |& grep so | grep -v "No such file"
execve("./sumdivisors3.o", ["./sumdivisors3.o"], [/* 79 vars */]) = 0
open("/usr/lib/libpari-gmp-tls.so.5", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libstdc++.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libgmp.so.10", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
open("/usr/lib/libnss_compat.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libnsl.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libnss_nis.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 3
sourceCpp命令创建的sourceCpp_4.so文件的ldd输出如下:
[s@SS library]$ ldd /tmp/Rtmpau9YqY/sourceCpp-x86_64-pc-linux-gnu-0.12.8/sourcecpp_3a105ad2bdba/sourceCpp_4.so
linux-vdso.so.1 (0x00007ffc28f9d000)
libR.so => not found
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f5077111000)
libm.so.6 => /usr/lib/libm.so.6 (0x00007f5076e0d000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f5076bf6000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f5076858000)
/usr/lib64/ld-linux-x86-64.so.2 (0x0000564489276000)
我使用ldd跟踪了所有这些文件,并且没有指向/usr/lib/libpari-gmp-tls.so.2.9.1或/usr/lib/libpari-gmp-tls.so.5库的链接。所以问题是为什么在给定必要的头文件的同时sourceCpp链接到那些文件(并且g ++可以)?
更新
sourceCpp的详细输出显示以下命令:
g++ -I/usr/local/lib64/R/include -DNDEBUG -I/usr/local/include -I"/usr/local/lib64/R/library/Rcpp/include" -I"/home/s/codes/cpp/projecteuler/library" -lpari -fpic -g -O2 -c sumdivisors2.cpp -o sumdivisors2.o
g++ -shared -L/usr/local/lib64/R/lib -L/usr/local/lib64 -o sourceCpp_5.so sumdivisors2.o -L/usr/local/lib64/R/lib -lR
我设置了标志(事实上-lpari就足够了:
Sys.setenv("PKG_CXXFLAGS"="-lpari")
根据gp2c输出,-lpari标志也应该包含在链接阶段,但这里的链接命令没有。它可以成为问题的根源吗?或者在此之前,为什么sourceCpp_5.so文件没有链接到必要的pari库?
结局:
还应通过以下方式显式声明链接的依赖库:
Sys.setenv(&#34; PKG_LIBS&#34; =&#34; -lm -lpari -lc&#34;)
库标志由gp2c输出给出。顺便说一下gcc版本问题,而不是创建一个原始pari头目录的符号链接,我在R库路径中创建了一个副本并注释掉了该行:
// #define GCC_VERSION&#34; gcc version 6.2.1 20160830(GCC)&#34;
现在编译成功了,R可以在R中的数论理论计算中享受PARI / GP速度,这要归功于Rcpp!
答案 0 :(得分:4)
没有pari.h标题的类似源文件与Rcpp编译良好,包含必要的部分(如标题,命名空间,导出行等)
使用sourceCpp()
时,一般用例不涉及使用系统安装的库,例如pari
,而无需使用已安装的R包以前建立了适当的链接标志。因此,单独的包将处理表面pari
到R会话,然后注册Rcpp插件管理器的插件,以便在头文件之后可以包含// [[Rcpp::depends(pkgname)]]
来设置适当的链接语句。
说到这一点,让pari
与Rcpp合作的第一个步骤是建立一个 RcppPari 包,最好是{{3} }。
为此,您可能需要:
RcppGSL
:验证用户系统上是否存在库 并且系统配置非常合适(例如编译器有效等)sourceCpp()
和cppFunction()
所必需的插件(上面讨论的是&#34;理想&#34;解决方案)。我使用ldd跟踪了所有这些文件,并且没有指向/usr/lib/libpari-gmp-tls.so.2.9.1或/usr/lib/libpari-gmp-tls.so.5库的链接。所以问题是为什么在给定必要的头文件的同时sourceCpp链接到那些文件(并且g ++可以)?
正如您所发现的那样,-lm -lpari -lc
中PKG_CXXFLAGS
的使用在链接阶段是否适当地调用了 。相反,需要在PKG_LIB
选项中设置标志。 @jtilly在评论中强调了这一点。
在深入编写程序包之前,您可能希望在Makevars
文件中查看R中预处理程序标志背后的不同含义: