Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 10:29:49

0001 // Author: Enrico Guiraud, Danilo Piparo CERN, Massimo Tumolo Politecnico di Torino  08/2018
0002 
0003 /*************************************************************************
0004  * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers.               *
0005  * All rights reserved.                                                  *
0006  *                                                                       *
0007  * For the licensing terms see $ROOTSYS/LICENSE.                         *
0008  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
0009  *************************************************************************/
0010 
0011 #ifndef ROOT_RDFDISPLAYER
0012 #define ROOT_RDFDISPLAYER
0013 
0014 #include "ROOT/RDF/Utils.hxx"  // IsDataContainer, InterpreterCalc
0015 #include "ROOT/RVec.hxx"
0016 #include "ROOT/TypeTraits.hxx"
0017 #include "TClassEdit.h"
0018 
0019 #include <vector>
0020 #include <string>
0021 #include <sstream>
0022 #include <type_traits>
0023 
0024 namespace ROOT {
0025 namespace Internal {
0026 namespace RDF {
0027 
0028 template<typename T>
0029 class DisplayHelper;
0030 std::string PrettyPrintAddr(const void *const addr);
0031 
0032 class RDisplayElement {
0033 private:
0034    enum class PrintingAction { ToBePrinted, ToBeIgnored, ToBeDotted };
0035    std::string fRepresentation;
0036    PrintingAction fPrintingAction;
0037 
0038 public:
0039    RDisplayElement(const std::string &representation);
0040    RDisplayElement();
0041    void SetPrint();
0042    void SetIgnore();
0043    void SetDots();
0044    bool IsPrint() const;
0045    bool IsIgnore() const;
0046    bool IsDot() const;
0047    const std::string &GetRepresentation() const;
0048    bool IsEmpty() const;
0049 };
0050 } // namespace RDF
0051 } // namespace Internal
0052 
0053 namespace RDF {
0054 
0055 /**
0056  * \class ROOT::RDF::RDisplay
0057  * \ingroup dataframe
0058  * This class is the textual representation of the content of a columnar dataset.
0059  *
0060  * This class is provided to the user, and it can be used to print on screen
0061  * the entries of the dataset requested through the Display action in a compact
0062  * representation or to return the full representation of the events as a string.
0063  * In order to apply proper formatting the content is buffered in memory as strings.
0064  */
0065 class RDisplay {
0066    template<typename T>
0067    friend class ROOT::Internal::RDF::DisplayHelper;
0068 
0069 public:
0070    enum class EPrintFormat {
0071       kMarkdown,
0072       kHtml
0073    };
0074 
0075    struct RPrintOptions {
0076       EPrintFormat fFormat;
0077    };
0078 
0079 private:
0080    using VecStr_t = std::vector<std::string>;
0081    using DElement_t = ROOT::Internal::RDF::RDisplayElement;
0082    static constexpr char fgSeparator = ' '; ///< Spacing used to align the table entries
0083    static constexpr unsigned fgMaxWidth = 100; ///< Maximum width of the table that Print() displays
0084 
0085    VecStr_t fTypes; ///< This attribute stores the type of each column. It is needed by the interpreter to print it.
0086    std::vector<std::vector<DElement_t>> fTable; ///< String representation of the data to be printed.
0087    std::vector<unsigned short> fWidths; ///< Tracks the maximum width of each column, based on the largest element.
0088 
0089    VecStr_t fRepresentations; ///< Used by the JITted code to store the string representation of the data.
0090    std::vector<VecStr_t> fCollectionsRepresentations; ///< Used by the JITted code to store the string representation of
0091                                                       ///< the data in case of collection. Each row corresponds to a
0092                                                       ///< column, each column to a value of the collection.
0093 
0094    size_t fNColumns; ///< Number of columns to be printed
0095 
0096    size_t fCurrentRow = 0;    ///< Row that is being filled
0097    size_t fNextRow = 1;       ///< Next row to be filled.
0098    size_t fCurrentColumn = 0; ///< Column that is being filled.
0099 
0100    size_t fNMaxCollectionElements = 10; // threshold on number of elements in collections to be Print()
0101 
0102    ////////////////////////////////////////////////////////////////////////////
0103    /// Appends a cling::printValue call to the stringstream
0104    /// This overload works for non-collection data types which are also not
0105    /// trivially representable as strings.
0106    /// \tparam T the type of the event to convert
0107    /// \param[in] stream Where the conversion function call will be chained.
0108    /// \param[in] element The event to convert to its string representation
0109    /// \param[in] index To which column the event belongs to
0110    /// \return false, the event is not a collection
0111    template <typename T,
0112              std::enable_if_t<!std::is_arithmetic_v<T> && !ROOT::Internal::RDF::IsDataContainer<T>::value, int> = 0>
0113    bool AddInterpreterString(std::stringstream &stream, T &element, const int &index)
0114    {
0115       stream << "*((std::string*)" << ROOT::Internal::RDF::PrettyPrintAddr(&(fRepresentations[index]))
0116              << ") = cling::printValue((" << fTypes[index] << "*)" << ROOT::Internal::RDF::PrettyPrintAddr(&element)
0117              << ");";
0118       return false;
0119    }
0120 
0121    ////////////////////////////////////////////////////////////////////////////
0122    /// Appends a string if the T type is an arithmetic type.
0123    /// This overload works for arithmetic data types that are trivially
0124    /// convertible to string.
0125    /// \tparam T the type of the event to convert
0126    /// \param[in] element The event to convert to its string representation
0127    /// \param[in] index To which column the event belongs to
0128    /// \return false, the event is not a collection
0129    template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
0130    bool AddInterpreterString(std::stringstream &, T &element, const int &index)
0131    {
0132       // Short-circuit the logic and just insert the string representation of
0133       // the symple type at the right index.
0134       fRepresentations[index] = std::to_string(element);
0135       return false;
0136    }
0137 
0138    ////////////////////////////////////////////////////////////////////////////
0139    /// Appends a string if the T type is boolean.
0140    /// \param[in] element The event to convert to its string representation
0141    /// \param[in] index To which column the event belongs to
0142    /// \return false, the event is not a collection
0143    bool AddInterpreterString(std::stringstream &, bool &element, const int &index)
0144    {
0145       // Short-circuit the logic and just insert the string representation of
0146       // the boolean value at the right index.
0147       fRepresentations[index] = (element ? "true" : "false");
0148       return false;
0149    }
0150 
0151    ////////////////////////////////////////////////////////////////////////////
0152    /// Appends collection.size() cling::printValue calls to the stringstream.
0153    /// \tparam T the type of the event to convert
0154    /// \param[in] stream Where the conversion function call will be chained.
0155    /// \param[in] collection The event to convert to its string representation
0156    /// \param[in] index To which column the event belongs to
0157    /// \return true, the event is a collection
0158    /// This function chains a sequence of call to cling::printValue, one for each element of the collection.
0159    template <typename T, std::enable_if_t<ROOT::Internal::RDF::IsDataContainer<T>::value &&
0160                                              !std::is_arithmetic_v<typename T::value_type>,
0161                                           int> = 0>
0162    bool AddInterpreterString(std::stringstream &stream, T &collection, const int &index)
0163    {
0164       size_t collectionSize = std::distance(std::begin(collection), std::end(collection));
0165       // Prepare the row to contain as many elements as the number of elements in the collection
0166       fCollectionsRepresentations[index] = VecStr_t(collectionSize);
0167 
0168       // Use GetSplit to get the encapsulated type of the collection. For example, GetSplit on
0169       // std::vector<std::vector<int>> will return std::vector<int>
0170       VecStr_t output;
0171       int nestedLoc = 0;
0172       TClassEdit::GetSplit(fTypes[index].c_str(), output, nestedLoc);
0173 
0174       // For each element, append a call and feed the proper type returned by GetSplit
0175       for (size_t i = 0; i < collectionSize; ++i) {
0176          stream << "*((std::string*)" << ROOT::Internal::RDF::PrettyPrintAddr(&(fCollectionsRepresentations[index][i]))
0177                 << ") = cling::printValue((" << output[1] << "*)"
0178                 << ROOT::Internal::RDF::PrettyPrintAddr(&(collection[i])) << ");";
0179       }
0180       return true;
0181    }
0182 
0183    ////////////////////////////////////////////////////////////////////////////
0184    /// Represent a collection of values as a collection of strings.
0185    /// \tparam T the type of the event to convert. This must be a collection of
0186    ///           values of arithmetic type, but not boolean.
0187    /// \param[in] collection The event to convert to its string representation
0188    /// \param[in] index To which column the event belongs to
0189    /// \return true, the event is a collection
0190    template <typename T, std::enable_if_t<ROOT::Internal::RDF::IsDataContainer<T>::value &&
0191                                              std::is_arithmetic_v<typename T::value_type> &&
0192                                              !std::is_same_v<typename T::value_type, bool>,
0193                                           int> = 0>
0194    bool AddInterpreterString(std::stringstream &, T &collection, const int &index)
0195    {
0196       auto collectionSize = std::distance(std::begin(collection), std::end(collection));
0197       VecStr_t collectionRepr(collectionSize);
0198       std::generate(collectionRepr.begin(), collectionRepr.end(), [i = 0, &collection]() mutable {
0199          auto valRepr = std::to_string(collection[i]);
0200          i++;
0201          return valRepr;
0202       });
0203       fCollectionsRepresentations[index] = std::move(collectionRepr);
0204       return true;
0205    }
0206 
0207    ////////////////////////////////////////////////////////////////////////////
0208    /// Represent a collection of booleans as a collection of strings.
0209    /// \tparam T the type of the event to convert. This must be a collection of
0210    ///           boolean values.
0211    /// \param[in] collection The event to convert to its string representation
0212    /// \param[in] index To which column the event belongs to
0213    /// \return true, the event is a collection
0214    template <typename T, std::enable_if_t<ROOT::Internal::RDF::IsDataContainer<T>::value &&
0215                                              std::is_same_v<typename T::value_type, bool>,
0216                                           int> = 0>
0217    bool AddInterpreterString(std::stringstream &, T &collection, const int &index)
0218    {
0219       auto collectionSize = std::distance(std::begin(collection), std::end(collection));
0220       VecStr_t collectionRepr(collectionSize);
0221       std::generate(collectionRepr.begin(), collectionRepr.end(), [i = 0, &collection]() mutable {
0222          auto valRepr = (collection[i] ? "true" : "false");
0223          i++;
0224          return valRepr;
0225       });
0226       fCollectionsRepresentations[index] = std::move(collectionRepr);
0227       return true;
0228    }
0229 
0230    ////////////////////////////////////////////////////////////////////////////
0231    /// AddInterpreterString overload for arrays of chars.
0232    ///
0233    /// \param[in] charArr The character array to convert to string representation
0234    /// \param[in] index To which column the event belongs
0235    /// \return false, the event is not a collection
0236    ///
0237    /// This specialization for arrays of characters skips the cling::printValue
0238    /// (i.e. appends nothing to the stream) and directly writes to fRepresentations the
0239    /// string representation of the array of chars.
0240    bool AddInterpreterString(std::stringstream &, ROOT::RVec<char> &charArr, const int &index)
0241    {
0242       // if null-terminated char array, do not copy the null terminator into std::string, it makes columns misaligned.
0243       const auto length = charArr[charArr.size()-1] == '\0' ? charArr.size() - 1 : charArr.size();
0244       const std::string arrAsStr(charArr.data(), length); // also works for non-null-terminated strings
0245       fRepresentations[index] = arrAsStr;
0246       return false; // do not treat this as a collection
0247    }
0248 
0249    ////////////////////////////////////////////////////////////////////////////
0250    /// Adds a single element to the next slot in the table
0251    void AddToRow(const std::string &stringEle);
0252 
0253    ////////////////////////////////////////////////////////////////////////////
0254    /// Adds a collection to the table
0255    ///
0256    /// Starting from the slot, the elements are added one under the other, each
0257    /// one using a single cell of an entire row
0258    void AddCollectionToRow(const VecStr_t &collection);
0259 
0260    ////////////////////////////////////////////////////////////////////////////
0261    /// Moves to the next cell
0262    ///
0263    /// Moves to the next cell, and if the row is full moves to the next row.
0264    void MovePosition();
0265 
0266    ////////////////////////////////////////////////////////////////////////////
0267    /// Get the number of columns that do NOT fit in the characters limit
0268    size_t GetNColumnsToShorten() const;
0269 
0270    ////////////////////////////////////////////////////////////////////////////
0271    /// Generate dashes between entries in Print() and AsString() Methods
0272    std::string DashesBetweenLines(size_t lastColToPrint, bool allColumnsFit) const;
0273 
0274    ////////////////////////////////////////////////////////////////////////////
0275    /// Adds a row of events to the table
0276    template <typename... Columns>
0277    void AddRow(Columns &... columns)
0278    {
0279       std::stringstream calc; // JITted code
0280       int columnIndex = 0;
0281       // Unwrapping the parameters to create the JITted code.
0282       bool isCollection [] {AddInterpreterString(calc, columns, columnIndex++)...};
0283 
0284       // Let cling::printValue handle the conversion. This can be done only through cling-compiled code.
0285       const std::string toJit = calc.str();
0286       if (!toJit.empty())
0287          ROOT::Internal::RDF::InterpreterCalc(calc.str(), "Display");
0288 
0289       // Populate the fTable using the results of the JITted code.
0290       for (size_t i = 0; i < fNColumns; ++i) {
0291          if (isCollection[i]) {
0292             AddCollectionToRow(fCollectionsRepresentations[i]);
0293          } else {
0294             AddToRow(fRepresentations[i]);
0295          }
0296       }
0297    }
0298 
0299    void EnsureCurrentColumnWidth(size_t w);
0300 
0301    std::string AsStringInternal(bool considerDots, const RPrintOptions &options = {EPrintFormat::kMarkdown}) const;
0302    std::string AsStringMarkdown(bool considerDots) const;
0303    std::string AsStringHtml() const;
0304 
0305 public:
0306    ////////////////////////////////////////////////////////////////////////////
0307    /// Creates an RDisplay to print the event values
0308    /// \param[in] columnNames Columns to print
0309    /// \param[in] types The type of each column
0310    /// \param[in] nMaxCollectionElements Number of maximum elements in collection.
0311    RDisplay(const VecStr_t &columnNames, const VecStr_t &types, size_t nMaxCollectionElements);
0312 
0313    ////////////////////////////////////////////////////////////////////////////
0314    /// Prints the representation to the standard output
0315    ///
0316    /// Collections are shortened to the first and last element. The overall width
0317    /// is shortened to a fixed number of columns that should fit the screen width.
0318    void Print(const RPrintOptions &options = {EPrintFormat::kMarkdown}) const;
0319 
0320    ////////////////////////////////////////////////////////////////////////////
0321    /// Returns the representation as a string
0322    std::string AsString(const RPrintOptions &options = {EPrintFormat::kMarkdown}) const;
0323 };
0324 
0325 } // namespace RDF
0326 } // namespace ROOT
0327 
0328 #endif