|
| 1 | +/// \file ROOT/RFile.hxx |
| 2 | +/// \ingroup Base ROOT7 |
| 3 | +/// \author Giacomo Parolini <[email protected]> |
| 4 | +/// \date 2025-03-19 |
| 5 | +/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback |
| 6 | +/// is welcome! |
| 7 | + |
| 8 | +#ifndef ROOT7_RFile |
| 9 | +#define ROOT7_RFile |
| 10 | + |
| 11 | +#include <ROOT/RError.hxx> |
| 12 | + |
| 13 | +#include <TClass.h> |
| 14 | + |
| 15 | +#include <string_view> |
| 16 | +#include <memory> |
| 17 | + |
| 18 | +class TKey; |
| 19 | +class TFile; |
| 20 | + |
| 21 | +namespace ROOT { |
| 22 | +namespace Experimental { |
| 23 | + |
| 24 | +class RFile; |
| 25 | +struct RFileKeyInfo; |
| 26 | + |
| 27 | +namespace Internal { |
| 28 | + |
| 29 | +ROOT::RLogChannel &RFileLog(); |
| 30 | + |
| 31 | +} // namespace Internal |
| 32 | + |
| 33 | +/** |
| 34 | +\class ROOT::Experimental::RFile |
| 35 | +\ingroup RFile |
| 36 | +\brief An interface to read from, or write to, a ROOT file, as well as performing other common operations. |
| 37 | +
|
| 38 | +TODO: more in-depth explanation |
| 39 | +*/ |
| 40 | +class RFile final { |
| 41 | + enum PutFlags { |
| 42 | + kPutAllowOverwrite = 0x1, |
| 43 | + kPutOverwriteKeepCycle = 0x2, |
| 44 | + }; |
| 45 | + |
| 46 | + std::unique_ptr<TFile> fFile; |
| 47 | + |
| 48 | + // Outlined to avoid including TFile.h |
| 49 | + explicit RFile(std::unique_ptr<TFile> file); |
| 50 | + |
| 51 | + /// Gets object `path` from the file and returns an **owning** pointer to it. |
| 52 | + /// The caller should immediately wrap it into a unique_ptr of the type described by `type`. |
| 53 | + [[nodiscard]] void *GetUntyped(std::string_view path, const TClass &type) const; |
| 54 | + |
| 55 | + /// Writes `obj` to file, without taking its ownership. |
| 56 | + void PutUntyped(std::string_view path, const TClass &type, const void *obj, std::uint32_t flags); |
| 57 | + |
| 58 | + /// \see Put |
| 59 | + template <typename T> |
| 60 | + void PutInternal(std::string_view path, const T &obj, std::uint32_t flags) |
| 61 | + { |
| 62 | + const TClass *cls = TClass::GetClass(typeid(T)); |
| 63 | + if (!cls) { |
| 64 | + throw ROOT::RException(R__FAIL(std::string("Could not determine type of object ") + std::string(path))); |
| 65 | + } |
| 66 | + PutUntyped(path, *cls, &obj, flags); |
| 67 | + } |
| 68 | + |
| 69 | + /// Given `path`, returns the TKey corresponding to the object at that path (assuming the path is fully split, i.e. |
| 70 | + /// "a/b/c" always means "object 'c' inside directory 'b' inside directory 'a'"). |
| 71 | + /// IMPORTANT: `path` must have been validated/normalized via ValidateAndNormalizePath() (see RFile.cxx). |
| 72 | + TKey *GetTKey(std::string_view path) const; |
| 73 | + |
| 74 | +public: |
| 75 | + // This is arbitrary, but it's useful to avoid pathological cases |
| 76 | + static constexpr int kMaxPathNesting = 1000; |
| 77 | + |
| 78 | + ///// Factory methods ///// |
| 79 | + |
| 80 | + /// Opens the file for reading |
| 81 | + static std::unique_ptr<RFile> Open(std::string_view path); |
| 82 | + |
| 83 | + /// Opens the file for reading/writing, overwriting it if it already exists |
| 84 | + static std::unique_ptr<RFile> Recreate(std::string_view path); |
| 85 | + |
| 86 | + /// Opens the file for updating |
| 87 | + static std::unique_ptr<RFile> OpenForUpdate(std::string_view path); |
| 88 | + |
| 89 | + ///// Instance methods ///// |
| 90 | + |
| 91 | + // Outlined to avoid including TFile.h |
| 92 | + ~RFile(); |
| 93 | + |
| 94 | + /// Retrieves an object from the file. |
| 95 | + /// `path` should be a string such that `IsValidPath(path) == true`, otherwise an exception will be thrown. |
| 96 | + /// If the object is not there returns a null pointer. |
| 97 | + template <typename T> |
| 98 | + std::unique_ptr<T> Get(std::string_view path) const |
| 99 | + { |
| 100 | + const TClass *cls = TClass::GetClass(typeid(T)); |
| 101 | + if (!cls) { |
| 102 | + throw ROOT::RException(R__FAIL(std::string("Could not determine type of object ") + std::string(path))); |
| 103 | + } |
| 104 | + void *obj = GetUntyped(path, *cls); |
| 105 | + return std::unique_ptr<T>(static_cast<T *>(obj)); |
| 106 | + } |
| 107 | + |
| 108 | + /// Puts an object into the file. |
| 109 | + /// The application retains ownership of the object. |
| 110 | + /// `path` should be a string such that `IsValidPath(path) == true`, otherwise an exception will be thrown. |
| 111 | + /// |
| 112 | + /// Throws a RException if `path` already identifies a valid object or directory. |
| 113 | + /// Throws a RException if the file was opened in read-only mode. |
| 114 | + template <typename T> |
| 115 | + void Put(std::string_view path, const T &obj) |
| 116 | + { |
| 117 | + PutInternal(path, obj, /* flags = */ 0); |
| 118 | + } |
| 119 | + |
| 120 | + /// Puts an object into the file, overwriting any previously-existing object at that path. |
| 121 | + /// The application retains ownership of the object. |
| 122 | + /// |
| 123 | + /// If an object already exists at that path, it is kept as a backup cycle unless `backupPrevious` is false. |
| 124 | + /// Note that even if `backupPrevious` is false, any existing cycle except the latest will be preserved. |
| 125 | + /// |
| 126 | + /// Throws a RException if `path` is already the path of a directory. |
| 127 | + /// Throws a RException if the file was opened in read-only mode. |
| 128 | + template <typename T> |
| 129 | + void Overwrite(std::string_view path, const T &obj, bool backupPrevious = true) |
| 130 | + { |
| 131 | + std::uint32_t flags = kPutAllowOverwrite; |
| 132 | + flags |= backupPrevious * kPutOverwriteKeepCycle; |
| 133 | + PutInternal(path, obj, flags); |
| 134 | + } |
| 135 | + |
| 136 | + /// Writes all objects to disk with the file structure. |
| 137 | + /// Returns the number of bytes written. |
| 138 | + size_t Write(); |
| 139 | + |
| 140 | + /// Flushes the RFile if needed and closes it, disallowing any further reading or writing. |
| 141 | + void Close(); |
| 142 | +}; |
| 143 | + |
| 144 | +} // namespace Experimental |
| 145 | +} // namespace ROOT |
| 146 | + |
| 147 | +#endif |
0 commit comments