// @(#)root/reflex:$Id: Tools.cxx 23671 2008-05-07 09:41:12Z axel $
// Author: Stefan Roiser 2004

// Copyright CERN, CH-1211 Geneva 23, 2004-2006, All rights reserved.
//
// Permission to use, copy, modify, and distribute this software for any
// purpose is hereby granted without fee, provided that this copyright and
// permissions notice appear in all copies and derivatives.
//
// This software is provided "as is" without express or implied warranty.

#ifndef REFLEX_BUILD
#define REFLEX_BUILD
#endif

#include "Reflex/Tools.h"

#include "Reflex/Kernel.h"
#include "Reflex/Type.h"
#include "Reflex/internal/OwnedMember.h"
#include <cstring>

#if defined(__GNUC__)
#include <cxxabi.h>
#elif defined(__SUNPRO_CC)
#include <demangle.h>
#endif

using namespace Reflex;

namespace FTypes {
   static const std::type_info & Char()       { static const std::type_info & t = Type::ByName("char").TypeInfo();               return t; }
   static const std::type_info & SigChar()    { static const std::type_info & t = Type::ByName("signed char").TypeInfo();        return t; }
   static const std::type_info & ShoInt()     { static const std::type_info & t = Type::ByName("short int").TypeInfo();          return t; }
   static const std::type_info & Int()        { static const std::type_info & t = Type::ByName("int").TypeInfo();                return t; }
   static const std::type_info & LonInt()     { static const std::type_info & t = Type::ByName("long int").TypeInfo();           return t; }
   static const std::type_info & UnsChar()    { static const std::type_info & t = Type::ByName("unsigned char").TypeInfo();      return t; }
   static const std::type_info & UnsShoInt()  { static const std::type_info & t = Type::ByName("unsigned short int").TypeInfo(); return t; }
   static const std::type_info & UnsInt()     { static const std::type_info & t = Type::ByName("unsigned int").TypeInfo();       return t; }
   static const std::type_info & UnsLonInt()  { static const std::type_info & t = Type::ByName("unsigned long int").TypeInfo();  return t; }
   static const std::type_info & Bool()       { static const std::type_info & t = Type::ByName("bool").TypeInfo();               return t; }
   static const std::type_info & Float()      { static const std::type_info & t = Type::ByName("float").TypeInfo();              return t; }
   static const std::type_info & Double()     { static const std::type_info & t = Type::ByName("double").TypeInfo();             return t; }
   static const std::type_info & LonDouble()  { static const std::type_info & t = Type::ByName("long double").TypeInfo();        return t; }
   static const std::type_info & Void()       { static const std::type_info & t = Type::ByName("void").TypeInfo();               return t; }
   static const std::type_info & LonLong()    { static const std::type_info & t = Type::ByName("long long").TypeInfo();          return t; }
   static const std::type_info & UnsLonLong() { static const std::type_info & t = Type::ByName("unsigned long long").TypeInfo(); return t; }
}

//-------------------------------------------------------------------------------
static std::string splitScopedName( const std::string & nam, 
                                    bool returnScope,
                                    bool startFromLeft = false ) {
//-------------------------------------------------------------------------------
// Split a scoped name. If returnScope is true return the scope part otherwise
// the base part. If startFromLeft is true, parse from left otherwise from the end.
   size_t pos = 0;
   if ( startFromLeft ) pos = Tools::GetFirstScopePosition( nam );
   else                 pos = Tools::GetBasePosition( nam ); 
   if ( pos != 0 ) {
      if ( returnScope )  return nam.substr(0,pos-2);
      else                return nam.substr(pos);
   }
   else {
      if ( returnScope )  return "";
      else                return nam;
   }

}


