Doxygen 源码分析: SymbolMap类

在这里插入图片描述

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

文章目录

    • 1. Doxygen 版本
    • 2. SymbolMap 类概要
    • 3. 添加符号: `SymbolMap<T>::add()`
    • 4. 删除符号: `SymbolMap<T>::remove()`
    • 5. 符号查找: `SymbolMap<T>::find()`
    • 6. 哪里用了 SymbolMap?
      • 6.1 初始化和反初始化 `Doxygen::symbolMap`
      • 6.2 使用 `Doxygen::symbolMap` 的工具函数
        • 加入符号: `addToMap()`
        • 删除符号: `removeFromMap()`
        • `dumpSymbolMap()`: 打印符号字典
      • 6.3 运行期使用 `Doxygen::symbolMap`
        • 声明全局变量
        • 遍历 symbolMap: 用于计算 Tooltip 文本
        • 遍历 symbolMap: 用于获取符号类型

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, 将在下一篇展开分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/22677.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是半实物仿真平台自动驾驶半实物仿真平台有哪些?

文章目录 半实物仿真平台介绍自动驾驶半实物仿真平台介绍1.CARLA2.AirSim3.LGSVL Simulator 半实物仿真平台介绍 半实物仿真平台是一种综合利用虚拟仿真和实际硬件设备的仿真系统。它将虚拟环境和真实硬件设备结合起来&#xff0c;旨在提供更真实、更准确的仿真体验。 在半实…

基于html+css的图展示90

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

Boundary IoU:Improving Object-Centric Image Segmentation Evaluation总结笔记

Boundary IoU:Improving Object-Centric Image Segmentation Evaluation&#xff08;边界Iou&#xff1a;改进以对象为中心的图像分割评价&#xff09; 目录 一、论文出发点 二、论文核心思想 三、相关工作 四、敏感度分析 五、Boundary IoU定义和实验证明 六、应用 七…

基于Gabor-小波滤波深度图表面法线的特征提取算法【通过正常Gabor-小波的直方图进行2D或3D特征提取】研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Redis+Lua脚本防止超卖

超卖就是因为查询库存和扣减库存两个操作不是原子性操作&#xff0c;通过rua脚本执行这两个操作可以保证这两个操作原子性 判断库存量是不是大于等于1&#xff0c;如果大于等于1对库存减1&#xff0c;否则就不去减库存 StringBuilder sb new StringBuilder();sb.append("…

【JAVA进阶】Stream流

&#x1f4c3;个人主页&#xff1a;个人主页 &#x1f525;系列专栏&#xff1a;JAVASE基础 目录 1.Stream流的概述 2.Stream流的获取 3.Stream流的常用方法 1.Stream流的概述 什么是Stream流&#xff1f; 在Java 8中&#xff0c;得益于Lambda所带来的函数式编程&#xff0…

使用go语言构建区块链 Part2.工作量证明

英文源地址 简介 在上一篇文章中, 我们构建了一个非常简单的数据结构, 这是区块链数据库的本质.并且我们可以通过它们之间的链式关系来添加区块: 每个区块都链接到前一个区块.哎, 我们的区块链实现有一个重大缺陷: 向链中添加区块既容易又便捷. 区块链和比特币的关键之一是增…

面对当下各种不确定性,如何面对,每天很忙碌,不慌

&#xff08;点击即可收听&#xff09; 疫情时期,都难,疫情之后,发现还更难 随着互联网的热度的下降,各大小公司纷纷勒紧裤腰带,受打击最大的无疑是底层打工人 每天一打开手机,会发现,一些大厂裁员信息霸榜头条,年龄也是一道坎 刚刚看到一个大v发的&#xff1a; 一个原先是跨国…

如何在 OpenSUSE 上安装 VirtualBox 7?

VirtualBox 是一款开源的虚拟化软件&#xff0c;允许用户在单个计算机上运行多个操作系统。本文将详细介绍如何在 OpenSUSE 上安装 VirtualBox 7。以下是安装过程的步骤&#xff1a; 步骤一&#xff1a;下载 VirtualBox 7 首先&#xff0c;我们需要下载 VirtualBox 7 的安装包…

