JavaCCとJJTreeを使ってjavaコードをC++へ変換 その2
前回のエントリにあった、System::new_array/System::delete_arrayの実装について説明します。
javaでは以下のように要素の数が異なる配列の作成が可能です。
int v[][] = { { 0, 1, 2}, { 0, 1} };
これはC++では以下のように表現できます。
int **v = new int*[2]; v[0] = new int[3]; v[1] = new int[2]; v[0][0] = 0;v[0][1] = 1;v[0][2] = 2; v[1][0] = 0;v[1][1] = 1;
C++に変換する上でGCは実装しないことにしていたので、これらもきちんと破棄する必要があります。
delete v[0]; delete v[1]; delete v;
ここで、vに対して操作して全てが破棄できれば便利ですよね?
最終的には以下のようなコードに変換します。
//生成 int **v = static_cast<int**>(System::new_array(2, 2 , sizeof(int*)) ); v[0] = static_cast<int*>(System::new_array(1, 3 , sizeof(int)) ); v[1] = static_cast<int*>(System::new_array(1, 2 , sizeof(int)) ); //破棄 System::delete_array<int*>(v);
vがSystem::delete_arrayに渡される時、int*の要素数がわからなければdelete v[0];とdelete v[1];を呼ぶことが出来ません。
そこで、System::new_array時にそれぞれの要素の数と要素の大きさ、ポインタ参照の深さを記録するようにします。
new_arrayの第1引数はポインタ参照の深さ、第2引数は要素数、第3引数は要素の大きさです。
実際にそれぞれの実装をお見せします。
MALLOCを読み替えればBREW以外でも利用可能です。
static void* new_array(int depth, int elementnum, size_t element_size){ void *ptr = MALLOC(elementnum * element_size + sizeof(int) * 3); *reinterpret_cast<int*>(ptr) = depth; *(reinterpret_cast<int*>(ptr) + 1) = elementnum; *(reinterpret_cast<int*>(ptr) + 2) = element_size; return reinterpret_cast<int*>(ptr) + 3; } template<typename T> static void _delete_array(void *ptr, int depth){ if( ptr == NULL ) return; int elementnum = *(reinterpret_cast<int*>(ptr) - 2); int element_size = *(reinterpret_cast<int*>(ptr) - 1); if( depth == 1 ){ LIB_ASSERT( elementnum > 0 ); if( elementnum == 1 ){ //newして確保した領域 delete reinterpret_cast<T>(ptr); //(int*)ptr - 3をFREEします }else{ //プリミティブな型の配列とか FREE(reinterpret_cast<int*>(ptr) - 3); } }else if( depth > 1 ){ for(int i=0; i<elementnum; i++){ _delete_array<T>( *reinterpret_cast<void**>(reinterpret_cast<char*>(ptr) + i * element_size), depth - 1); } FREE(reinterpret_cast<int*>(ptr) - 3); } } template<typename T> static void delete_array(void *ptr){ if( ptr == NULL ) return; int depth = *(reinterpret_cast<int*>(ptr) - 3); _delete_array<T>(ptr, depth); } static inline int size_array(void *ptr){ return *(reinterpret_cast<int*>(ptr) - 2); } //newとdeleteはカスタマイズしたものだけ記載しています void* operator new(size_t size) throw(){ void* ptr = MALLOC(size + sizeof(int) * 3); *reinterpret_cast<int*>(ptr) = 1; *(reinterpret_cast<int*>(ptr) + 1) = 1; *(reinterpret_cast<int*>(ptr) + 2) = size; return reinterpret_cast<int*>(ptr) + 3; } void operator delete(void* address) throw(){ address = reinterpret_cast<int*>(address) - 3; FREE(address); return; }
int型などプリミティブな型では、末端の要素は値で保持しますが、それ以外のクラスのインスタンスはポインタで保持します。
//生成 foo *v = System::new_array(2, 2, sizeof(foo*)); v[0] = new foo(); v[1] = new foo(); //破棄 System::delete_array<foo*>(v);
同じ1次元の配列でも非プリミティブ型ではポインタ参照の深さは2、プリミティブであれば1となります。
この場合インスタンスの生成にはそのままnewを用います。
末端の要素であるv[0]やv[1]にはdelete reinterpret_cast