template programming

             Quantity (template programming)

A. First Edition
This is all started by an email from Mr. Y who is such a guru in computer science and I really learned something from his 
work. And it is also an assignment from Dr. Grogono who is such a programming language expert and I think one of the most
important course I took in Concordia is from his "principle of programming language". It is not the hardest one. Neither is
it a perfect one for me because when I took it I was completely unprepared for it. As I mentioned in my diary that I wish I
could be given another chance to face it when I was better prepared by exposing myself to more discipline of programming 
language. Naturally this assignment is also a language-related problem which is so interesting that I even stop doing my 
already-piled-up assignment. 
B.The problem
The idea is to use template like this:
Quantity<L,M,T>, the parameters L is for length, M is for mass while T is for time.
eg.
Quantity<1,0,0> is length.
Quantity<2,0,0> is area (square meter)
Quantity<1,0,-1> is speed
Quantity<-3,1,0> is density
Quantity<0,0,0> is scalar
 
since template is checked at compile time, then incompatible match like:
length+ area will not pass the compilation. However area/length will do.
 
The complicated thing is different units of the same quantity:
meter, kilometer, yard, feet are all length, to represent them you can use inheritance.

 

 

C.The idea of program
 

My idea is a straight forward solution which is suggested by the hint in Mr. Y's email. I browsed his source and felt quite

impressed because he is using pure template to solve the "sub-class" problem while most people including professor would

take an inheritance approach. For example, for the Length family there are many non-standard units like foot, inch, mile

etc. How would you represent for them? Should we maintain the same "type-signature" by using "Quantity<1,0,0>" and let

those be derived classes with different class "name". (Here I create a concept of "type-signature" which means its type

represented by template parameter and class name. i.e. Quantity<1,0,0>)

However, I don't like this approach because it seems to me that even the non-standard units has different names and

properties, they don't introduce new methods at all! And what is the definition of class? A combination of data and its own

methods. If there is no new method to be created why should we introduce new classes? So, my solution is to regard those

non-standard units as instance of same class with different property of both name and "ratio" which is its ratio against

ISO standard units like "meter, kilogram, second" etc. For example, an entity of 3.5 feet would be declared as:

template <int L, int M, int T>

class Quantity

{

    Quantity(string name, double ratio, double value);//constructor

};

typedef Quantity<1,0,0> Length;

...

Length q1("foot", 0.913, 3.5);

 

Another interesting thing to me is that I actually try to create dynamic type. You see, in C++, everything must be declared

before be used. Type is also the case. You cannot use a type without declare it first. But what do you think the following?

template<int L1,int M1, int T1, int L2, int M2, int T2>
Quantity<L1+L2, M1+M2, T1+T2> operator*(const Quantity<L1,M1,T1>& q1,const Quantity<L2,M2,T2>& q2)

 

Do you think the new type "Quantity<L1+L2, M1+M2, T1+T2>" is declared before using? It seems to me yes at first glance.

Then just take a while for rendering and you may change your mind. To illustrate this more clearly, let's imagine that I

want to overload "operator *" as member method of class Quantity. Can you do that? Definitely not! Because your template

would be "template<int L, int M, int T>" instead of "template<int L1,int M1, int T1, int L2, int M2, int T2>".

 

The following is an excerpt from my email back to Mr. Y.

...

Sorry for late reply and actually I originally planned to reply when I did finish more work. However, it seems my supervisor is assigning me new research directions. (A more attractive one than previous pure theoretical pattern.) And according to my experience that if I left a half-finished project for more than two days, I will lose passion for it forever. So, better than nothing. I send my solution which is inspired by yours and it is a more straight forward one.

1. My solution is nothing new but purely using (L,M,T) as a type indicator. And for the derived quantity such as foot, seamile in Length, pound, ton in weight, I use a property "ratio" to differenciate them. It is coming from a common knowledge that all unit has an ISO standard unit and all other units has a certain ratio to these standard unit. Since those units along with their standard units all belong to same family, they should enjoy the same "type". So, that's why all length units have a sole type of <1,0,0,> and each individual "Length" sub unit has its own "ratio" property. This makes sense to common programmer and there is no need for inheritance because these non standard units don't introduce new method at all! The only thing they brings is their "name" and "ratio" and even the "standard" unit like "meter, gram, second" also have such properties. They are in effect same as other unit except their ratio is "1".

