使用autocxx实现Rust与C++交互时获取值异常问题排查
问题描述
在将Rust代码嵌入C环境时遇到值异常:C端调用board.columns()输出预期值7,但通过autocxx传递Board对象到Rust后,Rust端获取的board.columns()和传入的length都是随机无效值,需排查错误原因。
相关代码
CMakeLists.txt
cmake_minimum_required(VERSION 3.11.0) project(4Gewinnt VERSION 0.2.1) include(CTest) enable_testing() add_library(ai_lib SHARED src/mirror_ai.cpp ) # 引入Rust项目作为外部依赖 include(ExternalProject) ExternalProject_Add( rust_example SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/rust_example" BINARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/rust_example" CONFIGURE_COMMAND "" BUILD_COMMAND cargo build --release INSTALL_COMMAND "" BUILD_BYPRODUCTS "${CMAKE_CURRENT_SOURCE_DIR}/rust_example/target/release/librust_example.so" # 根据需要调整库名 ) # 指定C++可执行目标 add_executable(VierGewinnt src/main.cpp src/base_entity.cpp src/board.cpp src/color.cpp src/difficulty.cpp src/easy_ai.cpp src/game.cpp src/hard_ai.cpp src/medium_ai.cpp src/player.cpp ) target_link_libraries(VierGewinnt PRIVATE ai_lib) # 添加构建Rust项目的自定义命令 add_custom_command(TARGET VierGewinnt POST_BUILD COMMAND cargo build --release WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/rust_example" COMMENT "构建Rust项目" ) # 链接Rust库 add_dependencies(VierGewinnt rust_example) target_link_libraries(VierGewinnt PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/rust_example/target/release/librust_example.so") # 根据需要调整库名 # 包含目录 target_include_directories(VierGewinnt PRIVATE include lib "${CMAKE_CURRENT_SOURCE_DIR}/rust_example/include" ) target_compile_features(VierGewinnt PUBLIC cxx_std_20) target_compile_options(VierGewinnt PUBLIC -Wall -pedantic -Wextra)
rust.hpp
#pragma once #include "board.hpp" extern "C" { Column rust_main(Board board, int length); }
medium_ai.cpp
#include "medium_ai.hpp" #include "rust.hpp" #include <iostream> Column MediumAI::choose_slot(const Board &board) { int length = board.columns(); std::cout << "[cpp] board.columns: " << length << std::endl; Column result = rust_main(board, length); return result; }
lib.rs
use autocxx::prelude::*; use rand::Rng; include_cpp! { #include "board.hpp" #include "column.hpp" safety!(unsafe_ffi) generate!("Board") generate!("Column") } #[no_mangle] pub extern "C" fn rust_main(board: ffi::Board, length: i32) -> UniquePtr<ffi::Column> { println!( "[rust] board.columns: {}, length: {}", board.columns(), length ); // --> 两者均为随机无效值 let mut rand_slot: usize; let mut rng = rand::thread_rng(); loop { rand_slot = rng.gen_range(0..=board.columns()); println!("[rust] rand_slot: {}", rand_slot); if !board.is_full(ffi::Column::new(rand_slot).within_unique_ptr()) { break; } } let column = ffi::Column::new(rand_slot).within_unique_ptr(); column }
错误原因及解决方法
1. 参数传递的ABI不匹配
autocxx生成的Rust类型ffi::Board并非直接对应C++的Board值类型,按值传递Board会因内存布局、ABI差异导致Rust端解析错误,出现随机值。
解决方法:
传递Board指针而非值,保持两端类型匹配:
- 修改
rust.hpp:extern "C" { Column rust_main(const Board* board, int length); } - 修改
medium_ai.cpp的调用逻辑:Column result = rust_main(&board, length); - 修改Rust端函数签名:
pub extern "C" fn rust_main(board: &ffi::Board, length: i32) -> UniquePtr<ffi::Column> { // 原有逻辑不变 }
2. 返回值类型不匹配
C++声明rust_main返回Column值类型,但Rust端返回UniquePtr<ffi::Column>指针类型,这种不匹配会破坏栈帧,导致参数读取异常。
解决方法:
保持两端返回值类型一致:
- 若C++的
Column是值类型,修改Rust返回值为ffi::Column:pub extern "C" fn rust_main(board: &ffi::Board, length: i32) -> ffi::Column { // ... 原有逻辑 ffi::Column::new(rand_slot) } - 或修改C++端声明为指针类型,并处理内存生命周期:
extern "C" { Column* rust_main(const Board* board, int length); }
3. 编译ABI一致性检查
确保C++和Rust使用相同ABI标准:
- C++编译已启用
-fPIC(CMake配置中生成共享库默认包含),无需额外调整。 - 确认Rust编译目标平台与C++一致(如Linux下默认System V ABI,无需额外配置)。
内容的提问来源于stack exchange,提问作者floork
相关产品推荐
相关产品推荐

