我想实现一个c ++库,和许多其他的lib一样,我需要从用户那里获取字符串参数并返回字符串。当前标准定义了std :: string和std :: wstring(我更喜欢wstring)。从理论上讲,我必须使用字符串参数两次实现方法:
virtual void foo(std::string &) = 0; // convert internally from a previous defined charset to unicode virtual void foo(std::wstring &) = 0;
C ++ 0x不会让生活更轻松,对于我需要的char16_t和char32_t:
virtual void foo(std::u16string &) = 0; virtual void foo(std::u32string &) = 0;
在内部处理这些不同类型 - 例如将all放入私有向量成员 - 需要转换,包装......这太可怕了。
另一个问题是,如果用户(或我自己)想要使用自定义分配器或自定义特征类:everthing会产生一种全新的类型。例如,要为多字节字符集编写自定义codecvt特化,标准说我必须引入一个自定义state_type - 这需要一个自定义特征类,这会产生一个新的std :: basic_ifstream<>类型 - 这与期望std :: ifstream&的接口完全不兼容作为一个论点。
One -possible解决方案是将每个库类构造为管理用户指定的value_type,traits和allocator的模板。但这太过分了,使得抽象基类(接口)变得不可能。
另一种解决方案是只指定一个类型(例如u32string)作为默认值,每个用户必须使用此类型传递数据。但是现在想想一个使用3个库的项目,第一个lib使用u32string,第二个lib使用u16string和thirth lib wstring - >地狱。
我真正想要的是声明一个方法就像 void foo(put_unicode_string_here) - 而不引入我自己的UnicodeString或UnicodeStream类。
答案 0 :(得分:1)
如果你不想支持所有内容,总会有选择,但我个人觉得限制输入UTF-8是最简单的。只需使用普通的std::string
,每个人都很开心。实际上,用户(你的库)只需转换为UTF-8,如果他在Windows上,但是有很多方法可以完成这个简单的任务。
更新:另一方面,您可以为所有代码设置模板,并在整个代码中保留std::basic_string<T>
作为模板。如果根据模板参数的大小做不同的事情,这只会变得混乱。
答案 1 :(得分:0)
char_traits
确实是一个随机特质的无可救药的垃圾箱。每个字符串是否应预先指定编码机制本身支持的最大文件大小,区分大小写和(ugh)状态类型? NO。
然而,即使设计精良的特征,你所要求的也是不可能的。 string
和wstring
有意义的不同,因为内部字符类型的大小不同。要运行任何类型的算法,您需要查询char_t
的对象。这需要RTTI或虚函数,因为basic_string
在运行时不会(也不应该)维护该信息。
One -possible解决方案是将每个库类构造为管理用户指定的value_type,traits和allocator的模板。但这太过分了,使得抽象基类(接口)变得不可能。
这是唯一完整的解决方案。模板实际上做可以很好地使用抽象基类:许多模板可以从非模板抽象基础派生,或者基础也可以模板化。然而,由于编写完全通用的代码的敏感性和乏味,即使不是站不住脚也很困难。
另一个解决方案是只指定一种类型(例如u32string)作为默认值,每个用户必须使用此类型传递数据。但是现在想想一个使用3个库的项目,第一个lib使用u32string,第二个lib使用u16string和thirth lib wstring - &gt;地狱。
这就是为什么我害怕C ++ 11的“改进”Unicode支持。它简化了与文件数据的直接交互,并阻止了对通用 wchar_t
内部格式的抽象。最好为UTF-16和UTF-32要求特定的代码,并指定wchar_t
必须至少为21位。在干净的C ++接口中只有“哑”char
和“智能”wchar_t
库之前,我们可能不得不与额外的宽度竞争 - 而char16_t
只是一个瞬间的红旗。
但是,那是在路上。
如果您真的最终使用了许多不兼容的库,并且问题是在需要不同格式的函数之间传递数据,那么编写一个ScopeGuard-style实用程序来转换为您选择的通用格式,例如{ {1}}。此实用程序可以是一个模板,具有您需要的每种不兼容格式的显式特化,或者一组非模板化的类。