SDL中文论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 4073|回复: 4
打印 上一主题 下一主题

[WML] 函数注释:preprocessor_streambuf::underflow

[复制链接]

149

主题

331

帖子

2445

积分

版主

Rank: 7Rank: 7Rank: 7

积分
2445
跳转到指定楼层
楼主
发表于 2016-5-13 22:25:07 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
  1. /**
  2. * Called by an STL stream whenever it has reached the end of #out_buffer_.
  3. * Fills #buffer_ by calling the #current_ preprocessor, then copies its
  4. * content into #out_buffer_.
  5. * @return the first character of #out_buffer_ if any, EOF otherwise.
  6. */
  7. int preprocessor_streambuf::underflow()
  8. {
  9.         unsigned sz = 0;
  10.         if (char *gp = gptr()) {
  11.                 if (gp < egptr()) {
  12.                         // Sanity check: the internal buffer has not been totally consumed,
  13.                         // should we force the caller to use what remains first?
  14.                         return *gp;
  15.                 }
  16.                 // The buffer has been completely read; fill it again.
  17.                 // Keep part of the previous buffer, to ensure putback capabilities.
  18.                 sz = out_buffer_.size();
  19.                 buffer_.str(std::string());
  20.                 if (sz > 3) {
  21.                         buffer_ << out_buffer_.substr(sz - 3);
  22.                         sz = 3;
  23.                 }
  24.                 else
  25.                 {
  26.                         buffer_ << out_buffer_;
  27.                 }
  28.         } else {
  29.                 // The internal get-data pointer is null
  30.         }
  31.         const int desired_fill_amount = 2000;
  32.         int        in_avail;
  33.         while (current_ && (in_avail = buffer_.rdbuf()->in_avail()) < desired_fill_amount)
  34.         {
  35.                 // Process files and data chunks until the desired buffer size is reached
  36.                 if (!current_->get_chunk()) {
  37.                          // Delete the current preprocessor item to restore its predecessor
  38.                         delete current_;
  39.                 }
  40.         }
  41.         // Update the internal state and data pointers
  42.         out_buffer_ = buffer_.str();
  43.         char *begin = &*out_buffer_.begin();
  44.         unsigned bs = out_buffer_.size();
  45.         setg(begin, begin + sz, begin + bs);
  46.         if (sz >= bs)
  47.                 return EOF;
  48.         return static_cast<unsigned char>(*(begin + sz));
  49. }
复制代码
回复

使用道具 举报

149

主题

331

帖子

2445

积分

版主

Rank: 7Rank: 7Rank: 7

积分
2445
沙发
 楼主| 发表于 2016-5-13 22:25:33 | 只看该作者
STL,例如istream::get(),会在读#out_buffer_到结末时调用这函数
函数主要执行,1)调用#current_把数据放入#buffer_,2)#buffer_数据复制向#out_buffer。
返回值是#out_buffer_的第一个有效数据值

这函数存在一处较难理解地方,
  1. const int desired_fill_amount = 2000;
  2. while (current_ && buffer_.rdbuf()->in_avail() < desired_fill_amount)
  3. {
  4. // Process files and data chunks until the desired buffer size is reached
  5. if (!current_->get_chunk()) {
  6.   // Delete the current preprocessor item to restore its predecessor
  7. delete current_;
  8. }
  9. }
复制代码
以通常认为,delete current_是不会把current置null(为之后!ptr判断,一般程序会在delete后面跟一个ptr = NULL),但是分析以上代码的while条件,当一个文件已没有数据后buffer_.rdbuf()->in_avail() < desired_fill_amount判断将总是true,要退出while除非!current_成立,那么current_是如何、何时被置null的?——这里delete current_确实可以在释放掉current_内存块同时把current_置null!
回复 支持 反对

使用道具 举报

149

主题

331

帖子

2445

积分

版主

Rank: 7Rank: 7Rank: 7

积分
2445
板凳
 楼主| 发表于 2016-5-13 22:25:56 | 只看该作者
本帖最后由 ancientcc 于 2017-2-21 09:49 编辑

