Translation Guidelines
From MediaWiki
Understand C++ code sometimes is very hard. The difficulty comes from the fact that C++ programmers are free to do almost whatever they wish regarding how to organise source code, assign synonyms to anything via macros, templates and typedefs... oh, well the list of tricks may be exhaustive... :/
This article is organized like this:
- Must know: things you must know and follow from day one in order to avoid further headaches.
- Recommendations: things you need to know or be aware of. We also try to eradicate some misconceptions or bad practices.
- Strategy: explanation about what the translation task is and how to tackle the problem from a pragmatic and effective point of view.
- Techniques: dive deep into the problem of translating from C++ to Java, focusing on specific needs JQuantLib has.
Must know
- Translate from QuantLib v0.9.7
- First understand the strategy you need to employ during translations, after move on to translations themselves. This may look a waste of time, but this is only a one-time waste of time which will save you a lot of time later on!
- Communicate: use our IRC channel and our mailing list. Learn from others. Don't waste your time trying to guess things which can be easily answered by other developers. See: http://www.jquantlib.org/index.php/Support
- Coordinate: when working in groups, coordination is a key factor of success. Yes: we are still talking about communication.
- Be very careful (even passionate!) with our most important asset: our code. It must be correct, accurate, well documented and perform well.
Recomendations
Trunk must compile, always
Never commit to Subversion code which does not compile. Before commiting, please run Maven from the command line in order to make sure that everything compiles properly.
mvn clean verify
All test cases should pass, ideally
Before commiting to Subversion, please verify if all test cases pass. Maybe a change you've just done is inserting a regression to stable code, which is very bad :(
Ideally, all test cases should pass. This is not a requirement, but this is an aim we should pursue all the time in order to guarantee a minimum level of quality assurance. Remember that JQuantLib is used to perform calculations which may be used in financial transactions. So, correctness and accuracy are critical factors of success.
mvn clean verify
Subversion is not a backup medium, never
As you are probably very well aware of, Subversion is a source control system, which means it was designed; to keep track of changes to source code, to group files by revision, and tag them coherently in order to establish releases of software. Subversion is not intended to be a backup system or backup medium. If you commit your temporary work to SVN, you will be messing up the source code and creating inconsistent svn versions in a branch. In the event that you do not get a chance to complete your work for a period of several days/weeks/months the inconsistent state of the temporary backup code you commited will be released.
If you need to make backup copies, do it locally using your computer or using other resources.
Label code as tested or released
Currently, all development is being done on the trunk. This means that code not ready for release must be marked as such, otherwise users eventually will be calling non-released APIs. JQuantlib is a library, and as a library it's public APIs should change very rarely because changes to existing APIs cause users of JQuantlib to change their software systems. There's a specific thread further below about this subject.
Communicate, communicate and communicate
The most important cause of project failures is miscommunication. Projects rarely fail due to technical issues.
In general projects fail because people don't understand ...
- what they are doing;
- why they are doing certain things;
- what needs to be focused and what needs to be discarded;
- when certain tasks are needed and why.
All these concerns can be easily solved if people communicate with each other :)
It's easy: simply post your question to our mailing list of (even better!) participate in our IRC channel.
http://www.jquantlib.org/index.php/Support
Beware of cultural aspects
Understand that people from different countries have different cultures and it may be a concern when several people around the world participate on open conversations, like mailing lists and IRC channels. So, etiquette and respect are key factors in order to promote and keep a healthy work environment.
Saying "no"
- In certain asian cultures it may be offensive to say "no" and may be humiliating to hear "no" from someone;
- North Americans say "no" very incisively, straight away, without any problems;
- British avoid saying "no". They simply don't say anything at all or simply disappear.
- Brazilians say "no" only after giving a very long explanation of reasons, asking your pardon for that, etc, etc, etc.
- Italians say "no" sounding like if they are imposing their opinion with force and establishing territory.
So, when you are in a chat and you have to say "no" or variations of it, like these ...
- you cannot do that
- you've done something wrong
- your idea is incorrect
- your idea cannot be accepted at this time
... beware of these recommendations:
- be very cautious, very polite and make sure you know how to communicate what you have to say without disturbing the conversation and
- remember that you can change your mind in the future for whatever reason: give yourself a chance to regret with style! :)
Asking questions
- In certain cultures, it is shameful to ask questions: it demonstrates inferiority or not suitability for a task.
- Brazilians ask questions and answer questions all the time, usually more than it's reasonable to do.
- North Americans like direct questions. They get confused if you talk too much and ask a question in the end: They think you've asked several questions just because you employed several sentences and several ideas before finally coming up with a question.
- Italians talk with hands and use corporal expression a lot. Their questions can be intriguing without a webcam. :)
Recommendation: ask questions every-time you have a question, trying to be direct and straight to the point, but providing some additional information because the other person cannot read what's in your mind. Avoid slang or anything which involves personal or cultural aspects. You can relax after you established good cooperation to the other party and problems due to misunderstandings are unlikely to happen.
Giving answers
- North Americans provide very objective and many times too quick answers which usually don't help much.
- British may prefer not to provide any answer at all. Expect silence from Britons and you will not get disappointed.
- Brazilians in general provide answers inside a context in order to give a real life example of it.
- Italians may give a non-polite answer you wouldn't like to hear. The definition of politeness is relative.
- Nordic people may include a link to a scientific paper full of theory and no quick/practical examples at all.
- Asians may employ an exaggerated brevity and a weird grammar (if any) which may be very difficult to decipher.
Recommendation: use brevity but not too much; going straight to the point. Complement with a polite offer for more information, like this: Please don't hesitate to ask me further questions about it. or simply: Is it clear enough?. Many times we don't know exactly what the other party knows about a certain subject: it may be a waste of our time if we talk too much; it maybe non-effective if we dash the communication with a too brief answer.
Respect Time
- Remember that all members of this group are volunteers working on a best effort basis;
- Remember that your time is valuable: post questions to our mailing list instead of trying to guess things.
- Remember that time of others is valuable: help other contributors and share experiences.
Recomendation: Be confident and be positive: a positive attitude is able to overcome big issues!
Strategy
What do you do when you face a class with dozens of methods belonging to a tangled object model?
- Divide the problem into smaller pieces. This is frequently the only feasible approach.
Start from the beginning: create a very simple declaration of a Java class relative to that C++ class and put our copyright notice on top of it. Copy the original C++ copyright notice after our copyright notice.
If you think that you need to translate two or more classes in parallel due to their similarily, hierarchy, coupling or any other reason, the first step is really identify what their names must be and put the copyright notices on the top.
This is not very much but it is at least the first step done, done once and done properly and you will never have to return to the copyright notice again.
- Do it right on your first attempt and you will never have to do it again.
A class or an interface?
The next step is amend the declaration of your Java class in order to extend the correct classes and implement the correct interfaces. Another important point to consider is how the current class you are working on participates in the object model. You need to consider that Java does not provide multiple inheritance. Depending on the way this class participates in the object model, you'd better create an interface and change this class in order to implement that interface. There are scenarios where it's clear we need an interface because the original C++ code looks pretty much like an interface or because it's explicitly stated in the original comments. When we start to understand well what is happening with the original C++ code, it starts to become clear whether an interface should be used or not. The rule is more or less like this:
- Take your best efforts to determine if you need a class or an interface. If after enough analysis you are still in doubt, start using a class and continue translating the extended classes, etc. As your translation progresses, it will become clear in your mind whether you need to re-factor your code or not.
Copy the header file
Copy the C++ header .hpp file definitions into your Java class. References (the & symbol) and typedefs in the C++ sources, will give you a lot of compiler errors in Java. You may become confused with so many compiler errors.
- You can do some basic housekeeping simply replacing & by "" (nothing). You can also substitute -> by "." (period). You can also substitute keywords like virtual and const, mutable by their "equivalents" in Java.
C++ Java Comments const final On field, method and parameter modifiers const @ReadOnly See this article virtual abstract In Java, all methods are virtual by default whilst in C++ all methods are non-virtual by default. The keyword virtual (C++) can be easily confused with abstract (Java) because these methods need to be obviously overriden by descendent classes when you are coding in C++. Observe that not all virtual methods are abstract!. So, pay special attention to virtual keyword. A better approach would be simply copy/paste Java skeletons generated by our cpp2j Python script, which aims to help on C++ to Java translation. mutable This is another trick of C++ language which probably leads to badly designed object models. This excellent article explains in detail and gives several examples CompoundFactor @CompoundFactor double Covariance @Covariance double Diffusion @Diffusion double DiscountFactor @DiscountFactor double Drift @Drift double Expectation @Expectation double Real @Real double Rate @Rate double StdDev @StdDev double Time @Time double Variance @Variance double Volatility @Volatility double
Care must be taken with the original C++ code. Remember that QuantLib is very well designed and implemented. Be sure that there are always good reasons for the tricks they use in their code. If you are not sure of what is happening, simply
- Put "//TODO: code review :: blah, blah blah" in your Java code so that you can return later and fix possible issues when you have better understanding of the entire scenario.
JQuantlib is a direct translation of Quantlib in pure Java code. Because of that, it is important that we translate the QuantLib code directly without taking any un-needed liberties
Access modifiers for fields and methods
You can have a bad time trying to identify if fields and methods should be private, public, protected, final, abstract or virtual. It happens usually because the C++ code is disjoint, I mean: definitions in one file and implementation in another file, usually... As I said, the C++ programmer is free to do whatever mess they wish to do.
Properly understanding the visibility and protection of fields and methods is much more important than it seems at first glance. This understanding is critical for your translation as it will help you visualise the object model and interdependencies between classes. Understanding properly, you will be able to eventually propose a slightly different object model that fits better in the way Java applications are built. The general rule is:
- Copy the header file into your Java file and start translating from there. The header file will give you precious information about visibility of fields and methods.
After you copied the header file, translate the code and reorganise it in order to explicitly expose the visibility and protection of fields and methods. Organise methods and fields accordingly, keeping all public final methods together, for instance. The general rule here is:
- Eradicate the mess from the beginning. The earlier you organise your code, the easier it will be to understand the original C++ code and produce high quality Java code.
Below you can see an example how a C++ header file was translated and reorganised.
//
// private static final fields
//
private static final String NON_NEGATIVE_RATE = "rate cannot be negative";
//
// private final fields
//
private final double rate;
private final double time;
//
// private fields
//
private double max;
private int counter;
//
// public constructors
//
MyExample(double rate, double time) {
}
//
// public final methods
//
double final getRate() {
}
double final getTime() {
}
//
// TODO: code review
// It's not clear what these methods do and how they are used
//
private final getSuspiciousThing() {
}
Sometimes is not that easy to do this initial step because a C++ header file may contain implementation code as well, not only declarations.
Again, focus your efforts on organising the code properly. It will help you create small clusters of code without compiler errors in the beginning of your translation. As your translation goes further, your well organised code will help you isolate small clusters of code which do not compile yet.
Sometimes, due to the use of typedefs or templates probably, you will be really confused about a fields or method. You may even be in doubt as to whether you have a field or a method in your hands. It is not rare to be in doubt as to whether it is a local element or something shared from some base class, or something that will be defined in an extended class.
- In case of doubt, assign "private final" to a field or method. Don't worry, the compiler is your friend, in the future it will tell you whether you where wrong or not.
Translate constructors first
First Start translating the constructors. This is more or less natural because they appear at the top of the source code in general. Not only for this reason, but...
- Because we need to understand what the original C++ code does, it's critical to understand what constructors do and how they are called by extended classes and other classes.
It's a good opportunity to reinforce your understanding of the object model. For instance, if you believed that this class is extended from a certain base class, but then you realize that there's a pointer around and fields are being accessed via this pointer. Imagine that, after some analysis, you discover that this class not only extends a certain base class, but also has an association or composition to objects of that same base class. obviously, this understanding is critical for your translation.
To translate a constructor, follow the suggested sequence:
- Copy its code from the C++ implementation .cpp file into the previous skeleton constructor you created when you first organised your code.
- Substitute typedefs by JSR-308 complyant counterparts.
Techniques
Using final on formal parameters
The keyword "final" states your intention of never changing a value of a variable, does not matter if it is an Object or a primitive type. Unless for a very good reason, all constructor parameters and method parameters should be final, even when they are primitive types. When someone reads your constructor or method declaration, it's crystal clear that such variable will not be changed; it's not necessary to analyze any code to come up with such conclusion. Example:
public FixedRateCoupon(double nominal,
final Date paymentDate,
double rate,
final DayCounter dayCounter,
final Date accrualStartDate,
final Date accrualEndDate,
final Date refPeriodStart,
final Date refPeriodEnd) { ... }
We know immediately that only parameter rate is changed inside the method. We don't need to browse the entire method. But...hey!... it's a fixed rate coupon isn't it? ... why do we need to change a rate of a fixed rate coupon? Seems nonsense. Is there a good reason for it? Is this code really correct? Hummm... we'd better do a code review here because something seems to be wrong.
See also: Configuring Eclipse for Defensive Programming
Using final on fields
The keyword "final" states your intention of not never changing a value of a variable, does not matter if it is an Object or a primitive type. Certain fields, once initialized, will never change and you should inform such behaviour when you declare such field. When someone reads your field declarion, it's crystal clear that such variable will not be changed; it's not necessary to analyze any code to come up with such conclusion. Example:
private final DayCounter dayCounter;
private final int fixingDays;
private final double spread;
private final boolean isInArrears;
protected final double gearing_;
protected final InterestRateIndex index_;
private FloatingRateCouponPricer pricer;
Enforcing private access methods
In certain circumstances the original QL/C++ code may be very confusing. If you find a situation where you are in doubt about which access modifier should be used for a field, method, constructor, class or anything else not mentioned here, simply adopt "private". Don't worry because in future, if another class needs access to such object, you will discover such situations and you will be able to fix the access modifier. On the other hand, if you declare "public" and it should be "package protected", you will never discover this and you will end up allowing extraneous classes access to your beloved resources which should be properly protected from strangers.
Handling incomplete code
In case you had to comment out some code for any reason or in case you have only partially translated something, remember to throw an exception in order to avoid callers from using incomplete code or using wrong or incorrect results. Example:
final Date today = null; // TODO: how should I obtain today's date???? if (true) throw new UnsupportedOperationException(); ...
Handling code not ready to be released
At the moment we are developing everything in the trunk, in order to reduce time and associated costs related to management of branches. We will adopt branches only when the library reaches a certain level of maturity. When you translate new code but it's not yet well tested and obviously not ready for a release, please remember to set the code to "EXPERIMENTAL" and thereby avoid having application code execute your classes. Example:
public MyClass(final int n) {
if (System.getProperty("EXPERIMENTAL") == null)
throw new UnsupportedOperationException("Work in progress");
this.dates = new Date[n];
...
}
Using iterators
JQuantLib offers iterators which substitute pretty well for pointers in order to manipulate elements of arrays and matrices. There are important advantages offered by iterators:
- mimic pretty close the code of QuantLib/C++;
- makes JQuantLib/Java code much easier to understand;
- sometimes JQuantLib/Java code is much neater than QuantLib/C++ code
- avoid unnecessary temporary variables necessary during copies, offering better performance
Below we can see an example extracted from QuantLib/C++:
void CalibratedModel::setParams(const Array& params) {
Array::const_iterator p = params.begin();
for (Size i=0; i<arguments_.size(); ++i) {
for (Size j=0; j<arguments_[i].size(); ++j, ++p) {
QL_REQUIRE(p!=params.end(),"parameter array too small");
arguments_[i].setParam(j, *p);
}
}
QL_REQUIRE(p==params.end(),"parameter array too big!");
update();
}
... and how it became when translated to JQuantLib/Java:
public void setParams(final Array params) {
final ConstRowIterator from = params.constIterator();
for (int i=0; i<arguments_.size(); ++i) {
final RowIterator to = arguments_.get(i).params.iterator();
to.fill(from, to.size());
}
QL.require(from.remaining()==0, "parameter array too big"); // TODO: message
update();
}
Handling macros
A macro defines a block of code should be condiotionally compiled. There's no such thing in Java.
#if defined(QL_EXTRA_SAFETY_CHECKS)
QL_ENSURE(isIMMcode(IMMcode.str(), false), "the result " << IMMcode.str() << " is an invalid IMM code");
#endif
Class Settings is reponsible for mimicking macros:
if (Settings.isExtraSafetyChecks()) {
QL.ensure(isIMMcode(IMMcode.str(), false), "the result " << IMMcode.str() << " is an invalid IMM code");
}
Throwing exceptions when a return value is needed
There's a typical situation which happens when a method returns some value but we decided to call QL.fail() for some reason. This code will not compile because the compiler does not see any explicit return value in the branch where we call QL.fail(). In spite of the fact that we think the compiler should detect that QL.fail unconditionally interrupts execution. We need to remember that AspectJ is able to remove QL.fail() from the bytecode and, in this scenario, the compiler is correct to warn us that no return value was specified.
The solution is simple. Use LibraryException instead:
QL.error("this is a message");
throw new LibraryException("this is a message");
LibraryException, when instantiated, is responsible for notifying the logging system. So, throwing this exception is all that you need.
Writing messages to the console
Classes should never call System.out and/or System.err directly If you need to emit messages, please use methods QL.error(...), QL.warn(...), QL.info(...), QL.debug(...) or QL.trace(...) instead.
Please remove code which makes use of System.out and/or System.err directly, moving this code to our testsuite folder or project jquantlib-samples, according to what looks more convenient.
The only class allowed to call System.err directly (but not System.out directly!) is QL.java, which is responsible for delegating messages to a org.slf4j.Logger class and, if not present, emits logging messages directly to System.err.
Operator overloading
At the moment. several classes which make vast use of operator overloading where already translated and you can borrow ideas from there. Very good examples are classes Array and Matrix.
We also have a Python script which produces Java skeletons from C++ code. This script addresses most of the known cases, if not all.
If you face a situation of operator overloading, the easiest thing to do is to try to find an already produced Java skeleton for the class you are interested on. Please download this file and have a look. This file is produced by a Python script we developed and eventually we can generate another file like this, everytime we improve a little bit our script. For more information, please have a look at cpp2j.
Translating templates
C++ is a multi paradigm language, which filled the gap when pure object oriented languages were invented but not widespread adopted. It allows developers to take advantage of object orientation whilst keep previous investment on code previously not object oriented at all. Java is a step further to the direction of object orientation paradigm and, being closely related to C and C++. However translation of legacy C/C++ code is more or less straightforward, some language constructs are very difficult to translate and one of these are templates.
What templates are?
A very imprecise but fast explanation is: very, very powerful macros. If you have already used macros in C language, you can think that templates are pretty much like macros but better integrated to the language and much more powerful. Said that and not willing to spend more time explaining what templates are and how to use them, we will jump directly to use cases and how to translate them into Java.
How templates can be translated to Java?
Templates can be translated to Java by Generics, in general.
In reality, it is not a mere coincidence that both templates and generics share similar syntax. When Generics were introduced in Java, it happened due to the fact that it would be helpful to have a way to change the behavior of applications via parametrized language constructs.
In Java Generics, you pass a class name as a generic parameter and you are able to apply the same operation to a variety of classes. In addition, you can verify which class was passed as generic parameter and change how your application behave depending on what was received as parameter. When templates are used in the same way, the translation to Java Generics is more or less straightforward.
The problem arises when templates are used as mundane macro processors or even worse... when templates are used as a really powerful macro processor. In this situation, there's no already ready cookbook: it's necessary to understand what the C++ programmers were thinking and model something similar in Java.
What are the main differences between templates and generics?
A more or less elaborate technical, in depth explanation follows:
In the old C world, macros where processed by an external macro processor. In C++, the macro processor was embedded and integrated into the compiler, allowing templates (or the revamped macro processor) to have access to internal compiler data structures and radically changing the way the code generator works. In addition, templates keep the old, mundane behavior of a macro processor. The important point to mention is that the compiler back end processor are highly affected by templates.
In Java world, generics were never designed to be a mundane macro processor. In addition, generics have only a minimum interference on Java back end processor.
For the impatient, not willing to become compiler experts, the real world, hands on explanation is:
- In Java, generics allow you to add information to your classes and methods, allowing them to change their behavior at runtime.
- In C++, templates allow you to produce compiled code aiming a certain behavior, at compile time. It means that, in spite of the fact that the C++ developer wrote only one class or only one method, in reality, the compiler will produced more than one class or more than one method.
Operator overloading involving templates
On class Period (in C++ sources) you can see:
template <typename T> Period operator*(T n, TimeUnit units);
which means that you can multiply a template typename T by a TimeUnit and obtain a Period as answer. It means we need to declare T#mul(TimeUnit units).
template <typename T> Period operator*(TimeUnit units, T n);
which means that you can multiply a TimeUnit by a template typename T and obtain a Period as answer. It means we need to declare Period#mul(T n).
The difficulty is not understanding the code, but finding what typename T is.
The C++ compiler is very smart and every time it finds a template type T multiplying a TimeUnit, it "matches" the method declaration and expands the corresponding code. The difficulty (for us, humans) is to discover where these calls appear, in order to identify what can be all possible candidate typenames T for this operation.
In other words: In C++ you only need to provide such declaration once and the C++ compiler will expand the code for every occurrence of a typename T multiplying a TimeUnit. In Java, we need to provide all declarations, for all classes which can eventually need a multiplication by typename T.
So, in C++ ...
template <typename T> Period operator*(T n, TimeUnit units);
... and in Java ...
class A {
public Period mul(TimeUnit units) { ... }
}
class B {
public Period mul(TimeUnit units) { ... }
}
class C {
public Period mul(TimeUnit units) { ... }
}
... supposing obvisiously that we found in the C++ code something similar to:
Period p1 = a*t; // where a is class of A Period p2 = b*t; // where b is class of B Period p3 = c*t; // where c is class of C
Again, it all means that we need to have a very good tool which allows us browse the C++ code easily.
Another approach is providing "some implementations" and add more methods wherever necessary every time we stumble with a situation where such operation is missing. In general, this is the recommended approach because it consumes less time than trying to figure out all occurrences of a certain operation.
Super Type Tokens when the object model is complex
THIS ARTICLE IS INCOMPLETE. This goes straight to some ways to solve the problem, but we lack an explanation which puts TypeToken in context and explains how and when it is one possible solution. Much more still need to be written :/
Consider the following C++ template:
template <class T>
class Klass : public T {
....
}
There is no equivalent for this in Java. The following code will NOT compile.
public class Klass<T> extends T {
...
}
The "conventional way" would be (a) allocate the delegate instance and (b) pass the allocated instance to the class constructor
abstract class SomeAbstractClass {
}
class OneConcreteClass extends SomeAbstractClass {
}
class AnotherConcreteClass extends SomeAbstractClass {
}
class Klass<T extends SomeAbstractClass> extends SomeAbstractClass {
private T delegate;
public Klass(T t) {
this.delegate = t;
}
}
This certainly compiles and comes close to the C++ template mechanism but is not wholly correct because the original C++ sources sometimes is interested on extending OneConcreteClass or AnotherConcreteClass and so on. Also this implementation is open to abuse as seen in the following example:
OneConcreteClass delegate = new OneConcreteClass(); Klass<OneConcreteClass> klass = new Klass(delegate); // This is correct Klass<AnotherConcreteClass> klass1 = new Klass(delegate); // This also compiles but is not desirable
The solution is allow Klass to allocate the class it needs (a class derived from SomeAbstractClass) depending on the generic parameter passed as [generic] parameter. Enter TypeToken. Using TypeToken, you keep the responsibility of allocating the delegate inside the constructor, like this:
class Klass<T extends SomeAbstractClass> {
private T delegate;
public Klass() {
this.delegate = null;
try {
delegate = (T) TypeToken.getClazz(this.getClass()).newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
And now all you have to do is this:
Klass<OneConcreteClass> klass = new Klass();
What is the downside to doing this?
Well one obvious one is this style is not particularly conducive to injection.
Also, if you use some type of factory pattern to create delegates, then that might also need to be reworked.
But all in all this is a much cleaner and correct way of doing things and in addition, using TypeToken keeps JQuantLib API very close to original C++ QuantLib API.
Unary operators and binary operators
In the original C++ code you can see:
Period operator-(const Period&);
Period operator-(const Period& p1, const Period& p2);
... which means that we have 2 operations: the unary minus and the binary minus.
It means that in C++ you can do something like this:
Period p = -p1; // unary operation
Period p = p1 - p2; // binary operation
Observe that, in C++, a unary operator has one parameter whilst a binary operator has 2 parameters. In Java, this is different: we are not overloading operations already known by the compiler, but we are adding methods to classes in order to mimic the semantic we have in C++. We will not be able to mimic the syntax beauty of simply employing a minus sign, but at least we provide something more or less similar.
In Java , they translate to:
// unary minus
public Period negative() {
return new Period(-p.length(), p.units());
}
// binary operation
public Period sub(Period p) {
return this.add(p.negative());
}
Observe that, in Java, a unary operator has zero parameters whilst a binary operator has one parameter. This is like this because 'this' instance is already the first parameter!
Understanding what needs to be done and where
The original class Period (in C++) declares operator* :
template <typename T>
inline Period operator*(T n, TimeUnit units) {
return Period(Integer(n),units);
}
template <typename T>
inline Period operator*(TimeUnit units, T n) {
return Period(Integer(n),units);
}
inline Period operator*(Integer n, const Period& p) {
return Period(n*p.length(),p.units());
}
inline Period operator*(const Period& p, Integer n) {
return Period(n*p.length(),p.units());
}
Let's identify what we can implement immediately in our class Period (in Java). The two methods below we can implement in Period.java :
inline Period operator*(Integer n, const Period& p) {
return Period(n*p.length(),p.units());
}
inline Period operator*(const Period& p, Integer n) {
return Period(n*p.length(),p.units());
}
and they become ...
class Period {
public Period mul(int n) {
return Period(n*this.length(), this.units());
}
}
... and "ideally" ...
class Integer {
public Period mul(Period p) {
return Period(this.intValue() * p.length(), p.units());
}
}
Because Integer (and also primitive type int) are final classes/primitives, we cannot add a specific method "mul" to these classes/primitives which take a Period as parameter. Let's simply forget this item because it's simply not needed when we translate the code: we can simply translate n*p like p*n and we already have a methos which provides p*n for us.
The other 2 methods ...
template <typename T>
inline Period operator*(T n, TimeUnit units) {
return Period(Integer(n),units);
}
template <typename T>
inline Period operator*(TimeUnit units, T n) {
return Period(Integer(n),units);
}
... means that a certain class T needs will have something like this ...
public Period mul(TimeUnit units) {
int n = this.getInteger(); // or whatever similar method
return Period(n, units);
}
... and also means that TimeUnit needs to provide an explicit multiplication involving typename T, like this:
public Period mul(T t) {
int n = t.getInteger(); // or whatever similar method
return Period(n,units);
}
Q&A
Why JQuantLib uses final nearly everywhere?... even for primitive type parameters?
The keyword final means that a variable cannot be changed. It's a conceptual thing. It does not matter whether your variable is a primitive type, an array or an Object. We are simply conveying that the reference to the object, array, or primitive will not be reassigned during its lifetime. Period.
In particular, when final appears in parameters, some people erroneously think that we are trying to guarantee immutability of the variable which was originally passed as reference. This is not completely correct:
- This is correct to think we are really willing to guarantee immutability of the reference to the original object passed as an argument. We are really willing to keep its original assignment (reference) for the lifetime of the method call. When Objects are passed, the called method is able to change its parameters contents by directly changing them or calling methods which are not final in them. It is a good programming practice to avoid this possibility.
- But it is is wrong to think there's such a thing as pass by reference in Java. It's very important to understand that Java never (I repeat: never!) passes arguments by reference: it does not matter if you are passing a primitive type, an array or an Object. This concept is very important to be understood specially by people coming from C/C++ environments. This concept is very well explained at http://javadude.com/articles/passbyvalue.htm and will not be repeated here.
What the hell is those annotations @Time, @ReadOnly, etc all around?
Conceptually "5 pears" are different from "5 apples" and you should not assign the quantity of apples to a variable intended to store the quantity of pears. The upcoming JDK7 will provide means of detecting such conceptual erros. For more information see Strong Type Checking and Providing unmutability to receivers

