Commits

Anonymous committed 9ff079f

Add PyROOTConstFix.

Comments (0)

Files changed (8)

+2007-10-11  scott snyder  <snyder@bnl.gov>
+
+	* Tagging RootUtils-00-00-04.
+
+	* python/PyROOTFixes.py: (new)
+	* cmt/requirements: Install python files.
+
+2007-10-10  scott snyder  <snyder@bnl.gov>
+
+	* RootUtils/PyROOTConstFix.h: (new)
+	* src/PyROOTConstFix.cxx: (new)
+	* RootUtils/selection.xml: Add it.
+	* RootUtils/RootUtilsDict.h: Add it.
+
 2007-10-09  scott snyder  <snyder@bnl.gov>
 
 	* Tagging RootUtils-00-00-03.

RootUtils/PyROOTConstFix.h

+// This file's extension implies that it's C, but it's really -*- C++ -*-.
+// $Id: PyROOTConstFix.h,v 1.1 2007-10-11 21:49:02 ssnyder Exp $
+
+/**
+ * @file RootUtils/PyROOTConstFix.h
+ * @author scott snyder
+ * @date Oct 2007
+ * @brief Work around pyroot const bug.
+ */
+
+
+#ifndef ROOTUTILS_PYROOTCONSTFIX_H
+#define ROOTUTILS_PYROOTCONSTFIX_H
+
+
+#include <string>
+
+
+namespace RootUtils {
+
+
+/**
+ * @brief Work around pyroot const bug.
+ *
+ * In root 5.14.00, pyroot has a problem with how const vs. non-const
+ * overloads are handled.  Given two overloads that match with the exception
+ * of the const qualifier, pyroot will call the non-const version.
+ * But when it converts the returned value to python form, the type
+ * it uses is that of the first overload.  If the return types of the
+ * two overloads are not layout-compatible, then this can cause crashes.
+ *
+ * This is a problem in particular for DataVector.  There, for the element
+ * access methods, the const method returns a pointer directly, while
+ * the non-const method returns a proxy object.  PyROOT was calling
+ * the non-const version but then converting the result as if it were
+ * a pointer.
+ *
+ * As a workaround, the code here will overwrite the name of the
+ * const overload with trash, effectively hiding it from pyroot.
+ */
+class PyROOTConstFix
+{
+public:
+  /**
+   * @brief Fix one method.
+   * @param classname The name of the class containing the method to fix.
+   * @param funcname The name of the method to fix.
+   *
+   * Any const overload of the named method will have its name
+   * overwritten in the cint dictionary, effectively hiding it from pyroot.
+   */
+  static void fix_one (const char* classname, const char* funcname);
+
+
+  /**
+   * @brief Fix all DataVector methods of a given class.
+   * @param classname The name of the class to fix.
+   *
+   * This will apply @c fix_one to all of the DataVector methods
+   * affected by this problem.
+   */
+  static void fix_all (const char* classname);
+
+
+  /*
+   * @brief Test for a DataVector class and apply fix if needed.
+   * @param classname The name of the class to test.
+   * @returns True if the class or one of its bases was fixed, false otherwise.
+   *
+   * This function will test @c classname and any base classes
+   * to see if they are DataVector classes.  If so, @c fix_all
+   * will be applied to the class, and the function will return true.
+   */
+  static bool test_dv (const char* classname);
+};
+
+
+} // namespace RootUtils
+
+
+#endif // not ROOTUTILS_PYROOTCONSTFIX_H

RootUtils/RootUtilsDict.h

 // This file's extension implies that it's C, but it's really -*- C++ -*-.
