データセットに格納できるデータ型は一種類だけなのか?
これまでの説明(その1)(その2)では、データセットに格納するデータ型はH5::PredType::STD_I32LE
に限定していました。/usr/include/hdf5/serial/H5PredType.hを見ると他にも色々(例えばfloat型に相当するIEEE_F32BE/IEEE_F32LE、double型に相当するIEEE_F64BE/IEEE_F64LEなど)あるのですが、元のデータがある構造体の場合には、これを要素とした配列をそのまま格納したくなります。
この場合、Predefinedな型ではなく自分で複合型H5::CompType
を定義することができます。
複合型のデータ配列書き出しサンプル
次のようなサンプルプログラムを作ってみました。
#include <iostream>
#include <H5Cpp.h>
#define DATASET_NAME "/dataset"
struct cdata_t{
int i_data;
float f_data;
double d_data;
};
#define MEMBER1 "int_data"
#define MEMBER2 "float_data"
#define MEMBER3 "double_data"
#define N 5
int main(int argc, char *argv[])
{
H5::Exception::dontPrint();
H5::H5File file( argv[1], H5F_ACC_TRUNC );
hsize_t dims[] = { N };
H5::DataSpace dataspace( sizeof(dims) / sizeof(hsize_t), dims );
H5::CompType comptype( sizeof(cdata_t) );
comptype.insertMember( MEMBER1, HOFFSET(cdata_t,i_data), H5::PredType::NATIVE_INT );
comptype.insertMember( MEMBER2, HOFFSET(cdata_t,f_data), H5::PredType::NATIVE_FLOAT );
comptype.insertMember( MEMBER3, HOFFSET(cdata_t,d_data), H5::PredType::NATIVE_DOUBLE );
H5::DataSet dataset = file.createDataSet( DATASET_NAME, comptype, dataspace );
cdata_t *data = new cdata_t [dims[0]];
for(int i=0; i<static_cast<int>(dims[0]); i++ ){
data[i].i_data = i;
data[i].f_data = i * i;
data[i].d_data = 1.0 / (i+1);
}
dataset.write( data, comptype );
delete data;
dataset.close();
file.close();
return 0;
}
例によって処理を追っていきましょう。最初に構造体cdata_t
を定義し、それぞれのメンバに対応する文字列を決めています。
struct cdata_t{
int i_data;
float f_data;
double d_data;
};
#define MEMBER1 "int_data"
#define MEMBER2 "float_data"
#define MEMBER3 "double_data"
main()関数最初のファイルオープンは横着しています。
H5::Exception::dontPrint();
H5::H5File file( argv[1], H5F_ACC_TRUNC );
データスペース作成はこれまでと同様です。
hsize_t dims[] = { N };
H5::DataSpace dataspace( sizeof(dims) / sizeof(hsize_t), dims );
次が複合データ型を定義している部分になります。
H5::CompType comptype( sizeof(cdata_t) );
comptype.insertMember( MEMBER1, HOFFSET(cdata_t,i_data), H5::PredType::NATIVE_INT );
comptype.insertMember( MEMBER2, HOFFSET(cdata_t,f_data), H5::PredType::NATIVE_FLOAT );
comptype.insertMember( MEMBER3, HOFFSET(cdata_t,d_data), H5::PredType::NATIVE_DOUBLE );
何をしているかはおおよそご想像頂けると思います。コンストラクタにsizeof( cdata_t )
を与えることで、これから定義する型のメモリサイズを指定し、その後にinsertMember()メソッドを使ってメンバーを一つ一つ登録しています。
HOFFSET()
は構造体の各メンバーの相対アドレスを返すマクロで、H5Cpp.hに定義されています。標準のoffsetof()を使っても構いません。それぞれのメンバーが具体的にどの型であるのかを第3引数で指定しています。ここを自動判別させるのは難しいようです。
データセット作成時には、定義したcomptypeをそのままH5::DataType
インスタンスとして与えられます。
H5::DataSet dataset = file.createDataSet( DATASET_NAME, comptype, dataspace );
続くforループで適当にデータを作成し、
dataset.write( data, comptype );
として書き出します。最後にデータセットとファイルを閉じて終了です。
dataset.close();
file.close();
複合データ型配列の例
上記のプログラムを実行し、test.h5を作成しました。これをh5dumpで見ると次のように出力されます。
HDF5 "test.h5" {
GROUP "/" {
DATASET "dataset" {
DATATYPE H5T_COMPOUND {
H5T_STD_I32LE "int_data";
H5T_IEEE_F32LE "float_data";
H5T_IEEE_F64LE "double_data";
}
DATASPACE SIMPLE { ( 5 ) / ( 5 ) }
DATA {
(0): {
0,
0,
1
},
(1): {
1,
1,
0.5
},
(2): {
2,
4,
0.333333
},
(3): {
3,
9,
0.25
},
(4): {
4,
16,
0.2
}
}
}
}
}
データ型自体はH5T_COMPOUNDと表現されています。そしてそれが、
H5T_STD_I32LE "int_data";
H5T_IEEE_F32LE "float_data";
H5T_IEEE_F64LE "double_data";
という3つの型(と対応する文字列)を持つことが見て取れます。定義時にはNATIVE_INT、NATIVE_FLOAT、NATIVE_DOUBLEとしたものが、それぞれSTD_I32LE、IEEE_F32LE、IEEE_F64LEと解釈されていることにご注意下さい。これは実行したCPUに依存します。