JNI++ Project: Aim and Objectiveslast revision: Tue Jun 13 17:32:26 2000 1. OverviewThis paper describes in short the aim and objectives of the JNI++ open-source project. Since the JNI++ is, at present, not much more than an idea and vision, it takes the only thing already materialized: a simple but working example of couple of native C++ classes subclassed by a Java subclass. However, this example shows both sides of the business
2. JNI Programmer's DreamSuppose a valuable and mature C++ library full of usefull classes designed in an object-oriented way, i.e. prepared for being subclassed to fit the customers needs. Suppose, furthermore, the rather strange but not at all rare situation, that the source code of this library is not avail- or changable. Nethertheless this library is to be included into some nice Java project. And this is how the programmer might dream about how to do such job:2.1. The Example's Native Class'es DefinitionsThese are the definitions of the classes contained in the sample C++ library:class Bar
#ifndef Bar_h
#define Bar_h
/*
{group:Native}
Summary:
sample C++ superclass for demonstrating Java/C++ subclassing with JNI++
Purpose:
This simple C++ class is part of the JNI++ introductory example.
Note: The main task of Bar in the JNI++ context is to act as superclass of C++ class Foo
which, in turn, is subclassed by the Java class SubFoo. For this purpose,
it provides the virtual bar() method, which is overriden by the Java subclass SubFoo.
*/
class FOOBAREXPORT Bar
{
public:
/*
Summary: This constructs a Bar instance
Parameters:
message - a text message
Purpose:
Initializes the data member of Bar. Class Bar stores the message passed to its
constructor in a private member variable.
Note:
In the JNI++ example this is used for demonstrating calls to the C++ superclass
constructor through a class hierarchy during creation of a Java subclass including
parameter passing.
*/
Bar( const char * message );
/*
Summary: Destroys the instance.
Purpose:
The destructor frees the stored message
*/
virtual ~Bar();
/*
Summary:
Member function printing some messages
Parameters:
message - a text message (string passed from Java to C++ to C++)
object - a Bar instance (object passed from Java to C++ to C++)
Returns:
the object passed as parameter.
Purpose:
A call to this virtual function writes a line to the standard output
containing the message passed and the messaged stored in the object passed.
Then this object is returned as result value to bar's caller.
Since bar() is a virtual function, the subclass's override of bar() gets
called, if any.
Note: In the JNI++ example this is the key member: a virtual function overridden
by some descent Java subclass.
*/
virtual Bar * bar( const char * message, Bar * object );
/*
Summary:
Member function printing some messages
Parameters:
message - a text message (string passed from Java to C++ to C++)
object - a Bar instance (object passed from Java to C++ to C++)
Returns:
the object passed as parameter.
Purpose:
A call to this function writes a line to the standard output containing
the message passed and the messaged stored in the object passed.
Then the virtual(!) function bar is called with message and object propagated
as arguments. Finally, the reply of this call is returned to foo's caller.
Note: In the JNI++ example, this is the key member: the virtual function bar()
is overridden by the descent Java subclass SubFoo. Thus, the call to bar()
inside foo() leads to the call of the Java override bar() of SubFoo! This is
the real point of JNI++!
*/
Bar * foo( const char * message, Bar * object );
/*
Summary:
Get accessor for thisMessage
Returns:
the value of the data member thisMessage
Purpose:
This is the usual get accessor for the private data member thisMessage.
*/
const char * getMessage() const;
private:
/* {secret} */
char * thisMessage;
};
#endif
This is a very simple class defining a constructor, a virtual destructor,
a non-virtual function foo(), and a virtual function bar(). The virtual
function bar() is going to be overridden by a Java subclass.
Most of the source is inline documentation, which can be viewed following
this link.
Both functions will be called by the following C++ subclass also contained in the sample C++ library: class Foo
#ifndef Foo_h
#define Foo_h
#include "Bar.h"
/*
{group:Native}
Summary:
sample C++ subclass for demonstrating Java/C++ subclassing with JNI++
Purpose:
This simple C++ class is part of the JNI++ introductory example.
Note: The main task of Foo in the JNI++ context is to act as both, as subclass of the
native C++ class Bar and as superclass of the Java class SubBar. For this purpose,
it hides the foo() method of its superclass. In order to demonstrate passing of
values and objects, the class Foo furthermore provides a couple of members returning
simple and object values, respectively.
Todo: include more accessors for different simple and component types
*/
class FOOBAREXPORT Foo
: public Bar
{
public:
/*
Summary: This constructs a Foo instance
Parameters:
message - a text message
Purpose:
Class Foo forwards the message passed to its constructor to its superclass's
constructor and initializes its member variable.
Note:
In the JNI++ example this is used for demonstrating calls to the C++ superclass
constructor during creation of a Java subclass including parameter passing.
*/
Foo( const char * message );
/*
Summary: Destroys the instance.
Purpose:
The destructor frees the member variable.
*/
virtual ~Foo();
/*
Summary:
Member function calling the superclass's pendant
Parameters:
message - a text message (string passed from Java to C++)
object - a Bar instance (object passed from Java to C++)
Returns:
the return value of the call to the superclass's foo() member.
Purpose:
A call to this non-virtual writes a line to the standard output containing
the message passed and the messaged stored in the object passed. Then a
new object is created with message "moon!" and is stored in the member
variable. After that, the superclass Bar's function foo() is called with
the passed message and the newly created object as arguments. Finally the
Bar::foo()'s reply is returned to the caller as result.
Note:
In the JNI++ example this member hides the superclass's function foo() and
is oberridden by the Java subclass SubJava. Furthemore, it shows how to pass an
object created in the C++ library through a pipeline of C++ and Java objects.
*/
Bar * foo( const char * message, Bar * object );
/*
Summary:
Get accessor for thisBar
Returns:
the value of the data member thisBar
Purpose:
This is the usual get accessor for the private data member thisBar.
Note: In the JNI++ example this member demonstrates how to pass Objects
from C++ to Java.
*/
Bar * getBar();
/*
Summary:
Pass count to and from Java
Parameters:
count - the integer value to pass
Returns:
the value of the count parameter
Purpose:
Pass the value of the input parameter back to the caller
Note: In the JNI++ example this member demonstrates passing of simple
type values as input parameter and return value.
*/
int passCount( int count );
private:
/* {secret} */
Bar * thisBar;
};
#endif
This native C++ class is derived from the above mentioned native class Bar().
It, too, has a constructor and a virtual destructor. Furthermore, the non-virtual
functions foo() and getBar() are provided. These functions will be called
by a Java subclass.
Note that the shown private member does not at all affect the mixed language subclassing job to be undertaken. It is a weakness of C++ and Java that private members are exhibited to clients in the class definition. That's why it is not included in the online documentation. 2.2. The Example's Java SubclassAnd this is how a Java programmer might expect how a subclass of the C++ class Foo should look like and how the virtual functions of C++ class Foo and its superclass Bar are overridden:class SubFoo
// Purpose:
// package for testing the JNI++ FooBar example
package TestSuite.FooBar;
import FooBar.*;
// Summary: Java subclass of the native C++ class Foo
//
// Purpose: This class is an example of how to override
// virtual functions defined in native C++ superclasses
// by Java methods. Note that from the Java programmer's
// point of view there is nothing special about it.
// No information about the native background is available
// and/or necessary. All native stuff is done in Java proxies
// like the Java superclass Foo using JNI.
public class SubFoo
extends Foo
{
// Summary: This is a usual constructor of a Java subclass.
// Parameters:
// message - a message string
// Purpose:
// Nothing special about it. Simply calls superclass constructor-
SubFoo( String message )
{
super( message );
}
// Summary: Prints some messages.
// Parameters:
// message - a message string
// object - a Bar instance
// Returns: the reply of the superclass's foo()
// Purpose:
// This method appears courtesy of the native Foo::foo()
// function which it nicely hides and calls.
public Bar foo( String message, Bar object )
{
System.out.println(" +++ SubFoo.foo(" + message + "||" + getMessage() +
"||" + object.getMessage() + ")" );
return super.foo( message, object );
}
// Summary: Prints some messages.
// Parameters:
// message - a message string
// object - a Bar instance
// Returns: the reply of the superclass's bar()
// Purpose:
// This method appears courtesy of the native Bar::bar()
// function which it nicely overrides.
// Note: In the JNI++ example this Java function gets called from
// a native C++ function via the virtual function calling
// mechanism.
public Bar bar( String message, Bar object )
{
System.out.println(" +++ SubFoo.bar(" + message + "||" + getMessage() +
"||" + object.getMessage() + ")" );
return super.bar( message, object );
}
}
As you can see, the Java programmer don't even realize that the underlying
superclass is implemented in C++. The fact, that SubFoo.foo()
and SubFoo.bar() override superclass methods is determined by
name and signature, as usual in Java as well as in C++.
It is the vision of JNI++ that the Java programmer needs not to do more hand-coding than the above! The JNI++ programmer should do a genuine Java job and must not be forced to care about JNI proxies and the like. To provide for this is the task of 2.3. The JNI++ GeneratorAll necessary code beside the above Java subclass should be generated by an envisioned tool: the JNI++ generator.To confess, the above example is cheating a little. SubFoo is not directly subclassed from the native C++ class Foo. Instead it subclasses a Java proxy for Foo containing all the stuff necessary for calling C++ members from Java via native functions and JNI. And it prepares for callbacks from virtual C++ functions back to Java. But it is entirely generated, so that the Java programmer has the illusion to use the native C++ class Foo directly. To generate such Java proxies for native C++ classes is the task of the JNI++ generator. This generator parses the definition and interface files of the superclasses to be subclassed by Java. Then it generates all necessary code for the seemless integration of the C++ library into the Java world, namely Java proxy classes, JNI interface code, Java/C++ type conversion code, and C++ adapter classes for calls and callbacks between Java and C++. A detailed discussion of the code to be generated to make the example running will be provided in part 2 and part 3 of this paper. 2.4. The Example's Native Class'es ImplementationsAs in the supposed real-world situation the source code of the native class'es implementations will not be provided here. There will be only a functional desciption. Of course it is available: You can download it from the open-source links at the JNI++ home page.Now for the description: class Bar
2.5. The Example's Java Test FrameHere the Java source of a frame application for testing it all together:class main
package TestSuite.FooBar;
import FooBar.*;
public class Main
{
static
{
try
{
System.loadLibrary("FooBarJNI");
System.out.println(" !!! FooBarJNI lib found");
} catch (Error e)
{
System.out.println(" ??? FooBarJNI lib not found");
System.out.println(e.toString());
}
}
public static void main(String[] args)
{
{
Bar a = new Bar("universe!");
SubFoo b = new SubFoo("world!");
Bar c = b.foo("Hello", a);
a = null;
b = null;
c = null;
}
Runtime r = Runtime.getRuntime();
r.gc();
try
{
Thread.sleep(5000);
}
catch( Exception e )
{
System.out.println(" ??? something went wrong ");
}
}
}
And this is the output it produces: +++ example2 loaded +++ SubFoo.foo(Hello||world!||universe!) --- Foo::foo(Hello||world!||universe!) --- Bar::foo(Hello||world!||moon!) +++ SubFoo.bar(Hello||world!||moon!) --- Bar::bar(Hello||world!||moon!)
3. Some JNI++ ObjectivesThis is an unordered list of objectives (to be extended).
|
|||||||||||||||||||||||||