Gem #57: Ada / Java cross dispatching
by Quentin Ochem —AdaCore
Let's get started…
Consider the following API:
package API is type A_Tagged_Type is tagged null record; procedure Print_Me (V : A_Tagged_Type; Me : String); type An_Ada_Child is new A_Tagged_Type with null record; procedure Print_Me (V : An_Ada_Child; Me : String); procedure Call_Print_Me (Str : String; Val : A_Tagged_Type’Class); end API; package body API is procedure Print_Me (V : A_Tagged_Type; Me : String) is begin Put_Line ("FROM A TAGGED TYPE: " & Me); end Print_Me; procedure Print_Me (V : An_Ada_Child; Me : String) is begin Put_Line ("FROM AN ADA CHILD: " & Me); end Print_Me; procedure Call_Print_Me (Str : String; Val : A_Tagged_Type’Class) is begin Print_Me (Val, Str); end Call_Print_Me; end API;
Note that the call in the Ada Call_Print_Me is dispatching, so if I write in Ada:
V1 : A_Tagged_Type; V2 : An_Ada_Child; Call_Print_Me ("V1", V1); Call_Print_Me ("V2", V2);
It will call the Print_Me of the actual object, so that the output will be:
FROM A TAGGED TYPE: V1 FROM AN ADA CHILD: V2
Generating the binding code
As usual, generating the whole binding is done through a simple command:
ada2java api.ads –b test –o ada –c java –L my_lib
Writing the Java code
We now have two classes, test.API.A_Tagged_Type and test.API.An_Ada_Child, both of them regular non-final java classes, with An_Ada_Child derived explicitly from A_Tagged_Type. So we can write an example similar to what we did in Ada:
import test.API_Package; import test.API.A_Tagged_Type; import test.API.An_Ada_Child; import test.Standard.AdaString; public class My_Main { public static void main (String [] argv) { A_Tagged_Type v1 = new A_Tagged_Type (); An_Ada_Child v2 = new An_Ada_Child (); API_Package.Call_Print_Me (new AdaString ("V1"), v1); API_Package.Call_Print_Me (new AdaString ("V2"), v2); }
which will have exactly the same effect. But we can go even further; it is possible to derive the class A_Tagged_Type in Java, and override its primitive. Let’s do that in a class nested in My_Main, such as:
static class A_Java_Child extends A_Tagged_Type { public void Print_Me (AdaString Me) { System.out.println ("FROM A JAVA CHILD" + Me); } }
Note that here, what was an Ada dispatching primitive is now a Java member function. The first parameter of the Ada subprogram, the controlling operand “V”, has been bound to the Java implicit “this” parameter, so that the member ends up having only one explicit parameter instead of two. Incidentally, this kind of construction would not have been possible if the first parameter of the Ada primitive had not been controlling, and ada2java would have warned us about this.
Now we can use instances of this object just as instances of any of its parents, for example:
public static void main (String [] argv) { A_Tagged_Type v1 = new A_Tagged_Type (); An_Ada_Child v2 = new An_Ada_Child (); A_Tagged_Type v3 = new A_Java_Child (); API_Package.Call_Print_Me (new AdaString ("V1"), v1); API_Package.Call_Print_Me (new AdaString ("V2"), v2); API_Package.Call_Print_Me (new AdaString ("V3"), v3); }
And just like that, we have put in place a cross-language-dispatching call between Ada and Java. On the Ada side, the code generated by the binding generator and the AJIS run-time will be able to detect that this last object has a type extended in Java, and that the dispatching call has to be resolved using the Java object.
Compiling and running
As in the earlier Ada/Java interfacing Gems, the compile and run commands on Linux breaks down as follows:
gprbuild –p –P ada/my_lib.gpr LD_LIBRARY_PATH=`pwd`/ada/lib/:$LD_LIBRARY_PATH export LD_LIBRARY_PATH CLASSPATH=`pwd`:`pwd`/java:<your AJIS installation>/lib/ajis.jar:$CLASSPATH javac My_Main.java java My_Main
which displays on the screen:
FROM A TAGGED TYPE: V1 FROM AN ADA CHILD: V2 FROM A JAVA CHILD: V3