I have a C API that I'm trying to use within Clojure, through the JNA API. My issue can best be demonstrated with the following example. Say I have this C code in a library:
typedef struct {
int foo;
int bar;
double baz;
} MyStruct;
MyStruct createStruct() {
MyStruct myStruct;
myStruct.foo = 3;
myStruct.bar = 4;
myStruct.baz = 3.14;
return myStruct;
}
double addStruct(MyStruct myStruct) {
return myStruct.foo + myStruct.bar + myStruct.baz;
}
In this example, I'd like to call createStruct, and then pass that result to addStruct. The important point here is that MyStruct is passed by value as both a return type and an argument. At no point do I need to actually read the values of the fields in MyStruct.
Additionally, in my system, native functions are wrapped like this:
; `quux` is defined in `some-lib` and returns an `int`
(let [fn- (com.sun.jna.Function/getFunction "some-lib" "quux")]
(fn [& args]
(.invoke fn- Integer (to-array args))))
The goal is to get a type to substitute for Integer above that will wrap MyStruct as a value.
The only resource that I've found covering this subject is this article, but it only discusses how to pass structs by reference.
Given that, here are the different approaches I've tried to take to solve this problem:
Create a class that inherits from
Structure, which is JNA's built-in mechanism for creating and using structs. Given the information on that page, I tried to create the following class using only Clojure:class MyStruct extends Structure implements Structure.ByValue { int foo; int bar; double baz; }deftypedoesn't work for this scenario, since the class needs to inherit from the abstract classStructure, andgen-classdoesn't work because the class needs to have the public non-static fieldsfoo,barandbaz.From what I can tell, none of the standard Clojure Java interop facilities can create the above class.
Create a class that inherits from
Structureand override the struct field getter/setter methods. Sincegen-classis (I believe) the only Clojure construct that allows for direct inheritance, and it doesn't support multiple public non-static fields, the next alternative is to simply not use fields at all. Looking at theStructureabstract class documentation, it seems like there's a concoction of overrides possible to use 'virtual' struct fields, such that it really gets and sets data from a different source (such as thestatefield fromgen-class). Looking through the documentation, it seems like overridingreadField,writeField, and some other methods may have the intended effect, but the I was unclear how to do this from reading the documentation, and I couldn't find any similar examples online.Use a different storage class. JNA has a myriad of classes for wrapping native types. I'm wondering if, rather than defining and using a
Structureclass, I could use another generic class that can take an arbitrary number of bits (like howIntegercan hold anything that's 4 bits wide, regardless of what type the source 'actually' is). Is it possible to, for example, say that a function returns an array of bytes of length 16 (sincesizeof(MyStruct)is 16)? What about wrapping a fixed-size array in a container class that implementsNativeMapped? I couldn't find examples of how to do either.
com.sun.jna.Memory. From there, you can extract arbitrary types from arbitrary offsets in memory usingPointer.getXXX()methods. - technomageMemoryas a return type forcreateStructreturns aPointer, whose address is0x0000000400000003. In other words, it treats the raw memory value of the struct as a pointer. When dereferencing (through any of thereadorgetXXXmethods), it crashes the VM. - Kyle Lacystructparameter and return value semantics vary by compiler and theStructurelayout is actually used by the native code to determine how to set up the stack properly. I was suggesting you might be able to useMemoryto assign an arbitrary block of memory to an existingStructure.ByValueinstance. - technomageStructure.ByValuetag on both the actual argument and the library function mapping signature in order to pass a structure by value on to the native code. - technomagegetTypeInfo()for your structure, which returns FFI type information for your structure (which is itself a structure). - technomage