Rcpp не компилирует исходный код cpp с заголовками pari.h

Я написал источник cpp с включенным заголовочным файлом pari.h:

#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/ на каталог pari в /usr/include.

Однако вывод команды 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 преобразуется в код, возвращающий вектор, а функция не является основной. Также представлен аналогичный и простой код, который хорошо компилируется с 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 

Вывод strace скомпилированного файла g ++ показывает, что исполняемый файл связан с /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

Вывод ldd из файла sourceCpp_4.so, созданного командой sourceCpp, выглядит следующим образом:

[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 ("PKG_LIBS" = "- lm -lpari -lc")

Флаги библиотеки задаются выводом gp2c. Кстати, чтобы решить проблему с версией gcc, вместо создания символической ссылки на исходный каталог заголовков pari я создал копию внутри пути библиотеки R и закомментировал строку:

// # определение GCC_VERSION "gcc version 6.2.1 20160830 (GCC)"

Теперь компиляция прошла успешно, R может наслаждаться скоростью PARI / GP в теоретико-числовых вычислениях в R благодаря Rcpp!


person Serhat Cevikel    schedule 13.01.2017    source источник
comment
Итак, основная проблема, с которой вы столкнулись, - это undefined symbol: pari_mainstack? Правильный?   -  person coatless    schedule 13.01.2017
comment
Кроме того, это не ошибка Rcpp. Очень плохой стиль перекрестной публикации через несколько минут после публикации в официальном трекере ошибок.   -  person coatless    schedule 13.01.2017
comment
Если это не ошибка Rcpp, почему я могу скомпилировать ее с помощью g ++?   -  person Serhat Cevikel    schedule 13.01.2017
comment
Кстати, наоборот. Сначала я разместил его здесь, а затем открыл проблему на Github. Чем больше пар глаз, тем выше шансы найти решение.   -  person Serhat Cevikel    schedule 13.01.2017
comment
Между прочим, я считаю, что это важная проблема для HPC с R, поскольку PARI / GP очень важен в задачах, интенсивно связанных с теорией чисел, и нет прямого интерфейса от R к PARI / GP. Таким образом, единственный способ - использовать PARI / GP в режиме библиотеки из c ++ и скомпилировать с Rcpp в R.   -  person Serhat Cevikel    schedule 13.01.2017
comment
Во-первых, этот вопрос нацелен на использование внешней библиотеки, которая не привязана к пакету, и метод, выбранный для встраивания указанной библиотеки вместе с Rcpp, очень проблематичен. Тот факт, что вы можете скомпилировать свой код автономно, а затем ожидать, что он сразу же заработает, особенно с учетом директивы int main(), указывает на то, что вы не знаете некоторых идиом. Поэтому я поддерживаю свое предыдущее замечание, что это не ошибка Rcpp, а проблема пользователя, которую не следует публиковать в официальной системе отслеживания ошибок.   -  person coatless    schedule 13.01.2017
comment
Дело не в том, как написан мой код. Я изменил код, а также представил аналогичный, который хорошо компилируется с Rcpp, см. Выше.   -  person Serhat Cevikel    schedule 13.01.2017
comment
Хорошо, помогите мне помочь вам. Прямо сейчас кажется, что ваша проблема связана с множеством возможностей (например, неправильное использование sourceCpp(), прямое встраивание pari с заголовками Rcpp, наличие int main(), установлены неверные флаги и - основной виновник - R и pari не согласны по компиляторам). Если два фрагмента кода только что добавлены к вашему вопросу работают, то я не понимаю, в чем проблема, потому что vector<long> sumdivisors() - это буквально предыдущий фрагмент кода sin int main(). Подчеркните, что именно проблематично в вашем вопросе. Я буду бодрствовать следующие 30 минут.   -  person coatless    schedule 13.01.2017
comment
Спасибо @coatless за проявленный интерес. Я сделал это изменение только для того, чтобы показать, что проблема не в основной функции. Теперь два фрагмента одинаковы по конструкции. Второй работает с Rcpp, а первый - нет. Кроме того, первый код запускается с g ++ с предоставленными мной флагами (и это флаги, которые выдает вывод gp2c при преобразовании сценария gp в код c).   -  person Serhat Cevikel    schedule 13.01.2017
comment
Вполне вероятно, что я делаю ошибку, и это не ошибка Rcpp, поскольку есть некоторые пакеты Cran, которые имеют pari в качестве зависимостей. В чем может быть причина, по которой проблема не для g ++, а для Rcpp, может быть, их различия и т. Д.? Может быть, мне не хватает флага?   -  person Serhat Cevikel    schedule 13.01.2017
comment
Между прочим, я думаю, что предоставил весь материал, необходимый и достаточный для воспроизведения ситуации, может быть, вы сможете попробовать, когда у вас будет возможность.   -  person Serhat Cevikel    schedule 13.01.2017
comment
Вы должны поместить это в пакет вместо использования sourceCpp. После того, как вы поместите это в пакет, вы должны связать с pari, используя PKG_LIBS в src/Makevars вместо PKG_CXXFLAGS. Флаги компоновщика входят в PKG_LIBS.   -  person jtilly    schedule 13.01.2017
comment
Хорошая идея, я над этим поработаю   -  person Serhat Cevikel    schedule 13.01.2017


Ответы (1)


Подобные исходные файлы без заголовка pari.h хорошо компилируются с Rcpp с включенными необходимыми частями (такими как заголовок, пространство имен, строка экспорта и т. Д.)

При использовании sourceCpp() общий вариант использования не включает использование установленных в системе библиотек, таких как pari , без подключения к пакету R, который ранее установил соответствующие флаги связывания. В результате отдельный пакет будет обрабатывать выход pari на сеанс R и, впоследствии, регистрацию подключаемого модуля для диспетчера подключаемых модулей Rcpp, чтобы // [[Rcpp::depends(pkgname)]] можно было включить после файла заголовка для установки соответствующих операторов связи.

Сказав это, первым шагом к тому, чтобы заставить pari работать с Rcpp, является установка пакета RcppPari, желательно в форме _ 6_.

Для этого вам, вероятно, понадобятся:

  • configure.ac: чтобы убедиться, что библиотека выполняет < / em> существуют в системе пользователя, и конфигурация системы хорошо подходит (например, компилятор действителен и т. д.)
  • src / Makevars.in для установки соответствующих флагов precessor: чтобы управлять тем, как код, встроенный в пакет, должен быть как скомпилирован, так и связан. (См. Ссылки ниже.)
  • r / inline.R: чтобы установить плагины, необходимые для настроить строительные леса для использования sourceCpp() и cppFunction() (обсуждалось выше как "идеальное" решение).

Я отслеживал все эти файлы с помощью 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 в комментариях.

Прежде чем погрузиться в написание пакета, вы можете посмотреть на различные значения флагов препроцессора в R в отношении Makevars файла:

person coatless    schedule 13.01.2017