Doxygen 源码分析: SymbolMap类

在这里插入图片描述

2023-05-21 10:59:35
ChrisZZ imzhuo@foxmailcom
Hompage https://github.com/zchrissirhcz

1. Doxygen 版本

本次使用的 doxygen 版本如下, 是 1.9.8 正式发布版本对应的 commit

$ git log
commit 5fded4215d4f9271fe92c940fc4532d4704f5be1 (HEAD -> master, upstream/master)
Author: Dimitri van Heesch <doxygen@gmail.com>
Date:   Thu May 18 22:14:30 2023 +0200
evelopment
bump version to 1.9.8 for d

2. SymbolMap 类概要

在这里插入图片描述

template<class T>
class SymbolMap
{
  public:
    using Ptr = T *;
    using VectorPtr = std::vector<Ptr>;
    using Map = std::unordered_map<std::string,VectorPtr>;
    using iterator = typename Map::iterator;
    using const_iterator = typename Map::const_iterator;

    ...

  private:
    Map m_map; // 字典
};

SymbolMap<T> 是模板类, 提供了符号(symbol)到对象(object)的映射:

//! Class implementing a symbol map that maps symbol names to objects.

符号名字可以不是唯一的:

//! Symbol names do not have to be unique.

这可以从类定义中的 using Map = std::unordered_map<std::string, VectorPtr> 看出来, key 是 string, value 是一个向量。

提供了 SymbolMap<T>::add() 函数来添加符号, SymbolMap<T>::remove() 来删除符号,SymbolMap<T>::find() 来查找符号:

//! Supports adding symbols with add(), removing symbols with remove(), and
//! finding symbols with find().

SymbolMap<T> 类在 src/symbolmap.h 中实现, 不到100行。

3. 添加符号: SymbolMap<T>::add()

给定名字 name 和符号定义 def, 首先在现有字典(m_map)中查找(find())给定的名字 name, 如果找到则在对应的列表中追加 def 这一符号, 如果没找到则在字典中新建一个名为 name 的 key, 对应的 value 则是一个新建的只有一个元素的 vector, 也就是 {def}.

    //! Add a symbol \a def into the map under key \a name
    void add(const QCString &name,Ptr def)
    {
      auto it = m_map.find(name.str());
      if (it!=m_map.end())
      {
        it->second.push_back(def);
      }
      else
      {
        m_map.emplace(std::make_pair(name.str(),VectorPtr({def})));
      }
    }

4. 删除符号: SymbolMap<T>::remove()

首先在字典中查找给定的名字 name, 得到一个向量 v. 然后在向量 v 中查找给定的符号 def.
如果找到了 def, 则从向量中删除 def, 并进一步检查: 如果向量现在为空, 则从字典中删除 name.

疑问:这里假定了给定名字 name 一定可以查询到 def 符号。是否存在给定的 name 不合法导致没找到的情况?

    //! Remove a symbol \a def from the map that was stored under key \a name
    void remove(const QCString &name,Ptr def)
    {
      VectorPtr &v = find(name);
      auto it = std::find(v.begin(),v.end(),def);
      if (it!=v.end())
      {
        v.erase(it);
        if (v.empty())
        {
          m_map.erase(name.str());
        }
      }
    }

5. 符号查找: SymbolMap<T>::find()

包括了 const 和非 const 的两个版本。
从字典中查询给定的 name, 如果找到了, 则返回 name 对应的 value, 也就是一个向量。表示了所有挂靠在 name 名下的符号列表。
如果没找到,返回 m_noMatch. m_noMatch 其实就是空的向量。

    //! Find the list of symbols stored under key \a name
    //! Returns a pair of iterators pointing to the start and end of the range of matching symbols
    const VectorPtr &find(const QCString &name) const
    {
      auto it = m_map.find(name.str());
      return it==m_map.end() ? m_noMatch : it->second;
    }

    //! Find the list of symbols stored under key \a name
    //! Returns a pair of iterators pointing to the start and end of the range of matching symbols
    VectorPtr &find(const QCString &name)
    {
      auto it = m_map.find(name.str());
      return it==m_map.end() ? m_noMatch : it->second;
    }

6. 哪里用了 SymbolMap?

6.1 初始化和反初始化 Doxygen::symbolMap

src/doxygen.h 中定义的 Doxygen 类, 它的成员 sumbolMap 的类型是 SymbolMap<Definition>*.

/// This class serves as a namespace for global variables used by doxygen.
class Doxygen
{
    ...
    static SymbolMap<Definition>    *symbolMap;
    ...
};

并且在 initDoxygen() 函数中进行了初始化:

void initDoxygen()
{
  ...
  Doxygen::symbolMap = new SymbolMap<Definition>;
  ...
}

cleanUPDoxygen() 中释放了内存:

void cleanUpDoxygen()
{
  ...
  delete Doxygen::symbolMap;
  ...
}

从整个 doxygen 可执行文件的执行流程来看:

int main(int argc,char **argv)
{
  initDoxygen();
    |--Doxygen::symbolMap = new SymbolMap<Definition>;
  readConfiguration(argc,argv);
  checkConfiguration();
  adjustConfiguration();
  parseInput();
  generateOutput();
    |--cleanUpDoxygen();
        |--delete Doxygen::symbolMap;
  return 0;
}

实际上 Doxygen 类被当做是若干的全局变量的集合体使用, 全局只维护了一份 SymbolMap<Definition> 对象, 那就是 Doxygen::symbolMap.

6.2 使用 Doxygen::symbolMap 的工具函数

加入符号: addToMap()

将符号定义加入到 map:

static void addToMap(const QCString &name,Definition *d)
{
  bool vhdlOpt = Config_getBool(OPTIMIZE_OUTPUT_VHDL);
  QCString symbolName = name;
  int index=computeQualifiedIndex(symbolName);
  if (!vhdlOpt && index!=-1) symbolName=symbolName.mid(index+2);
  if (!symbolName.isEmpty())
  {
    //printf("adding symbol %s\n",qPrint(symbolName));
    Doxygen::symbolMap->add(symbolName,d);

    d->_setSymbolName(symbolName);
  }
}
删除符号: removeFromMap()

将符号定义从 map 中删除

static void removeFromMap(const QCString &name,Definition *d)
{
  Doxygen::symbolMap->remove(name,d);
}
dumpSymbolMap(): 打印符号字典
static void dumpSymbolMap()
{
  std::ofstream f = Portable::openOutputStream("symbols.sql");
  if (f.is_open())
  {
    TextStream t(&f);
    for (const auto &[name,symList] : *Doxygen::symbolMap)
    {
      for (const auto &def : symList)
      {
        dumpSymbol(t,def);
      }
    }
  }
}

6.3 运行期使用 Doxygen::symbolMap

声明全局变量
// src/doxyge.cpp, L157
SymbolMap<Definition>*Doxygen::symbolMap;
遍历 symbolMap: 用于计算 Tooltip 文本

使用了C++17 structured bindings 方式的遍历:

    for (const auto &[name,symList] : *Doxygen::symbolMap) // 遍历 symbolMap
    {
        ...
    }

完整代码:

static void computeTooltipTexts()
{
  std::size_t numThreads = static_cast<std::size_t>(Config_getInt(NUM_PROC_THREADS));
  if (numThreads>1)
  {
    ThreadPool threadPool(numThreads);
    std::vector < std::future< void > > results;
    // queue the work
    for (const auto &[name,symList] : *Doxygen::symbolMap) // 遍历 symbolMap
    {
      for (const auto &def : symList)
      {
        DefinitionMutable *dm = toDefinitionMutable(def);
        if (dm && !isSymbolHidden(toDefinition(dm)) && toDefinition(dm)->isLinkableInProject())
        {
          auto processTooltip = [dm]() {
            dm->computeTooltip();
          };
          results.emplace_back(threadPool.queue(processTooltip));
        }
      }
    }
    // wait for the results
    for (auto &f : results)
    {
      f.get();
    }
  }
  else
  {
    for (const auto &[name,symList] : *Doxygen::symbolMap)
    {
      for (const auto &def : symList)
      {
        DefinitionMutable *dm = toDefinitionMutable(def);
        if (dm && !isSymbolHidden(toDefinition(dm)) && toDefinition(dm)->isLinkableInProject())
        {
          dm->computeTooltip();
        }
      }
    }
  }
}
遍历 symbolMap: 用于获取符号类型

SymbolResolver 类有一个名为 getResolvedTypeRec() 的方法, 遍历了 symbolMap:

const ClassDef *SymbolResolver::Private::getResolvedTypeRec(
           StringUnorderedSet &visitedKeys,
           const Definition *scope,
           const QCString &n,
           const MemberDef **pTypeDef,
           QCString *pTemplSpec,
           QCString *pResolvedType)
{
  ...

  auto &range = Doxygen::symbolMap->find(name);
  if (range.empty())
  {
    return 0;
  }

  ...

  for (Definition *d : range)
  {
    getResolvedType(visitedKeys,scope,d,explicitScopePart,actTemplParams,
        minDistance,bestMatch,bestTypedef,bestTemplSpec,bestResolvedType);
    if  (minDistance==0) break; // we can stop reaching if we already reached distance 0
  }

  ...

}

进一步, 展开 getResolvedType(), 我们关注第三个参数 d:

  • 获取了它的类型: d->definitionType()
  • 转换为了 MemberDef: toMemberDef()
void SymbolResolver::Private::getResolvedType(
                         StringUnorderedSet &visitedKeys,                     // in
                         const Definition *scope,                             // in
                         const Definition *d,                                 // in
                         const QCString &explicitScopePart,                   // in
                         const std::unique_ptr<ArgumentList> &actTemplParams, // in
                         int &minDistance,                                    // inout
                         const ClassDef *&bestMatch,                          // out
                         const MemberDef *&bestTypedef,                       // out
                         QCString &bestTemplSpec,                             // out
                         QCString &bestResolvedType                           // out
                      )
{
  //fprintf(stderr,"getResolvedType(%s,%s)\n",qPrint(scope->name()),qPrint(d->qualifiedName()));
  // only look at classes and members that are enums or typedefs
  if (d->definitionType()==Definition::TypeClass ||
      (d->definitionType()==Definition::TypeMember &&
       ((toMemberDef(d))->isTypedef() ||
        (toMemberDef(d))->isEnumerate())
      )
     )
    ...
}

其中转换 definition 类型到具体的子类类型 MemberDef 的函数实现为:

const MemberDef *toMemberDef(const Definition *d)
{
  if (d && (typeid(*d)==typeid(MemberDefImpl) || typeid(*d)==typeid(MemberDefAliasImpl)))
  {
    return static_cast<const MemberDef*>(d);
  }
  else
  {
    return 0;
  }
}

至此, 我们从 SymbolMap<T> 的定义和基本使用, 抵达了 SymbolMap 中的每个 value 对应的 vector 中的每个元素 def 的具体使用了, 涉及到父类 Definition 和它的多个子类如 MemberDef, 将在下一篇展开分析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值