Использование сторонних файлов заголовков с Rcpp

У меня есть файл заголовка с именем coolStuff.h, который содержит функцию awesomeSauce(arg1), которую я хотел бы использовать в моем исходном файле cpp.

Структура каталога:

  • RworkingDirectory
    • sourceCpp
      • theCppFile.cpp
    • cppHeaders
      • coolStuff.h

Код:

#include <Rcpp.h>
#include <cppHeaders/coolStuff.h>
using namespace Rcpp;

// [[Rcpp::export]]
double someFunctionCpp(double someInput){

 double someOutput = awesomeSauce(someInput);

return someOutput;
}

Я получаю сообщение об ошибке:

 theCppFile.cpp:2:31: error: cppHeaders/coolStuff.h: No such file or directory

Я переместил файл и каталог повсюду и, похоже, не могу заставить это работать. Я вижу повсюду примеры использования сторонних заголовков, в которых говорится, что просто сделайте это:

#include <boost/array.hpp>

(Это от Hadley / devtools)

https://github.com/hadley/devtools/wiki/Rcpp

Так что же дает? Я искал все утро и не могу найти ответа на то, что мне кажется простым.

ОБНОВЛЕНИЕ 01.11.12

Хорошо, теперь, когда я понял, как создавать пакеты, использующие Rcpp в Rstudio, позвольте мне перефразировать вопрос. У меня есть отдельный файл заголовка coolStuff.h, который содержит функцию, которую я хочу использовать в моем коде cpp.

1) Где мне разместить coolStuff.h в структуре каталогов пакета, чтобы содержащаяся в нем функция могла использоваться CppFile.cpp?

2) Как мне вызвать coolStuff.h в файлах cpp? В очередной раз благодарим за помощь. Я многому научился из последнего разговора.

Примечание: я прочитал виньетку «Написание пакета, использующего Rcpp», и в нем не объясняется, как это сделать.

Ответ:

Хорошо, позвольте мне резюмировать ответ на мой вопрос, поскольку он разбросан по этой странице. Если я ошибаюсь в деталях, отредактируйте это или дайте мне знать, и я отредактирую это:

Итак, вы нашли .h или .cpp файл, содержащий функцию или какой-то другой фрагмент кода, который вы хотите использовать в .cpp файле, который вы пишете для использования с Rcpp.

Давайте продолжим вызывать этот найденный код coolStuff.h и вызывать функцию, которую вы хотите использовать awesomeSauce(). Назовем файл, который вы пишете, theCppFile.cpp.

(Я должен отметить здесь, что код в файлах .h и в файлах .cpp - это весь код C ++, и разница между ними заключается в том, что программист на C ++ поддерживает правильную организацию вещей. Я оставлю обсуждение разницы здесь , но простой поиск здесь на SO приведет вас к обсуждению разницы. Для вас, программиста R, которому нужно использовать немного кода, который вы нашли, реальной разницы нет.)

В КОРОТКОМ виде: вы можете использовать такой файл, как coolStuff.h, при условии, что он не вызывает других библиотек, путем вырезания и вставки в theCppFile.cpp, или, если вы создаете пакет, вы можете поместить файл в \src каталог с помощью файл theCppFile.cpp и используйте #include "coolStuff.h" в верхней части файла, который вы пишете. Последний более гибкий и позволяет использовать функции из coolStuff.h в других .cpp файлах.

ПОДРОБНОСТИ:

1) coolStuff.h не должен вызывать другие библиотеки. Это означает, что вверху не может быть никаких операторов include. Если это так, то то, что я подробно описываю ниже, вероятно, не сработает, и использование найденного кода, который вызывает другие библиотеки, выходит за рамки этого ответа.

2) Если вы хотите скомпилировать файл с sourceCpp(), вам нужно вырезать и вставить coolStuff.h в theCppFile.cpp. Мне сказали, что есть исключения, но sourceCpp() предназначен для компиляции одного .cpp файла, так что это лучший путь.

