Operation: SetBranchAddress

PLAN 4
(Fix return values from TChain::SetBranchAddress.)

svn diff [code]Index: tree/tree/inc/TChain.h

— tree/tree/inc/TChain.h (revision 46367)
+++ tree/tree/inc/TChain.h (working copy)
@@ -141,9 +141,21 @@
virtual void ResetBranchAddresses();
virtual Long64_t Scan(const char *varexp="", const char *selection="", Option_t *option="", Long64_t nentries=1000000000, Long64_t firstentry=0); // MENU
virtual void SetAutoDelete(Bool_t autodel=kTRUE);
+#if !defined(CINT)
virtual Int_t SetBranchAddress(const char *bname,void *add, TBranch **ptr = 0);
+#endif
virtual Int_t SetBranchAddress(const char *bname,void *add, TBranch **ptr, TClass *realClass, EDataType datatype, Bool_t isptr);
virtual Int_t SetBranchAddress(const char *bname,void *add, TClass *realClass, EDataType datatype, Bool_t isptr);

  • template Int_t SetBranchAddress(const char *bname, T **add, TBranch **ptr = 0) {

  • return TTree::SetBranchAddress<T>(bname, add, ptr);
    
  • }
    +#ifndef R__NO_CLASS_TEMPLATE_SPECIALIZATION

  • // This can only be used when the template overload resolution can distringuish between

  • // T* and T**

  • template Int_t SetBranchAddress(const char *bname, T *add, TBranch **ptr = 0) {

  • return TTree::SetBranchAddress<T>(bname, add, ptr);
    
  • }
    +#endif

    virtual void SetBranchStatus(const char *bname, Bool_t status=1, UInt_t *found=0);
    virtual void SetCacheSize(Long64_t cacheSize);[/code] Tested with both, interpreted code and (ACLiC pre-)compiled code. :-({|=

Hi,

PLAN 4
(Fix return values from TChain::SetBranchAddress.)

What’s broken? This patch is wrong (as it is redundant since those are already in TTree.h … and they would not be used in the TChain object was used via a TTree pointer).

Philippe.

That’s exactly the point.
Without this fix, everything works well ONLY when the user says “TTree *chain”.
This fix is for cases when the user says “TChain *chain”.

Note that all main documentation pages use explicitly “TChain *chain” and NOT “TTree *chain”.
See, for example:

  1. How to Use Chains (List of Files) ?
  2. ROOT User’s Guide → Chapter 12. Trees → Chains
    In both above example cases, there are the “chain.SetBranchAddress(…)” calls and, without this fix, they will directly execute the non-templated “TChain::SetBranchAddress” and NOT the templated “TTree::SetBranchAddress”.
    When this fix is applied, the “proper” templated “TChain::SetBranchAddress” methods will be called.

That’s why I believe this fix is NOT redundant.

Hi,

Fair enough, I upload you fix.

Thanks,
Philippe

[size=150][color=#00CC00]Mission Accomplished![/color][/size] :mrgreen:
[size=150][color=#00CC00]The operation was a complete success![/color][/size] =D>

Hello,

I’ve recently moved from ROOT 5.28 to 5.32.

I have a class based on STL map that I use as a general statistics gathering function to record Key Value pairs to a Tree. Key and Value are template parameters just as for STL maps and can be simple or complex types.

I have a case where the Value parameter is a struct and encounter an error from SetBranchAddress when reading the tree. According to the TTree documentation on SetBranchAddress the address pointed to must be the first member variable of the struct. However, since Value is a template parameter whose type is unknown until instantiation the first member variable is unknown and code cannot be written directly for it.

This was not flagged as an error in ROOT 5.28 and everything seemed to work OK. I did not encounter any problems with this at any rate. With 5.34 I get the error about the pointer not being the correct type as described in this thread. I did not quite follow what Mr. Coyote was attempting to do, but it seems to be the same type of problem he was attempting to solve.

The only solution I can think of is to pass in a function template to the template class to resolve the pointer type. It is extra hoops and obfuscation of the code though.

Out of curiosity why is a struct treated differently than a class based object? An object must be derived from TObject?

Thanks for any help.
Terry

[quote]Out of curiosity why is a struct treated differently than a class based object? An object must be derived from TObject?[/quote]class and struct are not treated different; However interpreter and compiled entities are treated different; only compiled class/struct can be stored easily. With interpreted class/struct you must tell the TTree was is the content (in the documentation this is referred to as using the ‘leaflist’ branch creation technique). If you are not using the leaflist technique you should pass to SetBranchAddress for top level branches, the address of the pointer.

[quote]This was not flagged as an error in ROOT 5.28 and everything seemed to work OK. I did not encounter any problems with this at any rate. With 5.34 I get the error about the pointer not being the correct type as described in this thread.[/quote]If you know for sure that the error message is incorrect (it probably is correct though) or if you only have a generic pointer to the Value, you can quiet down SetBranchAddress by making sure that the type you passing is a void* (for example by casting it).

To make things clearer, you may want to copy paste the relevant code.

Cheers,
Philippe.

Thank you for the explanation. That is helpful.

Casting to void* does indeed work. Thank you.

Perhaps v5.28 did not check the type. I only upgraded my ROOT version yesterday just before I posted and my analysis scripts worked on 5.28, but not on 5.32. For 5.28 I downloaded a binary distribution and for 5.32 I had to build from source, because I only have gcc 4.1.2 and alternatively perhaps it was a difference in the build parameters which caused this to show up for me in 5.32.

Anyway I am happy with the solution.

Terry

[quote]Perhaps v5.28 did not check the type.[/quote]Indeed, v5.28 was not checking the type.

Cheers,
Philippe.

Hi,

I hate to bring this up again, but it is still not completely clear to me. I’ve made a test case script that can be run as compiled and interpreted. The tree is intended to have a branch from a struct called Ray.
SetBranchAddressTest.C (3.49 KB)

What I observe are the following:
Compiled, Branch defined with NO leaf list - SetBranchAddress requires address of Ray*.
Compiled, Branch defined with Leaf List - SetBranchAddress requires address of Ray casted to (void*).
Interpreted, Branch defined with NO leaf list (Requires to write tree from compiled) - SetBranchAddress gives error ‘Can’t call Ray::Ray() in current scope SetBranchAddressTest.C:67’.
Interpreted, Branch defined with Leaf List - SetBranchAddress requires address of Ray casted to (void*).

It would be nice not to have to know how exactly the branch was created (with or without leaf list), but as it stands it seems that you need two different methods depending on the branch construction. SetBranchAddress with for a branch defined with a leaf list requires a declared object while a branch defined without a leaf list requires a pointer. Is there a reason for this?

Also, am I missing a preferred method of doing this properly?

Thanks

Hi,

Also, am I missing a preferred method of doing this properly?

Yes :slight_smile:. Check the TTreeReader.

What I observe are the following:

This observation seems to match the expectation (the 3 point looks like CINT and should be improved in v6).

Is there a reason for this?

The two use cases for both are slightly different, but as importantly they were developed at different time. For example, the interface taking the address to pointer was first developed at a time when C++ template were not in usable state.

The leaflist list technique (the one where you pass the address of a struct) was developed first. The resulting branch is sub-optimal but predates the implementation of the split and was a convenient way to quickly create branch based on a existing set of variable, but however requires the use to specify (again) the list of variable and their type [and one has to be weary of variable alignment].

The syntax creating a branch from an object is newer and we needed at the time a way to share the ownership of the object between the TTree and the user, the only way (at the time) was via a pointer (whose address was passed as a void*). We improve greatly the syntax (using C++ template technique now implemented in compiler) to make the calls much more type safe (for example you can now pass an object when creating a branch and you no longer need to specify the type when creating the branch and SetBranchAddress can usually detect if you are passing it the wrong pointer type).

We still want to significantly improve the interface in TTree itself (and have to deal with backward compatibility). We also have introduced new interfaces that hides most of the complexity of setting the branch address, namely the TTreeReader (see the result of MakeSelector when run with v6.06).

Cheers,
Philippe.

Thanks for the response and the thorough explanation. I’ll take a look at TTreeReader.