2. I really hate writing long parameter when using template. For example, if we declare a template like:
template <int L, int M, int T, int UL, int UM, int UT>
void somefun(){...}

When I use it, I have to write "somefun<l,m,t,l1,m1,t1>();". Even you can use some "typedef" trick to hide some of them, still it is not a good idea. So, my idea is to reduce the parameter as many as possible. So, this is another reason I limited parameter of template to "<L,M,T>". However, there is one seemingly impossible obstacle for "operator *" of two quantities. Originally I want to overload operator * as "member method" of my class Quantity. Then it seems impossible since the parameter needs introduce another group of L',M',T', but my class is defined only by L,M,T:

template <int L, int M, int T>
class Quantity
{...};

So, I later found it out that I can only do it in a global function operator overloading with six parameters!

template<int L1,int M1, int T1, int L2, int M2, int T2>
Quantity<L1+L2, M1+M2, T1+T2> operator*(const Quantity<L1,M1,T1>& q1,
                                        const Quantity<L2,M2,T2>& q2)
{....}

However, programmer doesn't even know I introduce three extre parameter when he uses this function like this:
Quantity<1,0,-1> q1;
Quantity<2,0,0> q2;

cout<<(q1+q2)<<endl;

My point is that I maintain my idea that "type" will only need three integer to indicate.

3. Your way using index to indicate units is quite interesting and thoughtful. However, I feel a bit difficulty in understanding it and I believe many people feel the similar way. If a library is supposed to be used by common programmer, it should follow the common sense of common people. And I feel people will find easier to follow my idea. This is only my personal view and welcome your comments.