这里以读取D:\Program Files\Battle for Wesnoth 1.6.1\data\hardwired\fonts.cfg为例来作具体说明。
fonts.cfg:文件大小,23,149 字节

1、preprocess_file执行到了new preprocessor_file(*buf, callstack, fname),
@buf,一个指向preprocessor_streambuf对像的指针;
@fname:D:\Program Files\Battle for Wesnoth 1.6.1\data\hardwired\fonts.cfg;

2、new操作从堆出申请出sizeof(preprocessor_file)大小的内存块,内存块地址:0x1d49660  

3、new操作开始执行preprocessor_file构造函数。

3.1、执行构造函数时首先执行其父类preprocessor的构造函数,注意这构造函数执行后一些成员的值:
old_preprocessor_: 0x00000000
target_.depth_ = 1
target_.current = 0x1d49660(此次分配的processor_file)

3.2、判断出name是文件,于是执行new preprocessor_data(t, called_macros_, file_stream, "", name, 1, directory_name(name), t.textdomain_)

3.2.1、new操作从堆出申请出sizeof(preprocessor_data)大小的内存块,内存块地址:0x1d49858

3.2.2、new操作开始执行preprocessor_data构造函数。

3.2.2.1、执行构造函数时首先执行其父类preprocessor的构造函数,注意这构造函数执行后一些成员的值:
old_preprocessor_: 0x1d49660, 也即以上的processor_file
target_.depth_ = 2
target_.current = 0x1d49858(此次分配的processor_data)
注:processor_file和processor_data中的target是同一个,其值就是那个指向preprocessor_streambuf对像的指针,buf

