Steve Sobol said:
But even with .NET you have a shim that allows you to deal with legacy
code.
yes, but one can write this shim in C++/CLI, and it is much nicer.
more so, not all C or C++ code is "legacy", rather it may be the case that a
large portion of the app is written, say, in C and C++, and Java or C# is
intended to be used as a high-level scripting language (along with
JavaScript and similar...).
hence Java can be used similar to Python, but less horridly nasty...
With .NET you have the issue of managed code using legacy code.
With Java you have the issue of Java bytecode using native code, and
that isn't going to go away due to Java's cross-platformness.
the barrier can be lowered, however...
there is no good reason it has to be JNI, and the issue is with the design
of the standard JVM and compilers, rather than with the native code.
I have achieved *much* more transparent interfacing between a JavaScript
variant and C.
meanwhile, there is no good way even to plug together between Java and
JavaScript, even though the underlying implementation could do so without
issue.
JNI is a beast. I only use JNI if I need to create a native Windows or
Linux launcher for a program; otherwise, I use JNA. JNA is about as
simple as it's going to get, and it works well.
JNA still has issues though, namely that it is wonky and can't deal with the
C ABI "on equal terms", and would seem to be a bit less powerful than JNI.
moving the interface gunk onto the Java side of things is not ideal either
(far from being transparent).
better would be if the VM was smart enough to "do the right thing" without
needing ugly gunk in either language (as in, transparent...).
Fixing said problems, IMHO, would entail getting rid of the *best*
reason to use Java (write once, run almost anywhere).
that is in the present case:
nearly every VM implements their own non-standard means to interface with C
(presumably in addition to JNI).
but, anyways, major options:
support delayed-binding in the compiler, and do some magic in the VM (this
was my original plan of action, but doesn't work so long as I am using a
standard compiler);
implement a less braindead alternative to JNI (which is along the lines of
what I have done for the time-being).
delayed-binding could allow binding with whatever is visible at the time,
essentially allowing transparently importing nearly the entire C toplevel
and typesystem de-facto (provided the implementation can see it), or gluing
against other languages (such as JS), but this can't be done with standard
compilers, or efficiently in the current bytecode design (absent hackery).
nevermind what level of evil hackery goes on behind the scenes.
the latter option basically looks just like JNI on the Java end (classes
with 'native' methods).
however, unlike JNI, the interface is capable of "negotiating" the arguments
with the C code (it looks up the C declaration, and sees what types it
expects, and also knows what exists on the Java end, and tries to figure out
how to best coerce the Java-side arguments into the C-side arguments).
it might also probe for different C-side functions, such as to make
interfacing a little more flexible.
technically, I just sort of hacked over the prior JNI mechanism and patched
it into other mechanisms elsewhere in the project (it looks up the proper
JNI function first, and failing this, falls back to the other options).
granted, I happen to be operating within the confines of a VM which knows
the C-side declarations for any functions/types/... and in some cases types
can have special modifiers attached (mostly these are used to give the VM
additional abilities to figure out C side types, such as when the object is
visible via a dynamically-typed reference or "void *" pointer, ...).
for example, in the interface, the 'this' argument is known by using a
special type which flags that a 'this' value is expected for this argument
(if omitted, 'this' is discarded).
so, for example:
public class Foo {
public native void bar(String str);
}
and in C, this could be used:
API void Foo_bar(dycThis self, char *str)
{
//captures 'this', and coerces the string to a C string
...
}
dycThis/dycObject/... are related to my framework's internal OO facilities
(sadly, these aren't transparently accessible from C either, and I am not as
inclined to do something along the lines of Objective-C nastiness to make it
work...). (partly this is because most of my C code is still compiled with
native compilers, as they tend to do the best job...).
but, this could be used:
API void Foo_bar(char16 *str)
{
//ignores this, coerces to a 16-bit string
...
}
or:
API void bar(char *str)
{
//treats it more like a toplevel function
}
or:
....
but, the above is a bit of a compromise, but at least, it provides a
slightly less ugly alternative...
the system is also capable of looking into structs, ... but there is little
hope of cleanly mapping this to Java (it is used with my JS variant though,
which is capable of looking into C structs).
as noted, types visible via "void *" pointers pose an issue, but my
framework also supports dynamic-type tagging, and it is common to indicate
(at the typedef's) that a given dynamic type name refers to a specific C
type, hence allowing dynamic typing on C structs (and type visibility
through void pointers).
on the Java end, something like this would require a wrapper class or
similar, say:
class TypedPointer extends Pointer {
...
public native int geti(String name);
public native long getl(String name);
public native float getf(String name);
public native double getd(String name);
public native Pointer getp(String name);
public native Object geto(String name);
...
public native void set(String name, int val);
public native void set(String name, long val);
...
}
or such...