Skip to content

Commit c1b66de

Browse files
committed
String tools: make WidenString and NarrowString work with multibyte characters
1 parent 977d540 commit c1b66de

File tree

2 files changed

+49
-7
lines changed

2 files changed

+49
-7
lines changed

Common/interface/StringTools.hpp

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,36 @@
3838
#include "StringTools.h"
3939
#include "ParsingTools.hpp"
4040

41+
#ifdef _MSC_VER
42+
# pragma warning(push)
43+
# pragma warning(disable : 4996) // 'This function or variable may be unsafe': mbtowc, wctomb
44+
#endif
45+
4146
namespace Diligent
4247
{
4348

49+
// See also: https://en.cppreference.com/w/cpp/string/multibyte/wctomb
4450
inline std::string NarrowString(const wchar_t* WideStr, size_t Len = 0)
4551
{
4652
if (Len == 0)
4753
Len = wcslen(WideStr);
4854
else
4955
VERIFY_EXPR(Len <= wcslen(WideStr));
5056

51-
std::string NarrowStr(Len, '\0');
57+
std::string NarrowStr;
58+
NarrowStr.reserve(Len * 4); /* MB_CUR_MAX = 4 */
5259

53-
const std::ctype<wchar_t>& ctfacet = std::use_facet<std::ctype<wchar_t>>(std::wstringstream().getloc());
60+
size_t Offset = 0;
5461
for (size_t i = 0; i < Len; ++i)
55-
NarrowStr[i] = ctfacet.narrow(WideStr[i], 0);
62+
{
63+
char mb[4];
64+
int n = wctomb(mb, WideStr[i]);
65+
if (n > 0)
66+
{
67+
Offset += n;
68+
NarrowStr.append(mb, mb + n);
69+
}
70+
}
5671

5772
return NarrowStr;
5873
}
@@ -62,18 +77,31 @@ inline std::string NarrowString(const std::wstring& WideStr)
6277
return NarrowString(WideStr.c_str(), WideStr.length());
6378
}
6479

80+
// See also: https://en.cppreference.com/w/cpp/string/multibyte/mbtowc
6581
inline std::wstring WidenString(const char* Str, size_t Len = 0)
6682
{
6783
if (Len == 0)
6884
Len = strlen(Str);
6985
else
7086
VERIFY_EXPR(Len <= strlen(Str));
7187

72-
std::wstring WideStr(Len, L'\0');
88+
std::wstring WideStr;
89+
WideStr.reserve(Len);
7390

74-
const std::ctype<wchar_t>& ctfacet = std::use_facet<std::ctype<wchar_t>>(std::wstringstream().getloc());
75-
for (size_t i = 0; i < Len; ++i)
76-
WideStr[i] = ctfacet.widen(Str[i]);
91+
for (size_t Offset = 0; Offset < Len;)
92+
{
93+
wchar_t wc;
94+
int n = mbtowc(&wc, Str + Offset, Len - Offset);
95+
if (n > 0)
96+
{
97+
WideStr += wc;
98+
Offset += n;
99+
}
100+
else
101+
{
102+
break;
103+
}
104+
}
77105

78106
return WideStr;
79107
}
@@ -294,3 +322,7 @@ size_t GetPrintWidth(Type Num, Type Base = 10)
294322
}
295323

296324
} // namespace Diligent
325+
326+
#ifdef _MSC_VER
327+
# pragma warning(pop)
328+
#endif

Tests/DiligentCoreTest/src/Common/StringToolsTest.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,11 @@ TEST(Common_StringTools, WidenString)
240240

241241
EXPECT_EQ(WidenString(std::string{""}), std::wstring{L""});
242242
EXPECT_EQ(WidenString(std::string{"abc"}), std::wstring{L"abc"});
243+
244+
constexpr char locale_name[] = "en_US.UTF-8";
245+
std::setlocale(LC_ALL, locale_name);
246+
std::locale::global(std::locale(locale_name));
247+
EXPECT_STREQ(WidenString("Bézier").c_str(), L"Bézier");
243248
}
244249

245250
TEST(Common_StringTools, NarrowString)
@@ -250,6 +255,11 @@ TEST(Common_StringTools, NarrowString)
250255

251256
EXPECT_EQ(NarrowString(std::wstring{L""}), std::string{""});
252257
EXPECT_EQ(NarrowString(std::wstring{L"abc"}), std::string{"abc"});
258+
259+
constexpr char locale_name[] = "en_US.UTF-8";
260+
std::setlocale(LC_ALL, locale_name);
261+
std::locale::global(std::locale(locale_name));
262+
EXPECT_STREQ(NarrowString(L"Bézier").c_str(), u8"Bézier");
253263
}
254264

255265
TEST(Common_StringTools, GetPrintWidth)

0 commit comments

Comments
 (0)