(ПРИМЕЧАНИЕ: я не даю никаких гарантий, что простое вырезание и вставка сработает из коробки. Возможно, вам придется переименовать переменные или, что более вероятно, переключить используемые типы данных, чтобы они соответствовали тем, которые вы используете в theCppFile.cpp. Но пока , вырезание и вставка сработало для меня с минимальными усилиями с 6 разными простыми .h файлами)

3) Если вам нужно использовать только код из coolStuff.h в theCppFile.cpp и больше нигде, вы должны вырезать и вставить его в theCppFile.cpp.

(Опять же, я не даю никаких гарантий, см. Примечание выше о вырезании и вставке)

4) Если вы хотите использовать код, содержащийся в coolStuff.h в theCppFile.cpp И других .cpp файлах, вам нужно подумать о создании пакета. Это несложно, но может быть немного сложно, потому что информация о создании пакетов с помощью Rcpp варьируется от исчерпывающей подробной документации, которую вы хотите использовать с любым пакетом R (но это выше вашей головы как новичка), и чувствительной к новичкам. знакомство (которое может упустить деталь, которая вам может понадобиться).

Вот что я предлагаю:

A) Сначала получите версию theCppFile.cpp с кодом из coolStuff.h вырезать и вставить в theCppFile.cpp, который компилируется с sourceCpp() и работает так, как вы ожидаете. Это не обязательно, но если вы новичок в пакетах Rcpp OR, было бы хорошо убедиться, что ваш код работает в этой простой ситуации, прежде чем переходить к более сложному случаю, описанному ниже.

Б) Теперь соберите свой пакет с помощью Rcpp.package.skeleton() или используйте функцию сборки в RStudio (НАСТОЯТЕЛЬНО рекомендуется). Подробную информацию об использовании Rcpp.package.skeleton() можно найти в hadley / devtools или Виньетка с атрибутами Rcpp. Полная документация по написанию пакетов с помощью Rcpp находится в разделе Написание пакета, который использует Rcpp, однако предполагает, что вы достаточно хорошо разбираетесь в C ++, и не использует новый способ выполнения Rcpp с помощью «атрибутов».

Не забудьте «Построить и перезагрузить», если используете RStudio, или compileAttributes(), если вы не в RStudio.

C) Теперь вы должны увидеть в каталоге \ R файл с именем RcppExports.R. Откройте его и проверьте. В RcppExports.R вы должны увидеть функции оболочки R для всех файлов .cpp, которые есть в вашем \src каталоге. Довольно мило.

D) Попробуйте функцию R, которая соответствует функции, которую вы написали в theCppFile.cpp. Это работает? Если так, то продолжай.

E) Создав пакет, вы можете переместить coolStuff.h в папку src с помощью theCppFile.cpp.

F) Теперь вы можете удалить код вырезания и вставки из theCppFile.cpp и в верхней части theCppFile.cpp (и любого другого файла .cpp, который вы хотите использовать код из coolStuff.h) поместите #include "coolStuff.h" сразу после #include <Rcpp.h>. Обратите внимание, что в строке ranker.h нет скобок, вместо этого есть "". Это соглашение C ++ при включении локальных файлов, предоставленных пользователем, а не файла библиотеки, такого как Rcpp или STL и т. Д.

G) Теперь вам нужно пересобрать пакет. В RStudio это просто «Сборка и перезагрузка» в меню «Сборка». Если вы не используете RStudio, запустите compileAttributes()

H) Теперь попробуйте функцию R еще раз, как вы делали на шаге D), надеюсь, она сработает.


