c++rustffi

Rust FFI works fine until I try and use std::cout/std::vector etc


I am trying to create a small example to show how to call C/C++ code in Rust.

Now I'm aware that you cannot call C++ code directly but you can wrap the C++ code in C. Then use that wrapper in Rust.

My issue is when any of my C/C++ code contains std::cout, std::vector<type> I get an error when compiling Rust.

Here is my Rust program

//main.rs

#[link(name = "/Users/alfredo/repos/rust-playground/c-cpp/src/another.o")]
extern "C" {
    pub fn my_super_function();
}

fn main() {
    unsafe {
        my_super_function();
    }
}

Here is my C++ program

//another.cpp

#include <iostream>
#include <vector>

class test_class {
public:
    int thing;

    test_class(){
        this->thing = 10;
    }
    
    void print_val() {
        //Works fine
        std::printf("Hello world %d +++\n", 1);
        printf("Hello world\n");

        //Rust Compiler does not like
        std::vector<int> test;
        //Also does not like 
        std::cout << "Hello world again" << std::endl;
    }
};


extern "C" {
    void my_super_function(){
        test_class testing;
        testing.print_val();
    }
}

I compile my c++ file with the command g++ -c another.cpp -o another.o in the /src directory of the cargo project.

But when I hit cargo run I get this big error message

~/repos/rust-playground/c-cpp/src (master*) » cargo run
   Compiling c-cpp v0.1.0 (/Users/alfredo/repos/rust-playground/c-cpp)
error: linking with `cc` failed: exit status: 1
  |
  = note: env -u IPHONEOS_DEPLOYMENT_TARGET -u TVOS_DEPLOYMENT_TARGET LC_ALL="C" PATH="/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/bin:/Users/alfredo/.pyenv/shims:/Users/alfredo/.local/bin:/Users/alfredo/.local/bin:/Users/alfredo/builds/emsdk:/Users/alfredo/builds/emsdk/upstream/emscripten:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/Users/alfredo/.pyenv/shims:/Users/alfredo/.local/bin:/Users/alfredo/.cargo/bin" VSLANG="1033" ZERO_AR_DATE="1" "cc" "-arch" "arm64" "/var/folders/j2/7mgrf3yx347gbplvscllcpy00000gn/T/rustc7nloQf/symbols.o" "/Users/alfredo/repos/rust-playground/c-cpp/target/debug/deps/c_cpp-fa1cdd46941c638e.16fn91fgrrtumhsg.rcgu.o" "/Users/alfredo/repos/rust-playground/c-cpp/target/debug/deps/c_cpp-fa1cdd46941c638e.3cu4p06zqfau76or.rcgu.o" "/Users/alfredo/repos/rust-playground/c-cpp/target/debug/deps/c_cpp-fa1cdd46941c638e.56ambxxn67rf7q42.rcgu.o" "/Users/alfredo/repos/rust-playground/c-cpp/target/debug/deps/c_cpp-fa1cdd46941c638e.5a21onrgi1kafdlk.rcgu.o" "/Users/alfredo/repos/rust-playground/c-cpp/target/debug/deps/c_cpp-fa1cdd46941c638e.kxoqkwimezc782q.rcgu.o" "/Users/alfredo/repos/rust-playground/c-cpp/target/debug/deps/c_cpp-fa1cdd46941c638e.34n6f4r7d6zv5d1o.rcgu.o" "-L" "/Users/alfredo/repos/rust-playground/c-cpp/target/debug/deps" "-L" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib" "-l/Users/alfredo/repos/rust-playground/c-cpp/src/another.o" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libstd-d7097f83793f285d.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libpanic_unwind-50e7fd4712e1104c.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libobject-9bf4c2305270bb3d.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libmemchr-b9180b0bd18086ab.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libaddr2line-09f75b2a7a30a183.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libgimli-72b430ce2d1ca406.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/librustc_demangle-8016ac6fb72599e3.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libstd_detect-13855c7195db552b.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libhashbrown-c4874185cc82a43a.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/librustc_std_workspace_alloc-6ef0176aaa60ff0c.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libminiz_oxide-1e9f0e423eed4f7c.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libadler-263f3ba6f4d2645b.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libunwind-85e43ed53a81d633.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libcfg_if-279824e18f4fd20b.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/liblibc-b7ead8c5aa11dde6.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/liballoc-37d126161ada8ba6.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/librustc_std_workspace_core-c7113231a51981ef.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libcore-0e8873809402687b.rlib" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libcompiler_builtins-c3f3955ff7203236.rlib" "-lSystem" "-lc" "-lm" "-L" "/Users/alfredo/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib" "-o" "/Users/alfredo/repos/rust-playground/c-cpp/target/debug/deps/c_cpp-fa1cdd46941c638e" "-Wl,-dead_strip" "-nodefaultlibs"
  = note: Undefined symbols for architecture arm64:
            "std::__1::locale::use_facet(std::__1::locale::id&) const", referenced from:
                std::__1::ctype<char> const& std::__1::use_facet[abi:ue170006]<std::__1::ctype<char>>(std::__1::locale const&) in another.o
            "std::__1::ios_base::getloc() const", referenced from:
                std::__1::basic_ios<char, std::__1::char_traits<char>>::widen[abi:ue170006](char) const in another.o
            "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__init(unsigned long, char)", referenced from:
                std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::basic_string[abi:ue170006](unsigned long, char) in another.o
            "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::~basic_string()", referenced from:
                std::__1::ostreambuf_iterator<char, std::__1::char_traits<char>> std::__1::__pad_and_output[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::ostreambuf_iterator<char, std::__1::char_traits<char>>, char const*, char const*, char const*, std::__1::ios_base&, char) in another.o
                std::__1::ostreambuf_iterator<char, std::__1::char_traits<char>> std::__1::__pad_and_output[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::ostreambuf_iterator<char, std::__1::char_traits<char>>, char const*, char const*, char const*, std::__1::ios_base&, char) in another.o
            "std::__1::basic_ostream<char, std::__1::char_traits<char>>::put(char)", referenced from:
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::endl[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&) in another.o
            "std::__1::basic_ostream<char, std::__1::char_traits<char>>::flush()", referenced from:
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::endl[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&) in another.o
            "std::__1::basic_ostream<char, std::__1::char_traits<char>>::sentry::sentry(std::__1::basic_ostream<char, std::__1::char_traits<char>>&)", referenced from:
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in another.o
            "std::__1::basic_ostream<char, std::__1::char_traits<char>>::sentry::~sentry()", referenced from:
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in another.o
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in another.o
            "std::__1::cout", referenced from:
                test_class::print_val() in another.o
            "std::__1::ctype<char>::id", referenced from:
                std::__1::ctype<char> const& std::__1::use_facet[abi:ue170006]<std::__1::ctype<char>>(std::__1::locale const&) in another.o
            "std::__1::locale::~locale()", referenced from:
                std::__1::basic_ios<char, std::__1::char_traits<char>>::widen[abi:ue170006](char) const in another.o
                std::__1::basic_ios<char, std::__1::char_traits<char>>::widen[abi:ue170006](char) const in another.o
            "std::__1::ios_base::__set_badbit_and_consider_rethrow()", referenced from:
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in another.o
            "std::__1::ios_base::clear(unsigned int)", referenced from:
                std::__1::ios_base::setstate[abi:ue170006](unsigned int) in another.o
            "std::terminate()", referenced from:
                ___clang_call_terminate in another.o
            "operator delete(void*)", referenced from:
                std::__1::allocator<int>::deallocate[abi:ue170006](int*, unsigned long) in another.o
                void std::__1::__libcpp_operator_delete[abi:ue170006]<void*>(void*) in another.o
            "___cxa_begin_catch", referenced from:
                ___clang_call_terminate in another.o
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in another.o
            "___cxa_call_unexpected", referenced from:
                std::__1::char_traits<char>::length[abi:ue170006](char const*) in another.o
                std::__1::ostreambuf_iterator<char, std::__1::char_traits<char>>::ostreambuf_iterator[abi:ue170006](std::__1::basic_ostream<char, std::__1::char_traits<char>>&) in another.o
                std::__1::vector<int, std::__1::allocator<int>>::__base_destruct_at_end[abi:ue170006](int*) in another.o
                std::__1::allocator<int>::deallocate[abi:ue170006](int*, unsigned long) in another.o
            "___cxa_end_catch", referenced from:
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in another.o
                std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:ue170006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long) in another.o
            "___gxx_personality_v0", referenced from:
                /Users/alfredo/repos/rust-playground/c-cpp/src/another.o
          ld: symbol(s) not found for architecture arm64
          clang: error: linker command failed with exit code 1 (use -v to see invocation)


