Java For C++ (Stefan Palme ) ================================================= Introduction ------------ java4cpp is a command line tool that creates code for C++ classes that wrap existing Java classes. For understanding this README and for being able to really use this tool you should have at least basic knowledge about Java, the Java API and C++. This README mostly uses classes and interfaces from the Java standard API. But this does not mean, that the wrapper generator only works for these standard classes - it works for ANY valid java class. The reason is, that the wrapper generator uses reflection to detect the structure of the classes, that you want to generate C++ classes for. For example, the Java class java.lang.Integer can be used in Java code in this way: ... java.lang.Integer i = new java.lang.Integer("10"); System.out.println("i=" + i.intValue()); System.out.println("class of object i is "+i.getClass().getName()); ... This code is of course not very optimal and even senseless - it's just an example to show the purpose of java4cpp... After creating a C++-wrapper-class for java.lang.Integer you get a C++-class java::lang::Integer (i.e. a class "Integer" in namespace "java::lang"), which can be used as following: #include #include int main(int argc, char** argv) { initJavaWrapper(argc, argv); java::lang::Integer* i = new java::lang::Integer("10"); printf("i=%i\n", i->intValue()); java::lang::Class* klass = i->getClass(); const char* name = klass->getName(); printf("class of object i is %s\n", name); delete[] name; delete klass; delete i; } As you can see, the C++-API for working with Java objects is very close to the original Java API for these objects. Creating C++ wrapper classes for existing Java classes ------------------------------------------------------ To create C++ wrapper classes for existing Java classes you have to follow these steps (but see below for a shortcut): - Create a text file containing the name of all Java classes you want to create wrappers for. For example: content of file javaClasses.txt: java.lang.Integer java.lang.String java.net.Socket org.mydomain.myprojects.MainClass When some of the classes you want to use have method return types, method arguments or fields of other classes, you don't have to specify them all. java4cpp automatically detects this and creates wrapper classes for these "internally used" classes too. For example, when you give java.lang.Integer in the text file, java4cpp will also automatically generate wrapper classes for: * java.lang.Number (because Integer extends Number), * java.lang.Comparable (because Integer implements Comparable), * java.lang.Object (because Integer.equals() takes an Object argument), * ... When automatically creating the wrapper class for e.g. java.lang.Number, the same mechanism applies. So java.lang.Number causes java4cpp to create java.lang.Object (because Number extends Object), ... This is the reason, why you get a lot of C++ wrapper classes even if you want to wrap only one simple Java class like java.lang.Object. - Create a configuration file for the wrapper generator. The file will be called CONFIGFILE here and should contain the following lines: basedir = /home/kleiner/projects/myJavaProject classfile = ${basedir}/javaClasses.txt targetpath = ${basedir}/c++-wrapper verbose = true "basedir" is just a variable that can be used via ${basedir} later in the configfile. You can define and use any variable you want (excluding the keywords described below). "classfile" contains the path to the text file containing the list of Java classes you want to generate C++ classes for. "targetpath" must be the name of an existing directory. java4cpp will write all generated code in this directory. When no targetpath is given, it defaults to the current directory. "verbose" can be "true" or "false". When "true", the wrapper generator will print a line for each Java class it creates a wrapper class for (so it is not very useful at the moment). When not given, verbose mode defaults to false. - Call java -cp java4cpp.jar:$YOUR_CLASSPATH org.kapott.wrappergen.c.Main CONFIGFILE $YOUR_CLASSPATH is maybe needed to make Java find all the classes you specified in javaClasses.txt. For example, if your Java project lives in /projects/myProject and the classfiles in /projects/myProject/classes, you probably have to include /projects/myProject/classes in the CLASSPATH when calling the wrapper generator. This is caused by the fact, that java4cpp uses "Class.forName(...)" to find the classes. CONFIGFILE is the name of the configuration file you created in the step before :-) This call will create one .cpp file and one .h file for each Java class. - Copy all the files from the directory "src/c" from the java4cpp-archive to the targetpath. These files contain additional code and helper classes required internally be the generated code for the C++ classes. This step will be done automatically in a later version of java4cpp. Now you have a lot of .cpp and .h files in the targetpath. There is a Makefile.java4cpp included in the Java4CPP archives. You can use this Makefile to automatically perform these steps. See the comments in Makefile.java4cpp on how to configure and use it. How to include the generated C++ code in your project ----------------------------------------------------- There are a lot of ways to include the generated files in your own projects. I will describe a very easy and fool-proof solution here. C++ developers will of course know other solutions and maybe prefer them... :-) To be able to use the wrapper classes, follow these steps (but see below for a shortcut): - Go into the directory where the generated files live (this directory has been specified via "targetpath" in the configuration file for the wrapper generator): cd /home/kleiner/projects/myJavaProject/c++-wrapper - Compile all .cpp files and link them together: g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -I. -fPIC -shared -o libjavawrapper.so *.cpp You will get a shared library "libjavawrapper.so" that has to be linked to your application. You don't need the .cpp files any longer, so you can remove them if you want: rm *.cpp - Write your C++ application and optionally use the newly available C++ wrapper classes to create and use Java objects. Hints for using the wrapper classes in your code can be found in a later section of this README. - When compiling your C++ application, you have to add the following to the compilation process: * For compiling your code, you have to add the "targetpath" (where all the include files for the C++ wrapper classes live) to the list of directories where to search for include files. And you have to use -fPIC to compile your code (don't know exactly why...) g++ -I/home/kleiner/projects/myJavaProject/c++-wrapper -fPIC ...other_options... * When linking the executable, you have to add two libraries to the link process: g++ ...other_options... \ -L/home/kleiner/projects/myJavaProject/c++-wrapper -ljavawrapper \ -L$JAVA_HOME/jre/lib/i386/client -ljvm The first library (libjavawrapper.so) is the library you created from the generated C++ wrapper classes. The second one is a library provided by the Java Runtime Engine. It is needed for function calls which create an instance of the JVM at runtime. The need to link this library to your application will be removed in a later version of java4cpp. The stdc++ library is probably already included in your list of libraries... After all these steps you will have an executable that can use Java classes via C++ method calls. The Java4CPP archives contains a Makefile.example that can be used as a template for your own application's Makefile on how to automate the steps of creating the wrapper code needed by your application. Licensing --------- You should be aware of the fact, that the C++ source code for accessing Java classes from C++ applications will be made of two types of code: Most of the generated files are created by executing the Java4CPP tool. But to be able to use the generated C++ source code, you also need some .cpp and .h files coming with Java4CPP. You have to copy these files from the Java4CPP archive into your own project. Because Java4CPP is licensed under the GPL (and these additional C++ source code files are no exception), your own project, which uses the C++ classes created with Java4CPP, must also be licensed under the GPL! How to call the executable -------------------------- There are some things to be taken care of when you call your application: - When you want to run your C++ application, you need the following on the system the application: * the original Java classes that are used by the C++ wrapper classes (because these classes are really only *wrappers*, they internally call the original Java classes to let them do all the work), * a working Java Runtime Engine, that is compatible with the Java classes you want to use, * the library containing all the C++ wrapper objects for the Java classes (only needed when this library has created as shared library - when included as static library directly in the executable, no extra library is needed, of course). - The three libraries libjavawrapper.so, libjvm.so and libverify.so (used internally by the JRE) must be found by the dynamic loader of your OS. Normally these libraries are stored in a location not in the default search path where the dynamic loader looks for shared libraries. To solve this, there are again a lot of solutions, here is only one: env LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/i386/client:$JAVA_HOME/lib/i386:/home/kleiner/projects/myJavaProject/c++-wrapper:$LD_LIBRARY_PATH ./myApplication ARGUMENTS This adds the required directories to the environment for the call of your application. - When your application code uses the C++ wrapper classes to create Java objects, the internally running Java Virtual Machine must be able to find the Java classes you want to use. When these Java classes are not part of your default CLASSPATH, you have to add a command line argument when calling your application: env LD_LIBRARY_PATH=... ./myApplication [myArguments] -Djava.class.path=/home/kleiner/projects/myJavaProject/classes [myArguments] To define the classpath where the JVM should look for Java classes, you have to specify an additional argument "-Djava.class.path=...". Your application code should ignore this additional argument. And your application code should provide all command line arguments to "initJavaWrapper()" (see later section in the README for hints how to use the C++ wrapper classes in your own code), so that the wrapper code knows about the additional classpath. To simplify this, the Java4CPP archives contain a shell script called "runme.sh". You can use it as a template for a shell script to start your own application. Just copy and modify the script for your own needs. All steps up to here can be done without really using application code that makes use of the wrapper classes. You should perform these steps and test, if your application still works (it should!). After all the modifications to the process of compiling and calling your application have been done and still produce a working application executable, you can start inserting code that uses the wrapper classes. How to use the generated wrapper classes in your own code --------------------------------------------------------- This is a list of important aspects to be aware of when you use the generated C++ wrapper classes in your own application code. Some things are not explained in detail here and may seem strange to you. Maybe you find an explanation of some of the gotchas in a later section of this README, where the internal stuff of the wrapper code is explained a little bit more detailed. - Include-Files: to make integration of Java objects for you as easy as possible, there is only one include file that is interesting for you. Every file of code of your application, that makes use of any of the C++ wrapper classes or the Java wrapper in general, should contain the following line: #include This header file is part of the header files that come with the java4cpp archive. It is responsible for including other internally needed header files and for including all the header files that define the C++ wrapper classes. For this include directive to work you should ensure, that the directory containing the generated wrapper classes is part of your INCLUDE-path when compiling the application. Furthermore you must ensure, that you did not forget to copy the files from src/c from the java4cpp archive into this directory. - Initializing the Java wrapper: before you can create your first Java proxy class in your application you have to initialize the wrapper engine. To do this, you have to call the method int result = initJavaWrapper(int argc, char** argv); (dont forget to include ). The arguments argc and argv define an array of strings (argv) and its size (argc). Each of the strings in this array will be given to the initialization routine for the Java Virtual Machine. This means, that each of the strings can contain an option understood by the JVM. When the JVM finds an option it does not understand, this option is ignored silently - so you could give all the command line arguments of your main application to this method: the command line arguments may contain mixed arguments understood by your application and arguments for the JVM. The most important JVM arguments may be "-Djava.class.path=..." to define the classpath where the JVM should look for Java classes used by your application code. A call to this method makes the Java wrapper engine create an instance of the Java Virtual Machine. You should ensure, that you create ONLY ONE Java Virtual Machine instance for your application. A reference to this JVM instance is kept internally by the Java wrapper engine. Currently there is no way to "remove" this JVM instance (because of limitations in the JNI invocation API). The return value of this function will be 0 on success and a value less that zero on failure. - API: the generated C++ classes provide the same API as their corresponding Java classes (with some exceptions). At the moment, only "public" constructors, methods and fields are accessibly by the C++ wrapper classes! The name of a C++ class equals the name of the corresponding Java class with two modifications: * The Java package containing of the Java class will be converted to a C++ namespace. So class java.lang.Object will become C++ class Object in namespace java::lang (full qualified name java::lang::Object). * Every "$" in the Java class name is replaced by "__": this is needed for emulation of nested classes (classes defined in classes). So the Java class "java.lang.Integer" is called "java::lang::Integer" in C++, and the Java class "org.kapott.myProject.AList$Entry" would be called org::kapott::myProject::AList__Entry in C++ (Java classes with "$" in their name are classes, that are nested in other classes. This feature is natively not available in C++ (at least I think so)). The object hierarchy of Java classes is also provided by the C++ classes. In Java, java.lang.Integer inherits from java.lang.Number, which inherits from java.lang.Object. In C++, java::lang::Integer inherits from java::lang::Number, which inherits from java::lang::Object. Because in Java there is no multi-inheritance, also the C++ class structure will be single- inherited (without all the ugly problems caused by multi inheritance). The concept of interfaces available in Java is also provided for the C++ classes, see a later section for a description of this. To create an instance of a Java class in your C++ code, you could do the following: java::lang::Integer* i = new java::lang::Integer("12"); org::kapott::myProject::AList* alist = new org::kapott::myProject::AList(); The available constructors match those from the original Java class, so java::lang::Integer will have the constructors new java::lang::Integer(int i); new java::lang::Integer(const char* st); (See the documentation of the corresponding Java classes to find the list of valid constructors, methods and fields). There are some exceptions to this: whenever a Java method or field has a name that corresponds to a C++ keyword, the NAME is replaced by NAME_. At the moment, only the C++ keyword "delete" is detected and mangled by the code generator (happens in class java.lang.StringBuffer; method delete()). To use any of the methods or fields of the original Java class, you just call them by calling the same method on the C++ class instance: int value = i->intValue(); org::kapott::myProject::AList__Entry entry = alist->getEntry(0); const char* entry_value = entry->value; java::lang::StringBuffer* aStringBuffer = new java::lang::StringBuffer("hello"); aStringBuffer->delete_(1,2); There are some details to be aware of for certain situations: * field values * data types of method arguments, method return types and fields * using arrays * using strings * using static methods or fields * using interfaces * Java exceptions For all this, see below... - Field values: values of member variables (=fields) are automatically updated by the java wrapper code whenever a method of the corresponding class is called: a Java class: public class MyClass() { public int x=5; public incrementX() { this.x++; } } some C++ code: MyClass* myClass = new MyClass(); printf("x = %i\n", myClass->x); // prints "5" myClass->incrementX(); printf("x = %i\n", myClass->x); // prints "6" Although x is just a member variable (also in the C++ wrapper classes) and not touched by the given C++ code its value is updated after the call to incrementX(). This works for *all* member variables, but only with methods in the same class. Following an example, that will not work this way: Java: public class MyClass() { public int x=5; } public class OtherClass() { public incrementX(MyClass cl) { cl.x++; } } C++: MyClass *myClass = new MyClass(); printf("x = %i\n", myClass->x); // prints "5" OtherClass *other = new OtherClass(); other->incrementX(myClass); printf("x = %i\n", myClass->x); // prints "5" (!) The reason is, that the wrapper code for MyClass does not know, that the instance of MyClass has been used and modified by another class. Whenever you know about this possible problem, you could write the following code: C++: MyClass *myClass = new MyClass(); printf("x = %i\n", myClass->x); // prints "5" OtherClass *other = new OtherClass(); other->incrementX(myClass); myClass->updateAllNonFinalVariables(wrapperIntern); printf("x = %i\n", myClass->x); // prints "6" (!) The magic about all this will be explained later in this README, where some of the internal stuff of the generated wrapper code will be explained. - Data types: All Java data types are mapped to corresponding C++ data types transparently by the C++ wrapper code. When a Java constructor or method requires or returns a value of a primitive type, or if a public field of a Java class has a primitive type, it is mapped to a corresponding C++ primitive type: Java C++ ---------------------- boolean bool byte char (!) char char (!) short short int int long long float float double double void void So, when you know a Java method requiring a "boolean" and an "int" value, you can call the corresponding C++ wrapper method by giving it a "bool" and an "int" value: Java declaration: void aMethod(boolean yes_or_no, int x); C++ call: obj->aMethod(true, 5); BE AWARE OF THE FACT, that both Java "byte" and "char" map to C++ "char" - so when you have a Java class with the following methods: void aMethod(char ch); void aMethod(byte by); there will be ONLY ONE C++ method void aMethod(char arg1); You do not even know, which one of the original Java methods is called by this C++ method. Maybe this will be fixed in a later version of java4cpp, when Java type "char" will be mapped to C++ wide_char... There are three non-primitive data types (references to objects, strings, arrays), which are described below. - Java objects: every Java datatype, that is not primitive (see above), is a reference to a Java object. So, when you have a Java method declaration: String getValueOf(int value, Translations t, String language, byte[] additionalInfo); there is only one primitive argument (value), whose use is described above. All the other arguments are in fact references to Java objects. References to java.lang.String and to arrays are handled in a special way (see below). All other references (i.e. references to all Java objects excluding Strings and arrays) are handled as following: Whenever a Java method requires an object reference (Translations), you have to call the corresponding C++ method with an instance of the appropriate C++ wrapper class: org::kapott::myProject::Translations* trans = new org::kapott::myProject::Translations(); ... language = ...; // not discussed here ... additionalInfo = ...; // not discussed here obj->getValueOf(5, trans, language, additionalInfo); This is the reason, why the wrapper code generator not only generates code for the classes you specified in the javaClasses.txt, but also for all classes used as arguments or return types of methods or fields. Of course, the OOP mechanism of inheritance works here. The Java class java.lang.Integer inherits from java.lang.Number: So a Java method void printNumber(java.lang.Number n); can be called from C++ as: java::lang::Integer *i = new java::lang::Integer(5); obj->printNumber(i); because "i" is an instance of a class that inherits from java::lang::Number, which is in fact required by printNumber(java::lang::Number *n); BUT BEWARE: This mechanism does not apply so easily for methods, that have arguments describing interfaces (see below)! The same mechanism works for method return types or for field values: in a Java class: public java.lang.Integer i; public java.lang.Object getRandomObject(); in C++: java::lang::Integer *i = obj->i; java::lang::Object *o = obj->getRandomObject(); - Strings: all Java method arguments, method return values or fields of type java.lang.String are mapped to C++ "const char*". So when you have a Java method String convertString(String x); it's C++ signature will be const char* convertString(const char* arg1); You can call it (obviously) by using const char* result = obj->convertString("a string"); Currently there is a problem with the String class: The Java wrapper code only wraps pure ASCII strings without embedded 0-characters correctly. So whenever you use a Java method or field, that requires or returns a String object (in C++: a const char*), you have to ensure, that the string contains only ASCII characters (between 0x01 and 0x7F). Furthermore, you have to be aware about the memory management stuff (see below). In Java the String class is of course a subclass of java.lang.Object (like any other Java class), so this also holds true for the C++ wrapper code: The Java method Hashtable.put(Object key, Object value) takes two instances of Object (i.e. any object reference). When you want to use *Strings* for "key" or "value", you CAN NOT write hashtable->put("myKey", "myValue"); (because the signature of the corresponding C++ method expects instances of java::lang::Object and not "const char*"), but instead you can do: java::lang::String* st1=new java::lang::String("myKey"); java::lang::String* st2=new java::lang::String("myValue"); hashtable->put(st1, st2); This way, "st1" and "st2" are instances of java::lang::String, which inherits from java::lang::Object, which is required by Hashtable.put(). (Short note: when calling 'hashtable.put("key", "value")' in pure Java code, internally exact the same happens: the call will effectively read 'hashtable.put(new String("key"), new String("value"))' ) There is another hint about Strings: In Java you can write the following: java.lang.Integer i = new java.lang.Integer(10); System.out.println("i=" + i); This will cause the Java compiler to automatically call the method "String toString()" of the Integer object to get a printable representation. This method is defined in java.lang.Object and available in any Java class and overwritten by most of the Java classes to create useful output. In C++, you can NOT try the following: java::lang::Integer* i = new java::lang::Integer(10); printf("i=%i\n", i); printf("i=%s\n", i); Both will not print the expected result, because "i" is (from C++'s point of view) just a pointer. Correct code: java::lang::Integer* i = new java::lang::Integer(10); printf("i=%s\n", i->toString()); (Be aware, that i->toString() returns a newly allocated memory block, that must be deleted by the application!) - Arrays: When a method requires or returns an array value, or when a field of a class has array type, you can not just use normal C++ arrays for mapping. Instead, you have to use the special Java Wrapper Classes JavaXYZArray(), where "XYZ" stands for the type of the array components. The reason is, that normal C++ arrays are in fact only pointers to the start of the array and contain no information about their size. An example: You have the following Java method: String[] getStrings(int[] values); The "values" argument requires an array of integers, while the method returns an array of String-Objects. In C++ code, you have to use it the following way: int* c_values = new int[10]; ... // fill int array JavaIntArray* j_values = new JavaIntArray(c_values, 10); JavaObjectArray* j_result = obj->getStrings(j_values); java::lang::Object** c_result = (java::lang::Object**)(j_result->getArrayData()); for (int i=0; i < j_result->getArrayLength(); i++) { printf("string %i: %s\n", i, c_result[i]->toString()); // when you want to use special methods of the String class, // you have to type-case first (see below on details about // type-casting!) java::lang::String* st = new java::lang::String(c_result[i]->getJavaObject()); printf(" length of string: %i\n", st->length()); } Note, that strings as array components are not handled specially (i.e., a string array is always a JavaObjectArray with java::lang::String as array components). Arrays of arrays may work too, but are not yet testet at the moment. TODO: maybe add more documentation here - Static methods or fields: in Java there exist so called static methods and fields. These are methods or fields not tied to a special instance of the corresponding class, but are global for all instances of this class. Here is a short Java example code: public class MyClass() { public static void printBanner(); public static int x=5; } ... MyClass.printBanner(); System.out.println("MyClass.x=" + MyClass.x); ... As you can see, there is no instance of the class MyClass - the methods and fields are accessed directly via the "class declaration". These static methods and fields can not be mapped to corresponding features in C++, so you have to use them in this way: To access a static method or field, you first have to create a dummy C++ object of this class. After that, you can call the static methods and access the static fields of this class by using the dummy object: org::kapott::MyClass* MyClass = new org::kapott::MyClass(wrapperIntern); MyClass->printBanner(); printf("MyClass.x=%i\n", MyClass->x); The argument "wrapperIntern" (declared by include ) ensures, that no real constructor of the corresponding Java class will be called. This is necessary, because some Java classes can never been instantiated (because they have only private constructors). So MyClass can be used only for calls to static methods or static fields. When you already have a "normal" instance of a class and want to call static methods of this class, you can use this "normal" instance without creating the "static only" variant: Java: int x = Integer.parseInt("10"); C++: // static-only: java::lang::Integer* i = new java::lang::Integer(wrapperIntern); int x = i->parseInt("10"); or: // "normal" class instance: java::lang::Integer* i = new java::lang::Integer(1); int x = i->parseInt("10"); But be aware of the difference: With the "static only" variant (created by calling the constructor with "(wrapperIntern)") you can ONLY access static methods and fields. "Normal" class instances (created by using any constructor provided by the Java API) can use static methods/fields AND "normal" object-methods/-fields. - Interfaces: Mapping the Java concepts of single-inheritance and interfaces to the C++ concept of multiple inheritance has been shown to be extremely problematic. The current solution requires the folling coding rules for C++ applications: Example #1: You have a class java.lang.Integer, that implements the interface java.lang.Comparable. One method of this interface is int compareTo(Object o); When you want to call a method declared by an interface on a class implementing this interface, you just can call that method like any other methods of the corresponding C++ class: java::lang::Integer* i1 = new java::lang::Integer(5); java::lang::Integer* i2 = new java::lang::Integer(10); printf("i1.compareTo(i2): %i\n", i1->compareTo(i2)); Example #2: A method or constructor requires an argument of an interface type: java.util.Hashtable(java.util.Map t); From the Java API you know, that the class java.util.Hashtable implements the interface java.util.Map, so you might want to try the following to create a new Hashtable from an existing Hashtable (which is also a Map): java::util::Hashtable* ht1 = new java::util::Hashtable(); ... java::util::Hashtable* ht2 = new java::util::Hashtable(ht1); THIS WILL NOT WORK. The reason is, that the concept of Java interfaces corresponds to C++ multiple inheritance. The wrapper generator does not use multiple inheritance (because of some really really really ugly problems). So instances of java::util::Hashtable CAN NOT be used for arguments of type java::util::Map in C++. But there is a really simple solution: you just have to create a proxy object of type java::util::Map that wraps the java::util::Hashtable object: java::util::Hashtable* ht1 = new java::util::Hashtable(); ... java::util::Map* map = new java::util::Map(ht1->getJavaObject()); java::util::Hashtable* ht2 = new java::util::Hashtable(map); Here you create an instance of java::util::Map - which is not possible in Java, because java.util.Map is an interface. Because of this, the class java::util::Map has a special constructor - instances of interface classes can be created by using the constructor "InterfaceName(obj->getJavaObject())", where "obj" is an instance of any class that implements the interface InterfaceName. The so created C++ object (map) "contains" the original C++-object (ht1), but provides only the API of the used interface class (java::util::Map). The other way around is also possible: Example #3: A method returns an interface type, or a field has an interface type. java.util.List java.util.Arrays.asList(Object[] objs); To use the method Arrays->asList in C++, you can do the following: JavaObjectArray* objArray = new JavaObjectArray(...); // create the static-only proxy for calling static methods java::util::Arrays* Arrays = new java::util::Arrays(wrapperIntern); java::util::List* list = Arrays->asList(objArray); On the object "list" you can now call all the methods defined in the interface java.util.List. But if you know, that the returned List-object is in fact an instance of java.util.ArrayList (which implements java.util.List), and you want to use the API provided by the ArrayList class, you have to create an appropriate object instance first: in Java: java.util.ArrayList arrayList = (java.util.ArrayList)Arrays.asList(objs); in C++: JavaObjectArray* objArray = new JavaObjectArray(objs, len); java::util::Arrays* Arrays = new java::util::Arrays(wrapperIntern); java::util::List* list = Arrays->asList(objArray); java::util::ArrayList* arrayList = new java::util::ArrayList(list->getJavaObject()); CAUTION: When you try this example, it will fail! The reason is, that Arrays.asList() does NOT return an instance of ArrayList - this is just an example for the case, where you know the real class of returned objects, but the method is declared to return an interface type, that the "real" class implements. This is approach is analog to simple type casting, described below. Please be aware, that there is NO TYPE CHECKING when calling these special constructors. When you use incompatible objects when creating interface proxies from class instances or when creating class instances from interface proxies you may get a Java exception ("no such method", "no such field") when creating or using the object. - Type casts: Following example: in Java: Hashtable ht = new Hashtable(); ht.put("x", new Integer(10)); // you put only Integer objects in the Hashtable ... Integer i = (Integer)ht.get("x"); // you know, the result is an Integer object in C++ you should do the following: java::util::Hashtable* ht = new java::util::Hashtable(); ht->put("x", new java::lang::Integer(10)); ... java::lang::Object* o = ht->get("x"); java::lang::Integer* i = new java::lang::Integer(o->getJavaObject()); This is the same approach as for using interfaces: To convert datatypes from the API-specified type to a known "real" type, you may call the constructor of the "real" type with "obj->getJavaObject()" as argument. Theoretically the following code should also work: java::util::Hashtable* ht = new java::util::Hashtable(); ht->put(new java::lang::Integer(2), new java::lang::Integer(10)); ... java::lang::Integer* i = (java::lang::Integer*)(ht->get(new java::lang::Integer(2))); But just type casting the result has not been tested very well and may give strange effects. Maybe this will be reliable possible in later versions of java4cpp. THIS TYPE-CASTING CODE WILL NOT WORK FOR TYPECASTING CLASS INSTANCE OBJECTS TO INTERFACE CLASSES OR VICE-VERSA! YOU HAVE BEEN WARNED. You have to use explicit creation of new objects as shown in examples #2 and #3 above instead. - Exceptions: Each call to a Java method may throw an exception. The wrapper code always checks, whether a call to the JVM caused an exception. If this is the case, a description of the Java exception will be printed out to STDERR, and a C++ exception will be thrown. The argument to the C++ exception is the Java exception object. In later versions of java4cpp there will be some helper functions for analyzing these Java exception objects. At the moment you could try the following, but THIS IS NOT TESTED (I do not even know, if this C++ code is valid): try { o1->callSomeMethod(); } catch (jthrowable exc) { java::lang::Throwable* thr = new java::lang::Throwable(exc); // call methods of java.lang.Throwable to look "inside" the exception } - Memory management: Every object you create must be manually deleted: java::lang::Integer* i = new java::lang::Integer(10); ... delete i; Every object returned by a call to a Java method must be manually freed: // java::util::Hashtable* ht; java::lang::Object* o = ht.get(key); ... delete o; The same holds true for strings and arrays: java::lang::Integer* i = new Integer(10); const char* st = i->toString(); ... delete[] st; delete i; // java::lang::Class *cl = i->getClass(); JavaObjectArray* constructors = cl->getConstructors(); java::lang::Object** constructors_data = constructors->getArrayData(); ... delete[] constructors_data; delete constructors; Also proxy objects have to be deleted manually: // create an static-only object for accessing java.util.Arrays java::util::Arrays* Arrays = new java::util::Arrays(wrapperIntern); ... delete Arrays; java::util::Hashtable* ht = new java::util::Hashtable(); java::util::Map* map = new java::util::Map(ht->getJavaObject()); ... delete map; delete ht; Correct management of memory and java reference counters have not been tested yet very carefully, so there may be problems with applications that run a long time and make exhaustive use of Java objects. This will be improved in later versions of java4cpp. How the code generator and the generated code work -------------------------------------------------- TODO