Skip to content

Commit 2d01c82

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

File tree

2 files changed

+47
-7
lines changed

2 files changed

+47
-7
lines changed

Common/interface/StringTools.hpp

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,34 @@
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());
5460
for (size_t i = 0; i < Len; ++i)
55-
NarrowStr[i] = ctfacet.narrow(WideStr[i], 0);
61+
{
62+
char mb[4];
63+
int n = wctomb(mb, WideStr[i]);
64+
if (n > 0)
65+
{
66+
NarrowStr.append(mb, mb + n);
67+
}
68+
}
5669

5770
return NarrowStr;
5871
}
@@ -62,18 +75,31 @@ inline std::string NarrowString(const std::wstring& WideStr)
6275
return NarrowString(WideStr.c_str(), WideStr.length());
6376
}
6477

78+
// See also: https://en.cppreference.com/w/cpp/string/multibyte/mbtowc
6579
inline std::wstring WidenString(const char* Str, size_t Len = 0)
6680
{
6781
if (Len == 0)
6882
Len = strlen(Str);
6983
else
7084
VERIFY_EXPR(Len <= strlen(Str));
7185

72-
std::wstring WideStr(Len, L'\0');
86+
std::wstring WideStr;
87+
WideStr.reserve(Len);
7388

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]);
89+
for (size_t Offset = 0; Offset < Len;)
90+
{
91+
wchar_t wc;
92+
int n = mbtowc(&wc, Str + Offset, Len - Offset);
93+
if (n > 0)
94+
{
95+
WideStr += wc;
96+
Offset += n;
97+
}
98+
else
99+
{
100+
break;
101+
}
102+
}
77103

78104
return WideStr;
79105
}
@@ -294,3 +320,7 @@ size_t GetPrintWidth(Type Num, Type Base = 10)
294320
}
295321

296322
} // namespace Diligent
323+
324+
#ifdef _MSC_VER
325+
# pragma warning(pop)
326+
#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)