Main Page | Class Hierarchy | Alphabetical List | Compound List | File List | Compound Members | File Members | Related Pages

Frequently Asked Questions

What are these strange FloatFunPoR, BaseCutPoR, and FillIteratorPoR classes?

Self-filling histograms take pointers to FloatFun, BaseCut, and FillIterator objects, because generally it makes sense to pass no such objects: If no BaseCut is passed, no cut is applied, and if no FillIterator is passed, we have exactly one entry per ntuple row.

On the other hand, to define logical or arithmetic operators between these objects, we have to work with references: We cannot define

inline AndOfTwoCuts& operator&& (const BaseCut *lhs_, 
                                 const BaseCut *rhs_) // does not work!
{
  return *new AndOfTwoCuts (*lhs_, *rhs_);
}
because we cannot overload an operator that takes only pointers.

So, if we want not to have to write

// If SFH1F takes only pointers
SFH1F *h1 = 
  new SFH1F ("JPsiElasticTT", 
             "#muODS: m_{J/#Psi}: elastic production, (#mu-#mu)", 
             40, 2., 4.,
             *this,
             jpsiMass, 
             &(*jpsiElastic&&*jpsiTrackTrack&&*jpsiNoCosmic&&*jpsi2mu));

we would have to use references everywhere, but then we cannot pass NULL pointers anymore, at least not easily (a possible way out would be to use an ugly, dirty, mean trick like *(static_cast<BaseCut *>(0))).

The logical way out would be to provide constructors with all posible combinations of pointers and references, which would mean increasing the number of constructors by something like a factor 16.

A better possibility is to introduce a class FloatFunPoR (which stands for "FloatFun Pointer Or Reference"). This class has two constructors, taking a pointer or a reference to a FloatFun, and store the pointer (in member FloatFunPoR:pff, meaning "Pointer to FitFun"). These constructors constitute automatic conversion operators, so that if we now call the SFH1F constructor with either a pointer or a reference to a FloatFun object (or to a subclass of it), this is automatically converted to an FloatFunPoR object, which is then used by the SFH1F constructor to extract the pointer to a FloatFun.

Clever, isn't it?

