javacpp
javacpp copied to clipboard
How to deal with user-define-type array
Here is a demo
#include <iostream>
class MyType {
public:
MyType() : a(0) {}
MyType(int i) : a(i) {}
int a;
};
class MyTypeUser {
public:
void use(MyType* a, int count) {
for (int i = 0; i < count; i++) {
std::cout<<a[i].a<<std::endl;
}
}
};
MyTypeUser::use
expects an array of MyType
. The generated code is only able to hold one MyType
actually.
generated MyTypeUser.use
definition
public native void use(MyType a, int count);
generated MyType
public class MyType extends Pointer {
static { Loader.load(); }
/** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */
public MyType(Pointer p) { super(p); }
public MyType() { super((Pointer)null); allocate(); }
private native void allocate();
public MyType(int i) { super((Pointer)null); allocate(i); }
private native void allocate(int i);
public native int a(); public native MyType a(int setter);
}
I tried to add the following mapping info following guide here(https://github.com/bytedeco/javacpp/wiki/Mapping-Recipes#specifying-names-to-use-in-java)
infoMap.put(new Info("MyType").pointerTypes("MyTypePointer"));
infoMap.put(new Info("MyTypePointer").valueTypes("MyTypePointer").pointerTypes("@Cast(\"MyType*\") PointerPointer", "@ByPtrPtr MyTypePointer").base("IntPointer"));
The generated code is almost the same.
public native void use(MyTypePointer a, int count);
public class MyTypePointer extends Pointer {
static { Loader.load(); }
/** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */
public MyTypePointer(Pointer p) { super(p); }
public MyTypePointer() { super((Pointer)null); allocate(); }
private native void allocate();
public MyTypePointer(int i) { super((Pointer)null); allocate(i); }
private native void allocate(int i);
public native int a(); public native MyTypePointer a(int setter);
}
Is it possible to define UDT pointer similarly to IntPointer, capable of holding one or more elements? @saudet
All subclasses of Pointer can be used for native arrays, if that's what you mean?
All subclasses of Pointer can be used for native arrays, if that's what you mean?
Yes we are on the same page. But I have problem creating array from java side. Could you provide any guide? Is this the right way?
public class Test {
public static void main(String[] args) {
System.out.println(new MyTypePointer().sizeof());
Pointer p = Pointer.malloc(Pointer.sizeof(MyTypePointer.class) * 4);
for (int i = 0; i < 4; i++) {
MyTypePointer myp = new MyTypePointer(p.getPointer(i));
myp.a(i);
}
MyTypeUser u = new MyTypeUser();
u.use(new MyTypePointer(p), 4);
}
}
Besides, run the following throws exception. Is this expected behaviour?
public class Test {
public static void main(String[] args) {
System.out.println(Pointer.sizeof(MyTypePointer.class));
System.out.println(new MyTypePointer().sizeof());
}
Exception in thread "main" java.lang.ClassCastException: class java.lang.Object
at java.lang.Class.asSubclass(Class.java:3404)
at org.bytedeco.javacpp.Loader.offsetof(Loader.java:1915)
at org.bytedeco.javacpp.Loader.sizeof(Loader.java:1928)
at org.bytedeco.javacpp.Pointer.sizeof(Pointer.java:814)
We can usually call something like new MyTypePointer(long)
to allocate an array, but since that type already has an int
constructor, the parser is skipping over the array allocator, that's all. We can still declare native void allocateArray(long size);
and call it however we want though.
Is there any guide on how to declare allocateArray
for parse generated class?
It's custom Java code: https://github.com/bytedeco/javacpp/wiki/Mapping-Recipes#mapping-a-declaration-to-custom-code
Hi @saudet , I get it to work with the following
First use javaText
infoMap.put(new Info("MyType").javaText("\n"
+ " @NoOffset @Properties(inherit = com.mycompany.myproject2.presets.myproject.class) \n"
+ " public class MyType extends Pointer { \n"
+ " static { Loader.load(); } \n"
+ " /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */ \n"
+ " public MyType(Pointer p) { super(p); } \n"
+ " public MyType(long size) { super((Pointer)null); allocateArray(size); }\n"
+ " \n"
+ " public MyType() { super((Pointer)null); allocate(); } \n"
+ " private native void allocate(); \n"
+ " private native void allocateArray(long size); \n"
+ " @Override public MyType position(long position) {\n"
+ " return (MyType)super.position(position);\n"
+ " }\n"
+ " public MyType(int i) { super((Pointer)null); allocate(i); } \n"
+ " private native void allocate(int i); \n"
+ " public native int a(); public native com.mycompany.myproject2.MyType a(int setter); \n"
+ " public native double padding(int i); public native com.mycompany.myproject2.MyType padding(int i, double setter); \n"
+ " @MemberGetter public native DoublePointer padding(); \n"
+ " } \n"
));
Test code as below
public static void main(String[] args) {
MyType mt = new MyType((long) 4);
for (long i = 0; i < 4; i++) {
MyType each = mt.position(i);
each.a((int) i);
}
MyTypeUser u = new MyTypeUser();
mt.position(0);
u.use(mt, 4);
}
Let me known if there is anything wrong or there is any better way, especially regarding with the following
- After traverse the array, I have to rewind it with
mt.position(0);
. Is there a better way? - Is it necessary to provide all the definition of
MyType
? Is it possible to add or overwrite one or more functions on demand?
We can use Pointer.getPointer() for something easier to use than position(), and of course we can provide Info.javaText only for some methods, including constructors.
As we all known, no Info.javaText
will not generate MyType(long)
constructor.
However adding the following will generate compiling error
infoMap.put(new Info("MyType::MyType").javaText("public MyType(long size) { allocateArray(size); }"));
Error message
constructor MyType(long) is already defined in class com.mycompany.myproject2.MyType
Generated class is below. MyType(long size)
is defined twice.
public class MyType extends Pointer {
static { Loader.load(); }
/** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */
public MyType(Pointer p) { super(p); }
/** Native array allocator. Access with {@link Pointer#position(long)}. */
public MyType(long size) { super((Pointer)null); allocateArray(size); }
private native void allocateArray(long size);
@Override public MyType position(long position) {
return (MyType)super.position(position);
}
@Override public MyType getPointer(long i) {
return new MyType((Pointer)this).offsetAddress(i);
}
public MyType(long size) { allocateArray(size); }
public native int a(); public native MyType a(int setter);
public native double padding(int i); public native MyType padding(int i, double setter);
@MemberGetter public native DoublePointer padding();
}
Then I tried the following
infoMap.put(new Info("MyType::MyType").javaText("public MyType(long size) { allocateArray(size); }").skip());
It worked. Is this the expected trick?
That's a bit weird, it should not output the default code when supplied with javaText, yes.
Actually, you're going to get the same thing just by setting Info.skip. What we put in Info.javaText doesn't get used when Info.skip is set. What is happening is that it also doesn't get picked up by the parser to figure out what's missing, so it just outputs the defaults. I've added a check for Info.skipDefaults to allow us to skip those manually when we need it, so something like this works now:
.put(new Info("MyType").skipDefaults())
.put(new Info("MyType::MyType").javaText("public MyType(long size) { allocateArray(size); }\n"
+ "private native void allocateArray(long size);"))