Doxygen 源码分析: SymbolMap类

news/2024/11/8 8:59:27/

在这里插入图片描述

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 namevoid 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 namevoid 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 symbolsconst 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 symbolsVectorPtr &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 workfor (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 resultsfor (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,                     // inconst Definition *scope,                             // inconst Definition *d,                                 // inconst QCString &explicitScopePart,                   // inconst std::unique_ptr<ArgumentList> &actTemplParams, // inint &minDistance,                                    // inoutconst ClassDef *&bestMatch,                          // outconst MemberDef *&bestTypedef,                       // outQCString &bestTemplSpec,                             // outQCString &bestResolvedType                           // out)
{//fprintf(stderr,"getResolvedType(%s,%s)\n",qPrint(scope->name()),qPrint(d->qualifiedName()));// only look at classes and members that are enums or typedefsif (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.ppmy.cn/news/77153.html

相关文章

自动化、智能、机器人-2023-

文明&#xff1a;农业、工业、信息、智能&#xff0c;以目前认知的四个阶段。 农业文明到工业文明&#xff1a;机械自动化 工业文明到信息文明&#xff1a;电气自动化 信息文明到智能文明&#xff1a;数据自动化 这些时代典型的机器人&#xff1a; 机械自动化 电气自动化 数…

OJ练习第113题——解码方法

解码方法 力扣链接&#xff1a;91. 解码方法 题目描述 一条包含字母 A-Z 的消息通过以下映射进行了 编码 &#xff1a; ‘A’ -> “1” ‘B’ -> “2” … ‘Z’ -> “26” 要 解码 已编码的消息&#xff0c;所有数字必须基于上述映射的方法&#xff0c;反向映射回…

达芬奇睡眠法 总结

我没有亲自尝试达芬奇睡眠法&#xff0c;总结信息来源于视频主&#xff0c;我不知道你是如何看到我的文章&#xff0c;但是如果你跟我一样对达芬奇睡眠法感兴趣&#xff0c;请记得一定不要轻易尝试&#xff0c;因为如果你严格按照达芬奇睡眠法这种极端的睡眠法施行&#xff0c;…

我劝你千万不要去做CSGO游戏搬砖项目

我劝你千万不要去做CSGO游戏搬砖项目 尽管童话姐姐本人做CSGO游戏搬砖已经三年多了&#xff0c;带的搬砖学员已达好几百人&#xff0c;但今天依旧要向那些未入行或还在考虑是否入行的朋友们发出警示。以下是我要告诉你们的大实话&#xff1a; 1、关于工作时间 这个项目要想赚…

nuxt视频播放(踩坑)

展示效果&#xff1a; nuxt样例视频 1.安装vue-video-player插件 ①执行命令 npm i vue-video-player 或者 npm i vue-video-player5.0.2 后者直接安装对应版本号的插件 ②安装完之后 执行npm i ③安装不对应的版本会报错 ④注意安装版本 不是vue3.0的 请安装&#xff08;“v…

黑盒测试能发现以下几类错误

黑盒测试能发现以下几类错误 黑盒测试是指在不考虑被测试软件的内部结构和工作原理的情况下&#xff0c;通过输入输出的方式对被测试软件进行测试。它主要关注被测试软件的功能是否达到预期的要求。黑盒测试能够发现以下几类错误。 1. 输入错误&#xff1a;黑盒测试可以检查被测…

day4 - 使用图像绘制动态时钟

本期的主要内容是利用OpenCV中包含的绘图函数&#xff0c;例如绘制线段、绘制矩形、绘制圆形等来绘制一个动态时钟的表盘。 完成本期内容&#xff0c;你可以&#xff1a; 掌握OpenCV常见的绘图函数 学会使用绘图函数绘制简单的图像 若要运行案例代码&#xff0c;你需要有&a…

【Database System Concept 7th】Chapter 2读书笔记

Chapter 2 Introduction to the Relation Model 2.1 Structure of Relational Databases2.2 Database Schema2.3 Keys2.4 2.5 2.6 这一章主要讲的是Relation Model&#xff08;关系模型&#xff09;的一些相关概念&#xff0c;并举了很多相关例子说明。下面举例子中提到的rela…