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















![[NodeJS] 优缺点及适用场景讨论](https://img-blog.csdnimg.cn/img_convert/fa16e0aff372bfee5239b1863c1ec8cc.png)



