天天看点

使用纤程简化枚举器1:枚举器的简单实现

枚举对象的COM模型的设计,倾向于对消费者(Consumer)友好。对于生产者(Producer)来说,枚举对象需要被设计为一个状态机(State Machine),对于复杂的枚举器,例如树遍历或复合枚举,实现起来可能是相当不容易的。

另一方面,生产者的回调模型(被大多数 Win32 函数使用)偏向于对枚举对象友好。

这一次,消费者需要被构造成一个状态机,如果消费者对每个回调都做一些复杂的事情,这就需要更多的开发工作。(即使没有,你也必须创建一个上下文结构来将状态从调用者通过枚举器传递给回调函数。)

例如,假设我们要编写一个遍历目录结构的例程,允许调用者指定在每个决策点要做什么。 让我们首先使用回调方式来设计它,代码如下图所示:

使用纤程简化枚举器1:枚举器的简单实现

这里的设计是调用者调用 EnumDirectoryTree 并提供一个回调函数,该函数被告知找到的每个文件,并可以决定枚举应该如何进行。

将其设计为回调可以使得 EnumDirectoryTree 的实现变得更加简单。

使用纤程简化枚举器1:枚举器的简单实现

请注意:我没有试图让这个函数变得高效,因为这不是我想表达的主题。 它非常浪费堆栈空间(在遍历一个大型目录树时可能会导致问题)。 这个函数也无法处理比 MAX_PATH 更深的路径; 解决这个问题超出了本系列的范围。 我也不担心重解析点,如果你不谨慎处理它,就可能导致无限循环。

好吧,上图的代码并不难写。 但那是因为我们让消费者的生活变得艰难。 消费者需要在每个回调中维护状态。 例如,假设你想要构建目录列表及其大小(包括和不包括子目录)。可能的代码如下图所示:

使用纤程简化枚举器1:枚举器的简单实现
使用纤程简化枚举器1:枚举器的简单实现
使用纤程简化枚举器1:枚举器的简单实现
使用纤程简化枚举器1:枚举器的简单实现

首先,上面的代码量还不小,更糟的是,程序的整个结构都被状态管理代码所掩盖。 确实很难一眼看出这段代码试图做什么。 相反,你必须盯着 EnumState的代码,并对正在发生的事情进行逆向工程。

(是的,我可以通过使用内置的堆栈类稍微简化这段代码,但正如我在智能指针的文章中所指出的那样,我尝试用纯C++来呈现这些文章,这样人们就不会争论哪个类库最好。)

明天,我们将看看如果函数 EnumDirectoryTree 由调用者而不是枚举器指定,世界会怎样!

总结

最后