4、程序开始读取数据,istream.get()调用preprocessor_streambuf::underflow(),underflow以着每一次2459字节向上送(>2000),这样读了9次后,最后一次只剩下了325字节(这个数字不等于23,149 - 2459*9 = 1018,为什么需要再解释,但不是这里要涉及的。!!!!!while (current_ && (in_avail = buffer_.rdbuf()->in_avail()) < desired_fill_amount), in_avail被变掉,217-->325!!(当然会变,上次没有把最后一次的current->get_chunk计算在内)。每次读取时underflow一些数值:

1th: current_: 0x1d49858, 2459
2th: current_: 0x1d49858, 2459
3th: current_: 0x1d49858, 2459
4th: current_: 0x1d49858, 2459
5th: current_: 0x1d49858, 2459
6th: current_: 0x1d49858, 2459
7th: current_: 0x1d49858, 2459
8th: current_: 0x1d49858, 2459
9th: current_: 0x1d49858, 2459
10th: current_: 0x1d49858, 325

5、在最后一次读时,current_->get_chunk()返回flase,至此开始调用delete current_。

5.1、这个current_指向preprocessor_data,delete析构preprocessor_data,析构了自己后去折构父类preprocessor,即调用preprocessor::~preprocessor(),该函数执行后注意几个变量值:
target_.current_ = old_preprocessor_ = 0x1d49660;
--target_.depth_ = --2 = 1;

经过这么个析构,preprocessor_streambuf中的current_被换到了0x1d49660,即指向preprocessor_file那个

6、underflow执行current_->get_chunk,processor_file::get_chunk执行时pos_ == end_,不执行任何操作就返回false,至此又一次调用delete current_(这次是processor_file的了)

6.1、这个current_指向preprocessor_file,delete析构preprocessor_file,析构了自己后去折构父类preprocessor,即调用preprocessor::~preprocessor(),该函数执行后注意几个变量值:
old_preprocessor_: 0x00000000
target_.depth_ = --1 = 0
target_.current = 0x00000000这个target_就是那个指向preprocessor_streambuf对像的指针,buf


到此delete current_在析构掉自己同时把current_也给置null了!
回复 支持 反对

使用道具 举报

149

主题

331

帖子

2445

积分

版主

Rank: 7Rank: 7Rank: 7

积分
2445
地板
 楼主| 发表于 2016-5-13 22:26:23 | 只看该作者
本帖最后由 ancientcc 于 2017-2-21 09:25 编辑

要判断std::istream长度,一般是用以下代码。in是std::istream对象。
  1. in.seekg(0, std::ios_base::end);
  2. size_t size = in.tellg();
复制代码

但in是preprocess_file返回的对象时,值是-1!原因是这个istream不是通常意义上磁盘文件,它的长度要随着不断std::istream::get而增长。具体表现在要get时,会问是否还有可读数据,此时就会调用underflow,如果有数据就被从preprocessor_data取出来,——如此循环,直到preprocessor_data没数据了。

当然,tellg可安全计算磁盘文件的长度。
回复 支持 反对

使用道具 举报

149

主题

331

帖子

2445

积分

版主

Rank: 7Rank: 7Rank: 7

积分
2445
5#
 楼主| 发表于 2017-2-21 11:07:26 | 只看该作者
本帖最后由 ancientcc 于 2017-2-21 16:17 编辑

生成一个可用于解析*.cfg文件/目录的istream句柄。
语法
  1. std::istream* preprocess_file(std::string const &fname, preproc_map *defines)
复制代码
参数
fname要解析的文件或目录。
defines预定义宏。preprocess_file会复制一份,调用程序在它之后可安全释放defines内存。

返回值
可用于接下read的句柄。

注释
  • 只是构造相关对象,不执行任何的读文件。
  • 它是把WML配置(*.cfg)转换为config两步中的第一步。有了它生成的句柄in后就可用read(cfg, in)去生成config,生成的config放在cfg。


从in不断读WML配置,去生成config,生成的结果放在cfg。
语法
  1. void read(config &cfg, std::istream &in, abstract_validator * validator)
复制代码
参数
cfg存放生成的config。
in能够读取WML配置的句柄。它分两种情况,一是源于磁盘上文件,此时用preprocess_file生成句柄,二是一个内存中的字符串,此可能就是std::istringstream。

返回值


注释
  • 它读取数据靠的是in.get(),对preprocess_file生成的句柄,会触发preprocessor_streambuf::underflow()。
  • 它是把WML配置(*.cfg)转换为config两步中的第二步。从in不断读WML配置,去生成config,生成的结果放在cfg。
  • 当in表示的是磁盘上文件时的read逻辑。
    1、in.get()没了数据时,触发读preprocessor_streambuf::underflow()。
    2、underflow调用current->get_chunk读数据,current_总是指向preprocessor栈的栈顶单元。preprocessor有两种类型,分别是preprocessor_file,preprocessor_data,它们都重载了get_chunk。preprocessor_file封装一个文件或目录,get_chunk功能是读取下一个文件/目录,所以对单文件来说,它第一次调用get_chunk就返回false(构造时已生成该文件的preprocessor_data)。preprocessor_data封装一个文件,get_chunk功能是读取该文件中数据。让以preprocess_file中的fname是文件还是目录进行分析。
    fname是文件。preprocessor_file构造函数会同时构造preprocessor_data。preprocessor_streambuf的preprocessor栈就有了两个单元。underflow()调用栈顶的preprocessor_data.get_chunk要数据,一旦它没了数据后,把preprocessor_data弹出preprocessor栈,调用栈顶preprocessor_file.get_chunk,它立即返回false,underflow不再有数据,生成结束。
    fname是目录。preprocess_file构造函数时不会生成preprocessor_data,preprocessor_streambuf的preprocessor栈只有preprocess_file这个单元。underflow()调用栈顶的preprocessor_file.get_chunk要数据,此函数为目录中第一个文件构造preprocessor_file(它不会为子目录构造,但如果该子目录下有_main.cfg,那它会构造该文件)!接下处理类似“fname是文件”,只是到了preprocessor_data时,preprocessor_streambuf的preprocessor栈有三个单元!所以preprocessor栈回到一个单元时,调用会回到preprocess_file.get_chunk,get_chunk处理第二个文件。依次类推,直到最后一个,underflow不再有数据,生成结束。


对preprocessor_file.get_chunk枚举出的文件/目录的路径,它由preprocess_file决定,没有当前目录的概念。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|丽谷软件|libsdl.cn

GMT+8, 2025-5-2 00:30 , Processed in 0.044611 second(s), 22 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表