(We haven't seen this trick anywhere else. Maybe we should have it patented? If amazon can get 1-click shopping patented...)

We Cannot Delete FloatFun, BaseCut, and FillIterator Objects. Isn't that a Memory Leak?

Short answer: Yes. Why do you care? How many of these objects do you plan to create?

Long answer: Needs to be written...

Why Do I Have to Create FloatFun, BaseCut, and FillIterator Objects with "new"?

To most people, it would be much more attrctive to write
  IDTrackIterator trackIter (ntuple);
  IpzFun          ipzfun (ntuple);
  PtFun           ptfun (ntuple, &trackIter);
instead of
  IDTrackIterator *trackIter   = new IDTrackIterator (ntuple);
  IpzFun          *ipzfun      = new IpzFun (ntuple);
  PtFun           *ptfunPtFun  = new PtFun (ntuple, trackIter);
or (our recommendation):
  IDTrackIterator& trackIter   = *new IDTrackIterator (ntuple);
  IpzFun&          ipzfun      = *new IpzFun (ntuple);
  PtFun&           ptfunPtFun  = *new PtFun (ntuple, &trackIter);

The problem with the first solution, using automatic variables, is this: Typically we (seem to) need all these objects only within one function, e.g. in the constructor of an EventLoop, where we book all histograms.

Automatic variables would be destroyed at the end of this function. However, the self-filling histograms store only pointers to these objects instead of copying them, so these pointers would become invalid.

But the objects are needed later to fill the histograms, so they must not be deleted too early. Allowing the use of automatic variables puts this burden on the shoulder of the user, who would have to make sure the objects live long enough, e.g. by making them data members of her EventLoop subclass.

This is burdensome and error-prone.

If, however, the objects are created with "new", they live until they are explicitly destroyed, which normally happens only when the program terminates.

Another solution would be that the self-filling histograms keep a copy of the objects. This needs an additional "clone()" method, because we need a sort of polymorphic copying. The resulting classes would look like this:

class IpzFun : public FloatFun {
  public: 
    IpzFun(Ntuple* ntuple) : nt(ntuple) {};
    virtual float operator() () const {return nt->ipz;}
    virtual IpzFun& clone() () const {return new IpzFun(*this);}
  protected: 
    Ntuple* nt;
};
The clone() method calls the automatically generated copy constructor, which in most cases is fine.

The disadvantage of this method is threefold:

Our first design actually used the clone approach, but these disadvantages convinced us to go the other way.

Writing all the Function Object Classes is Tiresome. What's the Advantage?

Object Oriented Programming is not primarily about writing less code, although we think that as soon as you use the visitors and sets or matrices of histograms, you'll find you will write less code.

In our opinion, OOP is about maintainability of code. We think that our toolbox allows you to write code that is better structured, has smaller programming units, and is therefore better to understand and modify.

When you start an analysis, you rarely know where you'll end up. The code tends to grow with time, gets more and more complicated as more and more cuts and control plots are added. This leads to software entropy, which you'll often not notice until it's too late (typically 2 weeks before you have to present your result at a conference, when you are asked to change the order of your cuts or introduce a new cut right at the beginning of your cut chain).

Reordering cuts is a real pain if you nest cuts in if-then-else structures. We find that this is much easier in our framework.

Once you start to bin your J/psi events in 10 t bins times 5 W bins, and want to plots the mass peaks for 5 different cut stages and 5 datasets (data plus 4 Monte Carlos), you'll find that managing these 6250 (count them!) histograms can be much easier by using a two-dimensional array of MatrixOfHistogram objects than hassling with 4-dimansional histogram arrays, 5 levels of if-then-else indentations and all that, 2 levels of loops and all that.

Also, plotting and fitting these histograms can be done quite efficiently in our framework.

Why Can't I Express Cuts with Strings, as in root?

We don't like string parsing at run time.

First, it's a pain to write the parser, while the compiler already has one.

Second, using objects instead of strings makes it possible to detect many errors already at compile time rather than run time. This is a general idea behind the type-safe system of C++, and one of the main advantages over other languages like Smalltalk.

Since by using C++ we already have all the disadvantages of C++, like clumsy notation, the necessity to care about variable definitions and memory leaks, we think we should also take advantage of C++'s benefits. Otherwise, we'd be much better off using another language in the first place.

Loooking at Your Class Hierarchy, You Use Multiple Inheritance a Lot. Isn't that Dangerous?

In principle, we are no big fans of multiple inheritance. If we use it, we try to confine it to mix-in classes that only add behaviour, not data to other classes, similar to "interfaces" in java. However, we do not everywhere achieve that.

Anyway, since multiple inheritance is used a lot in the root source code (by modern standards probably overused), we figured we didn't have to restrain ourselves too much.

In the Constructors of Classes SFSetOfHistograms and SFMatrixOfHistograms, the Order of Arguments is Somewhat Suboptimal. Why?

Short answer: For historical reasons.

Long answer:

In general we tried to adhere to a number of guidelines when we defined constructors:

For classes that are derived from root classes, provide constructors that take the same arguments as in root, put additional arguments (if any) behind the arguments of the root classes. Similarly, for derived classes in general we try to put additional arguments for conistructors at the end.

For SFSetOfHistograms and SFMatrixOfHistograms, we wanted to provide the same constructors as for class SFH1F, plus the BiningFun objects.

At that time, we always passed references to FloatFun and BaseCut objects, so we couldn't have default arguments, and at that time we didn't have FillIterator objects. If we did it again, we'd do it differently, but changing the order of the arguments would break all user code, and we don't think that's worth it.

In short: We did it wrong, but to correct it would mean a lot of work for our users, who are too dear to us to demand that.


Generated on Thu Oct 26 12:55:27 2006 for SFH by doxygen 1.3.2