-
-
Notifications
You must be signed in to change notification settings - Fork 593
Description
In the JNI code, the address field of Java objects is interpreted as a pointer to a C++ object using a C-style cast.
This always works when the effective class of the object is the class we are casting to. This works also in most cases where the object class derived. But not always.
First case where this cast is illegal is when the base class B is polymorphic and the derived class D inherits virtually from B:
Example 1:
#include <cstdio>
class B {
int b = 1;
public:
virtual void f() { printf("%d\n", b); }
};
class D: public virtual B {
int d = 2;
};
If we write in Java: new D().f() we get a segmentation fault.
To prevent this, we would need to replace the C-style cast by a static_cast<B*>(ptr) where ptr is a D*, but we never have a chance to. First time we enter C++ code during this call is in the JNI of method f and at this point the code is specific to B and knows nothing about D.
Example 2, we add this function to Example 1:
void g(B b) {
b.f();
}When I call from Java global.g(new D()), on my machine I get 2, which is incorrect. Others may get segmentation fault. Anyway the C-style cast (B*) made by the JNI of g on its argument is illegal.
Another case where C-style cast is illegal is multiple inheritance.
Example 3, we add:
class B2 {
int b2 = 3;
};
class D2: public B, public B2 {
int d2 = 4;
};Here, no segmentation fault nor incorrect output when we call new D2().f() or global.g(new D2()). Java doesn't support multiple inheritance and JavaCPP only keeps the first inheritance relationship. I doubt this is written in any specification, but in practice C-style casts works to up-cast to the first class. JavaCPP also produces automatically an asB2() instance method for D2 that does a static_cast<B2*>.
My suggestions:
- Drop the Java inheritance in case of C++ virtual inheritance of a polymorphic class. The Java inheritance is just useless and only causes segmentation faults or, worse, hard to diagnose erroneous results. I don't think there is a way to have Java inheritance works here. Is there ?
- Add an automatic
asB()method in this case, just like it's done for multiple inheritance, and also manually byInfoin Pytorch presets forModulesubclasses. - In addition to
asB(), we could also automatically remap all member functions ofBinD. In example 1, we would add aD.fnative method. This would allow the user to dod.f()like in C++ instead ofd.asB().f(). Similarly, we could add overloads for all methods taking aB(or one of its superclass) as argument, that take aDinstead. This is done manually byInfoin Pytorch too, in the specific case of theregister_modulemethod.
Suggestions 1 and 2 are important I believe.
3 is less essential, and I'm not sure there is an easy way to implement this in the parser.