5

While exposing functionalities from a C++ library to Java i recently faced a problem regarding C++ shared_ptr pointers. The case is that the Object itself as well as the jlonghandle related to that Object lives whithin the Java portion but subsequent structures access that Object using smart pointers.

The problem what i got is: When a smart pointer gets destroyed or reset the underlying object gets deleted as well. However the Java reference points still to that Object.

I tried a lot but i did not manage to keep ownership on the Java-Side. Are there any best practices or solutions to that problem ?

fyr
  • 20,227
  • 7
  • 37
  • 53

3 Answers3

7

If you want to hold to the object as long as Java keeps reference to it, here is the useful pattern:

jlong Java_xxx_getNativeRef(...)
{
    std::shared_ptr<MyObject> *pNew = new std::shared_ptr<MyObject>;
    *pNew = whatever-you-do to obtain an std::shared_ptr<MyObject>;
    return reinterpret_cast<jlong>(pNew);
}

Java_xxx_releaseNativeRef(..., jlong nativeRef)
{
    std::shared_ptr<MyObject> *pSp = reinterpret_cast<std::shared_ptr<MyObject> *>(nativeRef);
    delete *pSp;
    delete pSp; // thanks @LucasZanella
}

On the other hand, if you are concerned that the Java reference may become invalid because it outlives the lifecycle of the native object, but don't want Java to control release of MyObject, you can do the same trick with std::weak_ptr.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • Are you saying that `reinterpret_cast` increments the reference count of the `shared_ptr`? If not, this won't work, because the reference count will drop to zero on exit from `Java_xxx_getNativeRef()`. – Luke Hutchison Aug 21 '19 at 09:38
  • 2
    Note that I obtain a GOP (good old pointer) to a shared pointer. This way, when it leaves the scope of get, it is still not destroyed. – Alex Cohn Aug 21 '19 at 15:48
  • 1
    @AlexCohn I was doing this: https://stackoverflow.com/a/5995770/6655884 until you came with the idea of simply having a GOP. Obivously better :) – PPP Oct 27 '19 at 22:30
  • However I don't understand what `psp->clear();` means. Shouldn't it simply be `delete pSp;`? – PPP Oct 27 '19 at 22:43
  • @LucasZanella: you are right, **releaseNativeRef()** begs `delete pSp` in the end ‍♂️. But `psp->clear()` is essential: it releases the underlying shared pointer with all its payload, while missing `delete pSp` only leaks 64 bits at most. ***Fixed*** – Alex Cohn Oct 28 '19 at 14:36
  • 1
    @AlexCohn what is clear? there's no clear method in `std::shared_ptr`? – PPP Oct 29 '19 at 22:27
  • 1
    You are right again. It's not easy to remember what exactly I thought 5 years ago. On the face of it, this should be `delete *pSp`. – Alex Cohn Oct 30 '19 at 08:37
  • But `*pSp` is literally `std::shared_ptr` and thus can't be deleted. I think you meant `delete (*pSp).get()` however this is not necessary since when `Java_xxx_getNativeRef` finishes execution `std::shared_ptr` is destructed and thus deletes the pointer it owns automatically. – PPP Oct 30 '19 at 12:00
  • Not at all, that's the trick here. We steal the natural lifecycle of shared_ptr by wrapping it inside a GOP (good old pointer). If `Java_xxx_getNativeRef()` had deleted the shared pointer, there would have been no value in this trick. Yes, you can delete a shared_ptr explicitly calling the destructor. – Alex Cohn Oct 30 '19 at 12:41
  • @AlexCohn I don't get it, my intellisense says `expression must be a pointer to a complete object type` in `delete *pSp` – PPP Nov 01 '19 at 08:14
1

shared_ptr is exactly about managing ownership. If you want to manage ownership on the java side, i wouldnt actually use shared_ptr.

As a hack around, you could use shared_ptr with a custom Deleter, that does not actually delete the object.

ahaupt
  • 36
  • 2
  • Thats true and i totally agree, however it should be said that i cannot change the c++ code but are only able to change the Java side. – fyr Oct 15 '14 at 10:04
0

Smart pointers are often concerned with allocation/deallocation and shared_ptr is no exception. When you want references to objects without caring about allocations, use a reference or a pointer.

Is there a specific reason you can't use an ordinary pointer for your task?

psyill
  • 891
  • 6
  • 10