【新・言語進化論】アレで使われている言語って何?
第4回:C++からJavaへ分散コンピューティング環境を移植した
著者:NTTデータ 渡辺 出
公開日:2007/11/22(木)
実装したコードとライブラリの問題
続いて、ライブラリに関する問題について解説する。最近はさまざまな言語でライブラリが充実してきている。この充実によって、異なる言語を利用していたとしても、ほぼ同じ名前で同様の機能を備えたライブラリが揃う、といった状況が生まれている。
C++もJavaも共に有名な言語だけあり、世に出回っているライブラリの数も多い。移植時には「似たようなライブラリをすぐに利用できるため簡単だ」と思えるのだが、実際に検証してみると、よく知られているクラスでも微妙な違いがあることがわかる。
その1例がVectorの挙動だ。C++言語で書かれたオリジナルのソースコードにはstd::vectorがコレクションとして利用されていた。移植先のJavaではvectorクラスで代用可能だと思い動作検証を行ってみた(リスト6、リスト7)。
その結果として、Javaのvectorクラスはstd::vectorでの操作とほぼ同様のことが可能であることがわかったものの、微妙に異なる動作をする部分を発見した。
リスト6:C++の場合
std::vector<TestClass> vec;
vec->resize(10);
リスト7:Javaの場合
Vector<TestClass> vec = new Vector<TestClass>(10);
vec.setSize(10);
リスト8:sampleコード(Java bad)
class TestClass<T>
{
Vector<T> vector;
(省略)
void problemMethod(){
vector.setSize(10); //サイズを10に拡張
for(int i=0 ; i < 10; i++){
vector.setElementAt(new T(),i); //コンパイルエラー
}
}
}
リスト9:sampleコード(Java)
class TestClass<T>
{
Vector<T> vector;
Class genericsClass; //Genericsで指定されたクラス
public TestClass(Class cls){
genericsClass = cls; //コンストラクタにてGenericsで指定されるクラスを取得しておく
}
(省略)
void problemMethod(){ //実際はtry〜catch文でnewInstance()を囲う必要があります
vector.setSize(10); //サイズを10に拡張
for(int i=0 ; i < 10; i++){
vector.setElementAt((T)genericsClass.newInstance(),i); //取得してあるClassオブジェクトを使ってインスタンス化する
}
}
}
これらのコードはC++、Java共にvectorのサイズを変更するもので、コードのさまざまな場所で使用している。
「サイズを変更する」という点では同様の動きをするのだが、std::vectorでresizeが呼び出された場合、新規に追加された要素部分にはGenerics指定されているTestClassのデフォルトコンストラクタで初期化されたインスタンスが詰め込まれる。
一方JavaのVector.setSizeではvectorのサイズを変更するのみで、新規に追加された要素部分にはnullが入ってしまう。このように、似たようなライブラリを利用しても内部の挙動が違う箇所があり、しっかりと動作確認を行って代用することが重要となる。
このライブラリによる挙動の違いを単純にJavaに移植したものがリスト8のコードだ。C++で用いられたvectorのコードと同様にT型のインスタンスでVectorを埋めるようにしている。
単純にリスト8のように書いてしまうと、前ページで述べたGenericsの問題と同様のことが起こることになる。
今回は前述のvectorの問題を回避するため、vectorクラスを拡張することにした。具体的にはコンストラクタに型パラメタであるTを引数にとり、インスタンス生成にはClass.newInstance()を利用した。
リスト9のサンプルは説明のために実際の実装部分から割愛した部分があるが、実装の雰囲気は感じてもらえるだろう。実は他にもprimitive型のインスタンス化について細かなテクニックがあるため、詳細については「https://sourceforge.jp/projects/sekigahara/」からソースコードをダウンロードし、net.cellcomputing.himawari.accessory.STLvectorクラスのソースコードを参考にしてほしい。
移植の際は仕様をしっかりと理解する
このvectorクラス以外にも、コピーコンストラクタやgoto文、メモリ管理の有無などさまざまな違いが存在する。このため、これら言語仕様による違いを理解し、代用コードを書く必要がある。このような調査を行い、代用コードを書くことによって、お互いの言語の特徴をより知ることができるだろう。
プログラム言語は「どの言語が優れている」「どの言語は劣っている」と優劣をつけるものではないと筆者は考えている。今回のプロジェクトではマルチプラットフォーム性を重視したため、Javaを選択することとなった。
最近ではRubyやPythonなどの言語も注目されているので、各言語の特徴を理解して自分の使いやすい/用途に合致した言語を見つけることが重要だと考えている。
次回、11月30日公開の最終回では「ゲームコンソールのアレ」の中身に迫る! タイトルへ戻る