我正在编写lambda演算解释器,以获取乐趣和实践经验。我通过添加ctype构面(将标点符号定义为空格)来使iostream正确地标记标识符:

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 );

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

但是ctype<wchar_t>的工作原理与ctype<char>完全不同。没有主表;共有四种方法do_is x2,do_scan_isdo_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在这里是无关紧要的,但我的思考范围更广。)首先我是在滥用构面吗?以上甚至正确吗?实现更少的逻辑会是更好的样式吗?

最佳答案

(这是一年,没有实质性的回答,与此同时,我已经了解了很多有关iostream的知识……)

定制方面专门用于服务于字符串提取运算符in >> token。该运算符是根据use_facet< ctype< wchar_t > >( in.getloc() ).is( ios::space, c )“针对下一个可用的输入字符c”定义的。 (第21.3.7.9节)ctype::is只是ctype::do_is的 stub ,因此do_is似乎就足够了。

尽管如此,最新版本的GCC标准库确实根据operator>>实现了scan_is。要注意的是,然后将do_scan_is实现为对do_is,虚拟调度等的所有调用。头文件将do_scan_is描述为用户优化的钩子(Hook)。

因此,似乎该规则包含了仅提供第一个替代的实现。

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

无论如何,这一切都很好,但是使用streambuf_iterator<wchar_t>编写 token 生成器更容易,可扩展得多,并且简化了异常处理。

10-05 22:01