Skip to content

Commit fc69c73

Browse files
committed
Further improvements to scoped_thread_locale and improved the tests.
1 parent c88c58b commit fc69c73

File tree

6 files changed

+121
-92
lines changed

6 files changed

+121
-92
lines changed

Release/include/cpprest/asyncrt_utils.h

Lines changed: 8 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -163,72 +163,22 @@ namespace details
163163
/// <summary>
164164
/// Cross platform RAII container for setting thread local locale.
165165
/// </summary>
166-
#ifdef _MS_WINDOWS
167-
class thread_local_locale
166+
class scoped_thread_locale
168167
{
169168
public:
170-
thread_local_locale(const char * locale)
171-
{
172-
char *prevLocale = setlocale(LC_ALL, nullptr);
173-
if (prevLocale == nullptr)
174-
{
175-
throw std::runtime_error("Unable to retrieve current locale.");
176-
}
177-
178-
// Copy to a string because later calls can invalidate the returned pointer.
179-
m_prevLocale = prevLocale;
180-
181-
m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
182-
if (m_prevThreadSetting == -1)
183-
{
184-
throw std::runtime_error("Unable to enable per thread locale.");
185-
}
186-
if (setlocale(LC_ALL, locale) == nullptr)
187-
{
188-
throw std::runtime_error("Unable to set locale");
189-
}
190-
}
191-
~thread_local_locale()
192-
{
193-
setlocale(LC_ALL, m_prevLocale.c_str());
194-
_configthreadlocale(m_prevThreadSetting);
195-
}
169+
_ASYNCRTIMP scoped_thread_locale(const char * locale);
170+
_ASYNCRTIMP ~scoped_thread_locale();
196171
private:
172+
#ifdef _MS_WINDOWS
197173
std::string m_prevLocale;
198174
int m_prevThreadSetting;
199-
thread_local_locale(const thread_local_locale &);
200-
thread_local_locale & operator=(const thread_local_locale &);
201-
};
202175
#else
203-
class thread_local_locale
204-
{
205-
public:
206-
thread_local_locale(const char * locale)
207-
{
208-
m_changedLocale = newlocale(LC_ALL, locale, nullptr);
209-
if(m_changedLocale == nullptr)
210-
{
211-
throw std::runtime_error("Unable to create new locale.");
212-
}
213-
m_prevLocale = uselocale(original);
214-
if(m_prevLocale == nullptr)
215-
{
216-
freelocale(original);
217-
throw std::runtime_error("Unable to set locale");
218-
}
219-
}
220-
~thread_local_locale()
221-
{
222-
uselocale(m_prevLocale);
223-
freelocale(m_changedLocale);
224-
}
225-
private:
226176
locale_t m_prevLocale;
227-
locale_t m_changedLocale;
228-
thread_local_locale(const thread_local_locale &);
229-
thread_local_locale & operator=(const thread_local_locale &);
230-
};
177+
locale_t m_newLocale;
231178
#endif
179+
scoped_thread_locale(const scoped_thread_locale &);
180+
scoped_thread_locale & operator=(const scoped_thread_locale &);
181+
};
232182

233183
/// <summary>
234184
/// Our own implementation of alpha numeric instead of std::isalnum to avoid

Release/src/json/json.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ bool web::json::details::_Object::has_field(const utility::string_t &key) const
365365

366366
utility::string_t json::value::to_string() const
367367
{
368-
utility::details::thread_local_locale locale("C");
368+
utility::details::scoped_thread_locale locale("C");
369369
return m_value->to_string();
370370
}
371371

Release/src/json/json_parsing.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ class JSON_Parser
127127

