Better C++ interoperability

There isn't anything fundamental about foreign functions which prevents us from allowing generic foreign functions. The problem is that the C ABI has no notion of generics. We don't want to allow people to write extern "C" { fn f<T>(); } because we can't translate it to anything useful.

1 Like

Is this really a big problem, given Cargo? Everything is compiled from source, so, generic exports or not, anyone using the library has already downloaded and run the compiler plugin; requiring client crates to also use it creates no additional logistic issues (as opposed to general usability issues, which depend on the hypothetical new plugin architecture). The plugin, for its part, can depend on a crate containing a copy of Clang, which can depend on something like the current 'gcc' crate to invoke the system compiler to compile Clang itself - likely to be somewhat annoying to deal with in practice due to compilation time and binary size, but doable. As for the presence of the C++ library, the situation doesn't seem much different from C libraries today - either you bundle the whole library into a crate, or you look for it already installed somewhere on the system, in which case the headers will be there anyway if necessary (because C++ users need them too). The latter route would risk the annoyance of forcing everyone downstream to manually install the C++ library, but this is exactly the same today for a C library.

Edit: I really don't think distributing binary Rust libraries is something worth optimizing for.

I’ve heard through the grapevine that the C++ committee may be making an effort to define a “portable subset” of C++ that can be used across C++ compilers etc. This may be a useful starting point for us, or at least an interesting data point.

I’m a hobbyist gamedev, and I’ve been using Rust for ~18 months on a near-daily basis. For me personally, having some sane story for C++ interop is a huge deal. It’s a pretty big reason that I’ve abandoned my main Rust project. Lots of wheels are being reinvented in Rust, and given enough time, maybe the need for C++ libraries could almost vanish, but that’s not compelling for me. Right now, the space of libraries-that-are-implemented-better-in-C++ is huge, and it was a lot of work to figure out which wheels had been reinvented in Rust, how well, how recently, how fully, and whether I should take a stab on my own.

tl;dr I still plan to use Rust for small projects and more systemsy things, but I would actively recommend away from it for serious gamedev, and that makes me sad.

1 Like

I think we’re dealing with 2 different problems here:

  1. Making Rust code available to C++.

  2. Calling C++ from Rust. We can this in two subproblems:

    1. Representing classes/other arbitrary types in Rust

    2. Mapping these representations to calls to C++ code

For #1 there’s a workaround by making Rust available as standard C symbols using the known ffi methods.

I think #2.1 is a language-independent problem, so it might be better to implement it as one. I came up with two solutions:

  1. Allow compiler plugins to define new types and have them handle the compilation of these types.

  2. Make wrappers around foreign code (either manual or automatic)

My personal favourite is #1 because it allows for direct use of a library.

An example of what it could look like:

#![feature(plugin)]
#![plugin(cxx_interop)]

fn main() {
    include_cxx!("std::vector");

    let vec = cxx::std::vector::new<isize>();
    vec.insert(vec, 200);
}

But that would probably be very difficult to do. So #2 still has a chance.

1 Like

I’m aiming to create Rust bindings for Qt library. I’m currently developing a binding generator. There is still much work to be done, but some basic things already work and it looks promising. I’m working on it at https://github.com/rust-qt/cpp_to_rust.

4 Likes

I am not familiar with Qt API. Does it use macros, overloading, inheritance and templates? If so, I’d be curious to know, how are you planning to map these things into Rust?

It doesn’t use macros much. Most of them are just used for shortening the code. There are some features based on macros and Qt’s own C++ preprocessor (moc), and they’ll require special treatment. I think it will be possible to support everything important, though.

Qt uses overloading a lot. Overloading is emulated with traits implemented over tuples of arguments. The only drawback is that instead of writing method() and method(arg1, arg2) the user has to write method(()) and method((arg1, arg2)). Rust calls the FFI function corresponding to supplied methods automatically. This system is already implemented. I also intend to add alternative overloading method based on Option type when possible.

Inheritance is also used a lot in Qt. Supporting C++ inheritance includes 3 things:

  • Functions for downcasting and upcasting for available pointer types. Not implemented yet, but easy.
  • Methods inherited from base classes are added directly to wrappers of derived classes. It’s kind of a hack, but it provides easy access to inherited methods. It could be replaced with trait-based method injection, but I don’t like it because the user would need to bring the trait into scope.
  • Deriving from C++ classes is the most difficult, but also possible. The user will have to define extern “C” functions with specific signature and pass them to a special class constructor. These functions will be called as if they were reimplemented virtual methods of the class. The functions will receive pointers providing access to protected methods of the class. Maybe this will be made more convenient using Rust macros.

There are also issues related to automatic type conversion in C++. Functions and methods may receive pointers to base classes, and pointers to derived class will be automatically converted, but this is only one case. Qt uses automatic conversion between integers and enums, for example. Rust does not do any of this. It may be emulated with traits (like AsRef), but I’m not sure if it should be done.

Templates are my current task. Qt doesn’t use templates much. They are mostly used for vectors, smart pointers and so on. Instantiation of custom types from Rust side is currently out of the question. I want to generate wrappers for any template instantiation that can be encountered in API, in order to provide access to objects the user may acquire. I also want to organize inter-library template instantiation because Qt is separated to multiple libraries and they use templates of the QtCore library.

The bottom line is, Rust is good enough to emulate almost everything an API may need. It often is very inconvenient to do, but I’m writing an automatic code generator, so I can do things that are very inconvenient to implement but easy to use. Also, Qt’s API is really huge, and manual implementation is not an option anyway.

5 Likes

It might be worth looking at Cxx.jl which provides C++ interop for julia.

Funny I just started this over here https://users.rust-lang.org/t/pre-implementation-feedback-for-qt-with-rust/7300

I’m willing to help out with this.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.