redhat编译c语⾔命令,在Stata中编写估计命令:编写C语⾔插
件
这篇⽂章演⽰了如何⽤其他语⾔(如C,C ++或Java)编写的代码插⼊到Stata中。这种技术被称为Stata编写插件或编写动态链接库(DLL)。本⽂中,在C语⾔中编写⼀个插件,它实现了mymean11.ado中mymean_work()执⾏的计算,在⽂章在Stata中编写估计命令编写插件中讨论过。
编写⼀个hello-worldC插件
在进⾏任何计算之前,先说明如何编写和编译与Stata通信的C插件。Code block 1包含myhello.ado的代码,该代码调⽤C插件hello,它只显⽰Stata中的“Hello from C”。
第6⾏执⾏句柄hello的插件。第10⾏将hello.plugin中实现的插件加载到句柄hello中。执⾏语句在加载语句开始出现之前就是奇数。完整地读取Stata ado⽂件,并且在执⾏主要ado程序⾏之前加载每个ado程序,Mata函数或插件句柄。所以第10⾏实际上是在第6⾏之前执⾏的。
插件的句柄名称,本例中的hello,必须与主要ado程序的名称,本例中的myhello以及此.ado⽂件中定义的任何其他ado程序不同。Code block 2中的hello.c的代码。
第2⾏包括Stata插件页眉⽂件stplugin.h。第6⾏是Stata C插件⼊⼝函数的标准声明。您应该复制它。在stata_call()中,argc将包含传递给插件的参数数量,字符串向量argv将包含参数本⾝。
第8⾏声明并为C字符串msg分配空间。第10⾏将“Hello from C”中的新⾏添加到msg中。第11⾏有Stata显⽰msg包含的内容。12⾏将0作为返回码。请注意,我将⽂字0转换为预期类型ST_retcode。
现在讨论如何从hello.c创建插件hello.plugin。在包含myhello.ado和hello.c的⽬录中,我也有stplugin.c。stplugin.c定义了⼀个函数,使stata_call()函数对Stata可⽤。
不要更改stplugin.h或stplugin.c的内容。事实上,您甚⾄不需要看它们。在安装了命令⾏开发⼯具的OS X
Mac上,使⽤gcc通过输⼊stplugin.c和hello.c来创建hello.plugin,gcc -bundle -DSYSTEM=APPLEMAC stplugin.chello.c -o hello.plugin
上⾯的gcc命令编译两个.c⽂件并链接它们以创建myhello.ado可以调⽤的DLL hello.plugin。在本⽂的附录中,我提供了在其他平台上创建hello.plugin的说明。创建了hello.plugin后,就可以在Stata中执⾏myhello。⽰例1:myhello
为简单起见,我将stplugin.h,stplugin.c,hello.c,myhello.ado和hello.plugin放在同⼀⽬录中。对于较⼤的项⽬,我会将.ado和.plugin⽂件放在Stata的ADOPATH⽬录中,并使⽤我的编译器环境来管理我放置标题和C源⽂件的位置。对于这篇⽂章中的⽰例,我将所有.ado⽂件,头⽂件,C源⽂件和创建的.plugin⽂件放⼊⼀个⽬录中。访问插件中的Stata数据
hello.plugin使Stata显⽰在插件中创建的内容。下⼀步是让插件访问Stata中的数据。为了说明这个过程,我讨论了mylistc.ado,它使⽤插件列出指定变量的观察结果。我们先来看⼀下ado-code。
第6⾏中,syntax创建了三个本地宏。它将⽤户指定的变量放⼊本地宏varlist中。它将⽤户指定的任何if条件放⼊本地宏if。将⽤户指定的任何条件放⼊本地宏in中。为syntax指定了max = 3以将变量数量为3。我不需要它作为Stata / Mata程序⽰例,但它简化了⽰例C插件。
第7⾏中,marksample创建了⼀个样本包含变量,并将其名称放在本地宏touse中。样本包含变量对于每⼀个被排除的观察和每个被包含的观察都是0。marksample使⽤本地宏varlist中的变量,本地宏中的if的条件,以及本地宏in中的范围来创建样本包含变量。
(所有三个本地宏由syntax创建。)如果本地宏varlist中的任何变量包含缺失值,如果被本地宏if的条件排除,或者被本地宏in的范围排除,则排除观察。样本包含变量是未被排除的观测值之⼀。
第9⾏中,通过显⽰插件列出的值的变量名称进⼀步简化了C插件。
第10⾏,plugin调⽤mylistw.plugin。因为指定了'varlist',所以Stata插件接⼝(SPI)函数SF_vdata()能访问本地宏varlist中包含的变量。因为如果指定了`touse',如果`touse'中的样本包含变量为0,则SPI函数SF_ifobs()将返回0,如果样本包含变量为1,则函数将返回1。由于指定了“in”,因此SPI函数SF_in1()和SF_in2()分别返回范围内任何⽤户指定的第⼀个和最后⼀个观察值。指定“in”不是识别⽤户指定样本所必需的,因为如果`touse'已经指定了此样本包含信息。但是,指定“in”可以显著减少数据循环中的观察范围,从⽽加快代码速度。
在包含stplugin.h,stplugin.c和mylistw.c的⽬录中,通过键⼊以下代码在Mac上创建了mylistw.plugingcc -bundle -DSYSTEM=APPLEMAC stplugin.cmylistw.c -o mylistw.plugin
如果你正在阅读这篇⽂章,你可以阅读标准C。我讨论了mylistw.c如何说明Stata C插件的结构,并解释了代码中使⽤的SPI定义的类型和函数。有关SPI的完整详细信息,请访问:。
如果⼀切顺利,mylistw.c会向Stata返回0,如果出错,会返回⼀个⾮0错误的代码。
每当在mylistw.c中调⽤⼀个可能失败的函数时,我都会检查它的返回码。如果该函数失败,我会让Stata显⽰错误消息,并向Stata返回⾮0错误代码。
该逻辑为mylisw.c提供了整体结构。⼤多数代码处理错误条件或者注意不要在字符串缓冲区中放⼊超出其容量的字符。C插件使⽤SPI中定义的函数读取或写⼊Stata对象。mylistw.c不会返回任何结果,因此它具有简单的结构。.
使⽤SPI函数从Stata中指定的数据样本中读取数据。.
使⽤标准C和SPI函数列出指定样本的观察值,并保留指定样本中观察数量的计数器。.
使⽤标准的C和SPI函数来显⽰样本中的第⼀个观察结果,这是样本中的最后⼀个观察结果,以及指定样本中有多少观察结果。现在,我将讨论mylistw.c的具体部分。
在9-12⾏中,使⽤SPI定义的类型ST_int,ST_double和ST_retcode来处理SPI函数返回的变量或SPI函数的参数。使⽤这些定义的类型是必不可少的,因为它们与原始C类型的映射会随着时间⽽变化。
rc保存插件将返回到Stata的返回代码。在16⾏,我将rc初始化为0。如果SPI函数可能会失败,那么它会返回0的返回码。如果SPI函数⽆法执⾏请求,则返回的是⾮0返回码。每次调⽤可能失败的SPI函数时,我都会将它返回的代码存储在rc中。如果rc不是0,我会让Stata显⽰错误消息并使插件返回存储在rc中的⾮0值。
第18,20和22⾏使⽤SPI功能。SF_in1()将in范围指定的第⼀个观察值放⼊first。SF_in2()将in范围内指定的最后⼀个观察值放⼊last。如果没有为plugin指定in范围,则first包含1,last将包含数据集中的观察数。SF_nvars()将varlist中指定的变量数放⼊nVars。
第30-32⾏确保我们跳过mylistc.ado第10⾏中为插件指定的if所排除的观察结果。为了说明⼀些细节,请参考⽰例2。⽰例2:mylistc
在第30⾏中,对于观察值i来说,当为plugin指定的if为1,SF_ifobs(i)返回1,否则返回0。在mylist.ado的第10⾏中,我们看到传⼊plugin的if是“touse”。如上所述,本地宏touse的样本包含变量对于排除的观察值是0,对于包括的观察值是1。
在mylistc.ado第10⾏的范围内,使mylistw.c第27⾏中循环的观察结果只能从范围内任何指定的开始到结束。在⽰例2中,mylistw.c的第27⾏上的循环从2到10,⽽不是在⾃动数据集上对所有74个观察结果进⾏循环。
在⽰例2中,样本包含变量对于6个观察值为1,对于其他68个观察值为0。在in 2/10范围内不包括观察结果1和11-74的观察结果。在前10个观察中,2个被排除,因为缺少rep78。排除⼀个观察因为trunk是21。
为了⽐较,在⽰例3中列出了2和10之间的所有9个观察结果。案例3:list
.
解析⽤户输⼊;
. 创建⼀些名称和对象来保存结果;.
调⽤⼯作程序来进⾏计算;
. 将⼯作程序返回的结果存储在e()中;. 显⽰结果.
mymeanc.ado和mymean11.ado之间的主要区别在于⼯作程序是C插件⽽不是Mata函数。
第6⾏和第7⾏与mylistc.ado中的相同。有关这些⾏如何创建本地宏varlist的说明,本地宏touse中包含的样本包含变量以及包含任何⽤户指定范围的本地宏,请参阅Getting access to the Stata data in yourplugin中mylistc.ado的讨论。
第8⾏将临时名称放⼊本地宏b,V和N中。我们将这些名称⽤于C插件计算的结果,并知道不会覆盖⽤户存储在全局Stata内存中的任何结果。(回想⼀下,Stata矩阵和标量是Stata中的全局对象;Using temporary names for globalobjectsin
Programming an estimation command in Stata: A first
ado-command⽂中有讨论本话题。)另外,此外,当mymeanc终⽌时,Stata将删除tempname创建的临时名称中的对象。第10-12⾏创建Stata矩阵来保存结果。我们使⽤tempname为这些矩阵创建临时名称。
mymeanc.ado中的第14⾏类似于mylistc.ado中第10⾏的对应部分。在这种情况下,插件调⽤mycalcs.plugin来完成⼯作。varlist的细节,`if' `touse' 和 `in'在上⾯讨论过。
最新的是我们将参数`b'`V'`N'将临时名称传递给mycalcs.plugin。mycalcs.plugin. 做计算
. 估计均值放⼊Stata矩阵中,该矩阵的名称在本地宏b中
.
估计量的估计⽅差(VCE)放⼊名称在本地宏V中的Stata矩阵中.
样本中的观察数量放⼊名称在本地宏N中的Stata标量中
16-18⾏将变量名称放在估计均值向量的列条带上以及VCE矩阵的⾏和列条带上。第19-21⾏将结果存储在e()中。在第22⾏显⽰结果。
现在讨论创建mycalcs.plugin的代码。
在讨论细节之前,我们创建插件并运⾏⼀个例⼦。
在包含mycalcs.c,mycalcsw.h,mycalcsw.c,stplugin.c和stplugin.h的⽬录中,通过输⼊以下代码在Mac上创建mycalcs.plugingcc -bundle -DSYSTEM=APPLEMAC stplugin.cmycalcsw.c mycalcs.c -o mycalcs.plugin创建mycalcs.plugin后,运⾏⽰例3。⽰例3:mymeanc
现在讨论⽤于创建mycalcs.plugin的C代码的⼀些⽅⾯。
从代码块6中的mycalcs.c开始,包含⼊⼝函数stata_call()的代码。
总之,mycalcs.c中的代码执⾏以下任务。1.
它将作为参数传⼊的Stata对象名称放⼊可以传递给⼯作函数的C字符串中。2.
它使⽤⼯作函数InitCmat()为C数组bmat和vmat分配空间,以保存矩阵结果。3.
它使⽤⼯作函数MyAve()和MyV()来计算存储在bmat,vmat和nObs中的结果。4.
它使⽤⼯作函数CopyCtoStataMatrix()和SPI函数SF_scal_save()从bmat,vmat和nObs中将结果复制到在步骤1中解析其名称的Stata对象中。5.
它释放分配的C数组并返回返回代码。
mycalcs.c很容易阅读,因为我将所有细节都放⼊了⼯作函数中。这些函数是在mycalcsw.c中定义,在下⾯我们将讨论它们。
与mylistw.c⼀样,mycalcs.c使⽤返回代码rc来处理错误情况。如果⼀切顺利,则每个⼯作函数返回0,如果⽆法执⾏所请求的作业,则返回⾮0错误代码。
如果返回代码不为0,mycalcs.c会进⼊⼀个代码块来处理错误。每个错误块使Stata显⽰错误消息,它释放任何已分配的C数组,最后,导致stata_call()返回⾮0代码。现在在Code block
7中讨论mycalcsw.c中的⼯作函数。
如何在C数组中实现矩阵的两个⽅⾯值得讨论。⾸先,将矩阵存储为具有⾏主要存储的向量,正如第7-10⾏的注释中所提到的那样。其次,使⽤第14-18⾏定义的预处理器宏来使代码更易于阅读。请注意,在第166-169⾏上未定义这些宏。
除了使⽤SF_error()使Stata显⽰错误消息,如果malloc()不能分配内存,⼯作函数InitCmat()使⽤标准C来实现矩阵分配和初始化函数。⼯作函数MyAve()是在Mata中实现的MyAve()的C实现,详见: Programming an estimation command in Stata:
Preparing to write a plugin。当我讨论mylistw.c时,如上所述,MyAve()处理Stata数据和缺失值。在第71⾏调⽤的⼯作函数DivideByScalar(),通过存储在n中的样本观察数划分bmat中的每个元素。(强制转换可确保执⾏浮点⽽不是整数除法。)⼯作函数MyV()是在Mata中实现的MyV()的C实现,参考Programming an estimation command in Stata:Preparing to write a plugin。MyV()使⽤到⽬前为⽌讨论的⼤多数编码技术和函数。此函数⽐其他函数更长,但其中的所有内容都是标准C或我已经讨论过的内容。
⼯作函数CopyCtoStataMatrix()将结果从C数组复制到Stata矩阵。使⽤SF_mat_store(smat,(i + 1),(j + 1),C(i,j))将元素从C数组的第i⾏和第j列复制到Stata矩阵中的相应元素。Stata矩阵元素被指定为(i + 1)和(j + 1),因为在代码中的C矩阵使⽤基于零开始的索引,⽽SF_mat_store()使⽤基于⼀个索引的Stata矩阵元素。⼯作函数Divide By
Scalar()⽤标量划分C数组中的每个元素。为了完整起见,我现在讨论mycalcsw.h。
代码块8中给出的mycalcsw.h包含mycalcsw.c中定义的⼯作函数的函数原型。
完成和撤消
我展⽰了如何实现⼀个C插件,该插件执⾏mymean10.ado和mymean11.ado中Mata⼯作函数执⾏的计算,如程序29中所述。附录
在⽂中,我展⽰了如何使⽤命令⾏开发⼯具编译和链接⼀个OS 10 MAC的插件。在这⾥,我在Windows
10和RedHat Linux上为gcc编译器提供命令。Windows10
本⼩节提供了在位Windows 10系统上编译和链接Cygwin环境中插件的命令。与其他平台不同,我们不能只使⽤gcc。在Cygwin中,gcc编译应⽤程序以在Cygwin POSIX / Unix环境中运⾏。我们希望使⽤Cygwin编译⼀个链接到本机Windows应⽤程序并在其中运⾏的库。
Cygwin拥有适⽤于Windows(MinGW)的极简的GNU编译器,可以满⾜我们的需求。
相应编译器的名称取决于平台。在位x86-Intel机器上,我使⽤了x86_-w-mingw32-gcc编译器。hello.plugin
在包含stplugin.h,stplugin.c和hello.c的⽬录中,通过键⼊以下命令来创建hello.plugin:x86_-w-mingw32-gcc -shared -mno-clwb
stplugin.c hello.c -o hello.pluginmylistw.plugin
在包含stplugin.h,stplugin.c和mylistw.c的⽬录中,通过键⼊以下命令来创建mylistw.plugin:x86_-w-mingw32-gcc -shared -mno-clwbstplugin.c mylistw.c -o mylistw.pluginmycalcs.plugin
在包含stplugin.c,stplugin.h,mycalcs.c,mycalcsw.h和mycalcsw.c的⽬录中,通过键⼊以下命令来创建mycalcs.plugin:x86_-w-mingw32-gcc -shared -mno-clwbstplugin.c mycalcsw.c mycalcs.c -o mycalcs.pluginRedHat Linux
本⼩节提供了在RedHat
Linux上编译和链接插件的gcc命令。hello.plugin
在包含stplugin.h,stplugin.c和hello.c的⽬录中,通过键⼊以下命令来创建hello.plugin:gcc -shared -fPIC -DSYSTEM=OPUNIXstplugin.c hello.c -o hello.pluginmylistw.plugin
在包含stplugin.h,stplugin.c和mylistw.c的⽬录中,通过键⼊以下命令来创建mylistw.plugin:gcc -shared -fPIC -DSYSTEM=OPUNIXstplugin.c mylistw.c -o mylistw.plugin1mycalcs.plugin
在包含stplugin.c,stplugin.h,mycalcs.c,mycalcsw.h和mycalcsw.c的⽬录中,通过键⼊以下命令来创建mycalcs.plugin:gcc -shared -fPIC -DSYSTEM=OPUNIX
stplugin.c mycalcsw.c mycalcs.c -o mycalcs.plugin