0

I want to create a R package that contains the C++ code wrote by someone else. It is an experimental branch of poppler that has not been pulled into the master version of poppler. The changes concern several c++ files, for this reason :

  • I need to create a package.
  • I cannot create one single .cpp file with the desired features and link the R package to the local installation of poppler, using the configure file as does, for example, the R package PDFtools (sadly).

I have the following code in cpp (cutted for brievety), as well as all the header files and cpp files from poppler needed to compile the code inside the folder src.

#include <Rcpp.h>

//namespace Rcpp create class ambiguity
// using namespace Rcpp;

#include "glibc.h"
#include "config.h"
#include "poppler-document.h"         
#include "poppler-global.h"       
#include "poppler-page.h"           
#include "poppler-page-transition.h"  
#include "poppler-toc.h"
#include "poppler-font.h"
#include "poppler-image.h"         
#include "poppler-embedded-file.h"
#include "poppler-page-renderer.h"
#include "poppler-rectangle.h"

#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <ios>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>

#include "poppler-version.h"
using namespace poppler;

(...)

// [[Rcpp::export]]

std::string fontR(std::string file_name) {

  std::unique_ptr<poppler::document> doc(poppler::document::load_from_file(file_name));
  //
  // if (show_text_list) {
  const int pages = doc->pages();
  for (int i = 0; i < pages; ++i) {
    Rcpp::Rcout << "Page " << (i + 1) << "/" << pages << ":" << std::endl;
    std::unique_ptr<poppler::page> p(doc->create_page(i));
    print_page_text_list(p.get());
  }

  return file_name;
}

I have run the following to create the package :

sudo rm -r fontReadeR/

sudo Rscript -e 'Rcpp::Rcpp.package.skeleton("fontReadeR")'

sudo cp font_readR.cpp fontReadeR/src/ sudo cp cpp_files/*
fontReadeR/src/

cd fontReadeR/ 
sudo Rscript -e 'Rcpp::compileAttributes()'
sudo R CMD build .
R

And inside R I ran the following :

devtools::install()

The compilation of the code seems to goes well but ultimately I run into an error of undefined symbol :

g++  -I/usr/share/R/include -DNDEBUG  -I"/home/rollaet/R/x86_64-pc-linux-gnu-library/3.4/Rcpp/include"    -fpic  -g -O2 -fdebug-prefix-map=/build/r-base-AitvI6/r-base-3.4.4=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -g  -c rcpp_hello_world.cpp -o rcpp_hello_world.o
g++ -shared -L/usr/lib/R/lib -Wl,-Bsymbolic-functions -Wl,-z,relro -o fontReadeR.so RcppExports.o font_readR.o poppler-document.o poppler-embedded-file.o poppler-font.o poppler-global.o poppler-image.o poppler-page-renderer.o poppler-page-transition.o poppler-page.o poppler-private.o poppler-rectangle.o poppler-toc.o poppler-version.o rcpp_hello_world.o -L/usr/lib/R/lib -lR
installing to /home/rollaet/R/x86_64-pc-linux-gnu-library/3.4/fontReadeR/libs
** R
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded
Error: package or namespace load failed for ‘fontReadeR’ in dyn.load(file, DLLpath = DLLpath, ...):
 unable to load shared object '/home/rollaet/R/x86_64-pc-linux-gnu-library/3.4/fontReadeR/libs/fontReadeR.so':
  /home/rollaet/R/x86_64-pc-linux-gnu-library/3.4/fontReadeR/libs/fontReadeR.so: undefined symbol: _ZTI10BaseStream
Error: loading failed
Execution halted

I am new to Rcpp and I have iterated over several newbies errors (starting by using Rcpp::sourceCpp()), etc, but I am quite puzzled about this error and I don't see an obvious direction I should follow to solve it. It does not came from a specific part of the code as I tried to comment different part of the code, without success.

I have studied the structure of the package Matrix and took a look at nloptr (which was a bit complicated for me).

Could someone pinpoint me the direction to follow? Should I drop this approach and go to create a static library with the cpp files that I need?

Thanks for your help.

Edit: I think I found what could be the problematic part, there is an Overloading of the operator <<, which refer to stream :

static std::ostream& operator<<(std::ostream& stream, const poppler::ustring &str)
{
  const poppler::byte_array ba = str.to_utf8();
  for (unsigned int i = 0; i < ba.size(); ++i) {
    stream << (char)(ba[i]);
  }
  return stream;
}
Ferran Buireu
  • 28,630
  • 6
  • 39
  • 67
Cdk29
  • 1
  • 1
  • Welcome to StackOverflow. Your linking appears to incomplete, which is why you get 'unable to load shared object'. The `BaseStream` object is missing. That is at your end. – Dirk Eddelbuettel Mar 04 '20 at 17:09
  • And your package link line does not show any external library besides R. So is `BaseStream` defined in the set of files in `src/` ? If so, mayne you got the namespace wrong. Otherwise: hard to tell as we cannot reproduce. – Dirk Eddelbuettel Mar 04 '20 at 17:32
  • Thanks for the reply. I wiil look more closely in src, but I really don't know to which BaseStream object the compiler is refering too. I did a toy example before, linking the library to a local installation of poppler and removing some part of the c++ code that was refering to now classes introduced by the others header files, and I managed to compile and install the library. Yes, sorry for the non-reproducibility, but I don't see how I could provide a minimaly reproducible example without provided all the files in src :/ – Cdk29 Mar 05 '20 at 08:33
  • 1
    You need to set up a `Makevars` file that compiles the poppler library's code. Including the `*.h` headers is not enough, as the headers just define the interface. The `*.cpp` files implement the header files. Look at `Makevars` files in various packages as examples and google some `make` tutorials. The learning curve is slightly steep but it will be worth it. – thc Mar 05 '20 at 09:01
  • I have add an other part of the code, an overload of the operator <<. While I don't understand why it should be problematic, it is the only reference to a stream inside this cpp file. – Cdk29 Mar 05 '20 at 09:01
  • @thc thanks for the reply. But I was thinking that with Rcpp Makevars was not necessary ? (or maybe I did not understood properly the Rcpp vignette) – Cdk29 Mar 05 '20 at 09:02
  • 1
    Rcpp is incredible at being helpful and simplifying, but you still have to understand what is going on and set things up properly. It's not magic. You are asking Rcpp to read the mind of the developer, which isn't currently possible ;) PS: `basestream` is defined in `poppler-document.cc`. – thc Mar 05 '20 at 09:05
  • Okay thanks I will work on this then :) Just before to start : do I need just the header files and the cpp files needed for the cpp file that have the rcpp export, or the whole poppler library in src/ ? – Cdk29 Mar 05 '20 at 09:18
  • Hey @thc I thought you were working on that PR for a mind reader? ;-) – Dirk Eddelbuettel Mar 05 '20 at 16:32
  • @Cdk29 You'll need the header file(s) and the associated source (`.cc`) files for the components that you want. You don't necessarily need the whole library, depending on what you want to do. – thc Mar 05 '20 at 20:12
  • @DirkEddelbuettel Yes, write after the mind writer PR :) – thc Mar 05 '20 at 20:12

0 Answers0