JuliaInterop / CxxWrap.jl

Package to make C++ libraries available in Julia
Other
418 stars 67 forks source link

Behavior of types created in C++ when they go out of scope in Julia #403

Closed sloede closed 7 months ago

sloede commented 8 months ago

According to the README.md,

The default constructor and any manually added constructor using the constructor function will automatically create a Julia object that has a finalizer attached that calls delete to free the memory. To write a C++ function that returns a new object that can be garbage-collected in Julia, use the jlcxx::create function [...]

From this description it is not 100% clear to me what happens with types that are constructed by a library function on the C++ side that I do not control myself - will they also automatically have a finalizer attached?

For example, if on the C++ side I have something like

struct S {
  int value;

  S(int value_) : value(value_) {}
};

S foo() {
   return S(3);
}

and I wrap this as

#include "jlcxx/jlcxx.hpp"

JLCXX_MODULE define_julia_module(jlcxx::Module& mod)
{
  mod.add_type<S>("S");
  mod.method("foo", &foo);
}

then technically calling foo() in Julia is neither here nor there:

So far I have just assumed that CxxWrap.jl does The Right Thing and will automatically attach a finalizer to it, and thus once GC kicks in, the memory on the C++ side is freed as well. (Re-)reading the documentation again left me wondering if I am being overly optimistic about that.

barche commented 8 months ago

I double-checked, and yes, in the case where a function returns an object by value like in your example, the wrapped object gets a finalizer and is owned by Julia. If a reference or pointer is returned then the default assumption is that the pointed-to object lifetime is managed by C++.

sloede commented 8 months ago

Thank you very much - this makes a lot of sense 👍 If it is not too much to asked, it would be great if you could link the relevant code sections in here such that I (start to) understand better how libcxxwrap-julia and CxxWrap.jl work. Either way, feel free to close this issue any time.

P.S.: Just to be sure: returning a shared_ptr is not considered to be "return-by-pointer". That is, a finalizer will be added to a smart pointer such that when it goes out of scope, the C++ side is notified that the reference has been deleted and can decrease its own reference counter. Am I right in this assumpation?

barche commented 8 months ago

The code where the by-value objects are returned is here:

https://github.com/JuliaInterop/libcxxwrap-julia/blob/513d33f55819aad87e68235a3c229c456830e91f/include/jlcxx/type_conversion.hpp#L707

And indeed, it is the same for a shared_ptr!

sloede commented 8 months ago

And indeed, it is the same for a shared_ptr!

Thanks for the confirmation! I've created #406 to document this for future reference.