-// $Id: RootUtilsDict.h,v 1.3 2007-10-09 21:34:03 ssnyder Exp $
+// $Id: RootUtilsDict.h,v 1.4 2007-10-11 21:49:02 ssnyder Exp $
 
 /**
  * @file  RootUtils/RootUtilsDict.h
 #include "RootUtils/InitHist.h"
 #include "RootUtils/StdHackGenerator.h"
 #include "RootUtils/ScanForAbstract.h"
+#include "RootUtils/PyROOTConstFix.h"

RootUtils/selection.xml

     <class name="RootUtils::InitHist" />
     <class name="RootUtils::StdHackGenerator" />
     <class name="RootUtils::ScanForAbstract" />
+    <class name="RootUtils::PyROOTConstFix" />
 </lcgdict>
 apply_pattern installed_library
 library RootUtils  *.cxx
 
+apply_pattern declare_python_modules files="*.py"
+
 macro_append DOXYGEN_INPUT " ../doc" 
 
 private

python/PyROOTFixes.py

+"""Functions to work around PyROOT bugs for accessing DataVector.
+
+:author: scott snyder
+:contact: snyder@bnl.gov
+
+PyROOT (as of root 5.14.00h) has a couple bugs that bite us when accessing
+DataVector classes.
+
+ - The element accessors of DataVector return different types for the
+   const and non-const versions.  These are not just const and non-const
+   qualified versions of the same type, but distinct, layout-incompatible
+   types.  (The const version returns a reference directly, while the
+   non-const version returns a proxy object.)  PyROOT gets confused
+   in such a case: it will call the non-const method, but it will then
+   use the const return type to convert the return value.
+   As a workaround, when we see that we have
+   a DataVector class, we overwrite the names of the non-const methods
+   with trash.  This prevents PyROOT from considering them for overload
+   resolution, resolving the issue.
+
+ - Attempts to use element access via [] on a class deriving from DataVector
+   will cause an infinite recursion.  We also fix this up.
+
+Both of these workarounds are activated for a DataVector class (or a class
+deriving from DataVector) by calling fix_dv_container with the class name.
+
+
+"""
+__docformat__ = "restructuredtext en"
+
+import ROOT
+def _fix_getitem_unchecked (cl):
+    giu = '_getitem__unchecked'
+    if hasattr (cl, giu) and getattr (cl, giu) == getattr (cl, '__getitem__'):
+        for b in cl.__bases__:
+            _fix_getitem_unchecked (b)
+            if hasattr (b, giu):
+                setattr (cl, giu, getattr (b, giu))
+                return
+    return
+
+
+def fix_dv_container (clname):
+    """Work around PyROOT bugs for a DataVector class"""
+    ROOT.RootUtils.PyROOTConstFix.test_dv(clname)
+    cl = getattr (ROOT, clname)
+    if not cl: return
+    return _fix_getitem_unchecked (cl)
+
+def fix_method (clname, methname):
+    """Fix a single method of a class"""
+    return ROOT.RootUtils.PyROOTConstFix.fix_one (clname, methname)

python/__init__.py

Empty file added.

src/PyROOTConstFix.cxx

+// $Id: PyROOTConstFix.cxx,v 1.1 2007-10-11 21:49:03 ssnyder Exp $
+/**
+ * @file RootUtils/src/PyROOTConstFix.cxx
+ * @author scott snyder
+ * @date Oct 2007
+ * @brief Work around pyroot const bug.
+ */
+
+#include "RootUtils/PyROOTConstFix.h"
+#include "TROOT.h"
+#include "CallFunc.h"
+#include "Class.h"
+#include "Method.h"
+#include "TBaseClass.h"
+#include "TClass.h"
+
+
+namespace RootUtils {
+
+
+/**
+ * @brief Fix one method.
+ * @param classname The name of the class containing the method to fix.
+ * @param funcname The name of the method to fix.
+ *
+ * Any const overload of the named method will have its name
+ * overwritten in the cint dictionary, effectively hiding it from pyroot.
+ */
+void PyROOTConstFix::fix_one (const char* classname, const char* funcname)
+{
+  if (gROOT->GetClass (classname, kTRUE) == 0) return;
+
+  G__ClassInfo ci (classname);
+  if (!ci.IsValid()) return;
+
+  G__MethodInfo gmi (ci);
+  while (gmi.Next()) {
+    if (strcmp (gmi.Name(), funcname) == 0) {
+      // ??? Ignoring arg match here!!!!
+      if (!(gmi.Property() & G__BIT_ISCONSTANT)) {
+        // Trash the name so that PyROOT won't see it.
+        char* nm = (char*)gmi.Name();
+        while (*nm) {
+          *nm++ = '#';
+        }
+      }
+    }
+  }
+}
+
+
+/**
+ * @brief Fix all DataVector methods of a given class.
+ * @param classname The name of the class to fix.
+ *
+ * This will apply @c fix_one to all of the DataVector methods
+ * affected by this problem.
+ */
+void PyROOTConstFix::fix_all (const char* classname)
+{
+  fix_one (classname, "at");
+  fix_one (classname, "front");
+  fix_one (classname, "back");
+  fix_one (classname, "begin");
+  fix_one (classname, "end");
+  fix_one (classname, "operator[]");
+}
+
+
+/*
+ * @brief Test for a DataVector class and apply fix if needed.
+ * @param classname The name of the class to test.
+ * @returns True if the class or one of its bases was fixed, false otherwise.
+ *
+ * This function will test @c classname and any base classes
+ * to see if they are DataVector classes.  If so, @c fix_all
+ * will be applied to the class, and the function will return true.
+ */
+bool PyROOTConstFix::test_dv (const char* classname)
+{
+  // Use name rather than classname to handle typedefs properly.
+  TClass* cl = gROOT->GetClass (classname, kTRUE);
+  if (!cl) return false;
+  const char* name = cl->GetName();
+
+  // If it's a DataVector, fix it.
+  if (strncmp (name, "DataVector<", 11) == 0) {
+    fix_all (name);
+    return true;
+  }
+
+  // Scan base classes too.
+  TIter next (cl->GetListOfBases());
+  while (TBaseClass* bcl = dynamic_cast<TBaseClass*>(next())) {
+    TClass* cc = bcl->GetClassPointer();
+    if (cc) {
+      if (test_dv (cc->GetName()))
+        return true;
+    }
+  }
+  return false;
+}
+
+
+} // namespace RootUtils