How do you work with a C++ function that returns a shared_ptr when calling it from Rust over FFI?












4















C++



shared_ptr<Foo> create_foo();


Rust



extern "C" {
pub fn create_foo() -> ???;
}


Bindgen turns a shared_ptr into an opaque blob.



I can't just take the raw pointer because then the C++ code is unaware that I have a reference to Foo and might call its deconstructor.










share|improve this question

























  • Is it at all possible for you to design your code in such a way that either C++ or Rust owns the object and manages its lifetime? You could trigger a callback when it is destroyed, to tell the "other" language to stop holding references to it.

    – John Zwinck
    Nov 14 '18 at 11:32











  • You can also return a new shared_ptr<Foo>(...)... a bit like the double Box trick that is done sometimes from the Rust side.

    – rodrigo
    Nov 14 '18 at 12:06











  • Just to clarify this is an existing c++ library and I can't control how the pointer is created. I just get given a shared_ptr<Foo> from a function.

    – Tom
    Nov 14 '18 at 12:12











  • So you want to be able to access to pointer inner type ?

    – Stargateur
    Nov 14 '18 at 13:15











  • @Stargateur yep but I don't want it to be free'd while I'm using it and if I just use the raw pointer I can't guarantee that.

    – Tom
    Nov 14 '18 at 13:19
















4















C++



shared_ptr<Foo> create_foo();


Rust



extern "C" {
pub fn create_foo() -> ???;
}


Bindgen turns a shared_ptr into an opaque blob.



I can't just take the raw pointer because then the C++ code is unaware that I have a reference to Foo and might call its deconstructor.










share|improve this question

























  • Is it at all possible for you to design your code in such a way that either C++ or Rust owns the object and manages its lifetime? You could trigger a callback when it is destroyed, to tell the "other" language to stop holding references to it.

    – John Zwinck
    Nov 14 '18 at 11:32











  • You can also return a new shared_ptr<Foo>(...)... a bit like the double Box trick that is done sometimes from the Rust side.

    – rodrigo
    Nov 14 '18 at 12:06











  • Just to clarify this is an existing c++ library and I can't control how the pointer is created. I just get given a shared_ptr<Foo> from a function.

    – Tom
    Nov 14 '18 at 12:12











  • So you want to be able to access to pointer inner type ?

    – Stargateur
    Nov 14 '18 at 13:15











  • @Stargateur yep but I don't want it to be free'd while I'm using it and if I just use the raw pointer I can't guarantee that.

    – Tom
    Nov 14 '18 at 13:19














4












4








4


1






C++



shared_ptr<Foo> create_foo();


Rust



extern "C" {
pub fn create_foo() -> ???;
}


Bindgen turns a shared_ptr into an opaque blob.



I can't just take the raw pointer because then the C++ code is unaware that I have a reference to Foo and might call its deconstructor.










share|improve this question
















C++



shared_ptr<Foo> create_foo();


Rust



extern "C" {
pub fn create_foo() -> ???;
}


Bindgen turns a shared_ptr into an opaque blob.



I can't just take the raw pointer because then the C++ code is unaware that I have a reference to Foo and might call its deconstructor.







c++ rust shared-ptr ffi






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 15 '18 at 15:49









Shepmaster

153k14299436




153k14299436










asked Nov 14 '18 at 11:25









TomTom

212




212













  • Is it at all possible for you to design your code in such a way that either C++ or Rust owns the object and manages its lifetime? You could trigger a callback when it is destroyed, to tell the "other" language to stop holding references to it.

    – John Zwinck
    Nov 14 '18 at 11:32











  • You can also return a new shared_ptr<Foo>(...)... a bit like the double Box trick that is done sometimes from the Rust side.

    – rodrigo
    Nov 14 '18 at 12:06











  • Just to clarify this is an existing c++ library and I can't control how the pointer is created. I just get given a shared_ptr<Foo> from a function.

    – Tom
    Nov 14 '18 at 12:12











  • So you want to be able to access to pointer inner type ?

    – Stargateur
    Nov 14 '18 at 13:15











  • @Stargateur yep but I don't want it to be free'd while I'm using it and if I just use the raw pointer I can't guarantee that.

    – Tom
    Nov 14 '18 at 13:19



















  • Is it at all possible for you to design your code in such a way that either C++ or Rust owns the object and manages its lifetime? You could trigger a callback when it is destroyed, to tell the "other" language to stop holding references to it.

    – John Zwinck
    Nov 14 '18 at 11:32











  • You can also return a new shared_ptr<Foo>(...)... a bit like the double Box trick that is done sometimes from the Rust side.

    – rodrigo
    Nov 14 '18 at 12:06











  • Just to clarify this is an existing c++ library and I can't control how the pointer is created. I just get given a shared_ptr<Foo> from a function.

    – Tom
    Nov 14 '18 at 12:12











  • So you want to be able to access to pointer inner type ?

    – Stargateur
    Nov 14 '18 at 13:15











  • @Stargateur yep but I don't want it to be free'd while I'm using it and if I just use the raw pointer I can't guarantee that.

    – Tom
    Nov 14 '18 at 13:19

















Is it at all possible for you to design your code in such a way that either C++ or Rust owns the object and manages its lifetime? You could trigger a callback when it is destroyed, to tell the "other" language to stop holding references to it.

– John Zwinck
Nov 14 '18 at 11:32





Is it at all possible for you to design your code in such a way that either C++ or Rust owns the object and manages its lifetime? You could trigger a callback when it is destroyed, to tell the "other" language to stop holding references to it.

– John Zwinck
Nov 14 '18 at 11:32













You can also return a new shared_ptr<Foo>(...)... a bit like the double Box trick that is done sometimes from the Rust side.

– rodrigo
Nov 14 '18 at 12:06