//-------------------------------------------------------------------------------
EFUNDAMENTALTYPE Tools::FundamentalType( const Type & typ ) {
//-------------------------------------------------------------------------------
// Return an enum representing the fundamental type passed in.
   const std::type_info & tid = typ.FinalType().TypeInfo();
   
   if ( tid == FTypes::Char() )         return kCHAR;
   if ( tid == FTypes::SigChar() )      return kSIGNED_CHAR; 
   if ( tid == FTypes::ShoInt() )       return kSHORT_INT; 
   if ( tid == FTypes::Int() )          return kINT; 
   if ( tid == FTypes::LonInt() )       return kLONG_INT; 
   if ( tid == FTypes::UnsChar() )      return kUNSIGNED_CHAR; 
   if ( tid == FTypes::UnsShoInt() )    return kUNSIGNED_SHORT_INT; 
   if ( tid == FTypes::UnsInt() )       return kUNSIGNED_INT; 
   if ( tid == FTypes::UnsLonInt() )    return kUNSIGNED_LONG_INT; 
   if ( tid == FTypes::Bool() )         return kBOOL; 
   if ( tid == FTypes::Float() )        return kFLOAT; 
   if ( tid == FTypes::Double() )       return kDOUBLE; 
   if ( tid == FTypes::LonDouble() )    return kLONG_DOUBLE; 
   if ( tid == FTypes::Void() )         return kVOID; 
   if ( tid == FTypes::LonLong() )      return kLONGLONG; 
   if ( tid == FTypes::UnsLonLong() )   return kULONGLONG; 
   
   return kNOTFUNDAMENTAL;

}


//-------------------------------------------------------------------------------
std::string Tools::BuildTypeName( Type & t,
                                  unsigned int /* modifiers */ ) {
//-------------------------------------------------------------------------------
// Build a complete qualified type name.
   std::string mod = "";
   if ( t.IsConstVolatile()) mod = "const volatile";
   else if ( t.IsConst())    mod = "const";
   else if ( t.IsVolatile()) mod = "volatile";

   std::string name = t.Name();

   if (t.IsPointer() || t.IsPointerToMember()) name += " " + mod;
   else                                        name = mod + " " + name;

   if ( t.IsReference()) name += "&";

   return name;
}


//-------------------------------------------------------------------------------
std::vector<std::string> Tools::GenTemplateArgVec( const std::string & Name ) {
//-------------------------------------------------------------------------------
// Return a vector of template arguments from a template type string.

   std::vector<std::string> vec;
   std::string tname;
   GetTemplateComponents( Name, tname, vec);
   return vec;
}

void Tools::GetTemplateComponents( const std::string & Name, std::string &templatename, std::vector<std::string> &args) {

   // Return the template name and a vector of template arguments.

   size_t basepos = Tools::GetBasePosition( Name );
   const char *argpos = strstr( Name.c_str()+basepos, "<" );

   if (!argpos) return;

   size_t pos1 = argpos - Name.c_str();

   templatename = Name.substr(0, pos1);
   
   size_t pos2 = Name.rfind(">");

   if ( pos2 == std::string::npos ) return; 

   std::string tpl = Name.substr(pos1+1, pos2-pos1-1);
   if (tpl[tpl.size()-1] == ' ') {
      tpl = tpl.substr(0,tpl.size()-1);
   }
   unsigned int par = 0;
   std::string argName;
   char c = 0;
   for (std::string::size_type i = 0; i < tpl.length(); ++i) {
      c = tpl[i];
      if ( c == ',' ) {
         if ( ! par ) {
            StringStrip(argName);
            args.push_back(argName);
            argName = "";
         }
         else {
            argName += c;
         }
      }
      else {
         argName += c;
         if ( c == '<' ) ++par;
         else if ( c == '>' ) --par;
      }
   }
   if ( argName.length() ) {
      StringStrip(argName);
      args.push_back(argName);
   }
   return;
}


//-------------------------------------------------------------------------------
size_t Tools::GetBasePosition(const std::string& name) {
//-------------------------------------------------------------------------------
// -- Get the position of the base part of a scoped name.
   //
   // Remove the template part of the name <...>,
   // but we must be careful of:
   //
   //       operator<,   operator>,
   //       operator<=,  operator>=,
   //       operator<<,  operator>>,
   //       operator<<=, operator>>=
   //       operator->,  operator->*,
   //       operator()
   //
   int ab = 0;
   int rb = 0;
   size_t pos = 0;
   for (int i = name.size() - 1; (i >= 0) && !pos; --i) {
      switch (name[i]) {
         case '>':
            {
               int j = i - 1;
               if (j > -1) {
                  if ((name[j] == '-') || (name[j] == '>')) {
                     --j;
                  }
               }
               for ( ; (j > -1) && (name[j] == ' '); --j) {}
               if ((j > -1) && (name[j] == 'r') && ((j - 7) > -1)) {
                  // -- We may have an operator name.
                  if (name.substr(j - 7, 8) == "operator") {
                     i = j - 8;
                     break;
                  }
               }
               ab++;
            }
            break;
         case '<':
            {
               int j = i - 1;
               if (j > -1) {
                  if (name[j] == '<') {
                     --j;
                  }
               }
               for ( ; (j > -1) && (name[j] == ' '); --j) {}
               if ((j > -1) && (name[j] == 'r') && ((j - 7) > -1)) {
                  // -- We may have an operator name.
                  if (name.substr(j - 7, 8) == "operator") {
                     i = j - 8;
                     break;
                  }
               }
            }
            ab--;
            break;
         case ')':
            {
               int j = i - 1;
               for ( ; (j > -1) && (name[j] == ' '); --j) {}
               if (j > -1) {
                  if (name[j] == '(') {
                     --j;
                     for ( ; (j > -1) && (name[j] == ' '); --j) {}
                     if ((j > -1) && (name[j] == 'r') && ((j - 7) > -1)) {
                        // -- We may have an operator name.
                        if (name.substr(j - 7, 8) == "operator") {
                           i = j - 8;
                           break;
                        }
                     }
                  }
               }
            }
            rb++;
            break;
         case '(':
            {
               int j = i - 1;
               for ( ; (j > -1) && (name[j] == ' '); --j) {}
               if ((j > -1) && (name[j] == 'r') && ((j - 7) > -1)) {
                  // -- We may have an operator name.
                  if (name.substr(j - 7, 8) == "operator") {
                     i = j - 8;
                     break;
                  }
               }
            }
            rb--;
            break;
         case ':':
            if (!ab && !rb && i && (name[i-1] == ':')) {
               pos = i + 1;
            }
            break;
      }
   }
   return pos;
}


//-------------------------------------------------------------------------------
size_t Tools::GetFirstScopePosition( const std::string & name ) {
//-------------------------------------------------------------------------------
// Get the position of the first scope of a scoped name.
   int b = 0;
   size_t pos = 0;
   unsigned int i = 0;

   for ( i = 0; i < name.size(); ++i ) {
      switch (name[i]) {
      case '<':
      case '(':
         b++; break;
      case '>':
      case ')':
         b--; break;
      case ':':
         if ( b == 0 && name[i+1] == ':' ) {
            pos = i + 2;
            break;
         }
      default: continue;
      }
      if ( pos ) break;
   }
   return pos;
}


//-------------------------------------------------------------------------------
std::string Tools::GetScopeName( const std::string & name,
                                 bool startFromLeft ){
//-------------------------------------------------------------------------------
// Get the scope of a name. Start either from the beginning (startfFromLeft=true) or end.
   return splitScopedName( name, true, startFromLeft );
}


//-------------------------------------------------------------------------------
std::string Tools::GetBaseName( const std::string & name,
                                bool startFromLeft ) {
//-------------------------------------------------------------------------------
// Get the base of a name. Start either from the beginning (startFromLeft=true) or end.
   return splitScopedName( name, false, startFromLeft );
}


//-------------------------------------------------------------------------------
bool Tools::IsTemplated(const char * name ) {
//-------------------------------------------------------------------------------
// Check if a type name is templated.
// Only check the current scope, i.e. IsTemplated("A<T>::B") will return false!
// Functions are treated as templated if they have an explicit template argument
// in front of their argument list, e.g. both "operator A<T>()" and "int f<T>()"
// are determined to be templated.

   // Watch out for A<T>::op>() - which we treat as non templated!
   // So a trailing '>' is not sufficient, even if there is a '<' in
   // the type name.

   // level of nested parantheses, e.g. for the _not_ templated
   // f(A<int>(*)(B<float>))
   unsigned int paraLevel = 0;

   size_t len = strlen(name);

   if (len==0) return false;

   for (size_t i = len - 1; i > 0; --i) {
      if (name[i] == ')')
         ++paraLevel;
      else if (name[i] == '(') {
         if (paraLevel)
            --paraLevel;
         else
            std::cerr << "Reflex::Tool::IsTemplated(): ERROR: level of parantheses < 0 for "
                      << name << std::endl;
      } else if (!paraLevel) {
         if (name[i] == ':' && name[i - 1] == ':')
            // Reached the first scope that is not in a template arg.
            // Only the last scope is tested here, so this type name
            // is not templated.
            return false;
         if (name[i] == '>') {
            // check for operator> or operator>> or operator->
            if (i > 7) {
               int j = i;
               if (name[i - 1] == '>' || name[i - 1] == '-') --j;
               while (j && isspace(name[j]))
                  --j;
               if (j > 7 && !strncmp(name + j - 8, "operator", 8))
                  // it's the operator!
                  return false;
            }
            // not the operator> or operator>>
            return true;
         }
      }
   }
   return false;
   /* alpha 
      size_t i = strlen(name)-1;
      while (name[i] == ' ') --i;
      if (( name[i] == '>' ) && ( strchr(name,'<') != 0 )) return true;
      return false;
   */
}


//-------------------------------------------------------------------------------
void Tools::StringSplit( std::vector < std::string > & splitValues, 
                         const std::string & str,
                         const std::string & delim ) {
//-------------------------------------------------------------------------------
// Split a string by a delimiter and return it's vector of strings.
   if (!str.size()) {
      return;
   }

   std::string str2 = str;
  
   size_t pos = 0;
  
   while (( pos = str2.find_first_of( delim )) != std::string::npos ) {
      std::string s = str2.substr(0, pos);
      StringStrip( s );
      splitValues.push_back( s );
      str2 = str2.substr( pos + delim.length());
   }
  
   StringStrip( str2 );
   splitValues.push_back( str2 );
}


//-------------------------------------------------------------------------------
std::string Tools::StringVec2String( const std::vector<std::string> & vec ) {
//-------------------------------------------------------------------------------
   std::string s = "";
   StdString_Iterator lastbutone = vec.end()-1;
   for( StdString_Iterator it = vec.begin(); it != vec.end(); ++it) {
      s += *it;
      if (it != lastbutone ) s += ", "; 
   }
   return s;
}


//-------------------------------------------------------------------------------
std::string Tools::Demangle( const std::type_info & ti ) { 
//-------------------------------------------------------------------------------
// Demangle a type_info object.
#if defined(_WIN32)
   static std::vector<std::string> keywords;
   if ( 0 == keywords.size() ) {
      keywords.push_back("class ");
      keywords.push_back("struct ");
      keywords.push_back("enum ");
      keywords.push_back("union ");
      keywords.push_back("__cdecl");
   }
   std::string r = ti.name();
   for ( size_t i = 0; i < keywords.size(); i ++ ) {
      while (r.find(keywords[i]) != std::string::npos) 
         r = r.replace(r.find(keywords[i]), keywords[i].size(), "");
      while (r.find(" *") != std::string::npos) 
         r = r.replace(r.find(" *"), 2, "*");
      while (r.find(" &") != std::string::npos) 
         r = r.replace(r.find(" &"), 2, "&");
   }
   return r;

#elif defined(__GNUC__)

   int status = 0;
   bool remove_additional_pointer = false;
   std::string  mangled = ti.name();

   // if the At Name is string return the final string Name 
   // abi::Demangle would return "std::string" instead
   if ( mangled == "Ss" ) return "std::basic_string<char>";

#if __GNUC__ <= 3 && __GNUC_MINOR__ <= 3
   // Function types are not decoded at all. We are an extra 'P' to convert it to a pointer
   // and remove it at the end.
   if ( mangled[0] == 'F' ) {
      mangled.insert(0,"P");
      remove_additional_pointer = true;
   }
#elif __GNUC__ >= 4
   // From gcc 4.0 on the fundamental types are not demangled anymore by the dynamic demangler
   if (mangled.length() == 1) {
      switch ( mangled[0] ) {
      case 'a': return "signed char";        break;
      case 'b': return "bool";               break;
      case 'c': return "char";               break;
      case 'd': return "double";             break;
      case 'e': return "long double";        break;
      case 'f': return "float";              break;
      case 'g': return "__float128";         break;
      case 'h': return "unsigned char";      break;
      case 'i': return "int";                break;
      case 'j': return "unsigned int";       break;
         //case 'k': return "";                   break;
      case 'l': return "long";               break;
      case 'm': return "unsigned long";      break;
      case 'n': return "__int128";           break;
      case 'o': return "unsigned __int128";  break;
         //case 'p': return "";                   break;
         //case 'q': return "";                   break;
         //case 'r': return "";                   break;
      case 's': return "short";              break;
      case 't': return "unsigned short";     break;
         //case 'u': return "";                   break;
      case 'v': return "void";               break;
      case 'w': return "wchar_t";            break;
      case 'x': return "long long";          break;
      case 'y': return "unsigned long long"; break;
      case 'z': return "...";                break;
      default:                               break;
      }
   }
#endif
   char * c_demangled = abi::__cxa_demangle( mangled.c_str(), 0, 0, & status );
   if ( status == -1 ) {
      throw RuntimeError("Memory allocation failure while demangling ");
   }
   else if ( status == -2 ) {
      throw RuntimeError( std::string(mangled) + " is not a valid Name under the C++ ABI");
   }
   else if ( status == -3 ) {
      throw RuntimeError( std::string("Failure while demangling ") + mangled +
                          ". One of the arguments is invalid ");
   }
   else {
      std::string demangled = c_demangled;
      free( c_demangled );
      if ( remove_additional_pointer ) {
         demangled = demangled.replace(demangled.find("(*)"), 3, "");
      }
      while ( demangled.find(", ") != std::string::npos ) {
         demangled = demangled.replace(demangled.find(", "), 2, ",");
      }
      return demangled;
   }

#elif defined(__SUNPRO_CC)

   const char* mangled = ti.name();
   size_t buffer = 1024;
   char * c_demangled = new char[buffer];
   int ret = cplus_demangle( mangled, c_demangled, buffer);
   while ( ret == -1 ) {
      buffer = buffer*2;
      delete[] c_demangled;
      c_demangled = new char[buffer];
      ret = cplus_demangle( mangled, c_demangled, buffer);
   }
   if ( ret == 1 ) {
      throw RuntimeError(std::string("Symbol ") + mangled + " not mangled correctly");
   }
   else {
      std::string demangled = Tools::NormalizeName(c_demangled);
      delete[] c_demangled;
      return demangled;
   }

#elif defined(__IBMCPP__)

   return Tools::NormalizeName(ti.name());

#endif
   return "";
}