person politicalEconomist    schedule 21.12.2012    source источник
comment
Без костей: Я пробовал: #include "../cppHeaders/coolStuff.h" #include <"../cppHeaders/coolStuff.h"> #include <../cppHeaders/coolStuff.h> Еще: : No such file or directory   -  person politicalEconomist    schedule 21.12.2012
comment
Что ж, если ваша структура такая, как описано, она должна работать. Есть ли опечатка в имени или пути включения? Ваша файловая система чувствительна к регистру и вы его неправильно поняли?   -  person JasonD    schedule 21.12.2012
comment
JasonD, я думаю, что это верно для ванильного мира C ++. Но с Rcpp не работает. Скорее, я должен сказать, что это зависит от того, как вы компилируете файл C ++. Если вы используете sourceCpp() для компиляции файла и создания функции R, тогда простой #include path / filename.h не будет работать. Однако похоже, что если вы используете Rcpp.package.skeleton() или аналогичный мастер создания пакетов в Rstudio, тогда при некоторых условиях будет работать простой #include path / filename.h. Как только я, наконец, разберусь со всем этим, я напишу сводку вызова другого кода в ваших файлах cpp с помощью Rcpp.   -  person politicalEconomist    schedule 11.01.2013
comment
Внутри пакета вам необходимо предоставить Makevars, который добавляет каталоги в компиляцию, например PKG_CPPFLAGS + = -I ../ inst / include /   -  person jjallaire    schedule 11.01.2013
comment
Отличный ответ - спасибо за подробности. По теме я нашел справочник Хэдли по написанию пакетов, чтобы найти хороший баланс между детализацией и удобочитаемостью. . В частности, в нем очень дружелюбно обсуждается использование Rcpp в пакетах.   -  person Mullefa    schedule 20.05.2015
comment
@jjallaire Это был самый полезный комментарий на всей странице. Может быть, добавить это к своему ответу?   -  person James Hirschorn    schedule 17.05.2020


Ответы (7)


Проблема в том, что sourceCpp специально разработан для создания только одного автономного исходного файла. Если вы хотите, чтобы у sourceCpp были зависимости, они должны быть:

  1. В систему включить каталоги (т.е. /usr/local/lib или /usr/lib); или

  2. В пакете R, который вы указываете в атрибуте Rcpp::depends

Как сказал Дирк, если вы хотите создать более одного исходного файла, вам следует подумать об использовании пакета R, а не sourceCpp.

Обратите внимание: если вы работаете с пакетом и выполняете sourceCpp для файла в каталоге src пакета, он будет строить его так, как будто он находится в пакете (т.е. вы можете включать файлы из каталога src или каталог inst / include).

