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