mirror of
https://github.com/ToruNiina/toml11.git
synced 2024-11-09 14:20:06 +00:00
fix #218: consider locale while serialization
This commit is contained in:
commit
1340692442
5
.github/workflows/main.yml
vendored
5
.github/workflows/main.yml
vendored
@ -20,6 +20,7 @@ jobs:
|
||||
sudo apt-add-repository ppa:ubuntu-toolchain-r/test
|
||||
sudo apt-get update
|
||||
sudo apt-get install libboost-test-dev
|
||||
sudo apt-get install language-pack-fr # test serializer w/ locale
|
||||
sudo apt-get install ${{ matrix.compiler }}
|
||||
- name: Configure
|
||||
run: |
|
||||
@ -48,6 +49,7 @@ jobs:
|
||||
sudo apt-add-repository ppa:ubuntu-toolchain-r/test
|
||||
sudo apt-get update
|
||||
sudo apt-get install libboost-test-dev
|
||||
sudo apt-get install language-pack-fr # test serializer w/ locale
|
||||
sudo apt-get install clang-${{ matrix.compiler }}
|
||||
- name: Configure
|
||||
run: |
|
||||
@ -80,6 +82,7 @@ jobs:
|
||||
sudo apt-add-repository ppa:mhier/libboost-latest
|
||||
sudo apt-get update
|
||||
sudo apt-get install boost1.70
|
||||
sudo apt-get install language-pack-fr # test serializer w/ locale
|
||||
sudo apt-get install ${{ matrix.compiler }}
|
||||
- name: Configure
|
||||
run: |
|
||||
@ -119,6 +122,7 @@ jobs:
|
||||
sudo apt-add-repository ppa:mhier/libboost-latest
|
||||
sudo apt-get update
|
||||
sudo apt-get install boost1.70
|
||||
sudo apt-get install language-pack-fr # test serializer w/ locale
|
||||
sudo apt-get install clang-${{ matrix.compiler }}
|
||||
- name: Configure
|
||||
run: |
|
||||
@ -216,7 +220,6 @@ jobs:
|
||||
- name: Test
|
||||
working-directory: ./build
|
||||
run: |
|
||||
./tests/test_literals --log_level=all
|
||||
file --mime-encoding tests/toml/tests/example.toml
|
||||
file --mime-encoding tests/toml/tests/fruit.toml
|
||||
file --mime-encoding tests/toml/tests/hard_example.toml
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
|
||||
#include <clocale>
|
||||
|
||||
template<typename Comment,
|
||||
template<typename ...> class Table,
|
||||
template<typename ...> class Array>
|
||||
@ -357,3 +359,47 @@ array_of_table = [
|
||||
BOOST_TEST(parsed.at("array_of_table").at(0).comments().size() == 1u);
|
||||
BOOST_TEST(parsed.at("array_of_table").at(0).comments().front() == " comment about the first element (table)");
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_serialize_under_locale)
|
||||
{
|
||||
// avoid null init (setlocale returns null when it failed)
|
||||
std::string setloc(std::setlocale(LC_ALL, nullptr));
|
||||
|
||||
// fr_FR is a one of locales that uses `,` as a decimal separator.
|
||||
if(const char* try_hyphen = std::setlocale(LC_ALL, "fr_FR.UTF-8"))
|
||||
{
|
||||
setloc = std::string(try_hyphen);
|
||||
}
|
||||
else if(const char* try_nohyphen = std::setlocale(LC_ALL, "fr_FR.utf8"))
|
||||
{
|
||||
setloc = std::string(try_nohyphen);
|
||||
}
|
||||
// In some envs, fr_FR locale has not been installed. Tests must work even in such a case.
|
||||
// else
|
||||
// {
|
||||
// BOOST_TEST(false);
|
||||
// }
|
||||
BOOST_TEST_MESSAGE("current locale at the beginning of the test = " << setloc);
|
||||
|
||||
const std::string str = R"(
|
||||
pi = 3.14159
|
||||
large_int = 1234567890
|
||||
)";
|
||||
std::istringstream iss(str);
|
||||
const auto ref = toml::parse(iss);
|
||||
const auto serialized_str = toml::format(ref, /*width = */ 80);
|
||||
|
||||
BOOST_TEST_MESSAGE("serialized = " << serialized_str);
|
||||
|
||||
std::istringstream serialized_iss(serialized_str);
|
||||
const auto serialized_ref = toml::parse(serialized_iss);
|
||||
|
||||
BOOST_TEST(serialized_ref.at("pi").as_floating() == ref.at("pi").as_floating());
|
||||
BOOST_TEST(serialized_ref.at("large_int").as_integer() == ref.at("large_int").as_integer());
|
||||
|
||||
const std::string endloc(std::setlocale(LC_ALL, nullptr));
|
||||
BOOST_TEST_MESSAGE("current locale at the end of the test = " << endloc);
|
||||
// check if serializer change global locale
|
||||
BOOST_TEST(setloc == endloc);
|
||||
}
|
||||
|
@ -7,6 +7,14 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <locale.h>
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#include <xlocale.h>
|
||||
#elif defined(__linux__)
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
#include "lexer.hpp"
|
||||
#include "value.hpp"
|
||||
|
||||
@ -120,7 +128,31 @@ struct serializer
|
||||
}
|
||||
std::string operator()(const integer_type i) const
|
||||
{
|
||||
return std::to_string(i);
|
||||
#if defined(_WIN32)
|
||||
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
||||
const std::string original_locale(setlocale(LC_NUMERIC, nullptr));
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
|
||||
const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0));
|
||||
locale_t original_locale(0);
|
||||
if(c_locale != locale_t(0))
|
||||
{
|
||||
original_locale = uselocale(c_locale);
|
||||
}
|
||||
#endif
|
||||
|
||||
const auto str = std::to_string(i);
|
||||
|
||||
#if defined(_WIN32)
|
||||
setlocale(LC_NUMERIC, original_locale.c_str());
|
||||
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
|
||||
if(original_locale != locale_t(0))
|
||||
{
|
||||
uselocale(original_locale);
|
||||
}
|
||||
#endif
|
||||
return str;
|
||||
}
|
||||
std::string operator()(const floating_type f) const
|
||||
{
|
||||
@ -147,12 +179,40 @@ struct serializer
|
||||
}
|
||||
}
|
||||
|
||||
// set locale to "C".
|
||||
// To make it thread-local, we use OS-specific features.
|
||||
// If we set process-global locale, it can break other thread that also
|
||||
// outputs something simultaneously.
|
||||
#if defined(_WIN32)
|
||||
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
||||
const std::string original_locale(setlocale(LC_NUMERIC, nullptr));
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
|
||||
const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0));
|
||||
locale_t original_locale(0);
|
||||
if(c_locale != locale_t(0))
|
||||
{
|
||||
original_locale = uselocale(c_locale);
|
||||
}
|
||||
#endif
|
||||
|
||||
const auto fmt = "%.*g";
|
||||
const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f);
|
||||
// +1 for null character(\0)
|
||||
std::vector<char> buf(static_cast<std::size_t>(bsz + 1), '\0');
|
||||
std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f);
|
||||
|
||||
// restore the original locale
|
||||
#if defined(_WIN32)
|
||||
setlocale(LC_NUMERIC, original_locale.c_str());
|
||||
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
|
||||
if(original_locale != locale_t(0))
|
||||
{
|
||||
uselocale(original_locale);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string token(buf.begin(), std::prev(buf.end()));
|
||||
if(!token.empty() && token.back() == '.') // 1. => 1.0
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user