覆盖ctype <wchar_t> </wchar_t>

时间:2010-02-26 05:00:29

标签: c++ unicode iostream facet

我正在为乐趣和练习编写一个lambda演算解释器。我通过添加ctype facet来将标点符号定义为空格,从而使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在这里是无关紧要的,但我的想法更广泛。)我是否首先滥用方面?以上是否正确?实现更少的逻辑会更好吗?

2 个答案:

答案 0 :(得分:3)

(这是一年没有实质性的答案,我在此期间学到了很多关于iostreams的信息......)

自定义构面专门用于提供字符串提取运算符in >> token。对于下一个可用的输入字符 c ,该运算符按use_facet< ctype< wchar_t > >( in.getloc() ).is( ios::space, 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>编写一个标记化器就更容易,更易于扩展,并简化了异常处理。

答案 1 :(得分:2)

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

这种差异是基于这样一个事实:人们不希望他们的UNICODE计划中有几兆字节的表。