Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call a C++ dynamic library from Rust?

Tags:

c++

rust

ffi

I want to call a C++ dynamic library (*.so) from Rust, but I don't want to build it from Rust. Like this,

cc::Build::new()
    .file("src/foo.cc")
    .shared_flag(true)
    .compile("libfoo.so");

In some cases, I only need to call several functions, not all the functions. How can I use it?

like image 797
Chi Wei Shen Avatar asked Oct 22 '18 06:10

Chi Wei Shen


People also ask

Can I call C from Rust?

Rust can link to/call C functions via its FFI, but not C++ functions. While I don't know why you can't call C++ functions, it is probably because C++ functions are complicated. You can just define C linkage on any C++ function, making it available from C and thus also Rust. extern "C" is your friend here.

Can C Interop Rust?

Interoperability with foreign code Rust guarantees that the layout of a struct is compatible with the platform's representation in C only if the #[repr(C)] attribute is applied to it.

What is extern C in Rust?

The extern keyword is used in two places in Rust. One is in conjunction with the crate keyword to make your Rust code aware of other Rust crates in your project, i.e., extern crate lazy_static; . The other use is in foreign function interfaces (FFI). extern is used in two different contexts within FFI.


2 Answers

Before you go further, make sure you have a basic idea of Rust FFI (foreign function interface).

In Rust, it's easy to call C, but hard to call C++.

To call C functions in Rust, you just have to wrap them with extern, do some basic type casting and sometimes unsafe.

To call C++ functions, since Rust does not have built-in knowledge of C++ features, you may have to do a lot of manual translation. For example, here is part of the documentation from Rust-Qt:

Many things are directly translated from C++ to Rust:

  • Primitive types are mapped to Rust's primitive types (like bool) and types provided by libc crate (like libc::c_int).
  • Fixed-size numeric types (e.g int8_t or qint8) are mapped to Rust's fixed size types (e.g. i8).
  • Pointers, references and values are mapped to Rust's respective types.
  • C++ namespaces are mapped to Rust submodules.
  • C++ classes and structs are mapped to Rust structs. This also applies to all instantiations of template classes encountered in the library's API, including template classes of dependencies.
  • Free functions are mapped to free functions.
  • Class methods are mapped to structs' implementations.
  • Destructors are mapped to Drop and CppDeletable implementations.
  • Function pointer types are mapped to Rust's equivalent representation. Function pointers with references or class values are not supported.
  • static_cast and dynamic_cast are available in Rust through corresponding traits.

Names of Rust identifiers are modified according to Rust's naming conventions.

When direct translation is not possible:

  • Contents of each include file of the C++ library are placed into a separate submodule.
  • Method overloading is emulated with wrapping arguments in a tuple and creating a trait describing tuples acceptable by each method. Methods with default arguments are treated in the same way.
  • Single inheritance is translated to Deref and DerefMut implementation, allowing to call base class methods on derived objects. When deref coercions are not enough, static_cast should be used to convert from derived to base class.
  • Getter and setter methods are created for each public class field.

Not implemented yet but planned:

  • Translate C++ typedefs to Rust type aliases.
  • Implement operator traits for structs based on C++ operator methods (issue). Operators are currently exposed as regular functions with op_ prefix.
  • Implement Debug and Display traits for structs if applicable methods exist on C++ side.
  • Implement iterator traits for collections.
  • Subclassing API (issue).
  • Provide access to a class's public variables (issue).
  • Provide conversion from enums to int and back (used in Qt API).
  • Support C++ types nested into template types, like Class1<T>::Class2.

Not planned to support:

  • Advanced template usage, like types with integer template arguments.
  • Template partial specializations.
  • Template methods and functions.

My suggestion is to wrap your C++ library as a C library, then call it the official FFI way, or use rust-bindgen to automatically do the wrapping.

If you still want to call C++ in Rust, rustcxx seems like a handy tool.

As to the library linking, it's pretty simple:

  • Put the library into your system library searching paths like /usr/lib or /usr/local/lib/, make sure it can be found by ldconfig -p.
  • Or use the environment variable LD_LIBRARY_PATH to specify the path where your library lays when you run cargo from the CLI.
like image 185
Galaxy Avatar answered Sep 27 '22 17:09

Galaxy


According to Rust's official website, there is no official support for linkage with C++. Instead you can try and use C libraries.

There is also a thread on this issue in their users forum, where users suggest some 3rd party projects aiming to tackle this issue:

  • bindgen - Auto generation of FFI for Rust
  • cpp-to-rust - Allows to use C++ libraries from Rust. The main target of this project is Qt.

I didn't use them, so I can't recommend anything particular or share my experience, but good luck :)

like image 27
Daniel Trugman Avatar answered Sep 27 '22 17:09

Daniel Trugman