【CXX】4.1 CXX与Cargo集成配置详解

news/2025/2/22 14:36:47

一、规范设置

在Rust项目中,为了与C++代码进行互操作,cxx crate提供了一种便捷的方式。通过cxx-build crate,Cargo可以被扩展为一个C++构建系统。这在Cargo.toml文件中通过设置依赖来实现:

rust">[dependencies]
cxx = "1.0"

[build-dependencies]
cxx-build = "1.0"

build.rs文件是Cargo的构建脚本,它在这里用于配置cxx-build。以下是一个示例脚本,展示了如何桥接Rust和C++代码,并添加额外的C++源文件以及指定C++标准:

rust">// build.rs 
fn main() { 
cxx_build::bridge("src/main.rs")  // 返回 cc::Build 
.file("src/demo.cc") 
.std("c++11") 
.compile("cxxbridge-demo"); 
 
println!("cargo:rerun-if-changed=src/main.rs"); 
println!("cargo:rerun-if-changed=src/demo.cc"); 
println!("cargo:rerun-if-changed=include/demo.h"); 
} 

二、头文件默认根路径

在cxx-build中,C++头文件默认路径是crate。如果您的crate名称为yourcratename,并且头文件位于path/to/header.h,则在C++代码中应这样包含它:
#include "yourcratename/path/to/header.h"
如果您希望为头文件选择不同的前缀,可以通过修改build.rs中的CFG.include_prefix来实现。

rust">// build.rs
use cxx_build::CFG;
fn main() {
    CFG.include_prefix = "my/project";

    cxx_build::bridge(...)...
}

随后,位于path/to/header.h的标头现在可以包含为:

rust">#include "my/project/path/to/header.h"

三、包括生成的代码

如果你的#[cxx::bridge]模块包含一个extern “Rust"块,即从Rust向C++或任何共享数据结构公开的类型或函数,那么CXX生成的C++头文件可以使用Rust源文件名称添加”.h"获得。 如:

rust">// 头文件从路径path/to/lib.rs生产: 
#include "yourcratename/path/to/lib.rs.h"

四、包括依赖项中的头文件

您可以手写头文件(.h文件)包含在Cargo包中,也可以包含CXX生成的头文件。
它的工作原理与本地头文件的include相同,如:
`#include "dependencycratename/path/to/their/header.h``
请注意,跨crate导入仅在直接依赖项之间可用。传递依赖性是不支持的。
此外,只有当依赖项的Cargo.toml清单包含链接键时,直接依赖项的头文件才可导入。否则,其头文件将无法从同一crate外部导入。

五、高级功能

以下CFG设置仅在您编写需要支持下游crates #include(包括其C++公共头文件)的库时与您相关。

1、公开导出头文件目录 (CFG.exported_header_dirs)

CFG.exported_header_dirs(绝对路径向量)定义了一组额外的目录,从这些目录中,当前crate直接依赖的crate以及导出此crate头文件的其他将能够#include headers。
向exported_header_dirs添加目录类似于通过cc crate的build::include将其添加到当前构建中,但也使该目录可用于希望#包含crate中某个头的下游crate。如果仅使用Build::include添加目录,则包含您的标头的下游crate也需要手动将相同的目录添加到自己的构建中。
使用exported_header_dirs时,您的板条箱还必须在Cargo.toml中为自己设置一个链接键。原因是Cargo对没有链接键的构建脚本的执行没有顺序,这意味着下游机箱的构建脚本可能会在您决定将什么放入exported_header_dirs之前执行。
例子

rust">// build.rs

use cxx_build::CFG;
use std::path::PathBuf;

fn main() {
    let python3 = pkg_config::probe_library("python3").unwrap();
    let python_include_paths = python3.include_paths.iter().map(PathBuf::as_path);
    CFG.exported_header_dirs.extend(python_include_paths);

    cxx_build::bridge("src/bridge.rs").compile("demo");
}

例子
你的crate想要重新排列它导出的头文件,以及它们在crate源代码目录中的本地布局。
假设发布的crate包含一个文件./include/myheader.h,但希望它作为#include "foo/v1/public.h"可供下游crates使用。
// build.rs

use cxx_build::CFG;
use std::path::Path;
use std::{env, fs};

fn main() {
let out_dir = env::var_os(“OUT_DIR”).unwrap();
let headers = Path::new(&out_dir).join(“headers”);
CFG.exported_header_dirs.push(&headers);

// We contain `include/myheader.h` locally, but
// downstream will use `#include "foo/v1/public.h"`
let foo = headers.join("foo").join("v1");
fs::create_dir_all(&foo).unwrap();
fs::copy("include/myheader.h", foo.join("public.h")).unwrap();

cxx_build::bridge("src/bridge.rs").compile("demo");

}

公开导出依赖项

CFG.exported_header_prefixes(字符串向量)分别引用您的一个直接依赖项的include_pefixe或其前缀。它们描述了您的哪些依赖项参与了crate的C++公共API,而不是crate实现的私有使用。
一般来说,如果您的某个header#包含来自某个依赖项的内容,则需要将该依赖项的include_prefix放入CFG.exported_header_prefix中(或将其链接键放入CFG.export ed_header_links中;见下文)。另一方面,如果只有C++实现文件而不是头文件从依赖项导入,则不会导出该依赖项。
导出标头的意义在于,如果下游代码(crate𝒜)包含来自crate(")的标头的#include,并且您的标头包含来自依赖项(⻜)中的某些内容的#incleed,导出的依赖项⻜在下游机箱𝒜的构建过程中变得可用。否则,下游板条箱𝒜不知道⻜,也无法找到您的标头所指的标头,从而无法构建。
使用exported_header_prefixes时,您的板条箱还必须在Cargo.toml中为自己设置一个链接键。
例子
假设你有一个有5个直接依赖项的crate,每个依赖项的include_prefix都是:

"crate0"
"group/api/crate1"
"group/api/crate2"
"group/api/contrib/crate3"
"detail/crate4"

您的头包含前四个类型,因此我们将这些类型作为公共API的一部分重新导出,而crate4仅由您的cc文件内部使用,而不是您的头文件,因此我们不导出:

rust">// build.rs

use cxx_build::CFG;

fn main() {
    CFG.exported_header_prefixes = vec!["crate0", "group/api"];

    cxx_build::bridge("src/bridge.rs")
        .file("src/impl.cc")
        .compile("demo");
}

为了实现更细粒度的控制,有CFG.exported_header_links(字符串向量),每个链接都引用机箱直接依赖项之一的links属性(links manifest键)。
这实现了与CFG.exported_header_prefixes相同的结果,方法是将C++依赖项重新导出为机箱的公共API的一部分,但对于多个机箱可能共享相同的include_prefixes并且您希望导出一些但不导出其他机箱的情况,可以进行更精细的控制。Cargo保证链接属性是唯一的标识符。
使用exported_header_links时,您的板条箱还必须在Cargo.toml中为自己设置一个链接键。
例子

rust">// build.rs

use cxx_build::CFG;

fn main() {
    CFG.exported_header_links.push("git2");

    cxx_build::bridge("src/bridge.rs").compile("demo");
}

http://www.niftyadmin.cn/n/5862397.html

相关文章

Grok 3 开源体验与访问指南

目录 Grok 3 开源体验与访问指南 前言 Grok 3 是什么? 如何访问 Grok 3? 我的初步体验 开源的意义 结语 Grok 3 开源体验与访问指南 前言 就在今天(2025年2月20日),埃隆马斯克的 xAI 宣布 Grok 3 正式开源&#…

【电机控制器】ESP32-C3语言模型——DeepSeek

【电机控制器】ESP32-C3语言模型——DeepSeek 文章目录 [TOC](文章目录) 前言一、简介二、代码三、实验结果四、参考资料总结 前言 使用工具&#xff1a; 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、简介 二、代码 #include <Arduino.h&g…

Lua C API :使用 lua_tonumber 函数从 Lua 栈中提取数值

lua_tonumber 是 Lua C API 中的一个重要函数&#xff0c;用于从 Lua 栈中提取数值并将其转换为 C 中的 lua_Number 类型。这个函数非常有用&#xff0c;尤其是在 C 与 Lua 交互时&#xff0c;能够轻松地从 Lua 环境中获取数值并进行处理。 函数原型 lua_Number lua_tonumber…

Go 语言结合 Redis 实现固定窗口、滑动窗口、令牌桶和漏桶限流算法的示例代码

固定窗口算法 原理&#xff1a;将时间划分为固定大小的窗口&#xff0c;在每个窗口内对请求进行计数。如果请求数超过设定的阈值&#xff0c;则拒绝后续请求&#xff0c;直到进入下一个窗口。代码&#xff1a; package mainimport ("fmt""time""git…

如何将Python函数打包成.so库?

将Python函数打包成.so库的基本流程 安装依赖&#xff1a; 安装Cython&#xff1a;pip install cython安装OpenCV的Python包和开发库&#xff1a;pip install opencv-python # Ubuntu系统安装OpenCV开发库 sudo apt-get install libopencv-dev编写Cython代码&#xff08;.pyx文…

Unity游戏制作中的C#基础(4)数组声明和使用

一、数组的声明 在 C# 中&#xff0c;声明数组有多种方式&#xff0c;每种方式都有其适用的场景&#xff0c;下面为你逐一详细介绍&#xff1a; 1. 直接初始化声明 这种方式直观且便捷&#xff0c;在声明数组的同时就为其赋初值&#xff0c;让数组从诞生之初就拥有了具体的数据…

让浏览器AI起来:基于大模型Agent的浏览器自动化工具

最近有个非常火的项目,利用大模型Agent驱动浏览器完成各种操作,如网页搜索、爬虫分析、机票酒店预定、股票监控等,号称全面替代所有在浏览器上的操作,试用方式还是比较简单的,以下将进行简单介绍。 快速开始 通过pip安装: pip install browser-use安装web自动化框架:…

神经网络八股(三)

1.什么是梯度消失和梯度爆炸 梯度消失是指梯度在反向传播的过程中逐渐变小&#xff0c;最终趋近于零&#xff0c;这会导致靠前层的神经网络层权重参数更新缓慢&#xff0c;甚至不更新&#xff0c;学习不到有用的特征。 梯度爆炸是指梯度在方向传播过程中逐渐变大&#xff0c;…