diff --git a/configure.ac b/configure.ac index bfcc4a7c..7db49a5f 100644 --- a/configure.ac +++ b/configure.ac @@ -99,6 +99,13 @@ case ${ac_MKL} in AC_DEFINE([USE_MKL], [1], [Define to 1 if you use the Intel MKL]);; esac +############### HDF5 +AC_ARG_WITH([hdf5], + [AS_HELP_STRING([--with-hdf5=prefix], + [try this for a non-standard install prefix of the HDF5 library])], + [AM_CXXFLAGS="-I$with_hdf5/include $AM_CXXFLAGS"] + [AM_LDFLAGS="-L$with_hdf5/lib $AM_LDFLAGS"]) + ############### first-touch AC_ARG_ENABLE([numa], [AC_HELP_STRING([--enable-numa=yes|no|prefix], [enable first touch numa opt])], @@ -145,6 +152,12 @@ AC_SEARCH_LIBS([fftw_execute], [fftw3], [AC_DEFINE([HAVE_FFTW], [1], [Define to 1 if you have the `FFTW' library])] [have_fftw=true]) +AC_SEARCH_LIBS([H5Fopen], [hdf5_cpp], + [AC_DEFINE([HAVE_HDF5], [1], [Define to 1 if you have the `HDF5' library])] + [have_hdf5=true] + [LIBS="${LIBS} -lhdf5"], [], [-lhdf5]) +AM_CONDITIONAL(BUILD_HDF5, [ test "${have_hdf5}X" == "trueX" ]) + CXXFLAGS=$CXXFLAGS_CPY LDFLAGS=$LDFLAGS_CPY @@ -411,6 +424,7 @@ RNG choice : ${ac_RNG} GMP : `if test "x$have_gmp" = xtrue; then echo yes; else echo no; fi` LAPACK : ${ac_LAPACK} FFTW : `if test "x$have_fftw" = xtrue; then echo yes; else echo no; fi` +HDF5 : `if test "x$have_hdf5" = xtrue; then echo yes; else echo no; fi` build DOXYGEN documentation : `if test "$DX_FLAG_doc" = '1'; then echo yes; else echo no; fi` ----- BUILD FLAGS ------------------------------------- CXXFLAGS: diff --git a/lib/Grid.h b/lib/Grid.h index 0c5983f3..cb55d0c8 100644 --- a/lib/Grid.h +++ b/lib/Grid.h @@ -59,13 +59,13 @@ Author: paboyle /////////////////// // Grid headers /////////////////// -#include #include "Config.h" #include #include #include #include #include +#include #include #include #include diff --git a/lib/Makefile.am b/lib/Makefile.am index a779135f..fac622ca 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,4 +1,5 @@ extra_sources= +extra_headers= if BUILD_COMMS_MPI extra_sources+=communicator/Communicator_mpi.cc extra_sources+=communicator/Communicator_base.cc @@ -24,6 +25,12 @@ if BUILD_COMMS_NONE extra_sources+=communicator/Communicator_base.cc endif +if BUILD_HDF5 + extra_sources+=serialisation/Hdf5IO.cc + extra_headers+=serialisation/Hdf5IO.h + extra_headers+=serialisation/Hdf5Type.h +endif + # # Libraries # @@ -32,6 +39,9 @@ include Eigen.inc lib_LIBRARIES = libGrid.a -libGrid_a_SOURCES = $(CCFILES) $(extra_sources) +CCFILES += $(extra_sources) +HFILES += $(extra_headers) + +libGrid_a_SOURCES = $(CCFILES) libGrid_adir = $(pkgincludedir) nobase_dist_pkginclude_HEADERS = $(HFILES) $(eigen_files) Config.h diff --git a/lib/serialisation/BaseIO.h b/lib/serialisation/BaseIO.h index 1095baf1..0357915d 100644 --- a/lib/serialisation/BaseIO.h +++ b/lib/serialisation/BaseIO.h @@ -32,6 +32,7 @@ Author: Peter Boyle #include namespace Grid { + // Vector IO utilities /////////////////////////////////////////////////////// // helper function to read space-separated values template std::vector strToVec(const std::string s) @@ -67,6 +68,77 @@ namespace Grid { return os; } + // Vector element trait ////////////////////////////////////////////////////// + template + struct element + { + typedef T type; + static constexpr bool is_number = false; + }; + + template + struct element> + { + typedef typename element::type type; + static constexpr bool is_number = std::is_arithmetic::value + or is_complex::value + or element::is_number; + }; + + // Vector flatening utility class //////////////////////////////////////////// + // Class to flatten a multidimensional std::vector + template + class Flatten + { + public: + typedef typename element::type Element; + public: + explicit Flatten(const V &vector); + const V & getVector(void); + const std::vector & getFlatVector(void); + const std::vector & getDim(void); + private: + void accumulate(const Element &e); + template + void accumulate(const W &v); + void accumulateDim(const Element &e); + template + void accumulateDim(const W &v); + private: + const V &vector_; + std::vector flatVector_; + std::vector dim_; + }; + + + // Class to reconstruct a multidimensional std::vector + template + class Reconstruct + { + public: + typedef typename element::type Element; + public: + Reconstruct(const std::vector &flatVector, + const std::vector &dim); + const V & getVector(void); + const std::vector & getFlatVector(void); + const std::vector & getDim(void); + private: + void fill(std::vector &v); + template + void fill(W &v); + void resize(std::vector &v, const unsigned int dim); + template + void resize(W &v, const unsigned int dim); + private: + V vector_; + const std::vector &flatVector_; + std::vector dim_; + size_t ind_{0}; + unsigned int dimInd_{0}; + }; + + // Abstract writer/reader classes //////////////////////////////////////////// // static polymorphism implemented using CRTP idiom class Serializable; @@ -132,7 +204,128 @@ namespace Grid { } }; - // Generic writer interface + // Flatten class template implementation ///////////////////////////////////// + template + void Flatten::accumulate(const Element &e) + { + flatVector_.push_back(e); + } + + template + template + void Flatten::accumulate(const W &v) + { + for (auto &e: v) + { + accumulate(e); + } + } + + template + void Flatten::accumulateDim(const Element &e) {}; + + template + template + void Flatten::accumulateDim(const W &v) + { + dim_.push_back(v.size()); + accumulateDim(v[0]); + } + + template + Flatten::Flatten(const V &vector) + : vector_(vector) + { + accumulate(vector_); + accumulateDim(vector_); + } + + template + const V & Flatten::getVector(void) + { + return vector_; + } + + template + const std::vector::Element> & + Flatten::getFlatVector(void) + { + return flatVector_; + } + + template + const std::vector & Flatten::getDim(void) + { + return dim_; + } + + // Reconstruct class template implementation ///////////////////////////////// + template + void Reconstruct::fill(std::vector &v) + { + for (auto &e: v) + { + e = flatVector_[ind_++]; + } + } + + template + template + void Reconstruct::fill(W &v) + { + for (auto &e: v) + { + fill(e); + } + } + + template + void Reconstruct::resize(std::vector &v, const unsigned int dim) + { + v.resize(dim_[dim]); + } + + template + template + void Reconstruct::resize(W &v, const unsigned int dim) + { + v.resize(dim_[dim]); + for (auto &e: v) + { + resize(e, dim + 1); + } + } + + template + Reconstruct::Reconstruct(const std::vector &flatVector, + const std::vector &dim) + : flatVector_(flatVector) + , dim_(dim) + { + resize(vector_, 0); + fill(vector_); + } + + template + const V & Reconstruct::getVector(void) + { + return vector_; + } + + template + const std::vector::Element> & + Reconstruct::getFlatVector(void) + { + return flatVector_; + } + + template + const std::vector & Reconstruct::getDim(void) + { + return dim_; + } + + // Generic writer interface ////////////////////////////////////////////////// template inline void push(Writer &w, const std::string &s) { @@ -217,7 +410,7 @@ namespace Grid { upcast->writeDefault(s, output); } - // Reader template implementation //////////////////////////////////////////// + // Reader template implementation template Reader::Reader(void) { diff --git a/lib/serialisation/Hdf5IO.cc b/lib/serialisation/Hdf5IO.cc new file mode 100644 index 00000000..c5313495 --- /dev/null +++ b/lib/serialisation/Hdf5IO.cc @@ -0,0 +1,103 @@ +#include + +using namespace Grid; +#ifndef H5_NO_NAMESPACE +using namespace H5NS; +#endif + +// Writer implementation /////////////////////////////////////////////////////// +Hdf5Writer::Hdf5Writer(const std::string &fileName) +: fileName_(fileName) +, file_(fileName.c_str(), H5F_ACC_TRUNC) +{ + group_ = file_.openGroup("/"); + writeSingleAttribute(dataSetThres_, HDF5_GRID_GUARD "dataset_threshold", + Hdf5Type::type()); +} + +void Hdf5Writer::push(const std::string &s) +{ + group_ = group_.createGroup(s); + path_.push_back(s); +} + +void Hdf5Writer::pop(void) +{ + path_.pop_back(); + if (path_.empty()) + { + group_ = file_.openGroup("/"); + } + else + { + auto binOp = [](const std::string &a, const std::string &b)->std::string + { + return a + "/" + b; + }; + + group_ = group_.openGroup(std::accumulate(path_.begin(), path_.end(), + std::string(""), binOp)); + } +} + +template <> +void Hdf5Writer::writeDefault(const std::string &s, const std::string &x) +{ + StrType strType(PredType::C_S1, x.size()); + + writeSingleAttribute(*(x.data()), s, strType); +} + +void Hdf5Writer::writeDefault(const std::string &s, const char *x) +{ + std::string sx(x); + + writeDefault(s, sx); +} + +// Reader implementation /////////////////////////////////////////////////////// +Hdf5Reader::Hdf5Reader(const std::string &fileName) +: fileName_(fileName) +, file_(fileName.c_str(), H5F_ACC_RDONLY) +{ + group_ = file_.openGroup("/"); + readSingleAttribute(dataSetThres_, HDF5_GRID_GUARD "dataset_threshold", + Hdf5Type::type()); +} + +void Hdf5Reader::push(const std::string &s) +{ + group_ = group_.openGroup(s); + path_.push_back(s); +} + +void Hdf5Reader::pop(void) +{ + path_.pop_back(); + if (path_.empty()) + { + group_ = file_.openGroup("/"); + } + else + { + auto binOp = [](const std::string &a, const std::string &b)->std::string + { + return a + "/" + b; + }; + + group_ = group_.openGroup(std::accumulate(path_.begin(), path_.end(), + std::string(""), binOp)); + } +} + +template <> +void Hdf5Reader::readDefault(const std::string &s, std::string &x) +{ + Attribute attribute; + + attribute = group_.openAttribute(s); + StrType strType = attribute.getStrType(); + + x.resize(strType.getSize()); + attribute.read(strType, &(x[0])); +} diff --git a/lib/serialisation/Hdf5IO.h b/lib/serialisation/Hdf5IO.h new file mode 100644 index 00000000..2f891cd4 --- /dev/null +++ b/lib/serialisation/Hdf5IO.h @@ -0,0 +1,242 @@ +#ifndef GRID_SERIALISATION_HDF5_H +#define GRID_SERIALISATION_HDF5_H + +#include +#include +#include +#include +#include "Hdf5Type.h" + +#ifndef H5_NO_NAMESPACE +#define H5NS H5 +#endif + +// default thresold above which datasets are used instead of attributes +#ifndef HDF5_DEF_DATASET_THRES +#define HDF5_DEF_DATASET_THRES 6u +#endif + +// name guard for Grid metadata +#define HDF5_GRID_GUARD "_Grid_" + +namespace Grid +{ + class Hdf5Writer: public Writer + { + public: + Hdf5Writer(const std::string &fileName); + virtual ~Hdf5Writer(void) = default; + void push(const std::string &s); + void pop(void); + void writeDefault(const std::string &s, const char *x); + template + void writeDefault(const std::string &s, const U &x); + template + typename std::enable_if>::is_number, void>::type + writeDefault(const std::string &s, const std::vector &x); + template + typename std::enable_if>::is_number, void>::type + writeDefault(const std::string &s, const std::vector &x); + private: + template + void writeSingleAttribute(const U &x, const std::string &name, + const H5NS::DataType &type); + private: + std::string fileName_; + std::vector path_; + H5NS::H5File file_; + H5NS::Group group_; + unsigned int dataSetThres_{HDF5_DEF_DATASET_THRES}; + }; + + class Hdf5Reader: public Reader + { + public: + Hdf5Reader(const std::string &fileName); + virtual ~Hdf5Reader(void) = default; + void push(const std::string &s); + void pop(void); + template + void readDefault(const std::string &s, U &output); + template + typename std::enable_if>::is_number, void>::type + readDefault(const std::string &s, std::vector &x); + template + typename std::enable_if>::is_number, void>::type + readDefault(const std::string &s, std::vector &x); + private: + template + void readSingleAttribute(U &x, const std::string &name, + const H5NS::DataType &type); + private: + std::string fileName_; + std::vector path_; + H5NS::H5File file_; + H5NS::Group group_; + unsigned int dataSetThres_; + }; + + // Writer template implementation //////////////////////////////////////////// + template + void Hdf5Writer::writeSingleAttribute(const U &x, const std::string &name, + const H5NS::DataType &type) + { + H5NS::Attribute attribute; + hsize_t attrDim = 1; + H5NS::DataSpace attrSpace(1, &attrDim); + + attribute = group_.createAttribute(name, type, attrSpace); + attribute.write(type, &x); + } + + template + void Hdf5Writer::writeDefault(const std::string &s, const U &x) + { + writeSingleAttribute(x, s, Hdf5Type::type()); + } + + template <> + void Hdf5Writer::writeDefault(const std::string &s, const std::string &x); + + template + typename std::enable_if>::is_number, void>::type + Hdf5Writer::writeDefault(const std::string &s, const std::vector &x) + { + // alias to element type + typedef typename element>::type Element; + + // flatten the vector and getting dimensions + Flatten> flat(x); + std::vector dim; + const auto &flatx = flat.getFlatVector(); + + for (auto &d: flat.getDim()) + { + dim.push_back(d); + } + + // write to file + H5NS::DataSpace dataSpace(dim.size(), dim.data()); + + if (flatx.size() > dataSetThres_) + { + H5NS::DataSet dataSet; + + dataSet = group_.createDataSet(s, Hdf5Type::type(), dataSpace); + dataSet.write(flatx.data(), Hdf5Type::type()); + } + else + { + H5NS::Attribute attribute; + + attribute = group_.createAttribute(s, Hdf5Type::type(), dataSpace); + attribute.write(Hdf5Type::type(), flatx.data()); + } + } + + template + typename std::enable_if>::is_number, void>::type + Hdf5Writer::writeDefault(const std::string &s, const std::vector &x) + { + push(s); + writeSingleAttribute(x.size(), HDF5_GRID_GUARD "vector_size", + Hdf5Type::type()); + for (hsize_t i = 0; i < x.size(); ++i) + { + write(s + "_" + std::to_string(i), x[i]); + } + pop(); + } + + // Reader template implementation //////////////////////////////////////////// + template + void Hdf5Reader::readSingleAttribute(U &x, const std::string &name, + const H5NS::DataType &type) + { + H5NS::Attribute attribute; + + attribute = group_.openAttribute(name); + attribute.read(type, &x); + } + + template + void Hdf5Reader::readDefault(const std::string &s, U &output) + { + readSingleAttribute(output, s, Hdf5Type::type()); + } + + template <> + void Hdf5Reader::readDefault(const std::string &s, std::string &x); + + template + typename std::enable_if>::is_number, void>::type + Hdf5Reader::readDefault(const std::string &s, std::vector &x) + { + // alias to element type + typedef typename element>::type Element; + + // read the dimensions + H5NS::DataSpace dataSpace; + std::vector hdim; + std::vector dim; + hsize_t size = 1; + + if (group_.attrExists(s)) + { + dataSpace = group_.openAttribute(s).getSpace(); + } + else + { + dataSpace = group_.openDataSet(s).getSpace(); + } + hdim.resize(dataSpace.getSimpleExtentNdims()); + dataSpace.getSimpleExtentDims(hdim.data()); + for (auto &d: hdim) + { + dim.push_back(d); + size *= d; + } + + // read the flat vector + std::vector buf(size); + + if (size > dataSetThres_) + { + H5NS::DataSet dataSet; + + dataSet = group_.openDataSet(s); + dataSet.read(buf.data(), Hdf5Type::type()); + } + else + { + H5NS::Attribute attribute; + + attribute = group_.openAttribute(s); + attribute.read(Hdf5Type::type(), buf.data()); + } + + // reconstruct the multidimensional vector + Reconstruct> r(buf, dim); + + x = r.getVector(); + } + + template + typename std::enable_if>::is_number, void>::type + Hdf5Reader::readDefault(const std::string &s, std::vector &x) + { + uint64_t size; + + push(s); + readSingleAttribute(size, HDF5_GRID_GUARD "vector_size", + Hdf5Type::type()); + x.resize(size); + for (hsize_t i = 0; i < x.size(); ++i) + { + read(s + "_" + std::to_string(i), x[i]); + } + pop(); + } +} + +#endif diff --git a/lib/serialisation/Hdf5Type.h b/lib/serialisation/Hdf5Type.h new file mode 100644 index 00000000..8634f35b --- /dev/null +++ b/lib/serialisation/Hdf5Type.h @@ -0,0 +1,77 @@ +#ifndef GRID_SERIALISATION_HDF5_TYPE_H +#define GRID_SERIALISATION_HDF5_TYPE_H + +#include +#include +#include + +#ifndef H5_NO_NAMESPACE +#define H5NS H5 +#endif + +#define HDF5_NATIVE_TYPE(predType, cType)\ +template <>\ +class Hdf5Type\ +{\ +public:\ + static inline const H5NS::DataType & type(void)\ + {\ + return H5NS::PredType::predType;\ + }\ + static constexpr bool isNative = true;\ +}; + +#define DEFINE_HDF5_NATIVE_TYPES \ +HDF5_NATIVE_TYPE(NATIVE_B8, bool);\ +HDF5_NATIVE_TYPE(NATIVE_CHAR, char);\ +HDF5_NATIVE_TYPE(NATIVE_SCHAR, signed char);\ +HDF5_NATIVE_TYPE(NATIVE_UCHAR, unsigned char);\ +HDF5_NATIVE_TYPE(NATIVE_SHORT, short);\ +HDF5_NATIVE_TYPE(NATIVE_USHORT, unsigned short);\ +HDF5_NATIVE_TYPE(NATIVE_INT, int);\ +HDF5_NATIVE_TYPE(NATIVE_UINT, unsigned int);\ +HDF5_NATIVE_TYPE(NATIVE_LONG, long);\ +HDF5_NATIVE_TYPE(NATIVE_ULONG, unsigned long);\ +HDF5_NATIVE_TYPE(NATIVE_LLONG, long long);\ +HDF5_NATIVE_TYPE(NATIVE_ULLONG, unsigned long long);\ +HDF5_NATIVE_TYPE(NATIVE_FLOAT, float);\ +HDF5_NATIVE_TYPE(NATIVE_DOUBLE, double);\ +HDF5_NATIVE_TYPE(NATIVE_LDOUBLE, long double); + +namespace Grid +{ + template class Hdf5Type + { + public: + static constexpr bool isNative = false; + }; + + DEFINE_HDF5_NATIVE_TYPES; + + template + class Hdf5Type> + { + public: + static inline const H5NS::DataType & type(void) + { + if (typePtr_ == nullptr) + { + typePtr_.reset(new H5NS::CompType(sizeof(std::complex))); + typePtr_->insertMember("re", 0, Hdf5Type::type()); + typePtr_->insertMember("im", sizeof(R), Hdf5Type::type()); + } + + return *typePtr_; + } + static constexpr bool isNative = false; + private: + static std::unique_ptr typePtr_; + }; + + template + std::unique_ptr Hdf5Type>::typePtr_ = nullptr; +} + +#undef HDF5_NATIVE_TYPE + +#endif /* GRID_SERIALISATION_HDF5_TYPE_H */ diff --git a/lib/serialisation/MacroMagic.h b/lib/serialisation/MacroMagic.h index c9137dfe..8b027f30 100644 --- a/lib/serialisation/MacroMagic.h +++ b/lib/serialisation/MacroMagic.h @@ -109,38 +109,36 @@ THE SOFTWARE. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define GRID_MACRO_MEMBER(A,B) A B; +#define GRID_MACRO_COMP_MEMBER(A,B) result = (result and (lhs. B == rhs. B)); #define GRID_MACRO_OS_WRITE_MEMBER(A,B) os<< #A <<" "#B <<" = "<< obj. B <<" ; " <\ static inline void write(Writer &WR,const std::string &s, const cname &obj){ \ push(WR,s);\ GRID_MACRO_EVAL(GRID_MACRO_MAP(GRID_MACRO_WRITE_MEMBER,__VA_ARGS__)) \ pop(WR);\ -} \ -\ -\ +}\ template \ static inline void read(Reader &RD,const std::string &s, cname &obj){ \ push(RD,s);\ GRID_MACRO_EVAL(GRID_MACRO_MAP(GRID_MACRO_READ_MEMBER,__VA_ARGS__)) \ pop(RD);\ -} \ -\ -\ +}\ friend inline std::ostream & operator << (std::ostream &os, const cname &obj ) { \ os<<"class "<<#cname<<" {"<::type #define GRID_MACRO_ENUMVAL(A,B) A = B, @@ -149,7 +147,7 @@ friend inline std::ostream & operator << (std::ostream &os, const cname &obj ) { #define GRID_MACRO_ENUMCASEIO(A,B) case GRID_ENUM_TYPE(obj)::A: os << #A; break; #define GRID_SERIALIZABLE_ENUM(name,undefname,...)\ -class name: public Serializable\ +class name: public Grid::Serializable\ {\ public:\ enum EnumType\ @@ -161,7 +159,7 @@ public:\ name(void): value_(undefname) {};\ name(EnumType value): value_(value) {};\ template \ - static inline void write(Writer &WR,const std::string &s, const name &obj)\ + static inline void write(Grid::Writer &WR,const std::string &s, const name &obj)\ {\ switch (obj.value_)\ {\ @@ -171,7 +169,7 @@ public:\ }\ \ template \ - static inline void read(Reader &RD,const std::string &s, name &obj)\ + static inline void read(Grid::Reader &RD,const std::string &s, name &obj)\ {\ std::string buf;\ Grid::read(RD, s, buf);\ diff --git a/lib/serialisation/Serialisation.h b/lib/serialisation/Serialisation.h index 8f405d73..aa84e989 100644 --- a/lib/serialisation/Serialisation.h +++ b/lib/serialisation/Serialisation.h @@ -36,6 +36,9 @@ Author: Peter Boyle #include "BinaryIO.h" #include "TextIO.h" #include "XmlIO.h" +#ifdef HAVE_HDF5 +#include "Hdf5IO.h" +#endif ////////////////////////////////////////// // Todo: ////////////////////////////////////////// diff --git a/scripts/filelist b/scripts/filelist index 1ab95c7c..bf2fbc41 100755 --- a/scripts/filelist +++ b/scripts/filelist @@ -4,9 +4,8 @@ home=`pwd` # library Make.inc cd $home/lib -HFILES=`find . -type f -name '*.h' -not -path '*/Old/*' -not -path '*/Eigen/*'` -HFILES="$HFILES" -CCFILES=`find . -type f -name '*.cc' -not -name '*ommunicator*.cc'` +HFILES=`find . -type f -name '*.h' -not -name '*Hdf5*' -not -path '*/Old/*' -not -path '*/Eigen/*'` +CCFILES=`find . -type f -name '*.cc' -not -name '*Communicator*.cc' -not -name '*Hdf5*'` echo HFILES=$HFILES > Make.inc echo >> Make.inc echo CCFILES=$CCFILES >> Make.inc diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index e23aa1a3..8204b05b 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -28,130 +28,152 @@ Author: Peter Boyle /* END LEGAL */ #include -namespace Grid { - - GRID_SERIALIZABLE_ENUM(myenum, undef, red, 1, blue, 2, green, 3); - - class myclass: Serializable { - public: - - GRID_SERIALIZABLE_CLASS_MEMBERS(myclass, - myenum, e, - std::vector, ve, - std::string, name, - int, x, - double, y, - bool , b, - std::vector, array, - std::vector>, twodimarray, - ); - - myclass() {} - myclass(int i) - : array(4,5.1), twodimarray(3,std::vector(2,1.23456)), ve(2, myenum::blue) - { - e=myenum::red; - x=i; - y=2*i; - b=true; - name="bother said pooh"; - } - }; - -} - using namespace Grid; -int16_t i16 = 1; +GRID_SERIALIZABLE_ENUM(myenum, undef, red, 1, blue, 2, green, 3); + +class myclass: Serializable { +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(myclass, + myenum, e, + std::vector, ve, + std::string, name, + int, x, + double, y, + bool , b, + std::vector, array, + std::vector>, twodimarray, + std::vector>>, cmplx3darray + ); + myclass() {} + myclass(int i) + : array(4,5.1) + , twodimarray(3,std::vector(5, 1.23456)) + , cmplx3darray(3,std::vector>(5, std::vector(7, Complex(1.2, 3.4)))) + , ve(2, myenum::blue) + { + e=myenum::red; + x=i; + y=2*i; + b=true; + name="bother said pooh"; + } +}; + +int16_t i16 = 1; uint16_t u16 = 2; -int32_t i32 = 3; +int32_t i32 = 3; uint32_t u32 = 4; -int64_t i64 = 5; +int64_t i64 = 5; uint64_t u64 = 6; -float f = M_PI; -double d = 2*M_PI; -bool b = false; +float f = M_PI; +double d = 2*M_PI; +bool b = false; + +template +void ioTest(const std::string &filename, const O &object, const std::string &name) +{ + // writer needs to be destroyed so that writing physically happens + { + W writer(filename); + + write(writer, "testobject", object); + } + + R reader(filename); + O buf; + bool good; + + read(reader, "testobject", buf); + good = (object == buf); + std::cout << name << " IO test: " << (good ? "success" : "failure"); + std::cout << std::endl; + if (!good) exit(EXIT_FAILURE); +} int main(int argc,char **argv) { - { - XmlWriter WR("bother.xml"); - - // test basic type writing - push(WR,"BasicTypes"); - write(WR,std::string("i16"),i16); - write(WR,"u16",u16); - write(WR,"i32",i32); - write(WR,"u32",u32); - write(WR,"i64",i64); - write(WR,"u64",u64); - write(WR,"f",f); - write(WR,"d",d); - write(WR,"b",b); - pop(WR); - - // test serializable class writing - myclass obj(1234); // non-trivial constructor - write(WR,"obj",obj); - WR.write("obj2", obj); - std::cout << obj << std::endl; - - std::vector vec; - vec.push_back(myclass(1234)); - vec.push_back(myclass(5678)); - vec.push_back(myclass(3838)); - write(WR, "objvec", vec); - }; + std::cout << "==== basic IO" << std::endl; + XmlWriter WR("bother.xml"); + + // test basic type writing + std::cout << "-- basic writing to 'bother.xml'..." << std::endl; + push(WR,"BasicTypes"); + write(WR,std::string("i16"),i16); + write(WR,"u16",u16); + write(WR,"i32",i32); + write(WR,"u32",u32); + write(WR,"i64",i64); + write(WR,"u64",u64); + write(WR,"f",f); + write(WR,"d",d); + write(WR,"b",b); + pop(WR); + + // test serializable class writing + myclass obj(1234); // non-trivial constructor + std::vector vec; + + std::cout << "-- serialisable class writing to 'bother.xml'..." << std::endl; + write(WR,"obj",obj); + WR.write("obj2", obj); + vec.push_back(myclass(1234)); + vec.push_back(myclass(5678)); + vec.push_back(myclass(3838)); + write(WR, "objvec", vec); + std::cout << "-- serialisable class writing to std::cout:" << std::endl; + std::cout << obj << std::endl; + std::cout << "-- serialisable class comparison:" << std::endl; + std::cout << "vec[0] == obj: " << ((vec[0] == obj) ? "true" : "false") << std::endl; + std::cout << "vec[1] == obj: " << ((vec[1] == obj) ? "true" : "false") << std::endl; // read tests - myclass copy1, copy2, copy3; - std::vector veccopy1, veccopy2, veccopy3; + std::cout << "\n==== IO self-consistency tests" << std::endl; //// XML - { - XmlReader RD("bother.xml"); - read(RD,"obj",copy1); - read(RD,"objvec", veccopy1); - std::cout << "Loaded (XML) -----------------" << std::endl; - std::cout << copy1 << std::endl << veccopy1 << std::endl; - } + ioTest("iotest.xml", obj, "XML (object) "); + ioTest("iotest.xml", vec, "XML (vector of objects)"); //// binary - { - BinaryWriter BWR("bother.bin"); - write(BWR,"discard",copy1 ); - write(BWR,"discard",veccopy1 ); - } - { - BinaryReader BRD("bother.bin"); - read (BRD,"discard",copy2 ); - read (BRD,"discard",veccopy2 ); - std::cout << "Loaded (bin) -----------------" << std::endl; - std::cout << copy2 << std::endl << veccopy2 << std::endl; - } + ioTest("iotest.bin", obj, "binary (object) "); + ioTest("iotest.bin", vec, "binary (vector of objects)"); //// text - { - TextWriter TWR("bother.txt"); - write(TWR,"discard",copy1 ); - write(TWR,"discard",veccopy1 ); - } - { - TextReader TRD("bother.txt"); - read (TRD,"discard",copy3 ); - read (TRD,"discard",veccopy3 ); - std::cout << "Loaded (txt) -----------------" << std::endl; - std::cout << copy3 << std::endl << veccopy3 << std::endl; - } + ioTest("iotest.dat", obj, "text (object) "); + ioTest("iotest.dat", vec, "text (vector of objects)"); + //// HDF5 +#ifdef HAVE_HDF5 + ioTest("iotest.h5", obj, "HDF5 (object) "); + ioTest("iotest.h5", vec, "HDF5 (vector of objects)"); +#endif - std::vector iv = strToVec("1 2 2 4"); - std::vector sv = strToVec("bli bla blu"); + std::cout << "\n==== vector flattening/reconstruction" << std::endl; + typedef std::vector>> vec3d; - for (auto &e: iv) + vec3d dv, buf; + double d = 0.; + + dv.resize(4); + for (auto &v1: dv) { - std::cout << e << " "; + v1.resize(3); + for (auto &v2: v1) + { + v2.resize(5); + for (auto &x: v2) + { + x = d++; + } + } } - std::cout << std::endl; - for (auto &e: sv) - { - std::cout << e << " "; - } - std::cout << std::endl; + std::cout << "original 3D vector:" << std::endl; + std::cout << dv << std::endl; + + Flatten flatdv(dv); + + std::cout << "\ndimensions:" << std::endl; + std::cout << flatdv.getDim() << std::endl; + std::cout << "\nflattened vector:" << std::endl; + std::cout << flatdv.getFlatVector() << std::endl; + + Reconstruct rec(flatdv.getFlatVector(), flatdv.getDim()); + std::cout << "\nreconstructed vector:" << std::endl; + std::cout << flatdv.getVector() << std::endl; }