person jjallaire    schedule 21.12.2012
comment
Хорошо, я начинаю добираться туда. Как я могу посмотреть на файл .h и определить, является ли это простым случаем, о котором говорит Дирк ниже, или ему нужно создать собственный исходный файл, и поэтому я не могу просто # включить его? Учитывая огромное количество готовых файлов C ++, кажется, что это обычное дело, когда кто-то наткнется на файл, который делает что-то, что им нужно, а затем захочет использовать этот файл. Я знаю, что мой словарный запас не совсем подходящий, но я надеюсь, что вы все еще понимаете то, что я говорю. Я давний парень R, пытаюсь преобразовать мой очень медленный пакет в Rcpp. - person politicalEconomist; 21.12.2012
comment
Конечно, потратив целый день на попытки выяснить, почему мой путь к моему файлу был неправильным, я мог просто написать функцию, которую нашел сам. Но, надеюсь, это поможет мне в будущем и поможет другим. - person politicalEconomist; 22.12.2012
comment
Я смог использовать файл заголовка, просто вставив все его содержимое прямо в мой файл cpp. Затем sourceCpp скомпилировал все это без проблем. Это кажется очень немодульным способом программирования, потому что я, вероятно, захочу использовать те же функции в других файлах cpp. Это приведет к бесполезному созданию повторяющегося кода повсюду. Кроме того, я хочу использовать свой собственный код cpp в других файлах cpp, которые я пишу. Это потребует очень много времени на внесение изменений. - person politicalEconomist; 22.12.2012
comment
На самом деле нет способа извлечь код cpp из других файлов во время компиляции, как мы это делаем с source () в R во время выполнения? В любом случае было бы хорошо, если бы ошибка, возникающая при попытке sourceCpp файла, содержащего #inclide "filename.h", не вводила в заблуждение. Когда появляется сообщение об ошибке theCppFile.cpp:2:31: error: filename.h: No such file or directory, последнее, что я предполагаю, это то, что компилятору (или это оболочка sourceCpp ()) не нравится тот факт, что существует #include файла, которого нет в /usr/local/lib или /usr/lib . - person politicalEconomist; 22.12.2012
comment
Основная философия заключается в том, что внутри R есть система для создания нескольких исходных файлов, которые зависят друг от друга (и других библиотек), и это система пакетов. Идея sourceCpp заключалась не в создании альтернативной параллельной системы для пакетов, а в том, чтобы иметь простой способ написать одну функцию или исходный файл без необходимости сборки пакета. Я понимаю, что вы ищете золотую середину, и, возможно, это правильно, но мы хотим предпринять эти шаги осторожно. - person jjallaire; 22.12.2012
comment
Имеет смысл @jjallaire. Спасибо, что нашли терпение объяснить это. похоже, мне нужно изучить, как постепенно создавать пакеты. До сих пор я создавал прототипы в интерактивном режиме со сценариями, которые затем упаковывались в конце. - person politicalEconomist; 23.12.2012
comment
@ user1922182: Поначалу пакет может выглядеть устрашающе, но это не так. Посмотрите на package.skeleton() и Rcpp.package.skeleton() или начните с простых существующих пакетов. У пакетов есть огромный плюс, используйте их. Здесь даны ответы на множество полезных вопросов, поэтому ищите и SO. - person Dirk Eddelbuettel; 23.12.2012
comment
Хорошо, теперь, когда я понял, как создавать пакеты, использующие Rcpp в Rstudio, позвольте мне перефразировать вопрос. У меня есть отдельный файл заголовка coolStuff.h, который содержит функцию, которую я хочу использовать в моем коде cpp. 1) Где я должен разместить coolStuff.h в структуре каталогов пакета, чтобы содержащаяся в нем функция могла использоваться theCppFile.cpp? 2) Как мне вызвать coolStuff.h в файлах cpp? В очередной раз благодарим за помощь. Я многому научился из последнего разговора. Я прочитал виньетку «Написание пакета, использующего Rcpp», и в нем не объясняется, как это сделать. - person politicalEconomist; 11.01.2013
comment
Я отредактировал исходное сообщение, чтобы включить в него краткое изложение ответа на мой вопрос. Спасибо всем за помощь и терпение. - person politicalEconomist; 14.01.2013

Мне удалось связать любую библиотеку (в данном случае MPFR), установив две переменные среды перед вызовом sourceCpp:

Sys.setenv("PKG_CXXFLAGS"="-I/usr/include")
Sys.setenv("PKG_LIBS"="-L/usr/lib/x86_64-linux-gnu/ -lm -lmpc -lgmp -lmpfr")

Первая переменная содержит путь к заголовкам библиотеки. Второй включает путь к двоичному файлу библиотеки и имя его файла. В этом случае также требуются другие зависимые библиотеки. Для получения дополнительных сведений проверьте компиляцию g ++ и свяжите флаги. Эту информацию обычно можно получить с помощью pkg-config:

pkg-config --cflags --libs mylib

Для лучшего понимания я рекомендую использовать sourceCpp с подробным выводом, чтобы распечатать команды компиляции и связывания g ++:

sourceCpp("mysource.cpp", verbose=TRUE, rebuild=TRUE)
person B0RJA    schedule 01.04.2016
comment
Этот ответ здесь самый общий. Спасибо за подробности! - person ivan-k; 02.11.2016

Мне удалось связать библиотеку ускорения с помощью следующей глобальной команды в R перед вызовом sourceCpp

