大致关注 this tutorial ,我设法让这个玩具项目工作。它从 C++ 程序调用 Haskell 函数。
Foo.hs{-# LANGUAGE ForeignFunctionInterface #-}
module Foo where
foreign export ccall foo :: Int -> Int -> IO Int
foo :: Int -> Int -> IO Int
foo n m = return . sum $ f n ++ f m
f :: Int -> [Int]
f 0 = []
f n = n : f (n-1)
bar.c++#include "HsFFI.h"
#include FOO // Haskell module (path defined in build script)
#include <iostream>
int main(int argc, char *argv[]) {
hs_init(&argc, &argv);
std::cout << foo(37, 19) << "\n";
hs_exit();
return 0;
}
call-haskell-from-cxx.cabalname: call-haskell-from-cxx
version: 0.1.0.0
build-type: Simple
cabal-version: >=1.10
executable foo.so
main-is: Foo.hs
build-depends: base >=4.10 && <4.11
ghc-options: -shared -fPIC -dynamic
extra-libraries: HSrts-ghc8.2.1
default-language: Haskell2010
#!/bin/bash
hs_lib="foo.so"
hs_obj="dist/build/$hs_lib/$hs_lib"
ghc_version="8.2.1" # May need to be tweaked,
ghc_libdir="/usr/local/lib/ghc-$ghc_version" # depending on system setup.
set -x
cabal build
g++ -I "$ghc_libdir/include" -D"FOO=\"${hs_obj}-tmp/Foo_stub.h\"" -c bar.c++ -o test.o
g++ test.o "$hs_obj" \
-L "$ghc_libdir/rts" "-lHSrts-ghc$ghc_version" \
-o test
env LD_LIBRARY_PATH="dist/build/$hs_lib:$ghc_libdir/rts:$LD_LIBRARY_PATH" \
./test
893 – 但它不是很健壮,也就是说,如果我删除 Haskell 函数的实际调用,即 std::cout << foo(37, 19) << "\n";行,然后在链接步骤失败,并显示错误消息/usr/local/lib/ghc-8.2.1/rts/libHSrts-ghc8.2.1.so: undefined reference to `base_GHCziTopHandler_flushStdHandles_closure'
/usr/local/lib/ghc-8.2.1/rts/libHSrts-ghc8.2.1.so: undefined reference to `base_GHCziStable_StablePtr_con_info'
/usr/local/lib/ghc-8.2.1/rts/libHSrts-ghc8.2.1.so: undefined reference to `base_GHCziPtr_FunPtr_con_info'
/usr/local/lib/ghc-8.2.1/rts/libHSrts-ghc8.2.1.so: undefined reference to `base_GHCziWord_W8zh_con_info'
/usr/local/lib/ghc-8.2.1/rts/libHSrts-ghc8.2.1.so: undefined reference to `base_GHCziIOziException_cannotCompactPinned_closure'
...
foo 时构建脚本的输出通话包括在内,ldd在最终的可执行文件上:++ cabal build
Preprocessing executable 'foo.so' for call-haskell-from-C-0.1.0.0..
Building executable 'foo.so' for call-haskell-from-C-0.1.0.0..
Linking a.out ...
Linking dist/build/foo.so/foo.so ...
++ g++ -I /usr/local/lib/ghc-8.2.1/include '-DFOO="dist/build/foo.so/foo.so-tmp/Foo_stub.h"' -c bar.c++ -o test.o
++ g++ test.o dist/build/foo.so/foo.so -L /usr/local/lib/ghc-8.2.1/rts -lHSrts-ghc8.2.1 -o test
++ env LD_LIBRARY_PATH=dist/build/foo.so:/usr/local/lib/ghc-8.2.1/rts: sh -c 'ldd ./test; ./test'
linux-vdso.so.1 => (0x00007fff23105000)
foo.so => dist/build/foo.so/foo.so (0x00007fdfc5360000)
libHSrts-ghc8.2.1.so => /usr/local/lib/ghc-8.2.1/rts/libHSrts-ghc8.2.1.so (0x00007fdfc52f8000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fdfc4dbe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdfc49f4000)
libHSbase-4.10.0.0-ghc8.2.1.so => /usr/local/lib/ghc-8.2.1/base-4.10.0.0/libHSbase-4.10.0.0-ghc8.2.1.so (0x00007fdfc4020000)
libHSinteger-gmp-1.0.1.0-ghc8.2.1.so => /usr/local/lib/ghc-8.2.1/integer-gmp-1.0.1.0/libHSinteger-gmp-1.0.1.0-ghc8.2.1.so (0x00007fdfc528b000)
libHSghc-prim-0.5.1.0-ghc8.2.1.so => /usr/local/lib/ghc-8.2.1/ghc-prim-0.5.1.0/libHSghc-prim-0.5.1.0-ghc8.2.1.so (0x00007fdfc3b80000)
libgmp.so.10 => /usr/lib/x86_64-linux-gnu/libgmp.so.10 (0x00007fdfc3900000)
libffi.so.6 => /usr/local/lib/ghc-8.2.1/rts/libffi.so.6 (0x00007fdfc36f3000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fdfc33ea000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fdfc31e2000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fdfc2fde000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fdfc2dc1000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdfc5140000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fdfc2bab000)
最佳答案
这个答案解释了链接过程中发生了什么,为什么使用 -Wl,--no-as-needed 的解决方案工作以及应该做些什么来获得更强大的方法。
简而言之:-lHSrts-ghcXXX.so取决于 libHSbaseXXX.so , libHSinteger-gmpXXX.so和 libHSghc-primXXX.so必须在链接期间提供给链接器。
这里提出的解决方案依赖于大量的手动工作,并且可扩展性不强。但是我对 cabal 的了解还不够。告诉你如何自动化这个,但我希望你能迈出最后一步。
或者也许你会很好地使用 -Wl,--no-as-needed -解决方案,因为您知道幕后发生的事情。
让我们从不调用 foo 的情况下逐步完成版本的链接过程开始。 ,以某种简化的方式(here 是 Eli Bendersky 的一篇很棒的文章,即使它是关于静态链接的):
main在表中,这个符号的定义是未知的。 main找到它的目标文件test.o .但是,函数 main使用函数 hs_init和 hs_exit .因此我们找到了main 的定义。 ,但除非我们知道 hs_init 的定义,否则它不起作用和 hs_exit .所以现在我们必须寻找它们的定义。 foo.so , 但是 foo.so没有定义任何我们感兴趣的符号(foo 没有被使用!)并且链接器只是跳过 foo.so并且永远不会回头。 -lHSrts-ghcXXX.so .在那里它找到了 hs_init 的定义。和 hs_exit .因此,使用了共享库的全部内容,但需要定义诸如 base_GHCziTopHandler_flushStdHandles_closure 之类的符号。 .这意味着链接器开始寻找这些符号的定义。 foo 的情况有什么不同?用来? 2.步骤后不仅hs_init和 hs_exit被通缉但也foo ,可在 foo.so 中找到.所以foo.so必须包括在内。foo.so的方式已构建,包含以下信息:>>> readelf -d dist/build/foo.so/foo.so | grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [libHSrts-ghc7.10.3.so]
0x0000000000000001 (NEEDED) Shared library: [libHSbase-4.8.2.0-HQfYBxpPvuw8OunzQu6JGM-ghc7.10.3.so]
0x0000000000000001 (NEEDED) Shared library: [libHSinteger-gmp-1.0.0.0-2aU3IZNMF9a7mQ0OzsZ0dS-ghc7.10.3.so]
0x0000000000000001 (NEEDED) Shared library: [libHSghc-prim-0.4.0.0-8TmvWUcS1U1IKHT0levwg3-ghc7.10.3.so]
0x0000000000000001 (NEEDED) Shared library: [libgmp.so.10]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
>>> readelf -d dist/build/foo.so/foo.so | grep RPATH
0x000000000000000f (RPATH) Library rpath: [
/usr/lib/ghc/base_HQfYBxpPvuw8OunzQu6JGM:
/usr/lib/ghc/rts:
/usr/lib/ghc/ghcpr_8TmvWUcS1U1IKHT0levwg3:
/usr/lib/ghc/integ_2aU3IZNMF9a7mQ0OzsZ0dS]
NEEDED -flag)以及它们在系统上的什么位置(RPATH)。这些库被找到/打开/处理(即根据需要标记),因此存在所有必要的定义。g++ ...
-Wl,--trace-symbol=base_GHCziTopHandler_flushStdHandles_closure \
-Wl,--verbose \
-o test
foo.so通过 -Wl,--no-as-needed 包含在生成的可执行文件中正如@Yuras 所建议的那样。-lHSrts-ghcXXX.so 之后),而不是依赖于通过其他共享库偶然添加它们。显然,有些神秘的名称仅对我的安装有效:g++ ...
-L/usr/lib/ghc/base_HQfYBxpPvuw8OunzQu6JGM -lHSbase-4.8.2.0-HQfYBxpPvuw8OunzQu6JGM-ghc7.10.3 \
-L/usr/lib/ghc/integ_2aU3IZNMF9a7mQ0OzsZ0dS -lHSinteger-gmp-1.0.0.0-2aU3IZNMF9a7mQ0OzsZ0dS-ghc7.10.3 \
-L/usr/lib/ghc/ghcpr_8TmvWUcS1U1IKHT0levwg3 -lHSghc-prim-0.4.0.0-8TmvWUcS1U1IKHT0levwg3-ghc7.10.3 \
...
-o test
rpath 仅设置在 foo.so 中,但未使用 foo.so)。要修复它,我们可以扩展 LD_LIBRARY_PATH或添加 -rpath链接命令行:g++ ...
-L... -lHSbase-... -Wl,-rpath,/usr/lib/ghc/base_HQfYBxpPvuw8OunzQu6JGM \
-L... -lHSinteger-gmp-... -Wl,-rpath,/usr/lib/ghc/integ_2aU3IZNMF9a7mQ0OzsZ0dS \
-L... -lHSghc-prim-... -Wl,-rpath,/usr/lib/ghc/ghcpr_8TmvWUcS1U1IKHT0levwg3 \
...
-o test
foo.so 时似乎会这样做),但我不知道该怎么做,因为我没有使用 haskell/cabal 的经验。
关于c++ - 如何正确链接用 Haskell 编写的目标文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50569661/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl