dtolnay / cxx

Safe interop between Rust and C++
https://cxx.rs
Apache License 2.0
5.86k stars 331 forks source link

Match (C++) std::string and (Rust) String #1030

Open vervaekejonathan opened 2 years ago

vervaekejonathan commented 2 years ago

Hello, I'm trying to bind c++ and rust, but I get the following error:

error: invalid conversion from 'std::__cxx11::string (*)() {aka std::__cxx11::basic_string<char> (*)()}' to 'rust::cxxbridge1::String (*)()'

The Rust binding file:

#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        include!("xct.h");
        fn xct_detect() -> String;
    }
}

And the C++ header file:

#pragma once 

#include <string>

#ifdef __cplusplus
extern "C"
{
#endif

std::string xct_detect();

#ifdef __cplusplus
extern "C"
}
#endif

How do I match a std::string to a rust String?

hanusek commented 2 years ago

Maybe you should try this:

use cxx::{CxxString};

#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        include!("xct.h");
        fn xct_detect() -> CxxString;
    }
}

Here you have similar example: https://cxx.rs/binding/cxxstring.html#example https://cxx.rs/binding/string.html#example

n8henrie commented 2 years ago

I was trying to get a working example for you, but I can't get it right either.

Using this basic C++ library (and executable):

// xct.h
#include <string>

std::string xct_detect(std::string);
// xct.cpp
#include "xct.h"

std::string xct_detect(std::string s) { return "hello, " + s; }
// main.cpp
#include "xct.h"
#include <iostream>

int main() {
    std::cout << xct_detect(std::string("nate")) << std::endl;
    return 0;
}
#Makefile
main: clean main.cpp libxct.so
    g++ -L. -lxct main.cpp -o main
    ./main

libxct.so: xct.cpp xct.h
    g++ -shared xct.cpp -o libxct.so

.PHONY: clean
clean:
    rm -rf -- libxct.so main

Seems to link and run ok:

$ make
rm -rf -- libxct.so main
g++ -shared xct.cpp -o libxct.so
g++ -L. -lxct main.cpp -o main
./main
hello, nate

But trying to use cxx::CxxString to interface with it, following the example from https://cxx.rs/binding/cxxstring.html:

//build.rs
fn main() {
    cxx_build::bridge("src/lib.rs")
        .file("xct.cpp")
        .flag("-std=c++17")
        .warnings(false)
        .compile("xct");
}
//src/lib.rs
#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        // package name `xcttest`
        include!("xcttest/xct.h");
        fn xct_detect(s: &CxxString) -> &CxxString;
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use cxx::let_cxx_string;

    #[test]
    fn test_foo() {
        let_cxx_string!(name = "there");
        assert_eq!("hello, there", ffi::xct_detect(&name).to_str().unwrap());
    }
}

I get a bunch of warnings and errors:

error: cannot initialize a variable of type 'const ::std::string &(*)(const ::std::string &)' (aka 'const basic_string<char, char_traits<char>, allocator<char>> &(*)(const basic_string<char, char_traits<char>, allocator<char>> &)') with an lvalue of type 'std::string (std::string)' (aka 'basic_string<char, char_traits<char>, allocator<char>> (basic_string<char, char_traits<char>, allocator<char>>)'): type mismatch at 1st parameter ('const ::std::string &' (aka 'const basic_string<char, char_traits<char>, allocator<char>> &') vs 'std::string' (aka 'basic_string<char, char_traits<char>, allocator<char>>'))

Which I think is giving me a hard time because my (fake) API is taking and returning things by std::string whereas the example seems to be using const std::string & (which it looks like what it's expecting).