Java Cookbook:
Porting C++ to Java
Introduction, Basics, Next
Steps, Well-Mannered Objects, Esoterica, Background,
Index
Basics
The following sections take you through the main steps
necessary to convert your program from C++ to Java.
|
Placement is Everything
Before you start porting, first create the directory
structure that you will use. First, figure out what your main package name
is. To get it, take your domain name and reverse the fields. Thus xyz.com
becomes com.xyz . If you have two directories abc and
def for your project, then their packages become com.xyz.abc
and com.xyz.def . Now create directories that correspond to
this structure, and copy all of your sources to the appropriate directory.
The first thing you will notice is that Java does
not distinguish between class interface (declaration) and implementation
(definition) as C++ does. Rename your header file extensions to be .java.
Then take the implementation of each member and copy it in after the declaration
of that member, as if you were doing an inline method in C++.
The second step is to take all of the access keywords
(public, protected, private), and copy them at the front of each of the
succeeding methods and fields that they pertain to. Change the inheritance
syntax to use extends instead of a colon. If you use multiple inheritance,
see Primogenitur Entail.
Finally, break apart each class into a separate file, and at the top
of the class, put your package name, and a list of
imports. These imports should be a list of all the other packages that you
need to access.
Fixing Basic Structure
C++
|
inlined C++
|
Java
|
// file.h
class Foo :: Bar {
public:
int square();
int cube();
private:
int x;
}
// file.c (or .cpp)
int Foo::square() {
return x*x;
}
int Foo::cube() {
return x*x*x;
}
|
// file.h
class Foo :: Bar {
public:
int square() {
return x*x};
int cube() {
return x*x*x;
}
private:
int x;
}
|
// file.java
package com.xyz.abc;
import com.xyz.abc.*;
import com.xyz.def.*;
class Foo extends Bar {
public int square() {
return x*x};
public int cube() {
return x*x*x;
}
private int x;
}
|
Notes
- Java does not have the notion of
friend as in C++. See
Java has no friends for
more information about how to handle this.
- Java 1.1 does have nested classes, though Java 1.1 does not. If you
cannot wait for 1.1, you will have to move your nested classes out to the
top. We suggest using concatenating the names: for a nested class Foo
in a class Bar, use Bar_Foo.
To protect the innocent
Next, you need to change a few names. There are
a few cases where there is a relatively straightforward name change.
Simple Name Replacements
C++
|
Java
|
bool x = true;
|
boolean x = true;
|
Most name differences, however, depend on the context.
Context-Dependent Name Replacements
C++
|
Java
|
// const field
static const int x = 3;
// const method
int doSomething() const;
// character data
char ch = 'b';
// byte data (e.g. short numbers)
char b = 31;
// abstract method
int someMethod() = 0;
// non-virtual method
int someMethod();
// virtual method
virtual int someMethod();
// unknown object (no primative)
void* doAnother() {}
|
// const field
static final int x = 3;
// const method
int doSomething();
// character data
char ch = 'b';
// byte data
byte b = 31;
// abstract method
abstract int someMethod();
// non-virtual method
final int someMethod();
// virtual method
int someMethod();
// unknown object
Object doAnother() {}
|
Notes
Const , in particular, requires very special handling,
and is discussed in detail in Bullet-proofing.
The simplest approach at the start is to change it
to final for any field, and remove it otherwise.
- C and C++ do not distinguish between
char
as a small number or as a piece of character data; in general, though,
it usually corresponds to character data and can be left alone. It is also
discussed at more length below.
- Put
final in front of every method that doesn't contain
the word virtual , then delete all instances of virtual .
There is one complication; in C++, if a method is marked virtual
in a superclass, then it is implicitly virtual in all subclasses.
So, you may need to look at superclasses to see if the method is really
virtual.
- Remove the word inline everywhere. Note that these methods
are final, and will be faster to call than non-final
(virtual) methods.
- Remove the word register everywhere. This is just a hint to
the compiler anyway, and one that is often ignored by modern optimizations.
All lines are busy
Java does not support operator overloading. You
will miss this for about 5 minutes if you are programming in pure Java,
but it is a hassle when converting from C++. First you will need to change
all the definitions.
Here is a sample list of operators that could be overloaded, and some
typical Java equivalent names (there is no fixed set of replacement names;
these are only samples.) If you are porting good C++, then the meaning of
the operator does not deviate from the core meaning; if not, then you should
change the name to correspond to the real meaning (such as append
for +). The yellow items have special notes.
Operator Overload Replacement Names
C++
|
Java
|
+
-
!
%
*
/
^
&
|
~
>>
<<
|
plus
minus
not
remainder
times
dividedBy
bitXor
bitAnd
bitOr
bitNot
shiftRight
shiftLeft
|
|
C++
|
Java
|
||
&&
==
<
<=
!=
>
>=
()
[]
=
++
--
*=...
*
->
|
or
and
equals
isLess
isLessOrEquals
(see below)
(see below)
(see below)
(see below)
elementAt,
setElementAt
assign
increment
decrement
(see context)
getX, setX
getX, setX
|
|
Notes
- % is remainder in Java, not modulo. That is, -3%5 == -3, and not 2.
In C++, it is undefined whether it is remainder or modulo. So since your
C++ code is portable (right?), you never depended on the result with negative
numbers, and you don't need to make any changes. Otherwise, you will need
to change
x%y to (x%y - ((x < 0) ? y : 0))
- Don't bother defining an equivalent to !=. The value should always
be the same as if you called !(a == b), so just replace the call sites
by (!a.equals(b)). You can also do the same for > and >=.
- The parentheses operator differs so much from case to case that you
will have to look at the context to get a good name.
- For the pointer operators * and ->, Java has no real equivalents.
Use getters and setters as appropriate.
- Most home-grown operators will not distinguish between predecrement
and postdecrement, or preincrement and postincrement. If you really make
use of that, you can use the longer names.
- Assignment (and copy constructors) are more complex than other operators.
For more detail, see Difficult Assignments.
- For the index operators, define 2 methods. You will then have to fix
the call sites according to the usage.
Once you have changed all of the definitions, let
the compiler find the call-sites for you to fix.
Replacing Overloaded Operator Calls
C++
|
Java
|
// declaring
bool operator==
(const Foo& other) const;
// using
if (a == b)
a[3] = 5;
x = a[3];
|
// declaring
public boolean equals(Foo other) {
/*...*/
}
// using
if (a.equals(b))
a.setElementAt(3,5);
x = elementAt(3);
|
Giving pointers
Java is touted as having no pointers. In porting
from C++ code, however, you almost want to think of it as the reverse; all
objects are pointers--there are no stack objects or value parameters.
The syntax of the language hides this fact from you, but you have to be
careful, as the following examples show.
Replacing Pointers
C++
|
Java
|
// initializing
Foo* x = new Foo(3);
Foo y(4);
Foo z;
// assigning
Foo* a = x;
Foo* c = 0;
Foo* d = NULL;
Foo b = y;
// calling
x->doSomething();
y.doSomething();
// comparing
if (x == a);
if (y == b);
if (&y == &b);
|
// initializing
Foo x = new Foo(3);
Foo y = new Foo(4);
Foo z = new Foo();
// assigning
Foo a = x;
Foo c = null;
Foo d = null;
Foo b = y.clone();
// calling
x.doSomething();
y.doSomething();
// comparing
if (x == a);
if (y.equals(b));
if (y == b);
|
Important
- Note that assignment of objects does not
assign value; it is the equivalent of pointer assignment. You have to use
clone() to get a new object.
- Similarly, comparison of object with == is a
pointer comparison; you have to use
equals()
to get comparision by value.
- Java does not automatically convert numbers.
Use null instead of zero for a null object. If you are using pointer
arithmetic, click here.
No references necessary
Java also does not have references in the same
way as C++, although they use the term references for normal objects (much
like the conflation of objects and pointer to objects). Most C++ programs
only use them in passing parameters to a method, or getting a return value
back.
Fixing Parameter References
C++
|
Java
|
// input parameter
int method1(const Foo& x);
// Mutable output parameter
int method2(Foo x);
// Immutable output parameter
int method2(int& x);
// usage
Foo x;
z = y.method2(x);
w = x;
|
// input parameter
int method1(Foo x);
// Mutable output parameter
int method2(Foo x);
// Immutable output parameter
int method2(int[] x);
// usage
Foo[] x = new Foo[1];
z = y.method2(x);
w = x[1];
|
Notes
- Input parameters are simple; just remove the const (however, there
is a definite cost to doing this in terms of robustness of your code, see
Bullet-proofing for better approaches).
- Output parameters are more complex. Mutable objects (such as StringBuffer)
can be passed in directly. Immutable objects (such as String, Integer)
are more troublesome. You have three choices.
- Return the value from the method. This probably involves more work
in porting, since presumably the reason you had an output parameter was
that you were already using the return.
- The simplest way--though ugly--is to pass in an array as we did in
the example. Since arrays are always Mutable, you can just get/set the
first value in the array.
- The last way is to create a new class that contains fields corresponding
to the output parameters and return value, and return that. If you make
that new class Mutable, you can also use it as an output parameter &
modify it. This involves more effort, but is somewhat cleaner than the
array method.
Alternative Output Parameters
C++
|
Java
|
// output parameter
int method2(int& x);
// usage
int x;
z = y.method2(x);
w = x;
|
// output parameter
int method2(IntWrapper x);
// usage
IntWrapper x = new IntWrapper();
z = y.method2(x);
w = x.intField;
// new class
class IntWrapper {
public Int intField;
}
|
Return values can also be references. There are two common idioms for
reference returns in C++.
- Return
*this . This method allows chaining, as in x
= y = z;
- Return a reference to an input parameter. This allows use of use of
functional returns without requiring memory allocation. Below is an example
where
method2 fills in x , then returns it for
further use. Since the principal use of this is in handling memory allocation,
there is little need for it in Java, but it may make your porting easier
to leave it as is.
- Return a reference to a static.
All of these idioms can be used in Java, though the compiler may warn
you of problems if you are trying to set a Mutable. In that case, you will
have to supply some of the same techniques as with output parameters.
Fixing References
C++
|
Java
|
// definition
Foo& setX();
// return of ouput parameter
Foo& getY(Foo& Y);
// return static constant
const Foo& getAStatic();
// use
myObject.setX(3).setY(4);
Foo x;
myObject.doZ(myObject.getY(x));
z = x * Foo::getAStatic();
|
// definition
Foo setX();
// return of output parameter
Foo getY(Foo Y);
// return static
Foo getAStatic();
// use
myObject.setX(3).setY(4);
Foo x = new Foo();
myObject.doZ(myObject.getY(x));
z = x * Foo.getAStatic();
|
Notes
- There are a couple of other cases where references might be used in
C++, although they should not occur in good C++ code.
- The method creates a new object on the heap, but then hands back a
reference rather than the pointer. This is a prime candidate for memory
leaks, since there is no way for the original object to know when to toss
the storage. This is a bug in the C++ code, and should be corrected.
- The method returns a reference to an internal element. For example,
it could return
&myCharArray[5] . Although legal, this
can be the source of many nasty headaches, since the results are undefined
if the enclosing storage is altered or moved.
Honest-to-God Arrays
Java arrays are real objects, not just disguised
pointers. Generally you replace pointers used to iterate through an array
by offsets, and the * operator by an array access. Most of these cases will
be flagged by the compiler.
Fixing Arrays
C++
|
Java
|
// initializing
double x[10];
double* end = x + 10;
double* current = x;
// iterating
while (current < end) {
doSomethingTo(*current++);
}
|
// initializing
double[] x = new double[10];
int end = x.length;
int current = 0;
// iterating
while (current < end) {
doSomethingTo(x[current++]);
}
|
Notes
- Both the syntaxes
Foo x[] and Foo[] x work,
though the latter is more Java-like.
- Java arrays can supply you their length, rather than your having to
remember it independently. Wherever possible, use this instead of a hard-coded
length.
- As with fields of an object, the items in an array are initialized
to zero (for numerical primitives), false for boolean
and null for Objects.
Pitfalls
Declaring an array does not create object
to fill an array. This is another place
where objects behave like pointers, not values. Since arrays of objects
are--under the covers--arrays of pointers, they are initialized to null;
not to a list of default-constructed objects. If you want
them to be default-constructed objects, you must set them
yourself!
One other point: although the allocation of static arrays looks similar
to C++, be aware that there are vastly different footprint and performance
implications. Unlike C++, which builds a static table that is loaded in
at runtime, the Java compiler actually generates code to place every
single entry in the array, so a very large table could be a performance
and footprint hit.
Fixing Arrays
C++
|
Java
|
// initializing
Foo x[10];
// initializing
static const int x[] =
{1,2,3,4,5,6,7,8,...
|
// initializing
Foo[] x = new Foo[10];
for (int i = 0; i < x.length; ++i) {
x[i] = new Foo();
}
// initializing
static const int x[] =
{1,2,3,4,5,6,7,8,...
|
Introduction,
Basics, Next Steps,
Well-Mannered Objects, Esoterica,
Background, Index
|