Doxygen 源码分析: SymbolMap类

news2024/11/28 4:29:48

在这里插入图片描述

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1.golang的介绍、环境安装和编译器安装

一、Go的介绍 Go语言其实是Golanguage的简称&#xff0c;Go&#xff08;又称 Golang&#xff09;是 Google 的 Robert Griesemer&#xff0c;Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译并发型语言。Go 语言语法与 C 相近&#xff0c;但功能上有&#xff1a;内存安全…

Doxygen 源码分析: Definition类

2023-05-21 13:05:28 ChrisZZ imzhuofoxmailcom Hompage https://github.com/zchrissirhcz Blog https://blog.csdn.net/baiyu33 文章目录 1. Doxygen 版本2. Definition 类和它的8个子类3. Definition 类的 Private 成员4. Definition 类的 Public 成员4.1 特殊成员函数4.2 获…

(学习日记)AD学习 #1

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

Linux基本操作指令2

目录 指令1&#xff1a;rmdir指令&#xff1a; 指令2&#xff1a;rm命令&#xff1a; 指令3&#xff1a;man指令&#xff1a; 指令4&#xff1a;cp指令&#xff1a; 指令5&#xff1a;mv指令&#xff1a;类似于Windows的剪贴 指令6&#xff1a;cat指令 指令7&#xff1a;…

QT5.15.0使用gcc-arm-8.2-2018.08-x86_64-arm-linux-gnueabihf交叉编译的问题总结

目录 一、交叉编译 二、操作中踩过的坑 1、环境变量未生效 2、交叉编译QT代码操作 3、烧录时报错缺少xcb问题 4、小白的细小错误 三、--platform命令 3、1 -platform linuxfb 详细文档请点击此处 我的文档在原文档的基础上添加了非常详细的提醒&#xff0c;可以少走弯路…

软件性能测试过程详解与案例剖析

软件性能测试是软件开发过程中至关重要的一环&#xff0c;它能够帮助我们确保软件在不同负载和使用情况下的表现。在软件性能测试中&#xff0c;我们通常会关注软件的响应时间、吞吐量、并发用户数等指标&#xff0c;以评估软件性能。 软件性能测试过程主要分为以下几个步骤&am…

leetcode:322. 零钱兑换(暴力dfs,记忆化dfs,动态规划(朴素+优化),bfs+贪心)

记录常规的完全背包问题模型 1.暴力dfs2.优化dfs&#xff0c;记忆化dfs3.动态规划4.bfs 1.由于每件物品可以无限取&#xff0c;那么可以发现这是一个完全背包问题模型。 1.暴力dfs 最后要求的是&#xff1a;n种硬币&#xff0c;凑成总金额为amount。每种硬币无限取&#xff0…

Java8 教你一行代码搞定:如何计算map中value值

大家好&#xff0c;我是三叔&#xff0c;很高兴这期又和大家见面了&#xff0c;一个奋斗在互联网的打工人。 这期给大家讲一下在Java编程中&#xff0c;如何使用Java8对map的值进行计算&#xff0c;在实际开发中&#xff0c;也是经常遇到统计map中的value值之和。 Map是一种常…

Web安全:文件上传漏洞测试.

Web安全&#xff1a;文件上传漏洞测试. 现在大多的网站和Web应用系统都会有上传功能&#xff08;比如&#xff1a;文档&#xff0c;图片&#xff0c;头像&#xff0c;视频上传等.&#xff09;&#xff0c;而程序员在开发文件上传功能时&#xff0c;没有对代码做严格校验上传文…

解决大文件传输难题的方法和技巧

传统的传输大文件的方式&#xff0c;如电子邮件附件或USB驱动器&#xff0c;由于文件大小的限制和安全问题&#xff0c;变得越来越不方便。大文件共享是现代商业通信的一个重要方面&#xff0c;组织需要安全可靠的方式来传输这些文件。 传统文件传输方式的不便 传统的文件传输方…

