I create a Java object by calling native code which return a jobject value.
Java code:
Object myObj = nativeCreateObject();
Native code:
jobject* hold_ref;
JNIEXPORT jobject JNICALL
nativeCreateObject(JNIEnv *env ...) {
.....
result = env->NewGlobalRef(jobj);
hold_ref = &result;
return result;
}
my question is: whether I can use hold_ref later to release the myObj by reference in native layer?
e.g.
native code:
*hold_ref = NULL;
Then the myObj in Java layer is null?
if not, how can I release this object by native code?
I am not sure, what you want to achieve, but here is how it basicly works:
whether I can use hold_ref later to release the myObj by reference in native layer?
Yes, you can and should use env->DeleteGlobalRef(myObj) to free the global reference you have created so the garbage collector can clean up and finaly destroy the object.
Then the myObj in Java layer is null? if not, how can I release this object by native code?
There is no way your Java-variable will turn null magicaly when you delete the reference from jni native code. Java itself holds a reference to prevent the object is removed by the garbage collection.
You may want to use it this way:
C++
jobject* hold_ref;
JNIEXPORT jobject JNICALL nativeCreateObject(JNIEnv *env ...) {
.....
result = env->NewGlobalRef(jobj);
hold_ref = &result;
return result;
}
JNIEXPORT void JNICALL nativeDestroyObject(JNIEnv *env ...) {
.....
env->DeleteGlobalRef(jobj);
hold_ref = nullptr;
}
JAVA
// Creates two references (native and local variable)
Object myObj = nativeCreateObject();
// Deletes the native reference, but not the local one
nativeDeleteObject(myObj);
// myObj != null
myObj = null;
// now there is no reference to your created object
// the garbage collector may destroy it any time
If you want to invalidate your object in someway I suggest to manage the state and throw an exception, if the object was invalidated like this:
class MyInvalidateableObject {
private boolean invalidated = false;
public void invalidate() {
this.invalidated = true;
}
public void foo() {
if (invalidated)
throw new IllegalStateException("Object has been invalidated");
... // do the normal stuff
}
}
Simply call invalidate() on your object from native code to prevent it from being used anymore.
Related
Ok first off I'm very new to C++ so apologies if my understanding is poor. I'll try explain myself as best I can. What I have is I am using a library function that returns a std::shared_ptr<SomeObject>, I then have a different library function that takes a raw pointer argument (more specifically node-addon-api Napi::External<T>::New(Napi::Env env, T *data) static function). I want to create a Napi::External object using my std::shared_ptr. What I am currently doing is this:
{
// ...
std::shared_ptr<SomeObject> pSomeObject = something.CreateSomeObject();
auto ext = Napi::External<SomeObject>::New(info.Env(), pSomeObject.get());
auto instance = MyNapiObjectWrapper::Create({ ext });
return instance;
}
But I am worried this will run into memory issues.
My pSomeObject only exists in the current scope, so I imagine what should happen is after the return, it's reference count will drop to 0 and the SomeObject instance it points to will be destroyed and as such I will have issues with the instance I return which uses this object. However I have been able to run this code and call functions on SomeObject from my instance, so I'm thinking maybe my understanding is wrong.
My question is what should I do when given a shared pointer but I need to work off a raw pointer because of other third party library requirements? One option that was proposed to me was make a deep copy of the object and create a pointer to that
If my understanding on any of this is wrong please correct me, as I said I'm quite new to C++.
============================
Edit:
So I was missing from my original post info about ownership and what exactly this block is. The block is an instance method for an implementation I have for a Napi::ObjectWrap instance. This instance method needs to return an Napi::Object which will be available to the caller in node.js. I am using Napi::External as I need to pass a sub type of Napi::Value to the constructor New function when creating the Napi:Object I return, and I need the wrapped SomeObject object in the external which I extract in my MyNapiObjectWrapper constructor like so:
class MyNapiObjectWrapper
{
private:
SomeObject* someObject;
static Napi::FunctionReference constructor; // ignore for now
public:
static void Init(Napi::Env env) {...}
MyNapiObjectWrapper(const CallbackInfo& info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
// My original code to match the above example
this->someObject = info[0].As<const Napi::External<SomeObject>>().Data();
}
DoSomething()
{
this->someObject->DoSomething();
}
}
I have since come to realise I can pass the address of the shared pointer when creating my external and use it as follows
// modified first sample
{{
// ...
std::shared_ptr<SomeObject> pSomeObject = something.CreateSomeObject();
auto ext = Napi::External<SomeObject>::New(info.Env(), &pSomeObject);
auto instance = MyNapiObjectWrapper::Create({ ext });
return instance;
}
// modified second sample
class MyNapiObjectWrapper
{
private:
std::shared_ptr<SomeObject> someObject;
static Napi::FunctionReference constructor; // ignore for now
public:
static void Init(Napi::Env env) {...}
MyNapiObjectWrapper(const CallbackInfo& info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
// My original code to match the above example
this->someObject =
*info[0].As<const Napi::External<std::shared_ptr<SomeObject>>>().Data();
}
DoSomething()
{
this->someObject->DoSomething();
}
}
So now I am passing a pointer to a shared_ptr to create my Napi::External, my question now though is this OK? Like I said at the start I'm new to c++ but this seems like a bit of a smell. However I tested it with some debugging and could see the reference count go up, so I'm thinking I'm in the clear???
Here the important part of the documentation:
The Napi::External template class implements the ability to create a Napi::Value object with arbitrary C++ data. It is the user's responsibility to manage the memory for the arbitrary C++ data.
So you need to ensure that the object passed by data to Napi::External Napi::External::New exits until the Napi::External<T> object is destructed.
So the code that you have shown is not correct.
What you could do is to pass a Finalize callback to the New function:
static Napi::External Napi::External::New(napi_env env,
T* data,
Finalizer finalizeCallback);
And use a lambda function as Finalize, that lambda could hold a copy through the capture to the shared pointer allowing to keep the shared pointer alive until finalize is called.
std::shared_ptr<SomeObject> pSomeObject = something.CreateSomeObject();
auto ext = Napi::External<SomeObject>::New(
info.Env(),
pSomeObject.get(),
[pSomeObject](Env /*env*/, SomeObject* data) {});
I'm an absolute zero at C++. But I need to write a small c++ class for managing a d3ddevice.
My C# code is:
public class HCPPUtils
{
[DllImport("HSpectrum\\Assets\\HCPPUtils.dll")]
private static extern int Getd3Device(ICanvasResourceCreator resourceCreator);}
HCPPUtils hcp = new HCPPUtils();
var pnt = hcp.HGetOrCreate(ResourceCreator);
var d3dDevice = SharpDX.Direct3D11.Device.FromPointer<SharpDX.Direct3D11.Device>(new System.IntPtr(pnt));
My C++ code is:
extern "C"
{
__declspec(dllexport) int Getd3Device
(Microsoft::Graphics::Canvas::ICanvasResourceCreator^ canvasDevice)
{
ComPtr<ID3D11Device> m_device;
__abi_ThrowIfFailed(Windows::Graphics::DirectX::Direct3D11::GetDXGIInterface(canvasDevice->Device,m_device.GetAddressOf()));
return m_device???
}
}
How can i return a IntPtr from C++ code; so, how can i get IntPtr from ComPtr < ID3D11Device >?
[edited]
What I'm doing is...
I have a win2d canvasandimatedcontrol in my c# project. I want to draw direct3d object in it using sharpdx. But I found out that I need to have the d3ddevice object from win2d canvas. And there isn't a c# method to get it.
So the only solution I can imagine is to build a simple c++ project to which I can pass the canvas control and get the d3ddevice. The only problem is how to pass back the d3d device to c#. Sharp DX seems to have just a method Device.FormIntPtr to create it. But I'm not able to pass back the intptr to the c# object.
I tried to implement what Rook wrote, but I cannot understand how it could be useful for my scenario. I mean it could be usueful, but I need to pass the IDirect3DDevice object from a c++ project anyway.
I suspect what you need to do is to read the docs for things like this: http://microsoft.github.io/Win2D/html/M_Microsoft_Graphics_Canvas_CanvasDevice_CreateFromDirect3D11Device.htm
CanvasDevice implements ICanvasResourceCreator, so you could return it directly once you've created it using the static factory method.
Be careful with the scope and lifetime of m_device here, because you don't want its refcount to be decremented when Getd3Device returns and the ComPtr goes out of scope. I'm assuming that it is actually part of a class that will look after its lifetime, but it bears repeating just in case.
I've been trying to access the unity3d device today. This is how I passed the pointer back into unity/managed code:
cpp:
/*
delegate to pass directx device/context back to managed code
see https://forum.unity3d.com/threads/communicating-c-with-c.89930/#post-586885
*/
typedef int(__stdcall *ANSWERCB)(ID3D11Device* ptr);
static ANSWERCB cb;
extern "C" __declspec(dllexport) int GetDevice(ANSWERCB fp)
{
cb = fp;
if (cb)
{
return cb(s_CurrentAPI->GetDevice());
}
return 0;
}
cs:
[DllImport("RenderingPlugin")]
private static extern void GetDevice(Action<IntPtr> callback);
later I call:
GetDevice(devicePtr =>
{
Debug.Log(devicePtr);
if (devicePtr == IntPtr.Zero) return;
device = SharpDX.Direct3D11.Device.FromPointer<SharpDX.Direct3D11.Device>(devicePtr);
...
works fine in the editor as well as the built in the new 2017.1 beta version (as long as you copy the necessary 64bit system dlls to unitys plugin folder)
I know I can't keep a reference to the internals of an array so I was wondering if it is OK to keep a global pointer to a java array object or indeed any java object. And whether it makes any difference that I create it from the C++.
It works, but I was worried the garbage collector could potentially relocate the memory (which I understand it the reason for Get... and Release... methods on JNIEnv).
//global jfloatArray
jfloatArray jarray;
//called once
JNIEXPORT void Java_com_example_test1_Main_Init
(JNIEnv *env, jclass thiz){
//create once
jarray = env->NewFloatArray(10); //if valid, would it be as valid to pass it in?
}
//called repeatedly
JNIEXPORT void JNICALL
Java_com_example_test1_Main_loop(JNIEnv* env, jobject thiz) {
//use jarray in here
}
Edit:
Here is the correct code.
//global jfloatArray
jfloatArray jarray;
//called once
JNIEXPORT void Java_com_example_test1_Main_Init
(JNIEnv *env, jclass thiz){
//create once
//create it - this gives a local reference
jfloatArray local_jarray = env->NewFloatArray(10);
//get a global reference, cast it and set to the global "jarray"
jarray = (jfloatArray) env->NewGlobalRef(local_jarray);
//delete the local reference
env->DeleteLocalRef(local_jarray);
}
//called repeatedly
JNIEXPORT void JNICALL
Java_com_example_test1_Main_loop(JNIEnv* env, jobject thiz) {
//use jarray in here
}
Your reference is merely that -- a reference. It will not prevent the object it refers to from being relocated. It will prevent the object from being recollected; local references are automatically destroyed after returning, but since you're using a global variable, you should use a global reference, which necessitates manual management. See NewGlobalRef and DeleteGlobalRef.
I have a c++ class called androidGPS with public methods and variables. Inside this class I am using JNI to calls some java code. But I am stuck in the registerNatives function.
My c++ class has this void method:
void LocationChanged(JNIEnv* env, jobject object, jstring paramsString);
Implemented in this way:
void androidGPS::LocationChanged(JNIEnv*, jobject, jstring paramsString)
{
const char * nativeString = currEnv->GetStringUTFChars(paramsString, 0);
QString qstring(nativeString);
QStringList res;
res << qstring;
emit newReadAvailable(res);
}
The Java class that I try to link to has the following method:
public static native void sndonLocationChanged(String currLocation);
I am declaring the JNINativeMethods as a local variable before calling the register natives like:
JNINativeMethod methods[] =
{
{
"sndonLocationChanged",
"(Ljava/lang/String;)V",
(void *)&androidGPS::LocationChanged
}
};
The register natives call is this:
int numMethods;
numMethods = sizeof(methods) / sizeof(methods[0]);
if (currEnv->RegisterNatives(listenerClass, methods, numMethods) < 0)
{
if (currEnv->ExceptionOccurred())
{
classError = true;
emit error("JNI--Error running RegisterNatives");
stopGPSService();
return false; //Return with error
}
else
{
emit error("JNI--Error running RegisterNatives");
stopGPSService();
return false; //Return with error
}
}
The compiler gives me the warning:
warning: converting from 'void
(androidGPS::)(JNIEnv, _jobject*,
_jstring*)' to 'void*'
The c++ application crashes when I call the function sndonLocationChanged() in the class through JNI. I cannot see the error in the c++, but the Dalvik does not generate an error.
I guess is because I am not calling properly registerNatives or not declaring properly the methods (hence the warning).
If I declare LocationChanged static as:
static jboolean LocationChanged(JNIEnv* env, jobject object, jstring paramsString);
It does not fail but I cannot pass paramsString into class members!
Any idea how can I fix this?
Many thanks,
Carlos.
One option is that you give your Java code a means of getting a pointer to your native androidGPS object and passing it down to the non-class-function LocationChanged(), and it calls androidGPS::LocationChanged().
Callbacks into non-static C++ objects are problematic and typically are solved using a static helper function coupled with either a pointer to the object that the callback should be invoked on, or use of a static pointer to a singleton object.
I want to use the new and delete operators for creating and destroying my objects.
The problem is python seems to break it into several stages. tp_new, tp_init and tp_alloc for creation and tp_del, tp_free and tp_dealloc for destruction. However c++ just has new which allocates and fully constructs the object and delete which destructs and deallocates the object.
Which of the python tp_* methods do I need to provide and what must they do?
Also I want to be able to create the object directly in c++ eg "PyObject *obj = new MyExtensionObject(args);" Will I also need to overload the new operator in some way to support this?
I also would like to be able to subclass my extension types in python, is there anything special I need to do to support this?
I'm using python 3.0.1.
EDIT:
ok, tp_init seems to make objects a bit too mutable for what I'm doing (eg take a Texture object, changing the contents after creation is fine, but change fundamental aspects of it such as, size, bitdept, etc will break lots of existing c++ stuff that assumes those sort of things are fixed). If I dont implement it will it simply stop people calling __init__ AFTER its constructed (or at least ignore the call, like tuple does). Or should I have some flag that throws an exception or somthing if tp_init is called more than once on the same object?
Apart from that I think ive got most of the rest sorted.
extern "C"
{
//creation + destruction
PyObject* global_alloc(PyTypeObject *type, Py_ssize_t items)
{
return (PyObject*)new char[type->tp_basicsize + items*type->tp_itemsize];
}
void global_free(void *mem)
{
delete[] (char*)mem;
}
}
template<class T> class ExtensionType
{
PyTypeObject *t;
ExtensionType()
{
t = new PyTypeObject();//not sure on this one, what is the "correct" way to create an empty type object
memset((void*)t, 0, sizeof(PyTypeObject));
static PyVarObject init = {PyObject_HEAD_INIT, 0};
*((PyObject*)t) = init;
t->tp_basicsize = sizeof(T);
t->tp_itemsize = 0;
t->tp_name = "unknown";
t->tp_alloc = (allocfunc) global_alloc;
t->tp_free = (freefunc) global_free;
t->tp_new = (newfunc) T::obj_new;
t->tp_dealloc = (destructor)T::obj_dealloc;
...
}
...bunch of methods for changing stuff...
PyObject *Finalise()
{
...
}
};
template <class T> PyObjectExtension : public PyObject
{
...
extern "C" static PyObject* obj_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
{
void *mem = (void*)subtype->tp_alloc(subtype, 0);
return (PyObject*)new(mem) T(args, kwds)
}
extern "C" static void obj_dealloc(PyObject *obj)
{
~T();
obj->ob_type->tp_free(obj);//most of the time this is global_free(obj)
}
...
};
class MyObject : PyObjectExtension<MyObject>
{
public:
static PyObject* InitType()
{
ExtensionType<MyObject> extType();
...sets other stuff...
return extType.Finalise();
}
...
};
The documentation for these is at http://docs.python.org/3.0/c-api/typeobj.html and
http://docs.python.org/3.0/extending/newtypes.html describes how to make your own type.
tp_alloc does the low-level memory allocation for the instance. This is equivalent to malloc(), plus initialize the refcnt to 1. Python has it's own allocator, PyType_GenericAlloc, but a type can implement a specialized allocator.
tp_new is the same as Python's __new__. It's usually used for immutable objects where the data is stored in the instance itself, as compared to a pointer to data. For example, strings and tuples store their data in the instance, instead of using a char * or a PyTuple *.
For this case, tp_new figures out how much memory is needed, based on the input parameters, and calls tp_alloc to get the memory, then initializes the essential fields. tp_new does not need to call tp_alloc. It can for example return a cached object.
tp_init is the same as Python's __init__. Most of your initialization should be in this function.
The distinction between __new__ and __init__ is called two-stage initialization, or two-phase initialization.
You say "c++ just has new" but that's not correct. tp_alloc corresponds a custom arena allocator in C++, __new__ corresponds to a custom type allocator (a factory function), and __init__ is more like the constructor. That last link discusses more about the parallels between C++ and Python style.
Also read http://www.python.org/download/releases/2.2/descrintro/ for details about how __new__ and __init__ interact.
You write that you want to "create the object directly in c++". That's rather difficult because at the least you'll have to convert any Python exceptions that occurred during object instantiation into a C++ exception. You might try looking at Boost::Python for some help with this task. Or you can use a two-phase initialization. ;)
I don't know the python APIs at all, but if python splits up allocation and initialization, you should be able to use placement new.
e.g.:
// tp_alloc
void *buffer = new char[sizeof(MyExtensionObject)];
// tp_init or tp_new (not sure what the distinction is there)
new (buffer) MyExtensionObject(args);
return static_cast<MyExtensionObject*>(buffer);
...
// tp_del
myExtensionObject->~MyExtensionObject(); // call dtor
// tp_dealloc (or tp_free? again I don't know the python apis)
delete [] (static_cast<char*>(static_cast<void*>(myExtensionObject)));