4. I must admit I underestimated the trouble of "name" resolve. You can see I spent most of my time to design a good way for a better name representation. And eventually it is a half-finished work. My idea is to let "Quantity" class update its name automatically when operator * or / is invoked between different type of units. i.e. Speed.name="meter/second", Time.name="second". (Speed*Time).name="meter". However, when units have name of square or volume, like "meter.meter/second", "kilogram/meter.meter", the parsing becomes quite complicated. (At least I haven't found a straight forward way.) I have listed all the subsets of three unit in its first exponent. But I give it up.

...

And here is some of his comments:

It seems you have spent some time reading my stuff, thanks a lot.
 
I understand you want to distingush different units of the same quantity with different instances instead of classes.
 
However this is not what a library is supposed to do. The user programmer has to give the ratio and name to each none standard unit, if wrong value is given, the compiler has no idea. This is convenient for the library provider, but not the user programmer.
 
Nobody likes long list of parameters, however when providing a library, the provider's convenience is not the priority. As long as the implementation details are hidden from client programmers, no matter how complicated it is, the correctness and completeness are the priority to judge. A lot of good libraries used much more complicated template parameters, to undertand it is like reading books from Mars. For example a recursive template definition, which is very hard to understand, but extreme powerful. Based on this, there are template constructs like template linked list, arrays, etc. Not every programmer can understand. That's why meta-programming is hard. As a matter of fact, there is a solution of this problem on the web also using templates which is much more complicated than mine, 90% of programmers will give up reading it.
 
About the name problem, for sure it's hard to attack with your solution. Since you count on instance to handle unit. Because it looses the track of units information in the first place. And using a parser is really a bad idea.
 
E.g.
meter * inch, especially for compound units like meter*kg/inch/pound.
 
To be honest, I had the same idea as yours in the begining, but it's even worse than the inheritance one. At least with inheritance the library can provide some predefind units, but this idea can not even do that.
 
In my opinion, library is all for the user's convenience, not the provider's.
 
I really appreciate that you took time thinking about this question, I believe you can benefit from it. I remember Peter Grongno and I had a long debate over my solution, his initial point is also similar to yours, that is my solution looks complicated.
finally he admitted that mine is more elegant than his.
 
His solution has problems like: 1. name problem, (compund units can not be deduced from simple units.) 2. after calculation, the units get lost.
 3.Predefined quantity could be too many.
Even though, his solution is more complete than the one your proposed.
 

And here it goes:

Thanks for your detailed reply.
 
1. I was attempted to argue that more parameters means more possibility of errors and poor performance, then I had to admit that this is a weak argument. However, I do find my points. Your solution requires strictly type matching so that quantity like pounds and kilograms cannot be added together. You are absolutely right that by using compiler to do type checking can prevent careless mistakes in coding. However, all compilers have to compromise in type checking by relexing programmer's laziness with "type conversion". For example, in C++ you can allow string + "a char string" which is not strictly type matching.(This maybe a bad example as "string" may overload operator + for operand of "char string". I am not sure, but you can see the point.)  Such examples are countless. My point is that type checking can be relexed as long as compiler knows there is no possible disater happening. Say, pound and kilogram are different type but they are compatible as long as we overload operator "+" to convert their value by proper ratio, the result is always reasonable.
 
i.e. 1 kilogram+1 pound = 1.453 kilogram
 
2. Another argument is even weaker. My view in your solution is that essentially you are only using the first three integer to represent the type and use the rest three for proper unit name, ratio ect. From purely semantical perspective, type should be only justified by the first three integer. That is why I insist on my type only uses three integers because there are many other possible ways to indicate units and names etc. So, in my opinion even your code indicate that those non-standard units are not really considered as types but some auxiliary sub-types. (This is not a formal argument, please ignore them if you don't like them.)
 
3. Regarding to your essay, I am not quite sure if I correctly understand your point. If no, please let me know. My understanding is you are attacking the inheritance model in which derived class will inherite all method from its parent class even though it doesn't need them. And this problem has long been discussed by both programmers and language designers. And that is why interface is introduced into OOP. Interface allows parallel classes share same method without inheritance. i.e. class Bird and Plane have no connection in inheritance and even don't share same ancester. However, we wish they all have a "fly" method, then we design to let them both implement "fly" interface. In your example, if class B,C, D  only are interested a sub set of events handling methods, say handleEventW, handleEventX etc., we can let them to implement interface "handleEventW", "handleEventX" and they are not necessary to inheritate from class A. Of course my design is to assume all "handleEvent" methods are defined as interfaces. Do I understand your question correctly?
By the way, your method does have advantages like compile-time type checking and performance gain by using early binding. As for interface, in some cases it can only be check at run time which is slow and tricky even though we may gain some benefits of flexibility.
 
4. I must say I am a little bit envy you that you are using visual2003 because I have been searching for this kind of "member function pointer" callback for quite some time in VC++6.0 before I was given an authority anouncement that it is almost impossible unless some fancy "function object" are used.
http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.1
 
It is nice and your essay did push me to try to migrate from VC6 to VC2003. Thanks for that.
 
I am sure you have different view on above, please let me know and I am really happy to discuss with you.

 

And here it comes:

For some unknown reason, your reply was forwarded to my junk mail folder, so I just found it out. Hope this will not happen.
 
As for your first point, my library can do kilograms+pounds, if you read my main() carefully, you can find similar examples demonstrated in there.
 
I did use the last 3 template parameters for sub type, because they are different from the standard unit anyway. In this way, when a programmer gets a object in that type, he knows exactly what he is given, without subtype, they just know they have generic stuff, but no further detail.
 
For your third point, I wasn?ft really talking about too many methods carried to the derived classes. Callback has been a very important technique in programming for a long time, however there are various ways to implement it.
 
In my essay, the class ?glistener?h is the interface, not class A, maybe you mistook. A, B, C have to inherit from it. If there are 100 virtual functions in ?glistener?h, then A, B, C have to implement all of them although they are only interested in several.
 
Interface is the major way that Java can use for callback, which is a pure inheritance solution. To solve the problems mentioned above, there are wrapper classes called adaptors provided, basically they implemented all the virtual functions with empty bodies. Then A, B, C inherit from these adaptors other than the interfaces. Generally, it?fs like my ?glistener?h, but all the functions were implemented without a line of code. I wouldn?ft call it an elegant solution, more or less like a workaround.
 
In C++, there are so many choices for callback implementations: function objects, function pointers, interfaces, function pointer mixed with template. That?fs one of the reasons C++ is still the most powerful language.
 
Java could have another type of implementation: reflection. But it?fs not type safe, and I haven?ft seen anybody use it.
 
My essay didn?ft mean to attack inheritance and credit template, instead I just wanted to show the aspects they are suitable for, and their collaboration. Language like java doesn?ft provide constructs like function pointers, so inheritance was overused (or misused). This is one of the reasons I put this example in my essay.
 
This week an observation surprised me, that is we found that JDK 1.5 starts supporting template. You can use template in Java now! Maybe SUN has realized that Java had to find a way out of the threats of C++ and .NET. I haven?ft taken a close look at what java?fs template can do, but for sure it?fs very similar to C++.
 
Visual C++ 2003 has a very good compiler, you should have changed long time ago.
Libraries like boosts count on template, you have to get 2003 to use it.
 
I recommend you to take a look at boosts, it will give you more ideas how powful template could be. An example is the smart pointer provided in boost. We know a base class has to make its destructor virtual, otherwise destructors in the derived class can not be called properly with polyphism. However if you use smart pointer, this rules no long holds, it can do the right thing for you.
 
In one word, with template, operator overloading, function objects, C++ can provide the programmer a totally different syntax, which means the programmer can change C++ to a totally different language without chaning its compiler. This is the beauty of this language.
 
You are welcome to post our discussion on your website, no objection.
 

 

 
D.The major functions
 
There are some assumption in my program:
1. When two quantity is doing operation of + or -, the type is guaranteed by type checking at compile time. 
2. When two different category quantity are doing operation * or /, there is no limit on type consistency which means 
different type of quantity can multiply or be divided. i.e. weight/area, power*length. However, the final result type will
be decided by left-hand-side operand if two operands are different "sub-classes". For example, 
inch* foot = inch.inch
And the value would converted appropriately according to its ratio.
3. The instance name should be input using delimiters of "." or "/". 
i.e. Power q1("meter.kilogram/second", Ratio(1,1,1), 3500);
My program tries to maintain the name after operation "*" but it has big limit to first order of units. For example, units
like "pound/foot.foot" cannot be handled! I really feel hopeless and run out of time on this kind of coding. 
 
E.Further improvement
It cannot be run on VC++6 and you need to run it on "visual2003".
F.File listing
1. quantity.h
2. quantity.cpp (main)
 
file name: quantity.h
#include <iostream>
#include <string>
#include <cmath>

using namespace std;

/*
#define ADDITION	0
#define SUBTRACTION 1
#define MULTIPLY	2
#define DIVISION	3
*/

template<int L1, int M1, int T1>
struct QType
{
	
};




class Ratio
{
public:
	Ratio(double l, double m, double t):lRatio(l),mRatio(m),tRatio(t)
	{
	}
	double lRatio;
	double mRatio;
	double tRatio;
};

//int operator()(int value)


template<int L, int M, int T>
class Quantity
{
	friend ostream& operator<<(ostream& os, Quantity<L,M,T> & q)
	{
		os<<q._value<<"("<<q._name<<")";
		return os;
	}
protected:
	void retrieveNames(const string& _name, string names[3], int rank[3], int length)const
	{
		int iStart=0, iEnd=0;
		int current=0;
		
		//find first
		while (current<length-1)
		{
			iEnd=_name.find_first_of("./", iStart);		
			names[rank[current]]=_name.substr(iStart, iEnd-iStart);
			current++;
			iStart=iEnd+1;	
		}
		names[rank[current]]=_name.substr(iStart, string::npos);
		current++;

		while (current<3)
		{
			names[rank[current]]="";
			current++;
		}
	}

	int sortOutOrder(int L, int M, int T, int rank[3])const;


public:
	int _L, _M, _T;
	string _name;
	Ratio _ratio;	
	double _value;

	Quantity(const string& name, const Ratio& ratio, double value)
		: _name(name), _ratio(ratio),_value(value)
	{
		_L=L;
		_M=M;
		_T=T;
	}


	//this is a fancy function which is not so critical but boring
	void resolveName(string names[3])const;
	
	string updateName(int L, int M, int T, string names[3])const;

	
	double convert() const
	{
		double result=_value;
		result*=pow(_ratio.lRatio, L);
		result*=pow(_ratio.mRatio, M);
		result*=pow(_ratio.tRatio, T);
		return result;
	}

	double unconvert(int L, int M, int T, double value) const
	{
		double result=value;
		result/=pow(_ratio.lRatio, L);
		result/=pow(_ratio.mRatio, M);
		result/=pow(_ratio.tRatio, T);
		return result;
	}



};

typedef Quantity<1,0,0> Length;
typedef Quantity<2,0,0> Area;
//typedef Quantity<1,1,1> Energy;
typedef Quantity<1,1,-1> Power;



template<int L, int M, int T>
int Quantity<L,M,T>::sortOutOrder(int L, int M, int T, int rank[3])const
{
	int length;
	//I hate using a lot of "if then else" and I always prefer a linear order 
	//which is safe and clear.
	//first case is three name in order of 0,1,2
	//and we don't have the case of L<0&&M<0&&T<0
	if (L>0&&M>0&&T>0 || L>0&&M>0&&T<0|| L>0&&M<0&&T<0)
	{
		rank[0]=0;
		rank[1]=1;
		rank[2]=2;
		length=3;			
	}

	//three names and order is 0,2,1
	if (L>0&&M<0&&T>0)
	{
		rank[0]=0;
		rank[1]=2;
		rank[2]=1;
		length=3;
	}

	//three name but order is 2,0,1
	if (L<0&&M<0&&T>0)
	{
		rank[0]=2;
		rank[1]=0;
		rank[2]=1;
		length=3;			
	}

	//three names and order is 1,0,2
	if (L<0&&M>0&&T<0)
	{
		rank[0]=1;
		rank[1]=0;
		rank[2]=2;
		length=3;		
	}

	//three names and order is 1,2,0
	if (L<0&&M>0&&T>0)
	{
		rank[0]=2;
		rank[1]=0;
		rank[2]=1;
		length=3;	
	}

	//similarly we don't have the case of both negative ones, i.e. L<0&&M<0&&T==0
	//here goes the two names and order is 0,1,
	if (L>0&&M>0&&T==0||L>0&&M<0&&T==0)
	{
		rank[0]=0;
		rank[1]=1;
		rank[2]=-1;		
		length=2;	
	}

	//two names and order is 0,2
	if (L>0&&M==0&&T>0||L>0&&M==0&&T<0)
	{
		rank[0]=0;
		rank[1]=2;
		rank[2]=1;//still I need this info
		
		length=2;		
	}
	//two names and order is 1,2,
	if (L==0&&M>0&&T>0||L==0&&M>0&&T<0)
	{
		rank[0]=1;
		rank[1]=2;
		rank[2]=0;
		
		length=2;	
	}
	
	//two names and order is 1,0
	if (L<0&&M>0&&T==0)
	{
		rank[0]=1;
		rank[1]=0;
		rank[2]=2;
		
		length=2;
	}

	//two names and order is 2,0
	if (L<0&&M==0&&T>0)
	{
		rank[0]=2;
		rank[1]=0;
		rank[2]=1;
		
		length=2;		
	}

	//two names and order is 2,1
	if (L==0&&M<0&&T>0)
	{
		rank[0]=2;
		rank[1]=1;
		rank[2]=0;
		
		length=2;
	}

	//one name only
	if (L>0&&M==0&&T==0)
	{
		rank[0]=0;
		rank[1]=1;
		rank[2]=2;
		
		length=1;
	}

	if (L==0&&M>0&&T==0)
	{
		rank[0]=1;
		//the rest order is meaningless
		rank[1]=0;
		rank[2]=2;
		
		length=1;
	}
	if (L==0&&M==0&&T>0)
	{
		rank[0]=2;
		//you should ignore these orders
		rank[1]=0;
		rank[2]=1;
		
		length=1;
	}
	return length;
}


template<int L, int M, int T>
string Quantity<L,M,T>::updateName(int L, int M, int T, string names[3])const
{
	string result="";
	int rank[3];
	int length;
	int temp;
	length=sortOutOrder(L, M, T, rank);

	result=names[rank[0]];

	for (int i=1; i<length; i++)
	{
		switch (rank[i])
		{
		case 0:
			temp=L;
			break;
		case 1:
			temp=M;
			break;
		case 2:
			temp=T;
			break;
		}
		if (temp<0)
		{
			result.append("/");
		}
		else
		{
			result.append(".");
		}
		result.append(names[rank[i]]);
	}
	return result;
}


template<int L, int M, int T>
void Quantity<L,M,T>::resolveName(string names[3])const
{
	int rank[3];	
	int length;
	//I hate using a lot of "if then else" and I always prefer a linear order 
	//which is safe and clear.
	//first case is three name in order of 0,1,2
	//and we don't have the case of L<0&&M<0&&T<0
	length=sortOutOrder(L, M, T, rank);
	retrieveNames(_name, names, rank, length);
}


template<int L, int M, int T>
Quantity<L,M,T> operator+(const Quantity<L,M,T>& q1, const Quantity<L,M,T>& q2)
{
	string resultName=q1._name;
	Ratio resultRatio=q1._ratio;
	double resultValue=q1._value+q2.convert();
	Quantity<L,M,T> result(resultName, resultRatio, resultValue);

	return result;
}



	

template<int L1,int M1, int T1, int L2, int M2, int T2>
Quantity<L1+L2, M1+M2, T1+T2> operator*(const Quantity<L1,M1,T1>& q1,const Quantity<L2,M2,T2>& q2)
{
	string resultName;
	string names1[3], names2[3];
	Ratio resultRatio=q1._ratio;
	double resultValue=q1.convert(), otherValue=q2.convert();
	
	q1.resolveName(names1);
	resultName=q1.updateName(L1+L2, M1+M2, T1+T2, names1);
	
	resultValue*=otherValue;
	resultValue=q1.unconvert(L1+L2, M1+M2, T1+T2, resultValue);

	Quantity<L1+L2, M1+M2, T1+T2> result(resultName, resultRatio, resultValue);
	return result;
}


template<int L1,int M1, int T1, int L2, int M2, int T2>
Quantity<L1-L2, M1-M2, T1-T2> operator/(const Quantity<L1,M1,T1>& q1,const Quantity<L2,M2,T2>& q2)
{
	string resultName;
	string names1[3], names2[3];
	Ratio resultRatio=q1._ratio;
	double resultValue=q1.convert(), otherValue=q2.convert();
	
	q1.resolveName(names1);
	resultName=q1.updateName(L1-L2, M1-M2, T1-T2, names1);
	
	resultValue/=otherValue;
	resultValue=q1.unconvert(L1-L2, M1-M2, T1-T2, resultValue);

	Quantity<L1-L2, M1-M2, T1-T2> result(resultName, resultRatio, resultValue);
	return result;
}



file name: quantity.cpp
#include <iostream>
#include "quantity.h"

using namespace std;

int main()
{
	string names[3];
	string powerName="meter.kilo/second";
	string footName="foot";
	Power q1(powerName, Ratio(1,1000,1), 1.0), 
		q2((string)"foot", Ratio(0.91,0,0),10.0);

	Length q3(footName, Ratio(0.91,0,0), 1.0);



	//cout<<q1<<" + "<<q2<<" = "<<(q1+q2)<<"\n";
	q1.resolveName(names);
	

	cout<<q1.convert()<<endl;

	cout<<(q1*q3)<<endl;

	cout<<(q1/q3)<<endl;

	return 0;
}
 
 
The result is like following: 
 
How to compile?
1. first compile "idl" file by "idlj -fall Bank.idl".
2. Second compile server and client by placing server and client file into folders generated by "idlj".
javac ./BankServer/BankServer.java
javac ./BankClient/BankClient.java
 
How to run?

How to run orb:

start orbd -ORBInitialPort 2345

(And I presume this name server is running on a host name "argentina.encs.concordia.ca", see below explanation in server.)

How to run server?

Originally I plan to let all server to register its host name in "name server". However, the naming context is very complicated in CORBA that there is difference between "transient and persistence". I use the later one and all name context cannot be properly unbund. so, I give it up after a couple of days trial by hardcoding all host name in "BankServer". See below in "BankServer".

public static String branchHostNames[]={"chile", "uruguay", "argentina"};

So, the host name of server "Branch100", "Branch101", "Branch102" must have these three name as host name.

 

to run server "branch100" you have to run this server at a host name of "chile.encs.concordia.ca":

java BankServer.BankServer -ORBInitialHost argentina.encs.concordia.ca -ORBInitialPort 2345 100

 

to run server "branch101" you have to run this server at a host name of "uruguay.encs.concordia.ca":

java BankServer.BankServer -ORBInitialHost argentina.encs.concordia.ca -ORBInitialPort 2345 101

 

to run server "branch102" you have to run this server at a host name of "argentina.encs.concordia.ca"

java BankServer.BankServer -ORBInitialHost argentina.encs.concordia.ca -ORBInitialPort 2345 102
 

How to run client?

java BankClient.BankClient -ORBInitialHost argentina.encs.concordia.ca -ORBInitialPort 2345
 

 

 
 
				back.gif (341 bytes)       up.gif (335 bytes)         next.gif (337 bytes)