Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class _RFile_Put:
def __init__(self, rfile):
self._rfile = rfile

def __call__(self, name, obj):
def __call__(self, name, obj, title = ""):
"""
Non-templated Put()
"""
Expand All @@ -70,7 +70,7 @@ def __call__(self, name, obj):
raise TypeError(f"type {objType} is not supported by ROOT I/O")
else:
className = objType.__cpp_name__
self._rfile.Put[className](name, obj)
self._rfile.Put[className](name, obj, title)

def __getitem__(self, template_arg):
"""
Expand Down
69 changes: 42 additions & 27 deletions io/io/inc/ROOT/RFile.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,11 @@ class RFile final {
friend TFile *Internal::GetRFileTFile(RFile &rfile);

/// Flags used in PutInternal()
enum PutFlags {
enum EPutFlags {
/// When encountering an object at the specified path, overwrite it with the new one instead of erroring out.
kPutAllowOverwrite = 0x1,
kPutFlag_AllowOverwrite = 0x1,
/// When overwriting an object, preserve the existing one and create a new cycle, rather than removing it.
kPutOverwriteKeepCycle = 0x2,
kPutFlag_OverwriteKeepCycle = 0x2,
};

std::unique_ptr<TFile> fFile;
Expand All @@ -272,13 +272,14 @@ class RFile final {
std::variant<const char *, std::reference_wrapper<const std::type_info>> type) const;

/// Writes `obj` to file, without taking its ownership.
void PutUntyped(std::string_view path, const std::type_info &type, const void *obj, std::uint32_t flags);
void PutUntyped(std::string_view path, const std::type_info &type, const void *obj, std::uint32_t flags,
std::string_view title);

/// \see Put
template <typename T>
void PutInternal(std::string_view path, const T &obj, std::uint32_t flags)
void PutInternal(std::string_view path, const T &obj, std::uint32_t flags, std::string_view title = "")
{
PutUntyped(path, typeid(T), &obj, flags);
PutUntyped(path, typeid(T), &obj, flags, title);
}

/// Given `path`, returns the TKey corresponding to the object at that path (assuming the path is fully split, i.e.
Expand Down Expand Up @@ -328,52 +329,66 @@ public:
/// Retrieves an object from the file.
/// `path` should be a string such that `IsValidPath(path) == true`, otherwise an exception will be thrown.
/// See \ref ValidateAndNormalizePath() for info about valid path names.
/// If the object is not there returns a null pointer.
/// \return A copy of the object at `path`, or `nullptr` if there is no object of type `T` at the specified path.
template <typename T>
std::unique_ptr<T> Get(std::string_view path) const
{
void *obj = GetUntyped(path, typeid(T));
return std::unique_ptr<T>(static_cast<T *>(obj));
}

/// Puts an object into the file.
/// The application retains ownership of the object.
/// Puts object `obj` into the file, optionally giving it a title.
/// The object will be effectively copied into the file, so any further modifications won't be seen by the object
/// inside the file.
/// Note that the object is not necessarily written to storage until the RFile is closed or `Flush()` is called.
///
/// `path` should be a string such that `IsValidPath(path) == true`, otherwise an exception will be thrown.
/// See \ref ValidateAndNormalizePath() for info about valid path names.
///
/// Throws a RException if `path` already identifies a valid object or directory.
/// Throws a RException if the file was opened in read-only mode.
/// \throws ROOT::RException if `path` already identifies a valid object or directory.
/// \throws ROOT::RException if the file was opened in read-only mode.
template <typename T>
void Put(std::string_view path, const T &obj)
void Put(std::string_view path, const T &obj, std::string_view title = "")
{
PutInternal(path, obj, /* flags = */ 0);
PutInternal(path, obj, /* flags = */ 0, title);
}

/// Puts an object into the file, overwriting any previously-existing object at that path.
/// The application retains ownership of the object.
/// See Put for more details.
///
/// If an object already exists at that path, it is kept as a backup cycle unless `backupPrevious` is false.
/// Note that even if `backupPrevious` is false, any existing cycle except the latest will be preserved.
/// If an object already exists at that path,AND it has type `T`, it is kept as a backup cycle
/// unless `createNewCycle` is false.
/// If the existing object's type differs from `T`, an exception is thrown.
/// Note that even if `createNewCycle` is false only the *latest* cycle will be deleted.
///
/// Throws a RException if `path` is already the path of a directory.
/// Throws a RException if the file was opened in read-only mode.
/// \throws ROOT::RException if `path` is already the path of a directory.
/// \throws ROOT::RException if the file was opened in read-only mode.
template <typename T>
void Overwrite(std::string_view path, const T &obj, bool createNewCycle = true)
{
Overwrite(path, obj, "", createNewCycle);
}

/// Like Overwrite(std::string_view, const T&, bool) but allows giving a title to the object.
template <typename T>
void Overwrite(std::string_view path, const T &obj, bool backupPrevious = true)
void Overwrite(std::string_view path, const T &obj, std::string_view title, bool createNewCycle = true)
{
std::uint32_t flags = kPutAllowOverwrite;
flags |= backupPrevious * kPutOverwriteKeepCycle;
PutInternal(path, obj, flags);
std::uint32_t flags = kPutFlag_AllowOverwrite;
flags |= createNewCycle * kPutFlag_OverwriteKeepCycle;
PutInternal(path, obj, flags, title);
}

/// Writes all objects and the file structure to disk.
/// Returns the number of bytes written.
/// \return the number of bytes written.
size_t Flush();

/// Flushes the RFile if needed and closes it, disallowing any further reading or writing.
void Close();

/// Returns an iterable over all keys of objects and/or directories written into this RFile starting at path
/// `basePath` (defaulting to include the content of all subdirectories).
/// The iterable yields values of type ROOT::Experimental::RKeyInfo.
///
/// By default, keys referring to directories are not returned: only those referring to leaf objects are.
/// If `basePath` is the path of a leaf object, only `basePath` itself will be returned.
/// If `basePath` is the path of a directory, it won't appear in the listing.
Expand All @@ -386,17 +401,17 @@ public:
/// Example usage:
/// ~~~{.cpp}
/// for (RKeyInfo key : file->ListKeys()) {
/// /* iterate over all objects in the RFile */
/// // iterate over all objects in the RFile
/// cout << key.GetPath() << ";" << key.GetCycle() << " of type " << key.GetClassName() << "\n";
/// }
/// for (RKeyInfo key : file->ListKeys("", kListDirs|kListObjects|kListRecursive)) {
/// /* iterate over all objects and directories in the RFile */
/// // iterate over all objects and directories in the RFile
/// }
/// for (RKeyInfo key : file->ListKeys("a/b", kListObjects)) {
/// /* iterate over all objects that are immediate children of directory "a/b" */
/// // iterate over all objects that are immediate children of directory "a/b"
/// }
/// for (RKeyInfo key : file->ListKeys("foo", kListDirs|kListRecursive)) {
/// /* iterate over all directories under directory "foo", recursively */
/// // iterate over all directories under directory "foo", recursively
/// }
/// ~~~
RFileKeyIterable ListKeys(std::string_view basePath = "", std::uint32_t flags = kListObjects | kListRecursive) const
Expand Down
1 change: 1 addition & 0 deletions io/io/inc/TDirectoryFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class TDirectoryFile : public TDirectory {
Int_t WriteTObject(const TObject *obj, const char *name=nullptr, Option_t *option="", Int_t bufsize=0) override;
Int_t WriteObjectAny(const void *obj, const char *classname, const char *name, Option_t *option="", Int_t bufsize=0) override;
Int_t WriteObjectAny(const void *obj, const TClass *cl, const char *name, Option_t *option="", Int_t bufsize=0) override;
Int_t WriteObjectAny(const void *obj, const TClass *cl, const char *name, const char *title, Option_t *option="", Int_t bufsize=0);
void WriteDirHeader() override;
void WriteKeys() override;

Expand Down
12 changes: 8 additions & 4 deletions io/io/src/RFile.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,8 @@ void *RFile::GetUntyped(std::string_view path,
return obj;
}

void RFile::PutUntyped(std::string_view pathSV, const std::type_info &type, const void *obj, std::uint32_t flags)
void RFile::PutUntyped(std::string_view pathSV, const std::type_info &type, const void *obj, std::uint32_t flags,
std::string_view title)
{
const TClass *cls = TClass::GetClass(type);
if (!cls)
Expand Down Expand Up @@ -377,8 +378,8 @@ void RFile::PutUntyped(std::string_view pathSV, const std::type_info &type, cons
}
}

const bool allowOverwrite = (flags & kPutAllowOverwrite) != 0;
const bool backupCycle = (flags & kPutOverwriteKeepCycle) != 0;
const bool allowOverwrite = (flags & kPutFlag_AllowOverwrite) != 0;
const bool backupCycle = (flags & kPutFlag_OverwriteKeepCycle) != 0;
const Option_t *writeOpts = "";
if (!allowOverwrite) {
const TKey *existing = dir->GetKey(tokens[tokens.size() - 1].c_str());
Expand All @@ -390,7 +391,10 @@ void RFile::PutUntyped(std::string_view pathSV, const std::type_info &type, cons
writeOpts = "WriteDelete";
}

int success = dir->WriteObjectAny(obj, cls, tokens[tokens.size() - 1].c_str(), writeOpts);
const char *objName = tokens[tokens.size() - 1].c_str();
assert(dynamic_cast<TDirectoryFile *>(dir));
int success =
static_cast<TDirectoryFile *>(dir)->WriteObjectAny(obj, cls, objName, std::string(title).c_str(), writeOpts);

if (!success) {
throw ROOT::RException(R__FAIL(std::string("Failed to write ") + path + " to file"));
Expand Down
120 changes: 63 additions & 57 deletions io/io/src/TDirectoryFile.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -2010,63 +2010,7 @@ Int_t TDirectoryFile::WriteTObject(const TObject *obj, const char *name, Option_
return nbytes;
}

////////////////////////////////////////////////////////////////////////////////
/// Write object from pointer of class classname in this directory.
///
/// obj may not derive from TObject. See TDirectoryFile::WriteTObject for comments
///
/// ## Very important note
/// The value passed as 'obj' needs to be from a pointer to the type described by classname.
/// For example:
/// ~~~{.cpp}
/// TopClass *top;
/// BottomClass *bottom;
/// top = bottom;
/// ~~~
/// you can do:
/// ~~~{.cpp}
/// directory->WriteObjectAny(top,"top","name of object");
/// directory->WriteObjectAny(bottom,"bottom","name of object");
/// ~~~
/// <b>BUT YOU CAN NOT DO</b> the following since it will fail with multiple inheritance:
/// ~~~{.cpp}
/// directory->WriteObjectAny(top,"bottom","name of object");
/// ~~~
/// We <b>STRONGLY</b> recommend to use
/// ~~~{.cpp}
/// TopClass *top = ....;
/// directory->WriteObject(top,"name of object")
/// ~~~
/// See also remarks in TDirectoryFile::WriteTObject

Int_t TDirectoryFile::WriteObjectAny(const void *obj, const char *classname, const char *name, Option_t *option, Int_t bufsize)
{
TClass *cl = TClass::GetClass(classname);
if (!cl) {
TObject *info_obj = *(TObject**)obj;
TVirtualStreamerInfo *info = dynamic_cast<TVirtualStreamerInfo*>(info_obj);
if (!info) {
Error("WriteObjectAny","Unknown class: %s",classname);
return 0;
} else {
cl = info->GetClass();
}
}
return WriteObjectAny(obj,cl,name,option,bufsize);
}

////////////////////////////////////////////////////////////////////////////////
/// Write object of class with dictionary cl in this directory.
///
/// obj may not derive from TObject
/// To get the TClass* cl pointer, one can use
///
/// TClass *cl = TClass::GetClass("classname");
///
/// An alternative is to call the function WriteObjectAny above.
/// see TDirectoryFile::WriteTObject for comments

Int_t TDirectoryFile::WriteObjectAny(const void *obj, const TClass *cl, const char *name, Option_t *option, Int_t bufsize)
Int_t TDirectoryFile::WriteObjectAny(const void *obj, const TClass *cl, const char *name, const char *title, Option_t *option, Int_t bufsize)
{
TDirectory::TContext ctxt(this);

Expand Down Expand Up @@ -2136,6 +2080,7 @@ Int_t TDirectoryFile::WriteObjectAny(const void *obj, const TClass *cl, const ch
oldkey = GetKey(oname);
}
key = fFile->CreateKey(this, obj, cl, oname, bsize);
key->SetTitle(title);
if (newName) delete [] newName;

if (!key->GetSeekKey()) {
Expand All @@ -2155,6 +2100,67 @@ Int_t TDirectoryFile::WriteObjectAny(const void *obj, const TClass *cl, const ch
return nbytes;
}

////////////////////////////////////////////////////////////////////////////////
/// Write object from pointer of class classname in this directory.
///
/// obj may not derive from TObject. See TDirectoryFile::WriteTObject for comments
///
/// ## Very important note
/// The value passed as 'obj' needs to be from a pointer to the type described by classname.
/// For example:
/// ~~~{.cpp}
/// TopClass *top;
/// BottomClass *bottom;
/// top = bottom;
/// ~~~
/// you can do:
/// ~~~{.cpp}
/// directory->WriteObjectAny(top,"top","name of object");
/// directory->WriteObjectAny(bottom,"bottom","name of object");
/// ~~~
/// <b>BUT YOU CAN NOT DO</b> the following since it will fail with multiple inheritance:
/// ~~~{.cpp}
/// directory->WriteObjectAny(top,"bottom","name of object");
/// ~~~
/// We <b>STRONGLY</b> recommend to use
/// ~~~{.cpp}
/// TopClass *top = ....;
/// directory->WriteObject(top,"name of object")
/// ~~~
/// See also remarks in TDirectoryFile::WriteTObject

Int_t TDirectoryFile::WriteObjectAny(const void *obj, const char *classname, const char *name, Option_t *option, Int_t bufsize)
{
TClass *cl = TClass::GetClass(classname);
if (!cl) {
TObject *info_obj = *(TObject**)obj;
TVirtualStreamerInfo *info = dynamic_cast<TVirtualStreamerInfo*>(info_obj);
if (!info) {
Error("WriteObjectAny","Unknown class: %s",classname);
return 0;
} else {
cl = info->GetClass();
}
}
return WriteObjectAny(obj,cl,name,option,bufsize);
}

////////////////////////////////////////////////////////////////////////////////
/// Write object of class with dictionary cl in this directory.
///
/// obj may not derive from TObject
/// To get the TClass* cl pointer, one can use
///
/// TClass *cl = TClass::GetClass("classname");
///
/// An alternative is to call the function WriteObjectAny above.
/// see TDirectoryFile::WriteTObject for comments

Int_t TDirectoryFile::WriteObjectAny(const void *obj, const TClass *cl, const char *name, Option_t *option, Int_t bufsize)
{
return WriteObjectAny(obj, cl, name, /*title=*/ "", option, bufsize);
}

////////////////////////////////////////////////////////////////////////////////
/// Overwrite the Directory header record.

Expand Down
6 changes: 5 additions & 1 deletion io/io/test/rfile.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -231,13 +231,15 @@ TEST(RFile, PutOverwrite)
{
TH1D hist("hist", "", 100, -10, 10);
hist.FillRandom("gaus", 1000);
file->Put("hist", hist);
file->Put("hist", hist, "My Histo");
}

{
auto hist = file->Get<TH1D>("hist");
ASSERT_TRUE(hist);
EXPECT_EQ(static_cast<int>(hist->GetEntries()), 1000);
auto key = file->GetKeyInfo("hist").value();
EXPECT_EQ(key.GetTitle(), "My Histo");
}

// Try putting another object at the same path, should fail
Expand Down Expand Up @@ -267,6 +269,8 @@ TEST(RFile, PutOverwrite)
// ...but any cycle before the latest should still be there!
hist = file->Get<TH1D>("hist;1");
EXPECT_NE(hist, nullptr);
auto key = file->GetKeyInfo("hist;1").value();
EXPECT_EQ(key.GetTitle(), "My Histo");
}
}

Expand Down
3 changes: 2 additions & 1 deletion io/io/test/rfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
with RFile.Recreate(fileName) as rfile:
hist = ROOT.TH1D("hist", "", 100, -10, 10)
hist.FillRandom("gaus", 10)
rfile.Put("hist", hist)
rfile.Put("hist", hist, "My Histo")
rfile.Put("foo/hist", hist)
rfile.Put("foo/bar/hist", hist)
rfile.Put("foo/bar/hist2", hist)
Expand All @@ -73,6 +73,7 @@
key = rfile.GetKeyInfo("hist")
self.assertEqual(key.GetPath(), "hist")
self.assertEqual(key.GetClassName(), "TH1D")
self.assertEqual(key.GetTitle(), "My Histo")

key = rfile.GetKeyInfo("does_not_exist")
self.assertEqual(key, None)
Expand Down Expand Up @@ -153,7 +154,7 @@

fileName = "test_rfile_gettree_py.root"
try:
with ROOT.TFile.Open(fileName, "RECREATE") as file:

Check failure on line 157 in io/io/test/rfile.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (F841)

io/io/test/rfile.py:157:59: F841 Local variable `file` is assigned to but never used help: Remove assignment to unused variable `file`
tree = ROOT.TTree("tree", "tree")
x = array("i", [0])
tree.Branch("myColumn", x, "myColumn/I")
Expand Down
Loading