真题详解(语法分析输入记号流)-软件设计(八十)

真题详解&#xff08;求叶子结点数&#xff09;-软件设计&#xff08;七十九)https://blog.csdn.net/ke1ying/article/details/130787349?spm1001.2014.3001.5501 极限编程XP最佳实践&#xff1a; 测试先行、 按日甚至按小时为客户提供可运行的版本。 组件图的 插座 和插头…

Pytorch的CNN,RNNLSTM

CNN 拿二维卷积举例&#xff0c;我们先来看参数 卷积的基本原理&#xff0c;默认你已经知道了&#xff0c;然后我们来解释pytorch的各个参数&#xff0c;以及其背后的计算过程。 首先我们先来看卷积过后图片的形状的计算&#xff1a; 参数&#xff1a; kernel_size &#xff…

使用Linkage Mapper工进行物种分布建模的步骤详解(含实际案例分析)

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Linkage Mapper解密数字世界链接 文章目录 引言:一、介绍二、数据准备2.1 物种分布数据获取2.2 环境变量数据获取2.3 数据预处理

车道线检测

前言 目前&#xff0c;车道线检测技术已经相当成熟&#xff0c;主要应用在自动驾驶、智能交通等领域。下面列举一些当下最流行的车道线检测方法&#xff1a; 基于图像处理的车道线检测方法。该方法是通过图像处理技术从摄像头传回的图像中提取车道线信息的一种方法&#xff0c…

FreeRTOS(6)----软件定时器

一&#xff0c;软件定时器概述 软件定时器允许设置一段时间&#xff0c;当设定的时间到达之后就会执行指定的功能函数&#xff0c;被定时器调用的这个函数叫做定时器的回调函数。回调函数的两次执行间隔叫做定时器的定时周期。 二&#xff0c;回调函数的注意事项 回调函数是…

Django框架之模板过滤器

过滤器可以用来修改变量的显示样式。 使用方式 格式&#xff1a;{{变量|过滤器方法}}。可以连续使用&#xff0c;形式如&#xff1a;{{变量|过滤器方法1|过滤器方法2}}。 过滤器如下 Lower 转化为小写字母 格式&#xff1a;变量|lower Upper 转化为大写字母 格式&#xf…

chatgpt赋能python:Python的BeautifulSoup库和find_all()方法

Python的Beautiful Soup库和find_all()方法 在Web爬虫中&#xff0c;我们需要从网页中找到特定的HTML标记或属性&#xff0c;以便提取我们需要的数据。对于Python开发人员而言&#xff0c;Beautiful Soup是最流行的解析HTML和XML的库之一。该库可以让我们轻松地从HTML解析器中…

【Tcp通信服务器流程】

TCP通信流程 1、服务器端&#xff08;被动接收连接的角色&#xff09; &#xff08;1&#xff09;创建一个用于监听的套接字 - 监听&#xff1a;监听有客户端的连接 - 套接字&#xff1a;这个套接字其实就是一个文件描述符 &#xff08;2&#xff09;将这个监听文件描述符和…

26 KVM热迁移虚拟机

文章目录 26 KVM热迁移虚拟机26.1 总体介绍26.1.1 概述26.1.2 应用场景26.1.3 注意事项和约束限制 26.2 热迁移操作26.2.1 前提条件26.2.2 热迁移脏页率预测&#xff08;可选&#xff09;26.2.3 设置热迁移参数&#xff08;可选&#xff09;26.2.4 热迁移操作&#xff08;共享存…

微服务之事务处理

Informal Essay By English Hi guys、happy labor day. Everyone should have a good time to relax during the Labor Day holiday. But don’t forget to improve yourself during the holiday period 参考书籍&#xff1a; “凤凰架构” “微服务架构设计模式” 引言 …

golang 服务中 context 超时处理的思考

文章目录 前言起因&#xff1a;日志告警引发的思考什么是contextcontext的作用context超时之后继续执行 or 中断 最后 前言 公司运行的服务代码中&#xff0c;随处可见各种各样的日志信息&#xff0c;其中大多数是用来记录各种异常的日志&#xff0c;一方面&#xff0c;当出现…