CMS 3D CMS Logo

/data/refman/pasoursint/CMSSW_6_1_1/src/FWCore/RootAutoLibraryLoader/src/RootAutoLibraryLoader.cc

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 //
00003 // Package:     LibraryLoader
00004 // Class  :     RootAutoLibraryLoader
00005 //
00006 // Implementation:
00007 //     <Notes on implementation>
00008 //
00009 // Original Author:
00010 //         Created:  Wed Nov 30 14:55:01 EST 2005
00011 //
00012 
00013 // system include files
00014 #include <string>
00015 #include <iostream>
00016 #include <map>
00017 #include "TROOT.h"
00018 #include "G__ci.h"
00019 #include "boost/regex.hpp"
00020 
00021 // user include files
00022 #include "FWCore/RootAutoLibraryLoader/interface/RootAutoLibraryLoader.h"
00023 #include "FWCore/RootAutoLibraryLoader/src/stdNamespaceAdder.h"
00024 
00025 #include "FWCore/PluginManager/interface/PluginManager.h"
00026 #include "FWCore/PluginManager/interface/ProblemTracker.h"
00027 #include "FWCore/PluginManager/interface/PluginCapabilities.h"
00028 #include "FWCore/Utilities/interface/DictionaryTools.h"
00029 #include "FWCore/Utilities/interface/Exception.h"
00030 #include "FWCore/Utilities/interface/TypeWithDict.h"
00031 
00032 #include "Cintex/Cintex.h"
00033 #include "TClass.h"
00034 
00035 // We cannot use the MessageLogger here because this is also used by standalones that do not have the logger.
00036 
00037 //
00038 // constants, enums and typedefs
00039 //
00040 namespace {
00041   //Based on https://root.cern.ch/lxr/source/cintex/src/CINTSourceFile.h
00042   // If a Cint dictionary is accidently loaded as a side effect of loading a CMS
00043   // library Cint must have a file name assigned to that dictionary else Cint may crash
00044   class RootLoadFileSentry {
00045   public:
00046     RootLoadFileSentry() {
00047        G__setfilecontext("{CMS auto library loader}", &oldIFile_);
00048     }
00049 
00050     ~RootLoadFileSentry() {
00051       G__input_file* ifile = G__get_ifile();
00052       if (ifile) {
00053         *ifile = oldIFile_;
00054       }
00055     }
00056 
00057   private:
00058       G__input_file oldIFile_;
00059   };
00060 }
00061 
00062 //
00063 // static data member definitions
00064 //
00065 //hold onto the previous autolibrary loader
00066 typedef int (*CallbackPtr)(char*, char*);
00067 static CallbackPtr gPrevious = 0;
00068 static char const* kDummyLibName = "*dummy";
00069 
00070 //This is actually defined within ROOT's v6_struct.cxx file but is not declared static
00071 // I want to use it so that if the autoloading is already turned on, I can call the previously declared routine
00072 extern CallbackPtr G__p_class_autoloading;
00073 
00074 namespace edm {
00075    namespace {
00076 
00077       std::map<std::string, std::string>&
00078       cintToReflexSpecialCasesMap() {
00079          static std::map<std::string, std::string> s_map;
00080          return s_map;
00081       }
00082 
00083       void
00084       addWrapperOfVectorOfBuiltin(std::map<std::string, std::string>& iMap, char const* iBuiltin) {
00085          static std::string sReflexPrefix("edm::Wrapper<std::vector<");
00086          static std::string sReflexPostfix("> >");
00087 
00088          //Wrapper<vector<float, allocator<float> > >
00089          static std::string sCintPrefix("Wrapper<vector<");
00090          static std::string sCintMiddle(",allocator<");
00091          static std::string sCintPostfix("> > >");
00092 
00093          std::string type(iBuiltin);
00094          iMap.insert(make_pair(sCintPrefix + type + sCintMiddle + type + sCintPostfix,
00095                                sReflexPrefix + type + sReflexPostfix));
00096       }
00097 
00098       bool loadLibraryForClass(char const* classname) {
00099         //std::cout << "loadLibaryForClass" << std::endl;
00100         if(0 == classname) {
00101           return false;
00102         }
00103         //std::cout << "asking to find " << classname << std::endl;
00104         std::string const& cPrefix = dictionaryPlugInPrefix();
00105         //std::cout << "asking to find " << cPrefix + classname << std::endl;
00106         try {
00107           //give ROOT a name for the file we are loading
00108           RootLoadFileSentry sentry;
00109           if(edmplugin::PluginCapabilities::get()->tryToLoad(cPrefix + classname)) {
00110             TypeWithDict t(TypeWithDict::byName(classname));
00111             if(!t.isComplete()) {
00112               //would be nice to issue a warning here.  Not sure the remainder of this comment is correct.
00113               // this message happens too often (too many false positives) to be useful plus ROOT will complain about a missing dictionary
00114               //std::cerr << "Warning: ROOT knows about type '" << classname << "' but has no dictionary for it." << std::endl;
00115               return false;
00116             }
00117           } else {
00118             //see if adding a std namespace helps
00119             std::string name = root::stdNamespaceAdder(classname);
00120             //std::cout << "see if std helps" << std::endl;
00121             if (not edmplugin::PluginCapabilities::get()->tryToLoad(cPrefix + name)) {
00122               // Too many false positives on built-in types here.
00123               return false;
00124             }
00125             TypeWithDict t(TypeWithDict::byName(name));
00126             if (!bool(t)) {
00127               TypeWithDict t2(TypeWithDict::byName(name));
00128               if (!bool(t2)) {
00129                 //would be nice to issue a warning here
00130                 return false;
00131               }
00132             }
00133           }
00134         } catch(cms::Exception& e) {
00135           //would be nice to issue a warning here
00136           return false;
00137         }
00138         //std::cout << "loaded " << classname << std::endl;
00139         return true;
00140       }
00141 
00142       //Based on code in ROOT's TCint.cxx file
00143 
00144       int ALL_AutoLoadCallback(char* c, char* l) {
00145         //NOTE: if the library (i.e. 'l') is an empty string this means we are dealing with a namespace
00146         // These checks appear to avoid a crash of ROOT during shutdown of the application
00147         if(0 == c || 0 == l || l[0] == 0) {
00148           return 0;
00149         }
00150         ULong_t varp = G__getgvp();
00151         G__setgvp((long)G__PVOID);
00152         int result = loadLibraryForClass(c) ? 1:0;
00153         G__setgvp(varp);
00154         //NOTE: the check for the library is done since we can have a failure
00155         // if a CMS library has an incomplete set of reflex dictionaries where
00156         // the remaining dictionaries can be found by Cint.  If the library with
00157         // the reflex dictionaries is loaded first, then the Cint library then any
00158         // requests for a Type from the reflex library will fail because for
00159         // some reason the loading of the Cint library causes reflex to forget about
00160         // what types it already loaded from the reflex library.  This problem was
00161         // seen for libDataFormatsMath and libMathCore.  I do not print an error message
00162         // since the dictionaries are actually loaded so things work fine.
00163         if(!result && 0 != strcmp(l, kDummyLibName) && gPrevious) {
00164           result = gPrevious(c, l);
00165         }
00166         return result;
00167       }
00168 
00169       std::string
00170       classNameForRoot(std::string const& iCapName) {
00171         //need to remove any 'std::' since ROOT ignores it
00172         static boost::regex const ex("std::");
00173         std::string const to("");
00174 
00175         return regex_replace(iCapName, ex, to, boost::match_default | boost::format_sed);
00176       }
00177 
00178       //Cint requires that we register the type and library containing the type
00179       // before the autoloading will work
00180         struct CompareFirst {
00181           bool operator()(std::pair<std::string, std::string> const& iLHS,
00182                           std::pair<std::string, std::string> const& iRHS) const{
00183             return iLHS.first > iRHS.first;
00184           }
00185         };
00186 
00187       void registerTypes() {
00188         edmplugin::PluginManager* db =  edmplugin::PluginManager::get();
00189 
00190         typedef edmplugin::PluginManager::CategoryToInfos CatToInfos;
00191 
00192         CatToInfos::const_iterator itFound = db->categoryToInfos().find("Capability");
00193 
00194         if(itFound == db->categoryToInfos().end()) {
00195           return;
00196         }
00197 
00198         //in order to determine if a name is from a class or a namespace, we will order
00199         // all the classes in descending order so that embedded classes will be seen before
00200         // their containing classes, that way we can say the containing class is a namespace
00201         // before finding out it is actually a class
00202         typedef std::vector<std::pair<std::string, std::string> > ClassAndLibraries;
00203         ClassAndLibraries classes;
00204         classes.reserve(1000);
00205         std::string lastClass;
00206 
00207         //find where special cases come from
00208         std::map<std::string, std::string> specialsToLib;
00209         std::map<std::string, std::string> const& specials = cintToReflexSpecialCasesMap();
00210         for(std::map<std::string, std::string>::const_iterator itSpecial = specials.begin();
00211             itSpecial != specials.end();
00212             ++itSpecial) {
00213           specialsToLib[classNameForRoot(itSpecial->second)];
00214         }
00215         std::string const& cPrefix = dictionaryPlugInPrefix();
00216         for (edmplugin::PluginManager::Infos::const_iterator itInfo = itFound->second.begin(),
00217              itInfoEnd = itFound->second.end();
00218              itInfo != itInfoEnd; ++itInfo) {
00219           if (lastClass == itInfo->name_) {
00220             continue;
00221           }
00222           lastClass = itInfo->name_;
00223           if(cPrefix == lastClass.substr(0, cPrefix.size())) {
00224             std::string className = classNameForRoot(lastClass.c_str() + cPrefix.size());
00225             classes.push_back(std::pair<std::string, std::string>(className, itInfo->loadable_.string()));
00226             std::map<std::string, std::string>::iterator itFound = specialsToLib.find(className);
00227             if(itFound != specialsToLib.end()) {
00228               // std::cout << "Found " << lastClass << " : " << className << std::endl;
00229               itFound->second = itInfo->loadable_.string();
00230             }
00231           }
00232         }
00233         //sort_all(classes, std::greater<std::string>());
00234         //sort_all(classes, CompareFirst());
00235         //the values are already sorted by less, so just need to reverse to get greater
00236         for(ClassAndLibraries::reverse_iterator itClass = classes.rbegin(), itClassEnd = classes.rend();
00237             itClass != itClassEnd;
00238             ++itClass) {
00239 
00240           std::string const& className = itClass->first;
00241           std::string const& libraryName = itClass->second;
00242           //need to register namespaces and figure out if we have an embedded class
00243           static std::string const toFind(":<");
00244           std::string::size_type pos = 0;
00245           while(std::string::npos != (pos = className.find_first_of(toFind, pos))) {
00246             if (className[pos] == '<') {break;}
00247             if (className.size() <= pos + 1 || className[pos + 1] != ':') {break;}
00248             //should check to see if this is a class or not
00249             G__set_class_autoloading_table(const_cast<char*>(className.substr(0, pos).c_str()), const_cast<char*>(""));
00250             //std::cout << "namespace " << className.substr(0, pos).c_str() << std::endl;
00251             pos += 2;
00252           }
00253           G__set_class_autoloading_table(const_cast<char*>(className.c_str()), const_cast<char*>(libraryName.c_str()));
00254           //std::cout << "class " << className.c_str() << std::endl;
00255         }
00256 
00257         //now handle the special cases
00258         for(std::map<std::string, std::string>::const_iterator itSpecial = specials.begin();
00259             itSpecial != specials.end();
00260             ++itSpecial) {
00261           //std::cout << "registering special " << itSpecial->first << " " << itSpecial->second << " " << std::endl << "    " << specialsToLib[classNameForRoot(itSpecial->second)] << std::endl;
00262           //force loading of specials
00263           if(specialsToLib[classNameForRoot(itSpecial->second)].size()) {
00264             //std::cout << "&&&&& found special case " << itSpecial->first << std::endl;
00265             std::string name = itSpecial->second;
00266             TypeWithDict t(TypeWithDict::byName(name));
00267 
00268             if(!bool(t) and
00269                 (not edmplugin::PluginCapabilities::get()->tryToLoad(cPrefix + name))) {
00270               std::cout << "failed to load plugin for " << cPrefix + name << std::endl;
00271               continue;
00272             } else {
00273               //need to construct the Class ourselves
00274               TypeWithDict t(TypeWithDict::byName(name));
00275               if(!bool(t)) {
00276                 std::cout << "dictionary did not build " << name << std::endl;
00277                 continue;
00278               }
00279               TClass* namedClass = TClass::GetClass(t.typeInfo());
00280               if(0 == namedClass) {
00281                 std::cout << "failed to get TClass by typeid for " << name << std::endl;
00282                 continue;
00283               }
00284               namedClass->Clone(itSpecial->first.c_str());
00285               std::string magictypedef("namespace edm { typedef ");
00286               magictypedef += classNameForRoot(name) + " " + itSpecial->first + "; }";
00287               // std::cout << "Magic typedef " << magictypedef << std::endl;
00288               gROOT->ProcessLine(magictypedef.c_str());
00289             }
00290           }
00291         }
00292       }
00293    }
00294 
00295    //
00296    // constructors and destructor
00297    //
00298    RootAutoLibraryLoader::RootAutoLibraryLoader() :
00299 #if ROOT_VERSION_CODE >= ROOT_VERSION(5,27,6)
00300      classNameAttemptingToLoad_(0),
00301      isInitializingCintex_(true) {
00302 #else
00303      classNameAttemptingToLoad_(0) {
00304 #endif
00305       AssertHandler h;
00306       gROOT->AddClassGenerator(this);
00307       ROOT::Cintex::Cintex::Enable();
00308 #if ROOT_VERSION_CODE >= ROOT_VERSION(5,27,6)
00309       isInitializingCintex_ =false;
00310 #endif
00311       //set the special cases
00312       std::map<std::string, std::string>& specials = cintToReflexSpecialCasesMap();
00313       if(specials.empty()) {
00314          addWrapperOfVectorOfBuiltin(specials,"bool");
00315 
00316          addWrapperOfVectorOfBuiltin(specials,"char");
00317          addWrapperOfVectorOfBuiltin(specials,"unsigned char");
00318          addWrapperOfVectorOfBuiltin(specials,"signed char");
00319          addWrapperOfVectorOfBuiltin(specials,"short");
00320          addWrapperOfVectorOfBuiltin(specials,"unsigned short");
00321          addWrapperOfVectorOfBuiltin(specials,"int");
00322          addWrapperOfVectorOfBuiltin(specials,"unsigned int");
00323          addWrapperOfVectorOfBuiltin(specials,"long");
00324          addWrapperOfVectorOfBuiltin(specials,"unsigned long");
00325          addWrapperOfVectorOfBuiltin(specials,"long long");
00326          addWrapperOfVectorOfBuiltin(specials,"unsigned long long");
00327 
00328          addWrapperOfVectorOfBuiltin(specials,"float");
00329          addWrapperOfVectorOfBuiltin(specials,"double");
00330       }
00331       //std::cout << "my loader" << std::endl;
00332       //remember if the callback was already set so we can chain together our results
00333       gPrevious = G__p_class_autoloading;
00334       G__set_class_autoloading_callback(&ALL_AutoLoadCallback);
00335       registerTypes();
00336    }
00337 
00338    //
00339    // member functions
00340    //
00341 
00342    TClass*
00343    RootAutoLibraryLoader::GetClass(char const* classname, Bool_t load) {
00344       TClass* returnValue = 0;
00345       if(classNameAttemptingToLoad_ != 0 && !strcmp(classname, classNameAttemptingToLoad_)) {
00346          // We can try to see if the class name contains "basic_string<char>".
00347          // If so, we replace "basic_string<char>" with "string" and try again.
00348          std::string className(classname);
00349          std::string::size_type idx = className.find("basic_string<char>");
00350          if (idx != std::string::npos) {
00351             className.replace(idx, 18, std::string("string"));
00352             //if basic_string<char> was the last argument to a templated class
00353             // then there would be an extra space to separate the two '>'
00354             if(className.size() > idx + 6 && className[idx + 6] == ' ') {
00355               className.replace(idx + 6, 1, "");
00356             }
00357             classNameAttemptingToLoad_ = className.c_str();
00358             returnValue = gROOT->GetClass(className.c_str(), kTRUE);
00359             classNameAttemptingToLoad_ = classname;
00360             return returnValue;
00361          }
00362          //NOTE: As of ROOT 5.27.06 this warning generates false positives for HepMC classes because
00363          // ROOT has special handling for them built into class.rules
00364          //std::cerr << "WARNING[RootAutoLibraryLoader]: ROOT failed to create CINT dictionary for " << classname << std::endl;
00365          return 0;
00366       }
00367       //std::cout << "looking for " << classname << " load " << (load? "T":"F") << std::endl;
00368       if (load) {
00369         //std::cout << " going to call loadLibraryForClass" << std::endl;
00370         //[ROOT 5.28] When Cintex is in its 'Enable' method it will register callbacks to build
00371         // TClasses. During this phase we do not want to actually force TClasses to have to 
00372         // come into existence.
00373 #if ROOT_VERSION_CODE >= ROOT_VERSION(5,27,6)
00374         if (not isInitializingCintex_ and loadLibraryForClass(classname)) {
00375 #else
00376          if (loadLibraryForClass(classname)) {
00377 #endif
00378           //use this to check for infinite recursion attempt
00379           classNameAttemptingToLoad_ = classname;
00380           // This next call will create the TClass object for the class.
00381           // It will also attempt to load the dictionary for the class
00382           // if the second argument is kTRUE. This is the default, so it
00383           // need not be explicitly specified.
00384           returnValue = gROOT->GetClass(classname, kTRUE);
00385           classNameAttemptingToLoad_ = 0;
00386         }
00387       }
00388       return returnValue;
00389    }
00390 
00391    TClass*
00392    RootAutoLibraryLoader::GetClass(type_info const& typeinfo, Bool_t load) {
00393      //std::cout << "looking for type " << typeinfo.name() << std::endl;
00394       TClass* returnValue = 0;
00395       if(load) {
00396          return GetClass(typeinfo.name(), load);
00397       }
00398       return returnValue;
00399    }
00400 
00401    void
00402    RootAutoLibraryLoader::enable() {
00403       //static BareRootProductGetter s_getter;
00404       //static EDProductGetter::Operate s_op(&s_getter);
00405       static RootAutoLibraryLoader s_loader;
00406    }
00407 
00408    void
00409    RootAutoLibraryLoader::loadAll() {
00410      // std::cout << "LoadAllDictionaries" << std::endl;
00411      enable();
00412 
00413      edmplugin::PluginManager* db =  edmplugin::PluginManager::get();
00414 
00415      typedef edmplugin::PluginManager::CategoryToInfos CatToInfos;
00416 
00417      CatToInfos::const_iterator itFound = db->categoryToInfos().find("Capability");
00418 
00419      if(itFound == db->categoryToInfos().end()) {
00420        return;
00421      }
00422      std::string lastClass;
00423 
00424      //give ROOT a name for the file we are loading
00425      RootLoadFileSentry sentry;
00426 
00427      for (edmplugin::PluginManager::Infos::const_iterator itInfo = itFound->second.begin(),
00428           itInfoEnd = itFound->second.end();
00429           itInfo != itInfoEnd; ++itInfo) {
00430        if (lastClass == itInfo->name_) {
00431          continue;
00432        }
00433 
00434        lastClass = itInfo->name_;
00435        edmplugin::PluginCapabilities::get()->load(lastClass);
00436        //NOTE: since we have the library already, we could be more efficient if we just load it ourselves
00437      }
00438    }
00439 }
00440 
00441 //ClassImp(RootAutoLibraryLoader)