半亩地
我现在想做的是在R下调用vc++.
我用的是vc++6.0
我看了Writing R Extensions下的interfacing C++ code,不知道这在windows下是否可行。
自己按照下面的步骤试了试。
首先在vc++下建立了名为test的工程。然后建立了头文件(.h) (上面用的是.hh文件,在vc++6.0下头文件是.h)
// X.h
class X {
public: X (); ~X ();
};
class Y {
public: Y (); ~Y ();
};
建立cpp文件 (x.cpp) (上面用的是.cc文件,vc++下应该是cpp吧)
// X.cc
#include <iostream>
#include "X.h"
static Y y;
X::X() { std::cout << "constructor X" << std::endl; }
X::~X() { std::cout << "destructor X" << std::endl; }
Y::Y() { std::cout << "constructor Y" << std::endl; }
Y::~Y() { std::cout << "destructor Y" << std::endl; }
建立cpp文件(x_main.cpp)
// X_main.cpp:
#include "X.h"
extern "C" {
void X_main () {
X x;
}
} // extern "C"
在vc++编译器下是无法编译的,因为x_main()是无法识别的。(后来把x_main改为了main(),在vc++下编译通过了,可是没有想通为什么会输出一个constructor Y)
// X_main.cc:
#include "X.h"
extern "C" {
void X_main () {
X x;
}
} // extern "C"
)
然后在 cmd命令下进入了工程所有的文件夹,执行R CMD SHLIB X.cpp X_main.cpp
提示错误:make: *** No rule to make target `X.o', needed by `X.dll'. Stop.
无论x_main.cpp文件下的主函数用main还是x_main都是相同的错误。
注:我原来在R下调.c文件是通过的,就是说环境变量和Rtools都好着呢。
现在主要是想用 vc++。
然后看了The R for Windows FAQ (rw-FAQ),觉得之前的操作和前面的一样,在cmd下的操作改为
cl /MT /c X.cpp X_main.cpp
这个执行后没有错误提示
link /dll /out:X.dll /export:X_main X.obj X_main.obj
这个执行后提示错误:
Microsoft (R) Incremental Linker Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
LINK : error LNK2001: unresolved external symbol X_main
X.lib : fatal error LNK1120: 1 unresolved externals
LINK : fatal error LNK1141: failure during build of exports file
求助呀!!
是不是vc++应该和R的库连接出问题了呀
Ihavenothing
我建议你看一下Rcpp这个包的源代码,它主要有两个文件,Rcpp.h和Rcpp.cpp,这两个文件应该是可以自己编译的,如果成功了可以看一下它的结构来学习;另一个原因是这两个文件定义了一系列方便操作R对象的函数,这样有些比较底层的操作就可以简化。
Ihavenothing
此外我测试了一下你给的代码,可以编译成功啊,是不是有些细节问题没有处理好?比如文件名对不对之类的。
yanlinlin82
因为代码中有一句全局变量的定义“static Y y;”,所以在模块被加载的时候,会自动创建该变量,于是Y类的构造函数就被调用了。
所谓“在vc++编译器下……x_main()是无法识别的”,我觉得可能跟你选择的工程类型有关。如果你创建的工程不是dll类型工程,而是一个console类型工程,则编译器会要求代码或库中有一个main函数。上面的代码是没有语法问题的,我估计你遇到的不是编译错误,而是链接失败。
至于最后使用cl和link命令行,得到一个不存在X_main的符号的错误提示,是因为C++编译器会在extern "C"定义的符号前面加一个下划线,成为“_X_main”。如果你需要输出“X_main”,那你应该再写一个.def文件来定义输出符号。
半亩地
谢谢。
我现在进行了如下改正:
建工程的时候用的是MFC AppWizard(dll)
其他的没有改变
这时在vc++里面编译就通过不了了,提示错误:
fatal error C1010: unexpected end of file while looking for precompiled header directive
但是这里在cmd下执行
cl /MT /c X.cpp X_main.cpp
link /dll /out:X.dll /export:X_main X.obj X_main.obj
是成功的,并且生成了X.dll文件
在R下运行
> dyn.load(paste("X", .Platform$dynlib.ext, sep = ""))
> .C("X_main")
输出:
list( )
我看了下文档,在R中没有输出的问题,没有输出的问题是不是因为There is no guarantee that the output will appear in the R console??
问题是在vc++下为什么没法编译?要建立什么类型的工程?
还有这要不要求在R中加载什么包呀?
(我下了Rcpp包,但是没有看懂……)
yanlinlin82
在工程选项中,找到有关“precompiled header”的选项,选择不使用预编译头文件,然后应该就能编译通过了。
半亩地
在麻烦问一下,我按照提示,设置precompiled header 成no use,编译文件指定了Rgui.exe ,编译后直接进入了R的控制平台。没有任何输出。
我想能不能也在vc++的编译环境下,在vc++的调试平台下输出结果呢?就像普通的cpp文件一样,在那个dos窗口下输出。
记得原来在做matlab和vc++混编的时候是可以的,不知道在R里面怎么做。。
再谢。
半亩地
我下载了这个包,看了一下,感觉这个包做的主要是R和c++的数据转化。
但是我在只找到了Rcpp.h,没有找到Rcpp.cpp呀?
只有D:\统计\R语言\Rcpp_0.6.8\Rcpp\doc\man\man3有个Rcpp.cpp.3文件,用写字版打开看了一下,感觉不太像呀?
我现在是刚接触这个东西,感觉一头雾水,还有什么参考文档可以看的没??
yanlinlin82
你有些描述我看不太明白,也不清楚你最终想要实现什么。
“编译文件指定了Rgui.exe”指什么?是说用“运行”来调试程序么?这么是的话,当然就跟普通打开Rgui.exe一样的。接下来还是得要自己输入R命令的。
半亩地
编译文件是说这:
编译连接执行后出现对话框:Executable for Debug session
要求:please specify the exectable file
也不知道是不是这样理解,我以前做vc++和matlab混编的时候指定的是matlab.exe
我不是说在R下调试,我是说能不能像调试普通c++程序一样,运行后在dos下输出:
最后提示那个 please press any key to continue
还有,我的目的是,在R下调用vc++的函数,把R的数据传到c++ 的函数里面,然后再把vc++里面的运行结果返回到R下,再用R作分析。
上面说的例子只是做了一个混编的实验,还没有做到数据转换呢。
不知道我的想法能不能像这样实现?
Ihavenothing
哦,是要下载源码包,不能是已编译的包。
yanlinlin82
出现“Executable for Debug session”的对话框,就表示编译链接都已经成功通过了。你用VC6的话,想必是点击了那个感叹号的按钮,或者可能用的是F5,这是在链接成功后自动运行程序以便进行调试。
如果你选择了某个程序进行调试运行,那么当该程序调用了你的C++代码时,是可以在VC环境下单步跟踪调试的。你所说的“vc++和matlab混编”应该也大概是这个意思。
你做一个dll工程,输出一个函数,就是提供给R来调用的(使用dyn.load载入后用.C函数调用),所以启动Rgui.exe是对的,并不一定要追求那个“在dos下输出”的效果。不过,你的程序中用cout输出了内容,这在Rgui里是看不到的。可以在运行时使用R.exe代替Rgui.exe,就能看到输出内容了。要想在Rgui里看到输出,得使用R提供的Rprintf或REprintf函数,那就需要自己编译R的源码库了。
要实现你的目的,“在R下调用vc++的函数,把R的数据传到c++ 的函数里面,然后再把vc++里面的运行结果返回到R下,再用R作分析”,C++的函数中应该可以不需要调用R的函数,所以应该是可以不用编译R源码的。看看接口函数的标准,相应修改你的X_main函数的类型,要在R与C++之间互相传递数据其实很简单的。
另外,VC6太老了,很多东西很不标准,建议放弃。免费的可以考虑下载VC 2008 Express,开源的可以考虑gcc。
半亩地
谢谢,哈哈,我有点明白了。
把x_main()函数的形参设为指针形式,然后返回到R里面就是相应的列表形式是不是?
我这样定义了x_main(double* n,double * m)
然后回到R里面调用.C(“X_main”,a=1,b=2)后,得到了list(a,b)
(这里的形参是不是只能用double*形式的才可以传递,我用int *形式的时候,参数不会发生任何变化?)
只是有点不完美的是,此时在R中导入X.dll的时候,出现了一个警告信息:
警告信息:
In inDL(x, as.logical(local), as.logical(now), ...) :
DLL试图把FPU控制词从8001f改成9001f
虽然没影响最后的结果,可是这是什么原因呢?
半亩地
我下了源码包,现在遇到了上些问题:
1.我在一个dll工程中添加了包中src文件夹下所有的.h和.cpp文件,这里编译出现了一个错误和七个警告:
--------------------Configuration: ask2 - Win32 Debug--------------------
Compiling...
Rcpp.cpp
d:\program files\microsoft visual studio\vc98\include\xtree(118) : warning C4786: 'std::_Tree<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,
int>,std::map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,int,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<int> >::_Kfn,std::less<std::basic_string<char,std::char_traits<cha
r>,std::allocator<char> > >,std::allocator<int> >' : identifier was truncated to '255' characters in the debug information
……
d:\rwork\rcpp\src\rcpp.h(159) : warning C4786: 'std::reverse_iterator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits
<char>,std::allocator<char> > &,std::basic_string<char,std::char_traits<char>,std::allocator<char> > *,int>' : identifier was truncated to '255' characters in the debug information
d:\rwork\rcpp\src\rcpp.cpp(315) : error C2065: 'make_pair' : undeclared identifier
Error executing cl.exe.
Rcpp.obj - 1 error(s), 7 warning(s)
错误信息是:'make_pair' : undeclared identifier。由于警告信息太长,就没有一一列出,而且我觉得好像这七个警告信息是一样的类型。
另外,我参考了原来你的一个帖子,讲利用这个包进行数据传递的,我在这个工程下添加了一个cpp文件:
//test.cpp
#include "Rcpp.h"
#include "Rcpp.cpp"
RcppExport SEXP ans(SEXP vector)
{ RcppVector<double> vec(vector);
int n=vec.size();
double temp=vec(n-1);
vec(n-1)=vec(0);
vec(0)=temp;
RcppResultSet rs;
rs.add("result",vec);
return rs.getReturnList(); }
这时出现的错误信息和警告信息和上面的情况是相同的。
(注:错误信息同上,是'make_pair' : undeclared identifier)。
最后,我想问一下,用这种方法,如果编译通过后,是不是要生成相应cpp文件的dll,比如,上个test.cpp,生成test.dll后,在R下导入dll,然后用.CALL 调用这个cpp函数。
yanlinlin82
那个臭名昭著的C4786警告,是古老的VC6的编译器针对每个类型的名称,其缓冲区都固定只分配了256字节。在解析模板类型时,一旦类型扩展开,超过256字节,就会提示这个警告。
make_pair的类型找不到,我怀疑也跟VC6提供的STL库不标准有关。估计需要你自己手工加入:
#include <utility> // 或 #include <map>
using std::make_pair;
所以,还是建议你尽早放弃VC6。
半亩地
我下载了VC 2008 Express。
现在用这个在做的时候还是遇到了问题:
我建立了一人dll工程,然后在工程中添加了Rcpp源码包中的.h和.cpp文件,在编译的时候一开始遇到了些问题:
1.Cannot open include file: 'RcppDatetime.h‘之类。然后把include<RcppDatetime.h>改为了#include "RcppDatetime.h"
这个问题是解决了。
2.后来有一个error C3861: 'snprintf': identifier not found。然后把snprintf改为了_snprintf.这个问题也解决了。
error C3861: 'round': identifier not found,这个问题不知道是要加什么头文件,加了"inclue"math.h"不行, 于是自己写了一个round:
//m_us = static_cast<int>(round( (m_d - tt) * 1.0e6)); //把这段代码用下面的代码代替了
double temp=(m_d - tt) * 1.0e6;
int temp1;
if(temp>=0) temp1=(int)(temp+0.5);
else temp1=(int)(temp-0.5);
m_us = static_cast<int>(temp1);
这个问题也解决了。
这些问题我就是照着自己的理解做的,也不知道有没有道理。
现在遇到的问题是,round的问题解决后,编译出现了78个错误和3个警告。
错误信息是: error LNK2019: unresolved external symbol _Rf_protect referenced in function "public: void __thiscall RcppResultSet::add(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class RcppDate &)" (?add@RcppResultSet@@QAEXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AAVRcppDate@@@Z)
error LNK2001: unresolved external symbol _SET_VECTOR_ELT
fatal error LNK1120: 37 unresolved externals
警告信息是:
warning PRJ0018 : The following environment variables were not found:
1>$(COPYING)
1>Build log was saved at "file://d:\rwork\rcpp_1224\rcpp_1224\Debug\BuildLog.htm"
感觉是哪个外部文件没有找到。不知道怎么解决?
另外,如果我像上面说的要实现R和vc++间的数据传递,比如就是把R中的一个向量,首尾字符互换,然后传回R,是不是就是在上述工程中添加一个cpp文件
#include "Rcpp.h"
#include "Rcpp.cpp"
RcppExport SEXP ans(SEXP vector)
{ RcppVector<double> vec(vector);
int n=vec.size();
double temp=vec(n-1);
vec(n-1)=vec(0);
vec(0)=temp;
RcppResultSet rs;
rs.add("result",vec);
return rs.getReturnList(); }
然后生成相应cpp文件的dll,在R下导入dll,然后用.CALL 调用这个cpp函数?
yanlinlin82
#include <file> 与 #include "file" 的区别在于,前者只检查系统目录(在VC环境中配置的头文件目录),而后者还检查源文件所在的目录。所以,一般系统头文件用前者,而自己写的头文件用后者。如果你在工程属性中指定了RcppDatetime.h所在的目录,那用前者应该是可以的。
round函数我记得应该是在math.h里的(可以用MSDN来查一个函数所在的头文件)。而_snprintf 函数名的下划线是微软的编译器搞出来的,有一部分兼容Posix标准的,被加了下划线开头。
你最后的Link错误应该是使用到了R的库,但其中没有该函数的实现,最好是用VC2008编译后再链接进来,不同编译器的编译库文件不一定能混用。
半亩地
用VC2008编译后再链接进来?是什么意思?没明白,我用的已经是vc++ 2008 expression edition
使用了R的库,是不是要在library files中添加R中的外部连接库 lib呀,我记得用matlab的时候是这么做的?
另外,是不是像我上面说的那样,生成对应cpp的dll后在R中调用呀
yanlinlin82
是的,要添加R的lib。
半亩地
具体要添加lib的名字是什么呀?
是在vc++ directories——library fils中添加吗?
我在R的安装包中怎么也找不到lib文件呀