从相同的硬编码字符串文字初始化std :: string和std :: wstring

时间:2017-10-10 11:42:52

标签: c++ string unicode

当我偶然发现一个曾经困扰过我几次的情景时,我正在编写一些单元测试。

我需要生成一些字符串来测试JSON编写器对象。由于编写器支持UTF16和UTF8输入,我想用两者进行测试。

考虑以下测试:

class UTF8;
class UTF16;

template < typename String, typename SourceEncoding >
void writeJson(std::map<String, String> & data)
{
    // Write to file
}

void generateStringData(std::map<std::string, std::string> & data)
{
    data.emplace("Lorem", "Lorem Ipsum is simply dummy text of the printing and typesetting industry.");
    data.emplace("Ipsum", "Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book");
    data.emplace("Contrary", "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old");
}

void generateStringData(std::map<std::wstring, std::wstring> & data)
{
    data.emplace(L"Lorem", L"Lorem Ipsum is simply dummy text of the printing and typesetting industry.");
    data.emplace(L"Ipsum", L"Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book");
    data.emplace(L"Contrary", L"Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old");
}

template < typename String, typename SourceEncoding >
void testWriter() {
    std::map<String, String> data;
    generateStringData(data);
    writeJson<String, SourceEncoding>(data);
}

int main() {
    testWriter<std::string, UTF8>();
    testWriter<std::wstring, UTF16>();
}

除了重复的generateStringData()方法之外,我设法很好地包装了所有内容。 如果可以将两种generateStringData()方法合并为一个方法,我就会徘徊吗?

我知道我可以使用单一方法在UTF8中生成字符串,然后使用其他方法将字符串转换为UTF16,但我试图找出是否有另一种方式。

我考虑过/试过了什么?

  • 使用_T()TCHAR#ifdef UNICODE无法提供帮助,因为我需要在支持Unicode的同一平台上使用这两种口味(例如Win&gt; = 7)
  • 从非std::wstring的内容初始化L"",因为它需要wchar_t
  • 通过char初始化char赢得了工作,因为它还需要L''
  • 使用""s无法正常工作,因为返回类型取决于类型charT

2 个答案:

答案 0 :(得分:4)

简短的回答是否定的,你不能将两个generateStringData()实现合并在一起。

一个是输出char数据所必需的,另一个是输出wchar_t数据所必需的。您可以use #define macros减少代码中常见字符串文字的重复,但仍需要在L实现中使用wchar_t前缀,最好使用u8前缀char实现(为了确保数据实际上是UTF-8而不是编译器定义的),所以在运行时你仍然会在内存中使用单独的字符串。

即使您使用模板尝试合并这两个实现,您最终也需要使用模板特化来分离两种输出类型。

您最好只使用已有的重载(可能使用#define来减少代码中的重复项),或者在运行时执行UTF转换(您希望避免)。在后一种情况下,您可以通过在应用启动时执行一次转换并缓存结果以便重复使用来减少测试运行的开销。

答案 1 :(得分:2)

如果 您只需要将纯ASCII编码为charwchar_t s,那么您可以使用功能模板(无需专业化):

#include <iostream>
#include <map>
#include <string>
#include <utility>

template <typename StringType>
void generateStringData(std::map<StringType, StringType> &data) {
  static const std::pair<const char *, const char *> entries[] = {
    { "Lorem", "Lorem Ipsum is simply dummy text ..."},
    { "Ipsum", "Ipsum has been the industry's standard ..."}
  };
  for (const auto &entry : entries) {
    data.emplace(StringType(entry.first, entry.first + std::strlen(entry.first)),
                 StringType(entry.second, entry.second + std::strlen(entry.second)));
  }
}

int main() {
  std::map<std::string, std::string> ansi;
  generateStringData(ansi);
  std::map<std::wstring, std::wstring> wide;
  generateStringData(wide);

  std::cout << ansi["Lorem"] << std::endl;
  std::wcout << wide[L"Lorem"] << std::endl;
  return 0;
}

这仅适用于 ,因为任何ASCII字符的wchar_t版本只是扩展为16位的ASCII值。如果你有兴趣&#34;源字符串中的字符,实际上不会将它们转换为正确的UTF-16。

另请注意,您几乎肯定会在内存中找到四个字符串副本:可执行文件中的两个ASCII源字符串副本(来自函数模板的两个实例),以及{{1堆中有}和char个副本。

但这可能不比预处理器版本差。使用预处理器,您最终可能会在可执行文件中同时使用wchar_tchar版本,以及堆中的wchar_tchar副本。< / p>

预处理器方法可以做的是帮助你绕过这个答案顶部的那个大<​​strong> if ;使用预处理器,您可以使用非ASCII字符。

[实施说明:最初这些作业使用wchar_tstd::begin(entry.first),但其中包含字符串终结符作为字符串本身的一部分。]