Sys.setenv("PKG_CXXFLAGS"="-I \path-to-boost\")

В основном это зеркало этого сообщения, но с другим параметром компилятора: http://gallery.rcpp.org/articles/first-steps-with-C++11/

person statsninja    schedule 15.08.2013
comment
Нет, вы не связывали, вы включали файлы заголовков, которые можно использовать без связывания. - person Dirk Eddelbuettel; 16.08.2013

Пара вещей:

  1. "Сторонние библиотеки заголовков" как в вашей теме не имеют смысла.

  2. Сторонние заголовки могут работать через шаблонный код, где заголовки - это все, что вам нужно, т.е. есть только шаг включения, и компилятор все решает.

  3. Если вам понадобятся библиотеки и фактическое связывание объектного кода, вы не сможете использовать мощный и полезный sourceCpp, если не предоставите ему метаинформацию через плагины (или env. Vars).

  4. Так что в этом случае напишите пакет.

Легкие и простые вещи - это как раз то, что с Rcpp и новыми атрибутами или более старыми встроенными и cxxfunction. Больше для сложного использования --- и внешние библиотеки сложнее, вам нужно обратиться к документации. Для этого мы добавили в Rcpp несколько виньеток.

person Dirk Eddelbuettel    schedule 21.12.2012
comment
Я исправил заголовок. Я думаю, что придерживаюсь случая (2), а не случая (3), но я не уверен. Это всего лишь один файл .h с тремя функциями. Как узнать, могут ли они просто #include и пойти, или им нужно собрать пакет? Это где-то задокументировано? Я создаю пакет, однако сейчас я всего лишь создаю прототип, и я не хочу, чтобы мне приходилось перестраивать свой пакет каждый раз, когда я отлаживаю один небольшой файл cpp. - person politicalEconomist; 21.12.2012

Угловые скобки ‹> предназначены для системных включений, таких как стандартные библиотеки.

Для файлов, локальных для вашего собственного проекта, используйте кавычки: "".

Кроме того, если вы помещаете заголовки в другой каталог, путь заголовка должен быть указан локально по отношению к исходному файлу, включая его.

Итак, для вашего примера это должно работать:

#include "../cppHeaders/coolStuff.h"

Вы можете настроить пути поиска таким образом, чтобы файл можно было найти без этого, но обычно это стоит делать только для того, что вы хотите включить в несколько проектов, или иначе ожидаете, что кто-то «установит».

person JasonD    schedule 21.12.2012
comment
Спасибо за объяснение по поводу ‹› и, однако, это все еще не работает. Я даже поместил копию coolStuff в ту же папку с CppFile.cpp И копию в каталог Rworking. Затем я попробовал: #include "coolStuff.h" и все равно получаю ошибку. - person politicalEconomist; 21.12.2012

Мы можем добавить его, записав путь к заголовку в переменной PKG_CXXFLAGS файла .R/Makevars, как показано ниже. Ниже приведен пример добавления файла заголовка xtensor, установленного вместе с Anaconda в macOS.

⋊> ~ cat ~/.R/Makevars                                                                                                                              
CC=/usr/local/bin/gcc-7
CXX=/usr/local/bin/g++-7
CPLUS_INCLUDE_PATH=/opt/local/include:$CPLUS_INCLUDE_PATH
PKG_CXXFLAGS=-I/Users/kuroyanagi/.pyenv/versions/miniconda3-4.3.30/include
LD_LIBRARY_PATH=/opt/local/lib:$LD_LIBRARY_PATH
CXXFLAGS= -g0 -O3 -Wall
MAKE=make -j4
person Keiku    schedule 01.02.2018

Это сработало для меня в Windows:

Sys.setenv("PKG_CXXFLAGS"='-I"C:/boost/boost_1_66_0"')

Изменить: на самом деле вам это не нужно, если вы используете заголовки Boost (спасибо Ральфу Стубнеру):

// [[Rcpp::depends(BH)]]

person Simon Woodward    schedule 14.09.2018