Skip to content

Latest commit

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

通过 cmake 混合构建 Rust & Cpp

Rust 提供了非常好用的 FFI, 可以方便我们将 Rust 代码跟 C/Cpp 之间互操. 在开始之前先弄个基本的种子例子, 我决定还是用 SDL2 来做演示场景. 后续有可能会尝试一下音视频之类的, SDL2 很适合拿来学习.

准备工作

先把 SDL2 装上, macOS 系统下就访问 brew 中文 官网, 可以找到 brew 的安装方法

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

如果已经安装了 brew, 那就直接安装 sdl2

brew install sdl2

然后再用 cmake 配置一个 Cpp 项目环境, 没有装 cmake 也可以用 brew 安装

brew install cmake

接下来就是

mkdir rust_client &&cd rust_client
mkdir gui
touch gui/main.cpp
touch CMakeLists.txt

我们现在开始编辑 CMakeLists.txt

cmake_minimum_required(VERSION3.15)
set(SHORT_NAME cpp-with-rust)
project(${SHORT_NAME})
add_subdirectory(rs)
add_subdirectory(gui)

然后在 gui 目录下编辑 CMakeLists.txt, 具体 SDL2 目录以自己的环境为准

set(CMAKE_CXX_STANDARD 14)
include(FindSDL2.cmake)
find_package(SDL2REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS})
set(SOURCE main.cpp)
add_executable(gui${SOURCE})
get_target_property(CLIENT_DIR rs LOCATION)
target_link_libraries(gui${SDL2_LIBRARIES})
target_link_libraries(gui${CLIENT_DIR}/librs.dylib)
add_dependencies(guirs)

这里的 FindSDL2.cmake 是一个 github 上人家的配置文件, 解决找不到 SDL2 的头文件的问题. 可以访问该地址下载 SDL2Test.
还有个问题 get_target_property(RS_DIR rs LOCATION) 是后面创建的 Rust 项目里 CMakeLists.txt 定义的, 现在先这样写.
然后我们在 main.cpp 里用 SDL2 创建个窗口

#include"SDL.h"enum {
WINDOW_WIDTH = 960,
WINDOW_HEIGHT = 544,
SCREEN_CENTER = SDL_WINDOWPOS_CENTERED_MASK,
};
intmain(int argc, char **argv) {
if (SDL_Init(SDL_INIT_VIDEO) < 0) return -1;
SDL_Window *window = SDL_CreateWindow("Rust FFI Demo", SCREEN_CENTER, SCREEN_CENTER,
WINDOW_WIDTH,
WINDOW_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (window == nullptr) return -1;
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);
if (renderer == nullptr) return -1;
SDL_Event event;
while (true) {
if (SDL_PollEvent(&event)) {
if (SDL_QUIT == event.type) break;
SDL_SetRenderDrawColor(renderer, 100, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
}
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return0;
}

终于轮到 Rust 项目上场了, 直接在项目根目录下 cargo 走起

cargo new --lib rs

现在当前项目结构大概长这样

$ tree -L 3
.
├── CMakeLists.txt
├── build
├── gui
│ ├── CMakeLists.txt
│ ├── FindSDL2.cmake
│ └── main.cpp
└── rs
├── CMakeLists.txt
├── Cargo.lock
├── Cargo.toml
├── src
│ └── lib.rs
└── target
└── debug

打开 Cargo.toml 文件编辑一下

[package]
name = "rs"version = "0.1.0"authors = ["Author <xxx@example.com>"]
edition = "2018"
[dependencies]
[lib]
crate-type = ["cdylib"]

我们主要关注点 [lib] 下的内容, 添加 crate-type ["cdylib"] 这里意思是说创建 C 动态库, 其实也可以创建静态库, 具体参数是 crate-type = ["cdylib", "staticlib"] 其实也可以指定编译出来的库名字, 譬如指定为 app, 就是添加 name = "app"

重点来了, 创建 rs 目录下的 CMakeLists.txt, 添加如下内容

if (CMAKE_BUILD_TYPESTREQUAL"Debug")
set(CARGO_CMD cargo build)
set(TARGET_DIR "debug")
else ()
set(CARGO_CMD cargo build --release)
set(TARGET_DIR "release")
endif ()
set(RS_SO "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/librs.dylib")
add_custom_target(rsALLCOMMENT"Compiling rs module"COMMANDCARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}${CARGO_CMD}COMMANDcp${RS_SO}${CMAKE_CURRENT_BINARY_DIR}WORKING_DIRECTORY${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties(rs PROPERTIES LOCATION ${CMAKE_CURRENT_BINARY_DIR})
add_test(NAMErs_testCOMMANDcargotestWORKING_DIRECTORY${CMAKE_CURRENT_SOURCE_DIR})

现在为了保证项目干净整洁, 我们可以在项目根目录创建个 build, 然后再用 cmake 生成 make file

mkdir build &&cd build
cmake ..
make
./gui/gui

看看效果, 我看到的效果是一个暗红色空白窗口. 好了现在整个项目算了配置完成了.

Cpp 调用 Rust

通常我们学习当然是从 Hello World 开始的啦, 那就用控制台打印一下吧. 编辑 Rust src 下的 lib.rs 文件

#[no_mangle]pubextern"C"fnhello_rust(){println!("Hello Rust!");}

日常 Hello Rust, no_mangle 保证函数签名不被混淆, 这一步很重要, 不然 Cpp 调用的时候就会找不到对应的函数, 然后我们用 Cpp 调用一下.

// ...
#include"rust.hpp"// ...intmain(int argc, char **argv) {
// ...bool quit = true;
while (quit) {
if (SDL_PollEvent(&event)) {
if (SDL_QUIT == event.type) break;
if (SDL_KEYDOWN == event.type) {
switch (event.key.keysym.sym) {
caseSDLK_ESCAPE:
quit = false;
break;
case SDLK_j:
hello_rust();
break;
}
}
// ...
}
}
// ...return0;
}

肯定注意到有个 #include "rust.hpp", 原来是把 hello_rust 函数 extern 出来

// rust.hppextern"C" {
voidhello_rust();
};

然后在 SDL 主线程事件循环中通过键盘字母 j 触发调用, 也稍微改了点 退出的逻辑. 最后别忘了在 gui 目录下 CMakeLists.txt 中修改一下 SOURCE

# ...set(SOURCE rust.hpp main.cpp)
# ...

最后重复构建操作

cd build
cmake ..
make
./gui/gui

执行一下看看效果, 只要按了字母 j 就会在控制台打印 Hello Rust!
看样子简单的 Cpp 调用 Rust 完成了.


项目地址

https://github.com/limitLiu/cpp-with-rust.git

About

A Rust FFI demo on macOS.

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages