I have lost a bit of time recently due to some features of the TFormula class which do not seem to be adequately documented. So I thought I'd summarize them here for everyone's reference. These comments refer to ROOT 3.05/04, but should apply equally to virtually all ROOT versions back to 2.x. - TFormula violates a common good C++ programming practice: Never call a virtual function from a constructor! TFormula calls the virtual method Compile() in its non-default constructor. While this is convenient, it causes the usual problems associated with such style, viz. unexpected and/or incorrect behavior and the need for code duplication in derived classes. The source code comments are outright misleading and really should be changed! Compile() calls Analyze(), which calls DefinedVariable(). DefinedVariable()'s documentation reads "This member function can be overloaded in derived classes". Good luck doing so - it does not work unless one also re-implements the TFormula constructor in the derived class and calls Compile() from there. This would be ok (unless one is a style perfectionist) if only it was properly documented. Documentation improvements would be highly welcome. - DefinedVariable() is supposed to work in conjunction with DefinedValue(). However, it seems that some initialization is missing in TFormula::Compile() to make this work reliably. While Compile() resets virtually everything else, it does _not_ reset the two member variables fNval and fAlreadyFound which control the behavior of the DefinedVariable/ DefinedValue mechanism. As a result, re-compiling an object derived from TFormula that uses this mechanism may produce unpredicable results. For instance, suppose DefinedVariable("x") returns 0 when the object is constructed. Then some data changes (e.g. a new configuration is loaded) and DefinedVariable("x") now returns 1. The application recompiles all formulas as a result of the configuration changes, which should take care of things. Mysteriously, however, some formulas now fail to produce sensible results. The reason is that during recompilation the object may think that a variable is already defined when it isn't, or use incorrect indices for variables during evaluation (in the example, DefinedValue(0) might be called to get the value of "x" when the call should be DefinedValue(1)). Whether or not this occurs depends on the particulars of how DefinedVariable/DefinedValue are implemented and on the expression used. That fNval and fAlreadyFound are not reset in Compile() looks like a bug that has gone unnoticed so far. If it is, could it be corrected? Until it is, people need to work around it by implemeting a Compile() method in the derived class to reset those status variables. Best regards, Ole Hansen Jefferson Lab
This archive was generated by hypermail 2b29 : Thu Jan 01 2004 - 17:50:12 MET