如何在跨平台(Windows,iOS,Android)C ++应用程序中表示string?

我正在开发一个应用程序,其中的核心代码库将为Windows,iOS和Android的跨平台。

我的问题是:我应该如何在内部代表这个应用程序使用的string能够在所有三个平台上有效地使用它们?

重要的是要注意,我在Windows中大量使用DirectWrite,其中API函数通常期望传递wchar_t *(btw。API文档声明“指向Unicode字符数组的指针”,我不知道这是否意味着它们使用UTF-16编码)

我看到了三种不同的方法(不过我发现用跨平台的方式来掌握用C ++处理Unicodestring的细节是非常困难的,所以也许我会错过一些重要的概念):

  • 在内部使用std :: string(并将string存储为UTF-8编码?),并将它们转换为wx_t *,以供DirectWrite API使用(我不知道文本处理API需要什么Android和iOS)。
  • 在内部使用std :: wstring。 如果我理解正确的话,这从内存使用的angular度来看是不会有效的,因为在iOS和Android上wchar_t是4个字节(这是否意味着我将不得不在Windows上将string存储在UTF-16中) Android / iOS上的UTF-32?)
  • 为抽象基类创build一个抽象的string,并针对不同的平台实现内部存储。

什么是最好的解决scheme? 顺便说一下,是否有任何现有的跨平台库抽象string处理? (以及Unicodestring的读取和序列化)

(更新:删除有关char *和std :: string的区别问题的部分。)

我的问题的一部分来自我的误解,或者不完全理解如何在C + +(我来自C#背景)的字符串wstring类工作。 std :: wstring VS std :: string这两个差异的优点和缺点已经被描述。

字符串和wstring如何工作

对我来说,关于string和wstring类重要的一个发现就是在语义上它们不代表一段编码文本 ,而只是一个char或wchar_t的“字符串”。 它们更像是一个简单的数据数组,具有一些特定于字符串的操作(如append和substr)而不是表示文本。 它们都不知道任何类型的字符串编码,它们将每个字符或wchar_t元素作为单独的字符单独处理。

编码

但是,在大多数系统上,如果你从一个字符串文字创建一个字符串,像这样的特殊字符:

std::string s("ű"); 

ű字符将在内存中由多个字节表示, 但与std :: string类无关 ,这是编译器的一个功能,因为它可以使用UTF8编码字符串文本(不是每个编译器都可以)。 (以L为前缀的字符串文本将由wchar_t-s以UTF16或UTF32或其他方式表示,具体取决于编译器)。
因此字符串“ű”将在内存中用两个字节表示: 0xC5 0xB1 ,std :: string类不会知道这两个字节在语义上是指UTF8中的一个字符(一个Unicode码位),因此示例代码:

 std::string s("ű"); std::cout << s.length() << std::endl; std::cout << s.substr(0, 1); 

产生以下结果(取决于编译器,有些编译器不把字符串字面量当作UTF8,而有些编译器依赖于源文件的编码):

 2   

size()函数返回2,因为std :: string唯一知道的是它存储两个字节(两个字符)。 而且substr也是“原始地”工作,它返回一个包含单个字符0xC5的字符串,显示为because,因为它不是一个有效的UTF8字符(但不会影响std :: string)。

由此我们可以看出,处理编码的人是平台的各种文本处理API,比如简单的coutDirectWrite

我的方法

在我的应用程序中,DirectWrite非常重要,它只接受以UTF16编码的字符串(以wchar_t *指针的形式)。 所以我决定将这些字符串存储在内存中,并以UTF16格式编码。 不过,我想要一个跨平台的实现,它可以处理Windows,Android和iOS上的UTF16字符串,这在std :: wstring中是不可能的,因为它的数据大小(以及它适合使用的编码)是依赖于平台的。

要创建一个跨平台,严格的UTF16字符串类我模板basic_string上的数据类型是2个字节长 。 非常令人惊讶的是 – 至少对我来说 – 我几乎找不到关于这个在线的信息,我基于这个方法的解决方案。 这里是代码:

 // Define this on every platform to be 16 bytes! typedef unsigned short char16; struct char16_traits { typedef char16 _E; typedef _E char_type; typedef int int_type; typedef std::streampos pos_type; typedef std::streamoff off_type; typedef std::mbstate_t state_type; static void assign(_E& _X, const _E& _Y) {_X = _Y; } static bool eq(const _E& _X, const _E& _Y) {return (_X == _Y); } static bool lt(const _E& _X, const _E& _Y) {return (_X < _Y); } static int compare(const _E *_U, const _E *_V, size_t _N) {return (memcmp(_U, _V, _N * 2)); } static size_t length(const _E *_U) { size_t count = 0; while(_U[count] != 0) { count++; } return count; } static _E * copy(_E *_U, const _E *_V, size_t _N) {return ((_E *)memcpy(_U, _V, _N * 2)); } static const _E * find(const _E *_U, size_t _N, const _E& _C) { for(int i = 0; i < _N; ++i) { if(_U[i] == _C) { return &_U[i]; } } return 0; } static _E * move(_E *_U, const _E *_V, size_t _N) {return ((_E *)memmove(_U, _V, _N * 2)); } static _E * assign(_E *_U, size_t _N, const _E& _C) { for(size_t i = 0; i < _N; ++i) { assign(_U[i], _C); } return _U; } static _E to_char_type(const int_type& _C) {return ((_E)_C); } static int_type to_int_type(const _E& _C) {return ((int_type)(_C)); } static bool eq_int_type(const int_type& _X, const int_type& _Y) {return (_X == _Y); } static int_type eof() {return (EOF); } static int_type not_eof(const int_type& _C) {return (_C != eof() ? _C : !eof()); } }; typedef std::basic_string<unsigned short, char16_traits> utf16string; 

字符串被存储在上面的类中, 原始的UTF16数据被传递到各种平台的特定API函数,所有这些目前似乎都支持UTF16编码。
实现可能不完美,但append,substr和size函数似乎正常工作。 我仍然没有太多的C ++字符串处理经验,所以如果我错误地陈述了某些事情,可以随意评论/编辑。

std :: strings和char *之间的区别在于,std :: string类使用C ++特性,char *不使用。 一个std :: string是一个char类型的容器,定义了方便的方法来使用它,char *是指向你可以使用的一些内存的指针。

如果你正在寻找一些独立于平台的基类,我会指向你的QString 。 这是Qt库的一部分,旨在实现与C ++平台无关的实现。 它也是OpenSource ,所以你可以用它来了解别人如何实现平台独立字符串。 文档也很好

实现一个抽象类在每个平台上以不同的方式表示似乎是一个坏主意。 额外的工作实现和测试(在每个平台上),并会增加更多的开销,而不仅仅是使用std :: wstring(当然,您可以通过不使用抽象类来抵消开销,而是使用#ifdefs切换实现,但仍然额外工作)。

无论是使用std :: string或std :: wstring到处都似乎要走的路,实现一些实用功能,将您选择的字符串转换为系统相关的格式,你不会有问题。 我正在开发一个多平台的项目,这个项目已经在iOS,Windows,Linux和Mac上运行,在这个项目中我使用了多字节std :: string,没有太多问题,从来没有使用过std :: wstring,看看为什么它不会工作。