Skip to content

Conversation

@hageboeck
Copy link
Member

When calling TDirectory::Append(obj, replace=true), and obj is already in the directory, return without doing anything.
If obj has the same name as an existing object, but is physically different, the usual warning is raised.

This will enable workflows such as:

   auto h = new TH1D(...);
   directory->Append(h, true);

After this change, the above lines work both with and without auto registration to gDirectory. This is needed to prepare ROOT for working without auto registration.

@dpiparo dpiparo self-requested a review January 14, 2026 19:52
Copy link
Member

@pcanal pcanal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment.

@github-actions
Copy link

github-actions bot commented Jan 14, 2026

Test Results

    22 files      22 suites   3d 22h 24m 59s ⏱️
 3 811 tests  3 811 ✅ 0 💤 0 ❌
76 733 runs  76 733 ✅ 0 💤 0 ❌

Results for commit bea103b.

♻️ This comment has been updated with latest results.

@hageboeck hageboeck force-pushed the root7-ownership-1 branch 2 times, most recently from aa1d14c to 0728d2c Compare January 15, 2026 09:29
@hageboeck hageboeck requested a review from pcanal January 15, 2026 09:31
@pcanal pcanal requested a review from dpiparo January 15, 2026 12:55
@hageboeck hageboeck force-pushed the root7-ownership-1 branch 2 times, most recently from 7b10767 to ae2f59b Compare January 15, 2026 13:05
@hageboeck hageboeck force-pushed the root7-ownership-1 branch 2 times, most recently from 63070c2 to b04a01d Compare January 15, 2026 16:38
{
if (!obj || !fList) return;

unsigned int nExistingInstances = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This number needs to be per instance. I.e.:

auto ptr1 = new TNamed("abc", "abc");
auto ptr2 = new TNamed("abc", "abc");
...
list->Append(ptr1, false);
list->Append(ptr1, false);
list->Append(ptr2, false);
list->Append(ptr2, true);

If I am not mistaken the code as of commit af54f14 would only remove 'one' of the ptr1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the test, and it actually removes all instances. That's because they get pushed multiple times into the list of things to be removed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your comment did make me review the counter, though, and it's not needed. Now, all duplicates are removed using the toRemoveList.

When calling TDirectory::Append(obj, replace=true), and obj is already
in the directory, don't issue a warning. Instead, proceed to clear the
list of any duplicates until only one instance is left (this is the
documented behaviour).
If a physically different object of the same name is present, issue a
warning for each of them and remove them from the list.

This will enable workflows such as:
   auto h = new TH1D(...);
   dir->Append(h, true);

After this change, the above lines work both with implicit object
ownership off and on, resulting in only one instance in the directory.

Furthermore, this fixes a bug where the following code would unset the
directory of the histogram:
  auto h = new TH1X(...);
  dir->Append(h);
  dir->Append(h, /*replace=*/true);
  h->GetDirectory() == nullptr;

Finally, the warning message is made a bit more accurate. If an object
with the same name but a different type is being replaced, the old
message would still read:
"Replacing existing <Type of replacing object>"
Now, the type of the replaced object is used instead of the type that
replaces it.
@hageboeck hageboeck added the clean build Ask CI to do non-incremental build on PR label Jan 16, 2026
for (auto toRemove : toRemoveList) {
ROOT::DirAutoAdd_t func = toRemove->IsA()->GetDirectoryAutoAdd();
if (func && toRemove != obj) {
func(toRemove, nullptr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last problem (which I don't think we can fix) is that if an histogram (that is not obj but has the same name as obj) is both in the current directory and is attached to a completely distinct TDirectory, this will result in that second histogram being removed from the other directory but not this one ....

On the other hand this would mean the user did:

TFile f1(...);
auto h1 = new TH1F("histo", ....);
TFile f2(...);
auto h2 = new TH1F("histo", ....);
f2.Append(h1);
f2.Append(h2, true);

If I read the code correctly, this would result in f1 being empty and f2 containing both histograms.

Copy link
Member

@pcanal pcanal Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also related, if TH1::AddDirectoryStatus() is false, the function is a no-op :(
i.e. this 'fails':

TH1::AddDirectory(false);
TFile f1(...);
auto h1 = new TH1F("histo", ....);
h1.SetDirectory(&f1);
auto h2 = new TH1F("histo", ....);
f1.Append(h2, true);

@pcanal
Copy link
Member

pcanal commented Jan 16, 2026

Why is the proposed addition to the user code:

   auto h = new TH1D(...);
   directory->Append(h, true);

and not

   auto h = new TH1D(...);
   h->SetDirectory(directory);

?
(SetDirectory is a no-op when it is called multiple time with the same argument)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clean build Ask CI to do non-incremental build on PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants