从demangled符号中提取类

时间:2012-09-16 12:31:18

标签: c++ regex boost

我正在尝试使用boost::regex从nm的demangled symbol输出中提取(完整)类名。 这个示例程序

#include <vector>

namespace Ns1
{
namespace Ns2
{
    template<typename T, class Cont>
    class A
    {
    public:
        A() {}
        ~A() {}
        void foo(const Cont& c) {}
        void bar(const A<T,Cont>& x) {}

    private:
        Cont cont;
    };
}
}

int main()
{
    Ns1::Ns2::A<int,std::vector<int> > a;
    Ns1::Ns2::A<int,std::vector<int> > b;
    std::vector<int> v;

    a.foo(v);
    a.bar(b);
}

将为A类生成以下符号

Ns1::Ns2::A<int, std::vector<int, std::allocator<int> > >::A()
Ns1::Ns2::A<int, std::vector<int, std::allocator<int> > >::bar(Ns1::Ns2::A<int, std::vector<int, std::allocator<int> > > const&)
Ns1::Ns2::A<int, std::vector<int, std::allocator<int> > >::foo(std::vector<int, std::allocator<int> > const&)
Ns1::Ns2::A<int, std::vector<int, std::allocator<int> > >::~A()

我想提取类(实例)名称Ns1::Ns2::A<int, std::vector<int, std::allocator<int> > >,最好使用单个正则表达式模式,但是我在解析<>对中递归出现的类说明符时遇到问题。

是否有人知道如何使用正则表达式模式(boost::regex支持)?

我的解决方案(基于David Hammen的答案,因此接受):

我不使用(单个)正则表达式来提取类和命名空间符号。我创建了一个简单的函数,它从符号字符串的尾部剥离包围字符对(例如<>()):

std::string stripBracketPair(char openingBracket,char closingBracket,const std::string& symbol, std::string& strippedPart)
{
    std::string result = symbol;

    if(!result.empty() &&
       result[result.length() -1] == closingBracket)
    {
        size_t openPos = result.find_first_of(openingBracket);
        if(openPos != std::string::npos)
        {
            strippedPart = result.substr(openPos);
            result = result.substr(0,openPos);
        }
    }
    return result;
}

这用于从符号中提取名称空间/类的其他两个方法:

std::string extractNamespace(const std::string& symbol)
{
    std::string ns;
    std::string strippedPart;
    std::string cls = extractClass(symbol);
    if(!cls.empty())
    {
        cls = stripBracketPair('<','>',cls,strippedPart);
        std::vector<std::string> classPathParts;

        boost::split(classPathParts,cls,boost::is_any_of("::"),boost::token_compress_on);
        ns = buildNamespaceFromSymbolPath(classPathParts);
    }
    else
    {
        // Assume this symbol is a namespace global function/variable
        std::string globalSymbolName = stripBracketPair('(',')',symbol,strippedPart);
        globalSymbolName = stripBracketPair('<','>',globalSymbolName,strippedPart);
        std::vector<std::string> symbolPathParts;

        boost::split(symbolPathParts,globalSymbolName,boost::is_any_of("::"),boost::token_compress_on);
        ns = buildNamespaceFromSymbolPath(symbolPathParts);
        std::vector<std::string> wsSplitted;
        boost::split(wsSplitted,ns,boost::is_any_of(" \t"),boost::token_compress_on);
        if(wsSplitted.size() > 1)
        {
            ns = wsSplitted[wsSplitted.size() - 1];
        }
    }

    if(isClass(ns))
    {
        ns = "";
    }
    return ns;
}

std::string extractClass(const std::string& symbol)
{
    std::string cls;
    std::string strippedPart;
    std::string fullSymbol = symbol;
    boost::trim(fullSymbol);
    fullSymbol = stripBracketPair('(',')',symbol,strippedPart);
    fullSymbol = stripBracketPair('<','>',fullSymbol,strippedPart);

    size_t pos = fullSymbol.find_last_of(':');
    if(pos != std::string::npos)
    {
        --pos;
        cls = fullSymbol.substr(0,pos);
        std::string untemplatedClassName = stripBracketPair('<','>',cls,strippedPart);
        if(untemplatedClassName.find('<') == std::string::npos &&
        untemplatedClassName.find(' ') != std::string::npos)
        {
            cls = "";
        }
    }

    if(!cls.empty() && !isClass(cls))
    {
        cls = "";
    }
    return cls;
}

buildNamespaceFromSymbolPath()方法只是连接有效的命名空间部分:

std::string buildNamespaceFromSymbolPath(const std::vector<std::string>& symbolPathParts)
{
    if(symbolPathParts.size() >= 2)
    {
        std::ostringstream oss;
        bool firstItem = true;
        for(unsigned int i = 0;i < symbolPathParts.size() - 1;++i)
        {
            if((symbolPathParts[i].find('<') != std::string::npos) ||
               (symbolPathParts[i].find('(') != std::string::npos))
            {
                break;
            }
            if(!firstItem)
            {
                oss << "::";
            }
            else
            {
                firstItem = false;
            }
            oss << symbolPathParts[i];
        }
        return oss.str();
    }
    return "";
}