LabVIEWCompactRIO 开发指南27 创建模块化、可重复使用的子VI

LabVIEWCompactRIO 开发指南27 创建模块化、可重复使用的子VI 编写模块化代码几乎总是一个好主意&#xff0c;无论是为Windows、实时还是FPGA设备设计应用程序。子VI使代码更易于调试和故障排除&#xff0c;更易于记录和跟踪更改&#xff0c;并且通常更清晰&#xff0c;更易于…

一文读懂JVM架构解析

JVM 架构解析 Java 架构JVMJVM是如何工作的&#xff1f;类加载器子系统 运行时数据区执行引擎 每个 Java 开发人员都知道字节码经由 JRE&#xff08;Java运行时环境&#xff09;执行。但他们或许不知道 JRE 其实是由 Java虚拟机&#xff08;JVM&#xff09;实现&#xff0c;JVM…

css3 flex弹性布局学习

一、flex基本概念 当开启flex布局后&#xff0c;项目默认沿主轴排列。单个项目占据的主轴空间叫做main size&#xff0c;占据的交叉轴空间叫做cross size。 二、容器的属性 以下6个属性设置在容器上。 flex-direction flex-wrap flex-flow justify-content align-items align…

LabVIEWCompactRIO 开发指南26 同步循环

LabVIEWCompactRIO 开发指南26 同步循环 对于大多数控制和监视应用&#xff0c;代码执行的时间对于系统的性能和可靠性非常重要。在此电机控制示例中&#xff0c;有两个不同的时钟信号&#xff1a;采样时钟和PID时钟。这些是在应用程序中生成的布尔信号&#xff0c;用于在循环…

【HackTheBox MonitorsTwo】打靶记录

信息搜集 1、nmap 扫描一波 └─# nmap -sC -sV 10.10.11.211 Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-14 20:55 EDT Nmap scan report for 10.10.11.211 Host is up (0.25s latency). Not shown: 998 closed tcp ports (reset) PORT STATE SERVICE VERSION 2…

[NodeJS] 优缺点及适用场景讨论

概述&#xff1a; NodeJS宣称其目标是“旨在提供一种简单的构建可伸缩网络程序的方法”&#xff0c;那么它的出现是为了解决什么问题呢&#xff0c;它有什么优缺点以及它适用于什么场景呢&#xff1f; 本文就个人使用经验对这些问题进行探讨。 一. NodeJS的特点 我们先来看看N…

【数据结构】广度优先遍历(BFS)模板及其讲解

&#x1f38a;专栏【数据结构】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【勋章】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 目录 &#x1f381;定义 &#x1f381;遍历方法 &#x1f381;根…

Hadoop基础学习---3、HDFS概述、HDFS的Shell操作、HDFS的API操作

1、HDFS概述 1.1 HDFS产出背景及定义 1、HDFS产生背景 随着数据量越来越大&#xff0c;在一个操作系统存不住所有的数据&#xff0c;那么就分配到更多的操作系统管理的磁盘中&#xff0c;但是不方便管理和维护&#xff0c;迫切需要一种系统来管理多台机器上的文件&#xff0c…

记 LCG 例题

文章目录 题一(seed,a,b,n,c)题二(a,b,n,c)题三(a,n,output[6],output[7])题四(n,output)题五(output)题六(output)题七&#xff08;二元LCG&#xff09;题八&#xff08;三元LCG&#xff09; &#xff08;PS&#xff1a;网上有很多原理&#xff0c;这里就不过多赘述了&#xf…

【C++】 设计模式(单例模式、工厂模式)

文章目录 设计模式概念单例模式懒汉式方法一方法二总结 饿汉式单例模式的优点 工厂模式概念简单工厂工厂方法抽象工厂三种工厂方法的总结 设计模式 概念 设计模式是由先人总结的一些经验规则&#xff0c;被我们反复使用后、被多数人知晓认可的、然后经过分类编排&#xff0c;…