Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/YODA/Writer.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 // -*- C++ -*-
0002 //
0003 // This file is part of YODA -- Yet more Objects for Data Analysis
0004 // Copyright (C) 2008-2025 The YODA collaboration (see AUTHORS for details)
0005 //
0006 #ifndef YODA_Writer_h
0007 #define YODA_Writer_h
0008 
0009 #include "YODA/AnalysisObject.h"
0010 #include "YODA/Utils/Traits.h"
0011 
0012 #include <YODA/Config/BuildConfig.h>
0013 #ifdef HAVE_HDF5
0014 #ifdef WITH_HIGHFIVE
0015 #include <YODA/highfive/H5File.hpp>
0016 #else
0017 #include <highfive/H5File.hpp>
0018 #define YODA_H5 HighFive
0019 #endif
0020 #endif
0021 
0022 #include <type_traits>
0023 #include <fstream>
0024 #include <iostream>
0025 
0026 namespace YODA {
0027 
0028 
0029   static bool enableH5compression = Utils::getEnvParam("YODA_HDF5_COMPRESSION", true);
0030 
0031   /// Pure virtual base class for various output writers.
0032   class Writer {
0033   public:
0034 
0035     /// Virtual destructor
0036     virtual ~Writer() {}
0037 
0038 
0039     /// @name Writing a single analysis object.
0040     /// @{
0041 
0042     /// Write out object @a ao to file @a filename.
0043     void write(const std::string& filename, const AnalysisObject& ao);
0044 
0045     /// Write out object @a ao to output stream @a stream.
0046     void write(std::ostream& stream, const AnalysisObject& ao) {
0047       std::vector<const AnalysisObject*> vec{&ao};
0048       write(stream, vec);
0049     }
0050 
0051     /// Write out pointer-like object @a ao to output stream @a stream.
0052     template <typename T>
0053     typename std::enable_if_t<DerefableToAO<T>::value> //< -> void if valid
0054     write(std::ostream& stream, const T& ao) {
0055       write(stream, *ao);
0056     }
0057 
0058     /// Write out pointer-like object @a ao to file @a filename.
0059     template <typename T>
0060     typename std::enable_if_t<DerefableToAO<T>::value> //< -> void if valid
0061     write(const std::string& filename, const T& ao) {
0062       write(filename, *ao);
0063     }
0064 
0065     /// @}
0066 
0067 
0068     /// @name Writing multiple analysis objects by collection.
0069     /// @{
0070 
0071     /// Write out a vector of AO pointers (untemplated=exact type-match) to the given stream
0072     ///
0073     /// @note This is the canonical function for implementing AO writing to H5:
0074     /// all others call this.
0075     ///
0076     /// @note Among other reasons, this is non-inline to hide zstr from the public API
0077     #ifdef HAVE_HDF5
0078     void write(YODA_H5::File& file, const std::vector<const AnalysisObject*>& aos);
0079     #endif
0080 
0081     /// Write out a vector of AO pointers (untemplated=exact type-match) to the given stream
0082     ///
0083     /// @note This is the canonical function for implementing AO writing to stream:
0084     /// all others call this.
0085     ///
0086     /// @note Among other reasons, this is non-inline to hide zstr from the public API
0087     void write(std::ostream& stream, const std::vector<const AnalysisObject*>& aos, int precision = -1);
0088 
0089 
0090     /// Write out a collection of objects @a objs to output stream @a stream.
0091     ///
0092     /// @note The enable_if call checks whether RANGE is const_iterable, if yes the return
0093     ///       type is void. If not, this template will not be a candidate in the lookup
0094     template <typename RANGE>
0095     typename std::enable_if_t<CIterable<RANGE>::value>
0096     write(std::ostream& stream, const RANGE& aos) {
0097       write(stream, std::begin(aos), std::end(aos));
0098     }
0099 
0100     /// Write out a collection of objects @a objs to file @a filename.
0101     template <typename RANGE>
0102     typename std::enable_if_t<CIterable<RANGE>::value>
0103     write(const std::string& filename, const RANGE& aos) {
0104       write(filename, std::begin(aos), std::end(aos));
0105     }
0106 
0107     /// @}
0108 
0109 
0110     /// @name Writing multiple analysis objects by iterator range.
0111     /// @{
0112 
0113     /// Write out the objects specified by start iterator @a begin and end
0114     /// iterator @a end to output stream @a stream.
0115     ///
0116     /// @todo Add SFINAE trait checking for AOITER = DerefableToAO
0117     template <typename AOITER>
0118     void write(std::ostream& stream, const AOITER& begin, const AOITER& end, int precision = -1) {
0119       std::vector<const AnalysisObject*> vec;
0120       // vec.reserve(std::distance(begin, end));
0121       for (AOITER ipao = begin; ipao != end; ++ipao)  vec.push_back(&(**ipao));
0122       write(stream, vec, precision);
0123     }
0124 
0125 
0126     /// Write out the objects specified by start iterator @a begin and end
0127     /// iterator @a end to file @a filename.
0128     ///
0129     /// @todo Add SFINAE trait checking for AOITER = DerefableToAO
0130     template <typename AOITER>
0131     void write(const std::string& filename, const AOITER& begin, const AOITER& end) {
0132       std::vector<const AnalysisObject*> vec;
0133       // vec.reserve(std::distance(begin, end));
0134       for (AOITER ipao = begin; ipao != end; ++ipao)  vec.push_back(&(**ipao));
0135 
0136       if (filename != "-") {
0137         try {
0138           const size_t lastdot = filename.find_last_of(".");
0139           std::string fmt = Utils::toLower(lastdot == std::string::npos ? filename : filename.substr(lastdot+1));
0140           const bool compress = (fmt == "gz") || (fmt == "h5" && enableH5compression);
0141           useCompression(compress);
0142           #ifdef HAVE_HDF5
0143           // check if the requested format is H5
0144           if (Utils::startswith(fmt, "h5")) {
0145             try {
0146               YODA_H5::File h5(filename, YODA_H5::File::OpenOrCreate | YODA_H5::File::Truncate);
0147               write(h5, vec);
0148             } catch(...) {
0149               throw WriteError("Failed to open HDF5 file " + filename);
0150             }
0151             return;
0152           }
0153           #endif
0154           // try writing to stream
0155           std::ofstream stream;
0156           stream.exceptions(std::ofstream::failbit | std::ofstream::badbit);
0157           stream.open(filename.c_str());
0158           if (stream.fail())
0159             throw WriteError("Writing to filename " + filename + " failed");
0160           write(stream, vec);
0161         } catch (std::ofstream::failure& e) {
0162           throw WriteError("Writing to filename " + filename + " failed: " + e.what());
0163         }
0164       } else {
0165         try {
0166           write(std::cout, vec);
0167         } catch (std::runtime_error& e) {
0168           throw WriteError("Writing to stdout failed: " + std::string(e.what()));
0169         }
0170       }
0171 
0172     }
0173 
0174     /// @}
0175 
0176     /// Set precision of numerical quantities in this writer's output.
0177     void setPrecision(int precision) {
0178       _precision = precision;
0179     }
0180 
0181     /// Set precision of numerical quantities for current AO in this writer's output.
0182     void setAOPrecision(const bool needsDP = false) {
0183       if (needsDP)             _aoprecision = std::numeric_limits<double>::max_digits10;
0184       else if (_precision > 0) _aoprecision = _precision;
0185       else                     _aoprecision = 6;
0186     }
0187 
0188     /// Use libz compression?
0189     void useCompression(const bool compress=true) {
0190       _compress = compress;
0191     }
0192 
0193 
0194   protected:
0195 
0196     /// @name Main writer elements
0197     /// @{
0198 
0199     /// Write any opening boilerplate required by the format to @a stream
0200     virtual void writeHead(std::ostream&) {}
0201 
0202     /// @brief Write the body elements corresponding to AnalysisObject @a ao to @a stream
0203     virtual void writeBody(std::ostream& stream, const AnalysisObject* ao);
0204 
0205     /// @brief Write the body elements corresponding to AnalysisObject pointer @a ao to @a stream
0206     virtual void writeBody(std::ostream& stream, const AnalysisObject& ao);
0207 
0208     /// @brief Write the body elements corresponding to AnalysisObject @a ao to @a stream
0209     /// @note Requires that @a ao is dereferenceable to an AnalysisObject, via the DerefableToAO<T> trait,
0210     template <typename T>
0211     typename std::enable_if_t<DerefableToAO<T>::value> //< -> void if valid
0212     writeBody(std::ostream& stream, const T& ao) { writeBody(stream, *ao); }
0213 
0214     /// Write any closing boilerplate required by the format to @a stream
0215     virtual void writeFoot(std::ostream& stream) { stream << std::flush; }
0216 
0217     /// @}
0218 
0219 
0220     /// @name Specific AO type writer implementations, to be implemented in derived classes
0221     /// @{
0222 
0223     virtual void writeAO(std::ostream& stream, const AnalysisObject& ao) = 0;
0224 
0225     #ifdef HAVE_HDF5
0226     virtual void writeAOS(YODA_H5::File& file, const vector<const AnalysisObject*>& aos) = 0;
0227     #endif
0228 
0229     /// @}
0230 
0231 
0232     /// Output precision
0233     int _precision, _aoprecision;
0234 
0235     /// Compress the output?
0236     bool _compress;
0237 
0238   };
0239 
0240 
0241   /// Factory function to make a writer object by format name or a filename
0242   Writer& mkWriter(const std::string& format_name);
0243 
0244 
0245 }
0246 
0247 #endif