至少isClass()方法使用正则表达式扫描构造函数方法的所有符号(遗憾的是,它似乎不适用于仅包含成员函数的类):

std::set<std::string> allClasses;

bool isClass(const std::string& classSymbol)
{
    std::set<std::string>::iterator foundClass = allClasses.find(classSymbol);
    if(foundClass != allClasses.end())
    {
        return true;
    }

std::string strippedPart;
    std::string constructorName = stripBracketPair('<','>',classSymbol,strippedPart);
    std::vector<std::string> constructorPathParts;

    boost::split(constructorPathParts,constructorName,boost::is_any_of("::"),boost::token_compress_on);
    if(constructorPathParts.size() > 1)
    {
        constructorName = constructorPathParts.back();
    }
    boost::replace_all(constructorName,"(","[\\(]");
    boost::replace_all(constructorName,")","[\\)]");
    boost::replace_all(constructorName,"*","[\\*]");

    std::ostringstream constructorPattern;
    std::string symbolPattern = classSymbol;
    boost::replace_all(symbolPattern,"(","[\\(]");
    boost::replace_all(symbolPattern,")","[\\)]");
    boost::replace_all(symbolPattern,"*","[\\*]");
    constructorPattern << "^" << symbolPattern << "::" << constructorName << "[\\(].+$";
    boost::regex reConstructor(constructorPattern.str());

    for(std::vector<NmRecord>::iterator it = allRecords.begin();
        it != allRecords.end();
        ++it)
    {
        if(boost::regex_match(it->symbolName,reConstructor))
        {
            allClasses.insert(classSymbol);
            return true;
        }
    }
    return false;
}

如前所述,如果类没有提供任何构造函数,则最后一个方法不能安全地找到类名,并且在大符号表上非常慢。但至少这似乎涵盖了你可以从nm的符号信息中获得的东西。

我已经离开问题的标记,其他用户可能会发现正则表达式不正确。

2 个答案:

答案 0 :(得分:2)

这很难与perl的扩展正则表达式相提并论,后者比C ++中的任何东西都强大得多。我建议采用不同的方法:

首先摆脱看起来不像数据等功能的东西(寻找D指示符)。像virtual thunk to thisvirtual table for that等等的东西也会妨碍你;在你进行主要解析之前摆脱它们。这种过滤是regexp可以提供帮助的地方。你应该留下的是功能。对于每个函数,

  • 在最后的右括号后摆脱这些东西。例如,Foo::Bar(int,double) const变为Foo::Bar(int,double)

  • 去除函数参数。这里的问题是你可以在括号内加括号,例如,将函数指针作为参数的函数,这些函数又可以将函数指针作为参数。不要使用正则表达式。使用括号匹配的事实。完成此步骤后,Foo::Bar(int,double)变为Foo::Bara::b::Baz<lots<of<template>, stuff>>::Baz(int, void (*)(int, void (*)(int)))变为a::b::Baz<lots<of<template>, stuff>>::Baz

  • 现在在前端工作。使用类似的方案来解析该模板的内容。有了这个,那个混乱的a::b::Baz<lots<of<template>, stuff>>::Baz变成了a::b::Baz::Baz

  • 在此阶段,您的功能将显示为a::b:: ... ::ClassName::function_name。这里的一些命名空间中的自由函数存在一些问题。破坏者是一个阶级的死亡赠品;毫无疑问,如果函数名称以波浪号开头,则您有一个类名。只要您没有定义函数Foo的命名空间Foo,构造函数就近在咫尺,您手边有一个类。

  • 最后,您可能需要重新插入所剪切的模板内容。

答案 1 :(得分:1)

我使用简单的C ++进行提取function

请参阅完整代码的链接,背后的想法是:

  1. 基本级别令牌由::分隔。
  2. 如果有N个基本级别令牌,则第一个N-1描述className,最后一个是函数
  3. 我们按(<
  4. 升级(+1)
  5. 在结束)>时,我们会向下一级(-1)
  6. 基本级别当然意味着 - level == 0
  7. 我有强烈的感觉,这不能通过正则表达式完成,因为我们有无限级别的括号。我的功能有255个 - 可以切换到std::stack<char>无限级别。

    功能:

    std::vector<std::string> parseCppName(std::string line)
    {
       std::vector<std::string> retVal;
       int level = 0;
       char closeChars[256];
    
       size_t startPart = 0;
       for (size_t i = 0; i < line.length(); ++i)
       {
          if (line[i] == ':' && level == 0)
          {
              if (i + 1 >= line.length() || line[i + 1] != ':')
                 throw std::runtime_error("missing :");
              retVal.push_back(line.substr(startPart, i - startPart));
              startPart = ++i + 1;
          }
          else if (line[i] == '(') {
             closeChars[level++] = ')';
          } 
          else if (line[i] == '<') {
             closeChars[level++] = '>';
          } 
          else if (level > 0 && line[i] == closeChars[level - 1]) {
             --level;
          }
          else if (line[i] == '>' || line[i] == ')') {
             throw std::runtime_error("Extra )>");
          }
       }
       if (level > 0)
           throw std::runtime_error("Missing )>");
       retVal.push_back(line.substr(startPart));
       return retVal;
    }