//-------------------------------------------------------------------------------
void Tools::StringSplitPair( std::string & val1,
                             std::string & val2,
                             const std::string & str,
                             const std::string & delim ) { 
//-------------------------------------------------------------------------------
// Split a string by a delimiter into a pair and return them as val1 and val2.
   std::string str2 = str;
   size_t pos = str2.rfind( delim );
   if ( pos != std::string::npos ) { 
      val1 = str2.substr( 0, pos ); 
      val2 = str2.substr( pos + delim.length());
   }
   else { 
      val1 = str2; 
   }
   StringStrip( val1 );
   StringStrip( val2 );
}


//-------------------------------------------------------------------------------
void Tools::StringStrip( std::string & str ) {
//-------------------------------------------------------------------------------
// Strip spaces at the beginning and the end from a string.
   size_t sPos = 0;
   size_t ePos = str.length();
   while ( str[sPos] == ' ' ) { ++sPos; }
   while ( str[ePos] == ' ' ) { --ePos; }
   str = str.substr( sPos, ePos - sPos );
}


//-------------------------------------------------------------------------------
std::string Tools::GetTemplateArguments( const char * name ) {
//-------------------------------------------------------------------------------
// Return the template arguments part of a templated type name.
   std::string baseName = GetBaseName(name);
   return baseName.substr(baseName.find('<'));
}


//-------------------------------------------------------------------------------
std::string Tools::GetTemplateName( const char * name ) {
//-------------------------------------------------------------------------------
// Return the fully qualified scope name without template arguments.
   std::string scopeName = GetScopeName( name );
   std::string baseName = GetBaseName( name );
   std::string templateName = baseName.substr(0, baseName.find('<'));
   if ( scopeName.length()) return scopeName + "::" + templateName;
  
   return templateName;
}


//-------------------------------------------------------------------------------
bool isalphanum(int i) {
//-------------------------------------------------------------------------------
// Return true if char is alpha or digit.
   return isalpha(i) || isdigit(i);
}


//-------------------------------------------------------------------------------
std::string Tools::NormalizeName( const char * nam ) {
//-------------------------------------------------------------------------------
// Normalize a type name.
   std::string norm_name;
   char prev = 0;
   for (size_t i = 0; nam[i] != 0; i++) {
      char curr = nam[i];
      if (curr == ' ') {
         char next = 0;
         while (nam[i] != 0 && (next = nam[i + 1]) == ' ') {
            ++i;
         }
         if (!isalphanum(prev) || !isalpha(next)) {
            continue; // continue on non-word boundaries
         }
      } else if ((curr == '>' && prev == '>') || (curr == '(' && prev != ')')) {
         norm_name += ' ';
      }
      norm_name += (prev = curr);
   }

   return norm_name;
}


//-------------------------------------------------------------------------------
std::string Tools::NormalizeName( const std::string & nam ) {
//-------------------------------------------------------------------------------
   return Tools::NormalizeName(nam.c_str());
}

Last change: Wed Jun 25 08:31:19 2008
Last generated: 2008-06-25 08:31

This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.