error: could not compile `c-cpp` (bin "c-cpp") due to 1 previous error

Now If I remove the following lines

    std::vector<int> test;
    std::cout << "Hello world again" << std::endl;

And recompile the c++ code with g++ -c another.cpp -o another.o and hit cargo run boom it works! I believe that Rust maybe is not able to find where std::vector and std::cout are located for whatever reason.

Now at first I figured that it might be impossible to do what I want to do. But then I found this post in which they are able to execute the following code which gets called in Rust.

#include <iostream>
#include <stdint.h>
#include <unistd.h>

extern "C" {
  int print_it(int32_t num) {
    while (1) {
        std::cout << num << std::endl;
        sleep(1);
    }
  }
}

I think that the reason im not able to call std::cout is related to the reason im not able to call std::vector<type>. I am not sure why this is happening.

I did not originally choose something like cc to compile my code because I have a big C++ library which depends on one library which is an extremely big C++ library. I want to be able to call this C++ library in Rust. I figured if somehow I could just link the object file it would be the easiest since my C++ library generates an object file with CMake.

I was compiling with g++ since if I knew if I could use an object file generated with with g++ from the CLI then I could also do that with the object file generated from CMake for my library.

I was not sure if I could use cc to compile my C++ library. I would look into it more. Another user replied with suggestion of cxx I will look into this.


Solution

  • Make a build script with the following content:

    fn main() {
        let target = std::env::var("TARGET").unwrap();
        if target.contains("msvc") {
            // Do nothing.
        } else if target.contains("apple")
            | target.contains("freebsd")
            | target.contains("openbsd")
            | target.contains("aix")
            | target.contains("linux-ohos")
        {
            println!("cargo:rustc-link-lib=c++")
        } else if target.contains("android") {
            println!("cargo:rustc-link-lib=c++_shared")
        } else {
            println!("cargo:rustc-link-lib=stdc++")
        }
    }
    

    This will tell Cargo to link the standard library of C++. The code is pretty much taken from the code of cc responsible for the same task.