8

我正在编写一个 lambda 演算解释器来娱乐和练习。我让 iostreams 通过添加一个ctype将标点符号定义为空格的方面来正确标记标识符:

struct token_ctype : ctype<char> {
 mask t[ table_size ];
 token_ctype()
 : ctype<char>( t ) {
  for ( size_t tx = 0; tx < table_size; ++ tx ) {
   t[tx] = isalnum( tx )? alnum : space;
  }
 }
};

classic_table()可能会更干净,但这在 OS X 上不起作用!)

然后在我遇到标识符时交换构面:

locale token_loc( in.getloc(), new token_ctype );
…
locale const &oldloc = in.imbue( token_loc );
in.unget() >> token;
in.imbue( oldloc );

Web 上似乎很少有 lambda 演算代码。到目前为止,我发现的大部分内容都充满了 unicode λ字符。所以我想尝试添加 Unicode 支持。

ctype<wchar_t>工作方式与ctype<char>. 没有主表;有四种方法do_isx2 do_scan_is、 和do_scan_not。所以我这样做了:

struct token_ctype : ctype< wchar_t > {
 typedef ctype<wchar_t> base;

 bool do_is( mask m, char_type c ) const {
  return base::do_is(m,c)
  || (m&space) && ( base::do_is(punct,c) || c == L'λ' );
 }

 const char_type* do_is
  (const char_type* lo, const char_type* hi, mask* vec) const {
  base::do_is(lo,hi,vec);
  for ( mask *vp = vec; lo != hi; ++ vp, ++ lo ) {
   if ( *vp & punct || *lo == L'λ' ) *vp |= space;
  }
  return hi;
 }

 const char_type *do_scan_is
  (mask m, const char_type* lo, const char_type* hi) const {
  if ( m & space ) m |= punct;
  hi = do_scan_is(m,lo,hi);
  if ( m & space ) hi = find( lo, hi, L'λ' );
  return hi;
 }

 const char_type *do_scan_not
  (mask m, const char_type* lo, const char_type* hi) const {
  if ( m & space ) {
   m |= punct;
   while ( * ( lo = base::do_scan_not(m,lo,hi) ) == L'λ' && lo != hi )
    ++ lo;
   return lo;
  }
  return base::do_scan_not(m,lo,hi);
 }
};

(为平面格式道歉;预览以不同的方式转换了选项卡。)

代码不太优雅。我确实更好地表达了只有标点符号是额外的空格的概念,但如果我有classic_table.

有没有更简单的方法来做到这一点?我真的需要所有这些重载吗?(测试显示do_scan_not在这里是无关紧要的,但我想的更广泛。)我是否首先滥用了方面?以上是否正确?实现更少的逻辑会更好吗?

4

2 回答 2

3

(已经有一年没有实质性答案了,同时我也学到了很多关于 iostreams 的知识……)

自定义方面专门用于为字符串提取运算符提供服务in >> token。该运算符是根据use_facet< ctype< wchar_t > >( in.getloc() ).is( ios::space, c )“下一个可用的输入字符c ”定义的。(§21.3.7.9)ctype::is只是 的存根ctype::do_is,所以看起来do_is就足够了。

尽管如此,最新版本的 GCC 标准库确实operator>>scan_is. 问题在于,它随后被实现为对、虚拟调度等do_scan_is的一系列调用。do_is头文件描述do_scan_is为用户优化的钩子。

因此,as-if 规则似乎保护了仅提供第一个覆盖的实现。

请注意,检索掩码值的第二个覆盖是一个奇怪的输出。它可以通过低效地逐位构建掩码来实现第一个。在 GCC 中,它是根据系统调用来实现的,每个字符 15 次调用来逐位构建掩码的效率很低。这似乎牺牲了性能和兼容性。幸运的是,似乎没有人使用它。


无论如何,这一切都很好,但是简单地编写一个标记器使用streambuf_iterator<wchar_t>更容易,更可扩展,并且简化了异常处理。

于 2011-07-05T06:56:32.507 回答
2

恕我直言,您发布的代码很好。如果您想要更简单的代码(可能以牺牲效率为代价),您可以使用其他方法来实现一些方法,但是您执行它的方式是可以的。

这种差异是基于人们不希望在他们的 UNICODE 程序中有几兆字节的表这一事实。

于 2010-03-25T00:22:34.943 回答