我正在使用一个非常简单的项目来测试CXX,将 Rust 库链接到 C++ 可执行文件中。
我编写了一个foo() -> ()Rust 函数并尝试从 C++ 访问它,但链接器找不到它。
这是我所拥有的:
// lib.rs
#[cxx::bridge]
mod ffi {
extern "Rust" {
pub fn foo() -> ();
}
}
pub fn foo() -> () {
println!("foo")
}
Run Code Online (Sandbox Code Playgroud)
# Cargo.toml
[package]
name = "cpprust"
version = "0.1.0"
edition = "2021"
[lib]
name = "cpprust"
path = "src/lib.rs"
crate-type = ["staticlib", "rlib", "dylib"] # EDIT: this is incorrect, see note at the end of question
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cxx = "1.0"
Run Code Online (Sandbox Code Playgroud)
// main.cpp
void foo(); // I tried including lib.rs.h but it was not generated!
int main() {
foo();
}
Run Code Online (Sandbox Code Playgroud)
运行cargo build生成target\debug\libcpprust.so. 然后我尝试使用以下命令创建项目(编辑:g++命令不正确,请参阅问题末尾的注释):
g++ -L../target/debug/ -lcpprust -o cpprust main.cpp
/tmp/ccOA8kJy.o: In function `main':
main.cpp:(.text+0x5): undefined reference to `foo()'
collect2: error: ld returned 1 exit status
make: *** [Makefile:2: cpprust] Error 1
Run Code Online (Sandbox Code Playgroud)
这里有什么问题吗?
编辑:prog-fh 的很好的答案正确地指出我需要包含build.rsC++ 编译,即使没有 C++ 在板条箱内编译和访问。然而,即使在实现了他们的答案之后,我仍然收到相同的错误消息。事实证明,我还有另外两个问题:1)我的参数顺序g++不正确,而我pthread -l dl也需要。它应该是:
g++ -o cpprust main.cpp -I ../target/cxxbridge -L../target/debug -lcpprust -pthread -l dl
2)我的Cargo.toml文件也生成"rlib", "dylib"库类型,但这不知何故也导致了上面的错误;staticlib仅当生成时才有效。
考虑到本文档,build.rs脚本应该生成lib.rs.h您尝试中缺少的内容。
请注意,文档中的示例认为主程序来自 Rust,而 C++ 代码是扩展。\n在您的问题中,情况恰恰相反:您的主程序来自 C++,但由一些 Rust 代码扩展。
\n这个答案由两部分组成:
\n编辑以回答评论中的后续问题
\n正如下面第二个注释中所述build.rs,中选择的名称.compile("cpp_from_rust")将用于命名包含已编译的 C++ 代码的库(libcpp_from_rust.a例如)。\n然后 Rust 将使用该库来扩展 Rust 代码:libcpprust.a生成的主要目标由 Rust 包含libcpp_from_rust.a。
如果之前未提供 C++ 文件.compile()(如下面第一个最小示例所示),则此 C++ 库仅包含启用extern "Rust"从 C++ 访问的符号。
$ nm ./target/debug/build/cpprust-28371278e6cda5e2/out/libcpp_from_rust.a\n\nlib.rs.o:\n U _GLOBAL_OFFSET_TABLE_\n0000000000000000 T _Z13rust_from_cppv\n U cxxbridge1$rust_from_cpp\nRun Code Online (Sandbox Code Playgroud)\n另一方面,您已经在文档中发现允许多次调用,.file()以便为 C++ 库提供来自各种源文件的更多代码。
另一个问题是关于我们希望 Rust 生成的库类型。\n本文档枚举了 Rust 可以生成的各种二进制目标,特别是各种类型的库。\n因为在您最初的问题中,您希望主要可执行文件位于 C++ 端,这意味着 Rust 应该生成一个可以被视为系统库的库,而不是 Rust 特定的库,因为在生成可执行文件时 Rust 将不再涉及。\n在上述文档中,我们可以看到只有staticlib和cdylib适合这种用途。
在我的示例中,我选择staticlib是为了简单起见,但cdylib也可以使用。\n但是,它有点复杂,因为主库 ( libcpprust.so) 是动态生成的,Rust 不会插入 C++ 库 ( libcpp_from_rust.a) 进去; 因此,我们必须链接这个C++库,这不是很方便。
g++ -std=c++17 -o cpp_program src/main.cpp \\\n -I .. -I target/cxxbridge \\\n -L target/debug -l cpprust \\\n -L target/debug/build/cpprust-28371278e6cda5e2/out -l cpp_from_rust \\\n -pthread -l dl\nRun Code Online (Sandbox Code Playgroud)\n当然,因为我们现在处理的是共享库,所以我们必须在运行时找到它。
\n$ LD_LIBRARY_PATH=target/debug ./cpp_program\nRun Code Online (Sandbox Code Playgroud)\n我不知道其他一些类型的库 ( crate-type) 是否可以(偶然)与这个 C++ 主程序一起工作,但文档指出仅staticlib和cdylib适合这种用法。
最后,请注意,如果您使用crate-type = ["staticlib", "rlib", "dylib"] in Cargo.toml(如评论中所述),您将生成三个库:
target/debug/libcpprust.a从staticlib,target/debug/libcpprust.rlib从rlib,target/debug/libcpprust.so从dylib。不幸的是,当使用命令链接时g++ ... -l cpprust ...,链接器会更喜欢; 您将处于与上述相同的情况。.so.acdylib
最小示例的目录布局
\ncpprust\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Cargo.toml\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 build.rs\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 src\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lib.rs\n \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 main.cpp\nRun Code Online (Sandbox Code Playgroud)\nCargo.toml
[package]\nname = "cpprust"\nversion = "0.1.0"\nedition = "2021"\n\n[lib]\ncrate-type = ["staticlib"]\n\n[dependencies]\ncxx = "1.0"\n\n[build-dependencies]\ncxx-build = "1.0"\nRun Code Online (Sandbox Code Playgroud)\nbuild.rs
fn main() {\n // This will consider the ffi part in lib.rs in order to\n // generate lib.rs.h and lib.rs.cc\n // minimal example: no C++ code to be called from Rust\n cxx_build::bridge("src/lib.rs")\n .compile("cpp_from_rust");\n}\nRun Code Online (Sandbox Code Playgroud)\nsrc/lib.rs
#[cxx::bridge]\nmod ffi {\n extern "Rust" {\n fn rust_from_cpp() -> ();\n }\n}\n\npub fn rust_from_cpp() -> () {\n println!("called rust_from_cpp()");\n}\nRun Code Online (Sandbox Code Playgroud)\nsrc/main.cpp
/*\n Building this program happens outside of the cargo process.\n We simply need to link against the Rust library and the\n system libraries it depends upon\n\n g++ -std=c++17 -o cpp_program src/main.cpp \\\n -I .. -I target/cxxbridge \\\n -L target/debug -l cpprust \\\n -pthread -l dl\n*/\n\n// consider the ffi part of Rust code\n#include "cpprust/src/lib.rs.h"\n\n#include <iostream>\n\nint\nmain()\n{\n std::cout << "starting from C++\\n";\n rust_from_cpp();\n std::cout << "finishing with C++\\n";\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n该命令将在 中cargo build生成静态库。\n构建主程序只需依赖常用命令,只要我们找到相关的头文件和库(请参阅代码中的注释)。libcpprust.atarget/debug
请注意,主程序的 C++ 源代码位于src此处的目录中,但它也可以放在其他任何地方。
双向示例的目录布局
\ncpprust\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Cargo.toml\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 build.rs\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 src\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 cpp_from_rust.cpp\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 cpp_from_rust.hpp\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lib.rs\n \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 main.cpp\nRun Code Online (Sandbox Code Playgroud)\n我们刚刚添加了一对.hpp/.cpp文件。
build.rs
fn main() {\n // This will consider the ffi part in lib.rs in order to\n // generate lib.rs.h and lib.rs.cc\n // The generated library (libcpp_from_rust.a) contains the code\n // from cpp_from_rust.cpp and will be inserted into the generated\n // Rust library (libcpprust.a).\n cxx_build::bridge("src/lib.rs")\n .file("src/cpp_from_rust.cpp")\n .flag_if_supported("-std=c++17")\n .compile("cpp_from_rust");\n}\nRun Code Online (Sandbox Code Playgroud)\n请注意,这次构建过程实际上处理了一些从 Rust 调用的 C++ 代码(见下文)。
\nsrc/lib.rs
#[cxx::bridge]\nmod ffi {\n extern "Rust" {\n fn rust_from_cpp() -> ();\n }\n unsafe extern "C++" {\n include!("cpprust/src/cpp_from_rust.hpp");\n fn cpp_from_rust() -> ();\n }\n}\n\npub fn rust_from_cpp() -> () {\n println!("entering rust_from_cpp()");\n ffi::cpp_from_rust();\n println!("leaving rust_from_cpp()");\n}\nRun Code Online (Sandbox Code Playgroud)\nsrc/cpp_from_rust.hpp
#ifndef CPP_FROM_RUST_HPP\n#define CPP_FROM_RUST_HPP\n\n// declare a usual C++ function (no Rust involved here)\nvoid\ncpp_from_rust();\n\n#endif // CPP_FROM_RUST_HPP\nRun Code Online (Sandbox Code Playgroud)\nsrc/cpp_from_rust.cpp
#include "cpp_from_rust.hpp"\n\n#include <iostream>\n\n// define a usual C++ function (no Rust involved here)\nvoid\ncpp_from_rust()\n{\n std::cout << "called " << __func__ << "()\\n";\n}\nRun Code Online (Sandbox Code Playgroud)\nCargo.toml,src/main.cpp并且构建过程(cargo build,g++ ...)仍然与前面的示例相同。
| 归档时间: |
|
| 查看次数: |
3077 次 |
| 最近记录: |