128128
web::json::value ParseValue(typename JSON_Parser<CharType>::Token &first)
129129
{
130+
utility::details::scoped_thread_locale locale("C");
130131
auto _value = _ParseValue(first);
131132
#ifdef ENABLE_JSON_VALUE_VISUALIZER
132133
auto type = _value->type();
@@ -194,7 +195,7 @@ class JSON_StreamParser : public JSON_Parser<CharType>
194195
{
195196
public:
196197
JSON_StreamParser(std::basic_istream<CharType> &stream)
197-
: m_streambuf(stream.rdbuf()), m_locale("C")
198+
: m_streambuf(stream.rdbuf())
198199
{
199200
}
200201

@@ -205,16 +206,14 @@ class JSON_StreamParser : public JSON_Parser<CharType>
205206

206207
private:
207208
typename std::basic_streambuf<CharType, std::char_traits<CharType>>* m_streambuf;
208-
209-
::utility::details::thread_local_locale m_locale;
210209
};
211210

212211
template <typename CharType>
213212
class JSON_StringParser : public JSON_Parser<CharType>
214213
{
215214
public:
216215
JSON_StringParser(const std::basic_string<CharType>& string)
217-
: m_position(&string[0]), m_locale("C")
216+
: m_position(&string[0])
218217
{
219218
m_startpos = m_position;
220219
m_endpos = m_position+string.size();
@@ -233,7 +232,6 @@ class JSON_StringParser : public JSON_Parser<CharType>
233232
const CharType* m_position;
234233
const CharType* m_startpos;
235234
const CharType* m_endpos;
236-
::utility::details::thread_local_locale m_locale;
237235
};
238236

239237

Release/src/json/json_serialization.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ using namespace utility::conversions;
4141
#ifdef _MS_WINDOWS
4242
void web::json::value::serialize(std::ostream& stream) const
4343
{
44-
utility::details::thread_local_locale locale("C");
44+
utility::details::scoped_thread_locale locale("C");
4545

4646
// This has better performance than writing directly to stream.
4747
std::string str;
@@ -56,7 +56,7 @@ void web::json::value::format(std::basic_string<wchar_t> &string) const
5656

5757
void web::json::value::serialize(utility::ostream_t &stream) const
5858
{
59-
utility::details::thread_local_locale locale("C");
59+
utility::details::scoped_thread_locale locale("C");
6060

6161
// This has better performance than writing directly to stream.
6262
utility::string_t str;
@@ -238,6 +238,6 @@ utility::string_t web::json::value::as_string() const
238238

239239
utility::string_t json::value::serialize() const
240240
{
241-
utility::details::thread_local_locale locale("C");
241+
utility::details::scoped_thread_locale locale("C");
242242
return m_value->to_string();
243243
}

Release/src/utilities/asyncrt_utils.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,80 @@ using namespace utility::conversions;
5252
namespace utility
5353
{
5454

55+
namespace details
56+
{
57+
58+
#ifdef _MS_WINDOWS
59+
scoped_thread_locale::scoped_thread_locale(const char * locale)
60+
: m_prevLocale(), m_prevThreadSetting(-1)
61+
{
62+
char *prevLocale = setlocale(LC_ALL, nullptr);
63+
if (prevLocale == nullptr)
64+
{
65+
throw std::runtime_error("Unable to retrieve current locale.");
66+
}
67+
68+
if(std::strcmp(prevLocale, locale) != 0)
69+
{
70+
m_prevLocale = prevLocale;
71+
m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
72+
if (m_prevThreadSetting == -1)
73+
{
74+
throw std::runtime_error("Unable to enable per thread locale.");
75+
}
76+
if (setlocale(LC_ALL, locale) == nullptr)
77+
{
78+
_configthreadlocale(m_prevThreadSetting);
79+
throw std::runtime_error("Unable to set locale");
80+
}
81+
}
82+
}
83+
84+
scoped_thread_locale::~scoped_thread_locale()
85+
{
86+
if(m_prevThreadSetting != -1)
87+
{
88+
setlocale(LC_ALL, m_prevLocale.c_str());
89+
_configthreadlocale(m_prevThreadSetting);
90+
}
91+
}
92+
#else
93+
scoped_thread_locale::scoped_thread_locale(const char * locale)
94+
: m_prevLocale(nullptr), m_newLocale(nullptr)
95+
{
96+
char * prevLocale = setlocale(LC_ALL, nullptr);
97+
if(prevLocale == nullptr)
98+
{
99+
throw std::runtime_error("Unable to retrieve current locale.");
100+
}
101+
102+
if(std::strcmp(prevLocale, locale) != 0)
103+
{
104+
m_newLocale = newlocale(LC_ALL, locale, nullptr);
105+
if(m_newLocale == nullptr)
106+
{
107+
throw std::runtime_error("Unable to create new locale.");
108+
}
109+
m_prevLocale = uselocale(m_newLocale);
110+
if(m_prevLocale == nullptr)
111+
{
112+
freelocale(m_newLocale);
113+
throw std::runtime_error("Unable to set locale");
114+
}
115+
}
116+
}
117+
118+
scoped_thread_locale::~scoped_thread_locale()
119+
{
120+
if(m_prevLocale != nullptr)
121+
{
122+
uselocale(m_prevLocale);
123+
freelocale(m_newLocale);
124+
}
125+
}
126+
#endif
127+
}
128+
55129
#pragma region error categories
56130

57131
namespace details

Release/tests/Functional/json/parsing_tests.cpp

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -569,34 +569,41 @@ TEST(keep_order_while_parsing)
569569
TEST(non_default_locale)
570570
{
571571
std::string originalLocale = setlocale(LC_ALL, nullptr);
572+
#ifdef _MS_WINDOWS
572573
std::string changedLocale("fr-FR");
573-
setlocale(LC_ALL, changedLocale.c_str());
574-
575-
// string serialize
576-
utility::string_t str(U("[true,false,-1.55,5,null,{\"abc\":5555}]"));
577-
json::value v = json::value::parse(str);
578-
VERIFY_ARE_EQUAL(changedLocale, setlocale(LC_ALL, nullptr));
579-
VERIFY_ARE_EQUAL(str, v.serialize());
580-
VERIFY_ARE_EQUAL(changedLocale, setlocale(LC_ALL, nullptr));
574+
#else
575+
std::string changedLocale("fr_FR");
576+
#endif
581577

582-
setlocale(LC_ALL, originalLocale.c_str());
583-
setlocale(LC_NUMERIC, changedLocale.c_str());
578+
// If locale isn't installed on system just silently pass.
579+
if(setlocale(LC_ALL, changedLocale.c_str()) != nullptr)
580+
{
581+
// string serialize
582+
utility::string_t str(U("[true,false,-1.55,5,null,{\"abc\":5555}]"));
583+
json::value v = json::value::parse(str);
584+
VERIFY_ARE_EQUAL(changedLocale, setlocale(LC_ALL, nullptr));
585+
VERIFY_ARE_EQUAL(str, v.serialize());
586+
VERIFY_ARE_EQUAL(changedLocale, setlocale(LC_ALL, nullptr));
587+
588+
setlocale(LC_ALL, originalLocale.c_str());
589+
setlocale(LC_NUMERIC, changedLocale.c_str());
584590

585-
// cpprestsdk stream serialize
586-
utility::stringstream_t stream;
587-
stream << v;
588-
utility::string_t serializedStr;
589-
stream >> serializedStr;
590-
VERIFY_ARE_EQUAL(str, serializedStr);
591-
592-
// std stream serialize
593-
std::stringstream stdStream;
594-
v.serialize(stdStream);
595-
std::string stdStr;
596-
stdStream >> stdStr;
597-
VERIFY_ARE_EQUAL(str, utility::conversions::to_string_t(stdStr));
598-
599-
setlocale(LC_ALL, originalLocale.c_str());
591+
// cpprestsdk stream serialize
592+
utility::stringstream_t stream;
593+
stream << v;
594+
utility::string_t serializedStr;
595+
stream >> serializedStr;
596+
VERIFY_ARE_EQUAL(str, serializedStr);
597+
598+
// std stream serialize
599+
std::stringstream stdStream;
600+
v.serialize(stdStream);
601+
std::string stdStr;
602+
stdStream >> stdStr;
603+
VERIFY_ARE_EQUAL(str, utility::conversions::to_string_t(stdStr));
604+
605+
setlocale(LC_ALL, originalLocale.c_str());
606+
}
600607
}
601608

602609
} // SUITE(parsing_tests)

0 commit comments

Comments
 (0)