You can also return a new shared_ptr<Foo>(...)... a bit like the double Box trick that is done sometimes from the Rust side.

– rodrigo
Nov 14 '18 at 12:06













Just to clarify this is an existing c++ library and I can't control how the pointer is created. I just get given a shared_ptr<Foo> from a function.

– Tom
Nov 14 '18 at 12:12





Just to clarify this is an existing c++ library and I can't control how the pointer is created. I just get given a shared_ptr<Foo> from a function.

– Tom
Nov 14 '18 at 12:12













So you want to be able to access to pointer inner type ?

– Stargateur
Nov 14 '18 at 13:15





So you want to be able to access to pointer inner type ?

– Stargateur
Nov 14 '18 at 13:15













@Stargateur yep but I don't want it to be free'd while I'm using it and if I just use the raw pointer I can't guarantee that.

– Tom
Nov 14 '18 at 13:19





@Stargateur yep but I don't want it to be free'd while I'm using it and if I just use the raw pointer I can't guarantee that.

– Tom
Nov 14 '18 at 13:19












3 Answers
3






active

oldest

votes


















4














std::shared_ptr is a C++ class and a non-trivial type that can not be exported as is from a library — you need its definition in your target language to conform to the one in C++. To use FFI you need to provide your library functions with a simple C ABI (the C++ ABI is not stable and may change between compiler versions (as might Rust's ABI)) and I doubt that all of functions related to std::shared_ptr are such, so there is one more obstacle for that.



I'd suggest to return a raw C-pointer from your library instead and own it in Rust.



Even in C++, to load a C++ library you provide C-ABI functions (via extern C) to gain access to a pointer of your type and then you use it in C++ as how as you want.



So, a few points:





  1. Return a raw C pointer from a function without name mangling so that we know its name and can link to it:



    extern "C" Foo* create_foo();



  2. Add a deleter which knows how to properly deallocate the object:



    extern "C" void delete_foo(Foo *);



  3. Let the library user (Rust) decide how to own it, for example, by boxing the value and using it with atomic reference counter via std::sync::Arc (as std::shared_ptr does):



    extern "C" {
    fn create_foo() -> *mut Foo;
    fn delete_foo(p: *mut Foo);
    }

    struct MyFoo {
    raw: *mut Foo,
    }

    impl MyFoo {
    fn new() -> MyFoo {
    unsafe { MyFoo { raw: create_foo() } }
    }
    }

    impl Drop for MyFoo {
    fn drop(&mut self) {
    unsafe {
    delete_foo(self.raw);
    }
    }
    }

    fn main() {
    use std::sync::Arc;

    let value = Arc::new(MyFoo::new());
    let another_value = value.clone();
    println!("Shared counter: {}", Arc::strong_count(&value));
    }


  4. Let the C++ side forget about owning this pointer - you can't rely on it if it is used from outside the library and you give a raw pointer to it.



If you don't have any access to the library sources, can't do anything with it: the std::shared_ptr object will not release the pointer ever and we can't make it not to delete the pointer.






share|improve this answer





















  • 3





    "C++ abi is not stable" -> "Is not specified"

    – hellow
    Nov 14 '18 at 11:42











  • That means it has not been stabilized I guess :)

    – Victor Polevoy
    Nov 14 '18 at 11:43











  • It's a difference, because is has not been stabilized means (IMHO), that is about to stabilize and there is a standard for it. Not specified means there no specification at all.

    – hellow
    Nov 14 '18 at 11:44











  • Thanks for the suggestion. If I return a raw pointer how do I make sure c++ doesn't free the pointer? Foo * create_foo() { shared_ptr<Foo> smart_p = lib::create_foo(); return smart_p.get(); } Here smart_p's desctructor will be called at the end of the function and if its the last one it will free the pointer

    – Tom
    Nov 14 '18 at 11:50








  • 2





    TL;DR: How do you work with a C++ shared_ptr<T> in FFI?you don't.

    – Shepmaster
    Nov 15 '18 at 15:53



















1















I can't just take the raw pointer because then the C++ code is unaware that I have a reference to Foo and might calls it's deconstructor.




Yes and no. With your actual example. C++ will give ownership of the shared_ptr to the one who called create_foo, so C++ knows that there is still something that owns the pointer.



You need to add a get function that will get the value for you without losing ownership of the pointer, something like this:



extern "C" {
std::shared_ptr<Foo> create_foo() {
// do the thing
}
/* or maybe this
std::shared_ptr<Foo> &&create_foo() {
// do the thing
}
*/

Foo *get_foo(std::shared_ptr<Foo> &foo) {
foo.get();
}

void destroy_foo(std::shared_ptr<Foo> foo) {
}
/* or maybe this
void destroy_foo(std::shared_ptr<Foo> &&foo) {
}
*/
}


Also shared_ptr<Foo> is not valid C, so I don't know if bindgen and C++ with accept this (probably a warning) but that is already present in your code.



On the Rust side, you could do this:



// must be generated by bindgen and this might create a lot of problems
// this need to be the same struct as the shared_ptr on the C++ side.
// if even one octet is not correct you will run into bugs
// BE SURE that bindgen don't implement Copy for this
struct shared_ptr<T>;

struct Foo(i32);

extern "C" {
fn create_foo() -> shared_ptr<Foo>;

fn get_foo(foo: &shared_ptr<Foo>) -> *mut Foo;

fn destroy_foo(foo: shared_ptr<Foo>);
}

fn main() {
unsafe {
let my_shared_foo = create_foo();
let foo = get_foo(&my_shared_foo);
(*foo).0;
destroy_foo(my_shared_foo);
}
}


Of course this is just an example, and nothing is really safe in any of this. And as I can't test, please let me know if I wrote something that doesn't work. bindgen should do the job.






share|improve this answer


























  • I think that std::shared_ptr<Foo> &&create_foo() is wrong and makes no sense at all.

    – hellow
    Nov 14 '18 at 14:06











  • @E_net4 Yes I just create something that compile in Rust, but indeed this struct need to match the actual struct std::shared_ptr if not Rust can't move it correctly.

    – Stargateur
    Nov 14 '18 at 14:15











  • @hellow I not sure too, as we tell to rust to use C semantic, I suppose it doesn't but in this case destroy_foo(std::shared_ptr<Foo> &&foo) also don't make sense. Because rust will copy the value ? is this work ? I really don't know.

    – Stargateur
    Nov 14 '18 at 14:19













  • In you're example if we wrap the shared_ptr<Foo> in a struct and make it opaque then we can maintain ownership and still get the raw ptr eg: struct SmartPtr { smart_ptr: opaque_blob[u64; 2], raw_ptr: * mut Foo, }

    – Tom
    Nov 15 '18 at 1:16













  • @Tom That a bad idea to keep the raw pointer separate of shared_ptr, in my exemple I do it but I would advise you to create a structure that hide the raw pointer of shared_ptr.

    – Stargateur
    Nov 15 '18 at 1:59



















-1














You can return a pointer to a dynamically allocated std::shared_ptr



In the C++ side:



shared_ptr<Foo> create_foo();
extern "C" void *rust_create_foo()
{
shared_ptr<Foo> foo = create_foo();
return static_cast<void*>(new shared_ptr<Foo>(foo));
}
extern "C" void rust_delete_foo(void *ptr)
{
shared_ptr<Foo> *foo = static_cast<shared_ptr<Foo>*>(ptr);
delete foo;
}


And in the Rust side:



extern "C" {
pub fn rust_create_foo() -> *const c_void;
pub fn rust_delete_foo(foo: *const c_void);
}


If you fail to call rust_delete_foo then the dynamically allocated pointer will leak and so the object will never be deallocated.



Then, when you want to use the object, you will have to write wrapper functions that take that void*, do the cast and call the appropriate function.






share|improve this answer

























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53299119%2fhow-do-you-work-with-a-c-function-that-returns-a-shared-ptrt-when-calling-it%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    4














    std::shared_ptr is a C++ class and a non-trivial type that can not be exported as is from a library — you need its definition in your target language to conform to the one in C++. To use FFI you need to provide your library functions with a simple C ABI (the C++ ABI is not stable and may change between compiler versions (as might Rust's ABI)) and I doubt that all of functions related to std::shared_ptr are such, so there is one more obstacle for that.



    I'd suggest to return a raw C-pointer from your library instead and own it in Rust.



    Even in C++, to load a C++ library you provide C-ABI functions (via extern C) to gain access to a pointer of your type and then you use it in C++ as how as you want.



    So, a few points:





    1. Return a raw C pointer from a function without name mangling so that we know its name and can link to it:



      extern "C" Foo* create_foo();



    2. Add a deleter which knows how to properly deallocate the object:



      extern "C" void delete_foo(Foo *);



    3. Let the library user (Rust) decide how to own it, for example, by boxing the value and using it with atomic reference counter via std::sync::Arc (as std::shared_ptr does):



      extern "C" {
      fn create_foo() -> *mut Foo;
      fn delete_foo(p: *mut Foo);
      }

      struct MyFoo {
      raw: *mut Foo,
      }

      impl MyFoo {
      fn new() -> MyFoo {
      unsafe { MyFoo { raw: create_foo() } }
      }
      }

      impl Drop for MyFoo {
      fn drop(&mut self) {
      unsafe {
      delete_foo(self.raw);
      }
      }
      }

      fn main() {
      use std::sync::Arc;

      let value = Arc::new(MyFoo::new());
      let another_value = value.clone();
      println!("Shared counter: {}", Arc::strong_count(&value));
      }


    4. Let the C++ side forget about owning this pointer - you can't rely on it if it is used from outside the library and you give a raw pointer to it.



    If you don't have any access to the library sources, can't do anything with it: the std::shared_ptr object will not release the pointer ever and we can't make it not to delete the pointer.






    share|improve this answer





















    • 3





      "C++ abi is not stable" -> "Is not specified"

      – hellow
      Nov 14 '18 at 11:42











    • That means it has not been stabilized I guess :)

      – Victor Polevoy
      Nov 14 '18 at 11:43











    • It's a difference, because is has not been stabilized means (IMHO), that is about to stabilize and there is a standard for it. Not specified means there no specification at all.

      – hellow
      Nov 14 '18 at 11:44











    • Thanks for the suggestion. If I return a raw pointer how do I make sure c++ doesn't free the pointer? Foo * create_foo() { shared_ptr<Foo> smart_p = lib::create_foo(); return smart_p.get(); } Here smart_p's desctructor will be called at the end of the function and if its the last one it will free the pointer

      – Tom
      Nov 14 '18 at 11:50








    • 2





      TL;DR: How do you work with a C++ shared_ptr<T> in FFI?you don't.

      – Shepmaster
      Nov 15 '18 at 15:53
















    4














    std::shared_ptr is a C++ class and a non-trivial type that can not be exported as is from a library — you need its definition in your target language to conform to the one in C++. To use FFI you need to provide your library functions with a simple C ABI (the C++ ABI is not stable and may change between compiler versions (as might Rust's ABI)) and I doubt that all of functions related to std::shared_ptr are such, so there is one more obstacle for that.



    I'd suggest to return a raw C-pointer from your library instead and own it in Rust.



    Even in C++, to load a C++ library you provide C-ABI functions (via extern C) to gain access to a pointer of your type and then you use it in C++ as how as you want.



    So, a few points:





    1. Return a raw C pointer from a function without name mangling so that we know its name and can link to it:



      extern "C" Foo* create_foo();



    2. Add a deleter which knows how to properly deallocate the object:



      extern "C" void delete_foo(Foo *);



    3. Let the library user (Rust) decide how to own it, for example, by boxing the value and using it with atomic reference counter via std::sync::Arc (as std::shared_ptr does):



      extern "C" {
      fn create_foo() -> *mut Foo;
      fn delete_foo(p: *mut Foo);
      }

      struct MyFoo {
      raw: *mut Foo,
      }

      impl MyFoo {
      fn new() -> MyFoo {
      unsafe { MyFoo { raw: create_foo() } }
      }
      }

      impl Drop for MyFoo {
      fn drop(&mut self) {
      unsafe {
      delete_foo(self.raw);
      }
      }
      }

      fn main() {
      use std::sync::Arc;

      let value = Arc::new(MyFoo::new());
      let another_value = value.clone();
      println!("Shared counter: {}", Arc::strong_count(&value));
      }


    4. Let the C++ side forget about owning this pointer - you can't rely on it if it is used from outside the library and you give a raw pointer to it.



    If you don't have any access to the library sources, can't do anything with it: the std::shared_ptr object will not release the pointer ever and we can't make it not to delete the pointer.






    share|improve this answer





















    • 3





      "C++ abi is not stable" -> "Is not specified"

      – hellow
      Nov 14 '18 at 11:42











    • That means it has not been stabilized I guess :)

      – Victor Polevoy
      Nov 14 '18 at 11:43











    • It's a difference, because is has not been stabilized means (IMHO), that is about to stabilize and there is a standard for it. Not specified means there no specification at all.

      – hellow
      Nov 14 '18 at 11:44











    • Thanks for the suggestion. If I return a raw pointer how do I make sure c++ doesn't free the pointer? Foo * create_foo() { shared_ptr<Foo> smart_p = lib::create_foo(); return smart_p.get(); } Here smart_p's desctructor will be called at the end of the function and if its the last one it will free the pointer

      – Tom
      Nov 14 '18 at 11:50








    • 2





      TL;DR: How do you work with a C++ shared_ptr<T> in FFI?you don't.

      – Shepmaster
      Nov 15 '18 at 15:53














    4












    4








    4







    std::shared_ptr is a C++ class and a non-trivial type that can not be exported as is from a library — you need its definition in your target language to conform to the one in C++. To use FFI you need to provide your library functions with a simple C ABI (the C++ ABI is not stable and may change between compiler versions (as might Rust's ABI)) and I doubt that all of functions related to std::shared_ptr are such, so there is one more obstacle for that.



    I'd suggest to return a raw C-pointer from your library instead and own it in Rust.



    Even in C++, to load a C++ library you provide C-ABI functions (via extern C) to gain access to a pointer of your type and then you use it in C++ as how as you want.



    So, a few points:





    1. Return a raw C pointer from a function without name mangling so that we know its name and can link to it:



      extern "C" Foo* create_foo();



    2. Add a deleter which knows how to properly deallocate the object:



      extern "C" void delete_foo(Foo *);



    3. Let the library user (Rust) decide how to own it, for example, by boxing the value and using it with atomic reference counter via std::sync::Arc (as std::shared_ptr does):



      extern "C" {
      fn create_foo() -> *mut Foo;
      fn delete_foo(p: *mut Foo);
      }

      struct MyFoo {
      raw: *mut Foo,
      }

      impl MyFoo {
      fn new() -> MyFoo {
      unsafe { MyFoo { raw: create_foo() } }
      }
      }

      impl Drop for MyFoo {
      fn drop(&mut self) {
      unsafe {
      delete_foo(self.raw);
      }
      }
      }

      fn main() {
      use std::sync::Arc;

      let value = Arc::new(MyFoo::new());
      let another_value = value.clone();
      println!("Shared counter: {}", Arc::strong_count(&value));
      }


    4. Let the C++ side forget about owning this pointer - you can't rely on it if it is used from outside the library and you give a raw pointer to it.



    If you don't have any access to the library sources, can't do anything with it: the std::shared_ptr object will not release the pointer ever and we can't make it not to delete the pointer.






    share|improve this answer















    std::shared_ptr is a C++ class and a non-trivial type that can not be exported as is from a library — you need its definition in your target language to conform to the one in C++. To use FFI you need to provide your library functions with a simple C ABI (the C++ ABI is not stable and may change between compiler versions (as might Rust's ABI)) and I doubt that all of functions related to std::shared_ptr are such, so there is one more obstacle for that.



    I'd suggest to return a raw C-pointer from your library instead and own it in Rust.



    Even in C++, to load a C++ library you provide C-ABI functions (via extern C) to gain access to a pointer of your type and then you use it in C++ as how as you want.



    So, a few points:





    1. Return a raw C pointer from a function without name mangling so that we know its name and can link to it:



      extern "C" Foo* create_foo();



    2. Add a deleter which knows how to properly deallocate the object:



      extern "C" void delete_foo(Foo *);



    3. Let the library user (Rust) decide how to own it, for example, by boxing the value and using it with atomic reference counter via std::sync::Arc (as std::shared_ptr does):



      extern "C" {
      fn create_foo() -> *mut Foo;
      fn delete_foo(p: *mut Foo);
      }

      struct MyFoo {
      raw: *mut Foo,
      }

      impl MyFoo {
      fn new() -> MyFoo {
      unsafe { MyFoo { raw: create_foo() } }
      }
      }

      impl Drop for MyFoo {
      fn drop(&mut self) {
      unsafe {
      delete_foo(self.raw);
      }
      }
      }

      fn main() {
      use std::sync::Arc;

      let value = Arc::new(MyFoo::new());
      let another_value = value.clone();
      println!("Shared counter: {}", Arc::strong_count(&value));
      }


    4. Let the C++ side forget about owning this pointer - you can't rely on it if it is used from outside the library and you give a raw pointer to it.



    If you don't have any access to the library sources, can't do anything with it: the std::shared_ptr object will not release the pointer ever and we can't make it not to delete the pointer.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Nov 15 '18 at 15:52









    Shepmaster

    153k14299436




    153k14299436










    answered Nov 14 '18 at 11:37









    Victor PolevoyVictor Polevoy

    6,91833692




    6,91833692








    • 3





      "C++ abi is not stable" -> "Is not specified"

      – hellow
      Nov 14 '18 at 11:42











    • That means it has not been stabilized I guess :)

      – Victor Polevoy
      Nov 14 '18 at 11:43











    • It's a difference, because is has not been stabilized means (IMHO), that is about to stabilize and there is a standard for it. Not specified means there no specification at all.

      – hellow
      Nov 14 '18 at 11:44











    • Thanks for the suggestion. If I return a raw pointer how do I make sure c++ doesn't free the pointer? Foo * create_foo() { shared_ptr<Foo> smart_p = lib::create_foo(); return smart_p.get(); } Here smart_p's desctructor will be called at the end of the function and if its the last one it will free the pointer

      – Tom
      Nov 14 '18 at 11:50








    • 2





      TL;DR: How do you work with a C++ shared_ptr<T> in FFI?you don't.

      – Shepmaster
      Nov 15 '18 at 15:53














    • 3





      "C++ abi is not stable" -> "Is not specified"

      – hellow
      Nov 14 '18 at 11:42











    • That means it has not been stabilized I guess :)

      – Victor Polevoy
      Nov 14 '18 at 11:43











    • It's a difference, because is has not been stabilized means (IMHO), that is about to stabilize and there is a standard for it. Not specified means there no specification at all.

      – hellow
      Nov 14 '18 at 11:44











    • Thanks for the suggestion. If I return a raw pointer how do I make sure c++ doesn't free the pointer? Foo * create_foo() { shared_ptr<Foo> smart_p = lib::create_foo(); return smart_p.get(); } Here smart_p's desctructor will be called at the end of the function and if its the last one it will free the pointer

      – Tom
      Nov 14 '18 at 11:50








    • 2





      TL;DR: How do you work with a C++ shared_ptr<T> in FFI?you don't.

      – Shepmaster
      Nov 15 '18 at 15:53








    3




    3





    "C++ abi is not stable" -> "Is not specified"

    – hellow
    Nov 14 '18 at 11:42





    "C++ abi is not stable" -> "Is not specified"

    – hellow
    Nov 14 '18 at 11:42













    That means it has not been stabilized I guess :)

    – Victor Polevoy
    Nov 14 '18 at 11:43





    That means it has not been stabilized I guess :)

    – Victor Polevoy
    Nov 14 '18 at 11:43













    It's a difference, because is has not been stabilized means (IMHO), that is about to stabilize and there is a standard for it. Not specified means there no specification at all.

    – hellow
    Nov 14 '18 at 11:44





    It's a difference, because is has not been stabilized means (IMHO), that is about to stabilize and there is a standard for it. Not specified means there no specification at all.

    – hellow
    Nov 14 '18 at 11:44













    Thanks for the suggestion. If I return a raw pointer how do I make sure c++ doesn't free the pointer? Foo * create_foo() { shared_ptr<Foo> smart_p = lib::create_foo(); return smart_p.get(); } Here smart_p's desctructor will be called at the end of the function and if its the last one it will free the pointer

    – Tom
    Nov 14 '18 at 11:50







    Thanks for the suggestion. If I return a raw pointer how do I make sure c++ doesn't free the pointer? Foo * create_foo() { shared_ptr<Foo> smart_p = lib::create_foo(); return smart_p.get(); } Here smart_p's desctructor will be called at the end of the function and if its the last one it will free the pointer

    – Tom
    Nov 14 '18 at 11:50






    2




    2





    TL;DR: How do you work with a C++ shared_ptr<T> in FFI?you don't.

    – Shepmaster
    Nov 15 '18 at 15:53





    TL;DR: How do you work with a C++ shared_ptr<T> in FFI?you don't.

    – Shepmaster
    Nov 15 '18 at 15:53













    1















    I can't just take the raw pointer because then the C++ code is unaware that I have a reference to Foo and might calls it's deconstructor.




    Yes and no. With your actual example. C++ will give ownership of the shared_ptr to the one who called create_foo, so C++ knows that there is still something that owns the pointer.



    You need to add a get function that will get the value for you without losing ownership of the pointer, something like this:



    extern "C" {
    std::shared_ptr<Foo> create_foo() {
    // do the thing
    }
    /* or maybe this
    std::shared_ptr<Foo> &&create_foo() {
    // do the thing
    }
    */

    Foo *get_foo(std::shared_ptr<Foo> &foo) {
    foo.get();
    }

    void destroy_foo(std::shared_ptr<Foo> foo) {
    }
    /* or maybe this
    void destroy_foo(std::shared_ptr<Foo> &&foo) {
    }
    */
    }


    Also shared_ptr<Foo> is not valid C, so I don't know if bindgen and C++ with accept this (probably a warning) but that is already present in your code.



    On the Rust side, you could do this:



    // must be generated by bindgen and this might create a lot of problems
    // this need to be the same struct as the shared_ptr on the C++ side.
    // if even one octet is not correct you will run into bugs
    // BE SURE that bindgen don't implement Copy for this
    struct shared_ptr<T>;

    struct Foo(i32);

    extern "C" {
    fn create_foo() -> shared_ptr<Foo>;

    fn get_foo(foo: &shared_ptr<Foo>) -> *mut Foo;

    fn destroy_foo(foo: shared_ptr<Foo>);
    }

    fn main() {
    unsafe {
    let my_shared_foo = create_foo();
    let foo = get_foo(&my_shared_foo);
    (*foo).0;
    destroy_foo(my_shared_foo);
    }
    }


    Of course this is just an example, and nothing is really safe in any of this. And as I can't test, please let me know if I wrote something that doesn't work. bindgen should do the job.






    share|improve this answer


























    • I think that std::shared_ptr<Foo> &&create_foo() is wrong and makes no sense at all.

      – hellow
      Nov 14 '18 at 14:06











    • @E_net4 Yes I just create something that compile in Rust, but indeed this struct need to match the actual struct std::shared_ptr if not Rust can't move it correctly.

      – Stargateur
      Nov 14 '18 at 14:15











    • @hellow I not sure too, as we tell to rust to use C semantic, I suppose it doesn't but in this case destroy_foo(std::shared_ptr<Foo> &&foo) also don't make sense. Because rust will copy the value ? is this work ? I really don't know.

      – Stargateur
      Nov 14 '18 at 14:19













    • In you're example if we wrap the shared_ptr<Foo> in a struct and make it opaque then we can maintain ownership and still get the raw ptr eg: struct SmartPtr { smart_ptr: opaque_blob[u64; 2], raw_ptr: * mut Foo, }

      – Tom
      Nov 15 '18 at 1:16













    • @Tom That a bad idea to keep the raw pointer separate of shared_ptr, in my exemple I do it but I would advise you to create a structure that hide the raw pointer of shared_ptr.

      – Stargateur
      Nov 15 '18 at 1:59
















    1















    I can't just take the raw pointer because then the C++ code is unaware that I have a reference to Foo and might calls it's deconstructor.




    Yes and no. With your actual example. C++ will give ownership of the shared_ptr to the one who called create_foo, so C++ knows that there is still something that owns the pointer.



    You need to add a get function that will get the value for you without losing ownership of the pointer, something like this:



    extern "C" {
    std::shared_ptr<Foo> create_foo() {
    // do the thing
    }
    /* or maybe this
    std::shared_ptr<Foo> &&create_foo() {
    // do the thing
    }
    */

    Foo *get_foo(std::shared_ptr<Foo> &foo) {
    foo.get();
    }

    void destroy_foo(std::shared_ptr<Foo> foo) {
    }
    /* or maybe this
    void destroy_foo(std::shared_ptr<Foo> &&foo) {
    }
    */
    }


    Also shared_ptr<Foo> is not valid C, so I don't know if bindgen and C++ with accept this (probably a warning) but that is already present in your code.



    On the Rust side, you could do this:



    // must be generated by bindgen and this might create a lot of problems
    // this need to be the same struct as the shared_ptr on the C++ side.
    // if even one octet is not correct you will run into bugs
    // BE SURE that bindgen don't implement Copy for this
    struct shared_ptr<T>;

    struct Foo(i32);

    extern "C" {
    fn create_foo() -> shared_ptr<Foo>;

    fn get_foo(foo: &shared_ptr<Foo>) -> *mut Foo;

    fn destroy_foo(foo: shared_ptr<Foo>);
    }

    fn main() {
    unsafe {
    let my_shared_foo = create_foo();
    let foo = get_foo(&my_shared_foo);
    (*foo).0;
    destroy_foo(my_shared_foo);
    }
    }


    Of course this is just an example, and nothing is really safe in any of this. And as I can't test, please let me know if I wrote something that doesn't work. bindgen should do the job.






    share|improve this answer


























    • I think that std::shared_ptr<Foo> &&create_foo() is wrong and makes no sense at all.

      – hellow
      Nov 14 '18 at 14:06











    • @E_net4 Yes I just create something that compile in Rust, but indeed this struct need to match the actual struct std::shared_ptr if not Rust can't move it correctly.

      – Stargateur
      Nov 14 '18 at 14:15











    • @hellow I not sure too, as we tell to rust to use C semantic, I suppose it doesn't but in this case destroy_foo(std::shared_ptr<Foo> &&foo) also don't make sense. Because rust will copy the value ? is this work ? I really don't know.

      – Stargateur
      Nov 14 '18 at 14:19













    • In you're example if we wrap the shared_ptr<Foo> in a struct and make it opaque then we can maintain ownership and still get the raw ptr eg: struct SmartPtr { smart_ptr: opaque_blob[u64; 2], raw_ptr: * mut Foo, }

      – Tom
      Nov 15 '18 at 1:16













    • @Tom That a bad idea to keep the raw pointer separate of shared_ptr, in my exemple I do it but I would advise you to create a structure that hide the raw pointer of shared_ptr.

      – Stargateur
      Nov 15 '18 at 1:59














    1












    1








    1








    I can't just take the raw pointer because then the C++ code is unaware that I have a reference to Foo and might calls it's deconstructor.




    Yes and no. With your actual example. C++ will give ownership of the shared_ptr to the one who called create_foo, so C++ knows that there is still something that owns the pointer.



    You need to add a get function that will get the value for you without losing ownership of the pointer, something like this:



    extern "C" {
    std::shared_ptr<Foo> create_foo() {
    // do the thing
    }
    /* or maybe this
    std::shared_ptr<Foo> &&create_foo() {
    // do the thing
    }
    */

    Foo *get_foo(std::shared_ptr<Foo> &foo) {
    foo.get();
    }

    void destroy_foo(std::shared_ptr<Foo> foo) {
    }
    /* or maybe this
    void destroy_foo(std::shared_ptr<Foo> &&foo) {
    }
    */
    }


    Also shared_ptr<Foo> is not valid C, so I don't know if bindgen and C++ with accept this (probably a warning) but that is already present in your code.



    On the Rust side, you could do this:



    // must be generated by bindgen and this might create a lot of problems
    // this need to be the same struct as the shared_ptr on the C++ side.
    // if even one octet is not correct you will run into bugs
    // BE SURE that bindgen don't implement Copy for this
    struct shared_ptr<T>;

    struct Foo(i32);

    extern "C" {
    fn create_foo() -> shared_ptr<Foo>;

    fn get_foo(foo: &shared_ptr<Foo>) -> *mut Foo;

    fn destroy_foo(foo: shared_ptr<Foo>);
    }

    fn main() {
    unsafe {
    let my_shared_foo = create_foo();
    let foo = get_foo(&my_shared_foo);
    (*foo).0;
    destroy_foo(my_shared_foo);
    }
    }


    Of course this is just an example, and nothing is really safe in any of this. And as I can't test, please let me know if I wrote something that doesn't work. bindgen should do the job.






    share|improve this answer
















    I can't just take the raw pointer because then the C++ code is unaware that I have a reference to Foo and might calls it's deconstructor.




    Yes and no. With your actual example. C++ will give ownership of the shared_ptr to the one who called create_foo, so C++ knows that there is still something that owns the pointer.



    You need to add a get function that will get the value for you without losing ownership of the pointer, something like this:



    extern "C" {
    std::shared_ptr<Foo> create_foo() {
    // do the thing
    }
    /* or maybe this
    std::shared_ptr<Foo> &&create_foo() {
    // do the thing
    }
    */

    Foo *get_foo(std::shared_ptr<Foo> &foo) {
    foo.get();
    }

    void destroy_foo(std::shared_ptr<Foo> foo) {
    }
    /* or maybe this
    void destroy_foo(std::shared_ptr<Foo> &&foo) {
    }
    */
    }


    Also shared_ptr<Foo> is not valid C, so I don't know if bindgen and C++ with accept this (probably a warning) but that is already present in your code.



    On the Rust side, you could do this:



    // must be generated by bindgen and this might create a lot of problems
    // this need to be the same struct as the shared_ptr on the C++ side.
    // if even one octet is not correct you will run into bugs
    // BE SURE that bindgen don't implement Copy for this
    struct shared_ptr<T>;

    struct Foo(i32);

    extern "C" {
    fn create_foo() -> shared_ptr<Foo>;

    fn get_foo(foo: &shared_ptr<Foo>) -> *mut Foo;

    fn destroy_foo(foo: shared_ptr<Foo>);
    }

    fn main() {
    unsafe {
    let my_shared_foo = create_foo();
    let foo = get_foo(&my_shared_foo);
    (*foo).0;
    destroy_foo(my_shared_foo);
    }
    }


    Of course this is just an example, and nothing is really safe in any of this. And as I can't test, please let me know if I wrote something that doesn't work. bindgen should do the job.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Nov 15 '18 at 15:54









    Shepmaster

    153k14299436




    153k14299436










    answered Nov 14 '18 at 13:54









    StargateurStargateur

    8,59741848




    8,59741848













    • I think that std::shared_ptr<Foo> &&create_foo() is wrong and makes no sense at all.

      – hellow
      Nov 14 '18 at 14:06











    • @E_net4 Yes I just create something that compile in Rust, but indeed this struct need to match the actual struct std::shared_ptr if not Rust can't move it correctly.

      – Stargateur
      Nov 14 '18 at 14:15











    • @hellow I not sure too, as we tell to rust to use C semantic, I suppose it doesn't but in this case destroy_foo(std::shared_ptr<Foo> &&foo) also don't make sense. Because rust will copy the value ? is this work ? I really don't know.

      – Stargateur
      Nov 14 '18 at 14:19













    • In you're example if we wrap the shared_ptr<Foo> in a struct and make it opaque then we can maintain ownership and still get the raw ptr eg: struct SmartPtr { smart_ptr: opaque_blob[u64; 2], raw_ptr: * mut Foo, }

      – Tom
      Nov 15 '18 at 1:16













    • @Tom That a bad idea to keep the raw pointer separate of shared_ptr, in my exemple I do it but I would advise you to create a structure that hide the raw pointer of shared_ptr.

      – Stargateur
      Nov 15 '18 at 1:59



















    • I think that std::shared_ptr<Foo> &&create_foo() is wrong and makes no sense at all.

      – hellow
      Nov 14 '18 at 14:06











    • @E_net4 Yes I just create something that compile in Rust, but indeed this struct need to match the actual struct std::shared_ptr if not Rust can't move it correctly.

      – Stargateur
      Nov 14 '18 at 14:15











    • @hellow I not sure too, as we tell to rust to use C semantic, I suppose it doesn't but in this case destroy_foo(std::shared_ptr<Foo> &&foo) also don't make sense. Because rust will copy the value ? is this work ? I really don't know.

      – Stargateur
      Nov 14 '18 at 14:19













    • In you're example if we wrap the shared_ptr<Foo> in a struct and make it opaque then we can maintain ownership and still get the raw ptr eg: struct SmartPtr { smart_ptr: opaque_blob[u64; 2], raw_ptr: * mut Foo, }

      – Tom
      Nov 15 '18 at 1:16













    • @Tom That a bad idea to keep the raw pointer separate of shared_ptr, in my exemple I do it but I would advise you to create a structure that hide the raw pointer of shared_ptr.

      – Stargateur
      Nov 15 '18 at 1:59

















    I think that std::shared_ptr<Foo> &&create_foo() is wrong and makes no sense at all.

    – hellow
    Nov 14 '18 at 14:06





    I think that std::shared_ptr<Foo> &&create_foo() is wrong and makes no sense at all.

    – hellow
    Nov 14 '18 at 14:06













    @E_net4 Yes I just create something that compile in Rust, but indeed this struct need to match the actual struct std::shared_ptr if not Rust can't move it correctly.

    – Stargateur
    Nov 14 '18 at 14:15





    @E_net4 Yes I just create something that compile in Rust, but indeed this struct need to match the actual struct std::shared_ptr if not Rust can't move it correctly.

    – Stargateur
    Nov 14 '18 at 14:15













    @hellow I not sure too, as we tell to rust to use C semantic, I suppose it doesn't but in this case destroy_foo(std::shared_ptr<Foo> &&foo) also don't make sense. Because rust will copy the value ? is this work ? I really don't know.

    – Stargateur
    Nov 14 '18 at 14:19







    @hellow I not sure too, as we tell to rust to use C semantic, I suppose it doesn't but in this case destroy_foo(std::shared_ptr<Foo> &&foo) also don't make sense. Because rust will copy the value ? is this work ? I really don't know.

    – Stargateur
    Nov 14 '18 at 14:19















    In you're example if we wrap the shared_ptr<Foo> in a struct and make it opaque then we can maintain ownership and still get the raw ptr eg: struct SmartPtr { smart_ptr: opaque_blob[u64; 2], raw_ptr: * mut Foo, }

    – Tom
    Nov 15 '18 at 1:16







    In you're example if we wrap the shared_ptr<Foo> in a struct and make it opaque then we can maintain ownership and still get the raw ptr eg: struct SmartPtr { smart_ptr: opaque_blob[u64; 2], raw_ptr: * mut Foo, }

    – Tom
    Nov 15 '18 at 1:16















    @Tom That a bad idea to keep the raw pointer separate of shared_ptr, in my exemple I do it but I would advise you to create a structure that hide the raw pointer of shared_ptr.

    – Stargateur
    Nov 15 '18 at 1:59





    @Tom That a bad idea to keep the raw pointer separate of shared_ptr, in my exemple I do it but I would advise you to create a structure that hide the raw pointer of shared_ptr.

    – Stargateur
    Nov 15 '18 at 1:59











    -1














    You can return a pointer to a dynamically allocated std::shared_ptr



    In the C++ side:



    shared_ptr<Foo> create_foo();
    extern "C" void *rust_create_foo()
    {
    shared_ptr<Foo> foo = create_foo();
    return static_cast<void*>(new shared_ptr<Foo>(foo));
    }
    extern "C" void rust_delete_foo(void *ptr)
    {
    shared_ptr<Foo> *foo = static_cast<shared_ptr<Foo>*>(ptr);
    delete foo;
    }


    And in the Rust side:



    extern "C" {
    pub fn rust_create_foo() -> *const c_void;
    pub fn rust_delete_foo(foo: *const c_void);
    }


    If you fail to call rust_delete_foo then the dynamically allocated pointer will leak and so the object will never be deallocated.



    Then, when you want to use the object, you will have to write wrapper functions that take that void*, do the cast and call the appropriate function.






    share|improve this answer






























      -1














      You can return a pointer to a dynamically allocated std::shared_ptr



      In the C++ side:



      shared_ptr<Foo> create_foo();
      extern "C" void *rust_create_foo()
      {
      shared_ptr<Foo> foo = create_foo();
      return static_cast<void*>(new shared_ptr<Foo>(foo));
      }
      extern "C" void rust_delete_foo(void *ptr)
      {
      shared_ptr<Foo> *foo = static_cast<shared_ptr<Foo>*>(ptr);
      delete foo;
      }


      And in the Rust side:



      extern "C" {
      pub fn rust_create_foo() -> *const c_void;
      pub fn rust_delete_foo(foo: *const c_void);
      }


      If you fail to call rust_delete_foo then the dynamically allocated pointer will leak and so the object will never be deallocated.



      Then, when you want to use the object, you will have to write wrapper functions that take that void*, do the cast and call the appropriate function.






      share|improve this answer




























        -1












        -1








        -1







        You can return a pointer to a dynamically allocated std::shared_ptr



        In the C++ side:



        shared_ptr<Foo> create_foo();
        extern "C" void *rust_create_foo()
        {
        shared_ptr<Foo> foo = create_foo();
        return static_cast<void*>(new shared_ptr<Foo>(foo));
        }
        extern "C" void rust_delete_foo(void *ptr)
        {
        shared_ptr<Foo> *foo = static_cast<shared_ptr<Foo>*>(ptr);
        delete foo;
        }


        And in the Rust side:



        extern "C" {
        pub fn rust_create_foo() -> *const c_void;
        pub fn rust_delete_foo(foo: *const c_void);
        }


        If you fail to call rust_delete_foo then the dynamically allocated pointer will leak and so the object will never be deallocated.



        Then, when you want to use the object, you will have to write wrapper functions that take that void*, do the cast and call the appropriate function.






        share|improve this answer















        You can return a pointer to a dynamically allocated std::shared_ptr



        In the C++ side:



        shared_ptr<Foo> create_foo();
        extern "C" void *rust_create_foo()
        {
        shared_ptr<Foo> foo = create_foo();
        return static_cast<void*>(new shared_ptr<Foo>(foo));
        }
        extern "C" void rust_delete_foo(void *ptr)
        {
        shared_ptr<Foo> *foo = static_cast<shared_ptr<Foo>*>(ptr);
        delete foo;
        }


        And in the Rust side:



        extern "C" {
        pub fn rust_create_foo() -> *const c_void;
        pub fn rust_delete_foo(foo: *const c_void);
        }


        If you fail to call rust_delete_foo then the dynamically allocated pointer will leak and so the object will never be deallocated.



        Then, when you want to use the object, you will have to write wrapper functions that take that void*, do the cast and call the appropriate function.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 14 '18 at 17:42

























        answered Nov 14 '18 at 17:35









        rodrigorodrigo

        63.7k493129




        63.7k493129






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53299119%2fhow-do-you-work-with-a-c-function-that-returns-a-shared-ptrt-when-calling-it%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Bressuire

            Vorschmack

            Quarantine