2014-05-13 26 views
4

C++擁有的一件事是多個字符串或者說字符類型:char,wchar_t,char16_t,char32_t。因此,我們有不同的字符串類型定義:std::string,std::wstring,std::u16stringstd::u32string,它們是不同的字符串類型。如果我們正在談論Windows和COM,那麼也有平臺類型,如BSTR s。但是它並不止於此,如果我們正在談論Windows和COM,還有平臺類型,如BSTR s。我們甚至還沒有開始談論 字符編碼。在新的C++庫中處理多個字符串類型

如果您正在構建一個新庫,並且其中一個需求是支持所有這些字符串類型或字符類型,那麼您將如何執行此操作?現在讓我們忘記字符編碼。

我在想這件事,我拿出幾個選項,但沒有一個是理想的。讓我們假設你有一個registry_key類,它具有 支持所有這些字符類型,其OM的一部分或多或少(只說明它的一部分在這裏):

class registry_key 
{ 
public: 
    registry_key(unspecified_string_type keyname); 
    unspecified_string_type name() const; 
    unspecified_string_type path() const; 
} 

你就可以使用它像:

registry_key key("HKLM\\Software\\Adobe"); 
std::string name = key.name(); 

但是,它必須支持其他字符串類型。此外,沒有要求整個registry_key必須一致,只要字符類型去,或操作一個單一的字符類型。您可以調用構造函數並傳遞const char*,但將密鑰的名稱作爲u16string。這是下面的平臺的反映,它允許您在同一api集內調用wide(XxxW)和narrow(XxxA)apis。這種行爲是理想的。

對於構造函數(或帶參數的東西),這很簡單,因爲可以推導出類型。但是,對於返回字符串的函數而言,不會將任何內容作爲輸入,它不能。

至於選擇去,我有:

1)模板在STL與字符類型整個註冊表鍵,以同樣的方式basic_string,和其他類型一樣。所以,你會

wregistry_key key(L"HKLM\\Software\\Adobe"); 
std::wstring name = key.name(); 

u8registry_key key(u8"HKLM\\Software\\Adobe"); 
std::u16string name = key.name(); 

的問題是,這並沒有真正規模這是相當可怕的,如果它被應用到了很多的類型,任何事情處理字符串。從某種意義上來說,這是一個糟糕的設計選擇,因爲有些類甚至沒有太多關於字符串的內容,所以爲什麼首先將它作爲模板參數傳遞。

2)採用和使用單個字符串類型,如u16stringu32string。但如上所述,這是違背目標的。

3)前綴字符類型的函數名:

registry_key key("HKLM\\Software\\Adobe"); 
std::string name = key.name(); 
std::wstring name = key.wname(); 
std::u16string name = key.u8name(); 
std::u32string name = key.uname(); 

這一點,但仍然redundand。

4)創建一個新的字符串類型,它根本不是字符串類型。它有一種方式可以存儲不同類型的字符串,並使用用戶定義的轉換運算符查詢並轉換爲其他類型的字符串。所以這將是自動的。

platform_string str = L"foo"; 
std::string sstr = str; 
std::wstring swstr = str; 
std::u16string su16str = str; 
str = u"foo"; 

這將使寫註冊表類可能是這樣的:

class registry_key 
{ 
public: 
    registry_key(unspecified_string_type keyname); 
    platform_string name() const; 
    platform_string path() const; 
} 

而且你可以使用它作爲:

registry_key key("HKLM\\Software\\Adobe"); 
std::string name = key.name(); 
std::wstring name = key.name(); 
std::u16string name = key.name(); 

的問題,這是引進的想法看起來像一個新的字符串類型,即使它不是真的。它感覺破碎了。

是否有比更好的解決方案3)4)?或者更好的方法來解決這個問題?

回答

4

慣用的方法這種類型的問題是庫設計師挑選一個字符串類型並一致使用整個接口。如果您需要C兼容性,請使用C格式字符串,否則使用C++字符串。選擇圖書館功能所需的字符大小。

讓庫的調用者處理字符串轉換。

否則,你將有一個爛攤子。

4

如果你正在建設一個新的圖書館,並要求之一是支持所有這些字符串類型,或字符類型,你會怎麼做呢?

我不會。

std::codecvtboost::nowide之間,現在各種字符串格式之間的轉換並不完全麻煩。

我可能只是使用UTF-32內部(RAM便宜,這些天,對吧?)和UTF-8公共接口。的UTF-16(使用char16_twchar_t)公共接口威力也無可非議如同看見這麼多的平臺可以在內部使用,尤其是Windows,但我寧願避免這樣的事情(除非我正在爲它付出, 也許)。

wstringwchar_t由於可移植性問題應儘可能地避免,因爲wchar_t的定義是平臺相關的。只應使用具有明確寬度的字符類型(例如,char,char16_tchar32_t)。

您的選項(3)涉及到您的API的大小翻兩番......不,謝謝!

你的選擇(4)覺得這樣做會非常困難,增加一大塊複雜性以換取一點方便。

強制調用者進行字符串轉換似乎是處理問題最簡單,最安全和最容易的方式。解決方案(2)一路。

哦,和通常的http://utf8everywhere.org/鏈接爲完整性。

+0

依賴於平臺的'wchar_t'問題可以在C++ 11中繞過,它增加了'char16_t'類型 – Joe

+0

@joe,但是這就是爲什麼我提到'wchar_t'與UTF-16分開的原因;) – Rook

+0

我明白了,但是你可以提到這個替代方案已經存在了(並且是專門針對UTF-16的!) – Joe

1

儘管我會和其他人的建議一起使用,並讓我的圖書館處理一種字符串類型,但我希望您看看boost.filesystem,特別是路徑類 Boost Filesystem V3 design的設計。基本上你沒有模板類,內部只使用一個字符串類型,然後提供模板成員,它接受你決定的任何字符串類型並將其轉換爲內部表示。

+0

增強路徑的問題在於它是非常本地化和孤立的,如果你必須在很多地方做,它不會擴展,在我的庫中我有很多大量返回字符串的函數,如果它有對於其中的每一個人來說都是如此,這實際上是非常不可用的 –

+0

在這種情況下,縮放的唯一解決方案是使用單一類型並讓客戶端在需要時進行轉換。實際的類型應該取決於平臺,以避免不必要的轉換(Linux上的std :: string通常使用UTF-8,std :: u16string或std :: wstring),因此類似於: #ifdef WIN32 typedef std :: wstring string_type; #else typedef std :: string string_type; #endif –