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