Merge branch 'master' into colorize-err-msg

This commit is contained in:
ToruNiina
2019-12-11 22:49:38 +09:00
6 changed files with 191 additions and 33 deletions

View File

@@ -478,6 +478,40 @@ Note that, although `std::string` has `at()` member function, `toml::value::at`
throws if the contained type is a string. Because `std::string` does not throws if the contained type is a string. Because `std::string` does not
contain `toml::value`. contain `toml::value`.
### `operator[]`
You can also access to the element of a table and an array by
`toml::basic_value::operator[]`.
```cpp
const toml::value v{1,2,3,4,5};
std::cout << v[2].as_integer() << std::endl; // 3
const toml::value v{{"foo", 42}, {"bar", 3.14}};
std::cout << v["foo"].as_integer() << std::endl; // 42
```
When you access to a `toml::value` that is not initialized yet via
`operator[](const std::string&)`, the `toml::value` will be a table,
just like the `std::map`.
```cpp
toml::value v; // not initialized as a table.
v["foo"] = 42; // OK. `v` will be a table.
```
Contrary, if you access to a `toml::value` that contains an array via `operator[]`,
it does not check anything. It converts `toml::value` without type check and then
access to the n-th element without boundary check, just like the `std::vector::operator[]`.
```cpp
toml::value v; // not initialized as an array
v[2] = 42; // error! UB
```
Please make sure that the `toml::value` has an array inside when you access to
its element via `operator[]`.
## Checking value type ## Checking value type
You can check the type of a value by `is_xxx` function. You can check the type of a value by `is_xxx` function.
@@ -700,6 +734,7 @@ date information, but it can be converted to `std::chrono::duration` that
represents a duration from the beginning of the day, `00:00:00.000`. represents a duration from the beginning of the day, `00:00:00.000`.
```toml ```toml
# sample.toml
date = 2018-12-23 date = 2018-12-23
time = 12:30:00 time = 12:30:00
l_dt = 2018-12-23T12:30:00 l_dt = 2018-12-23T12:30:00
@@ -716,8 +751,12 @@ const auto o_dt = toml::get<std::chrono::system_clock::time_point>(data.at("o_dt
const auto time = toml::get<std::chrono::minutes>(data.at("time")); // 12 * 60 + 30 min const auto time = toml::get<std::chrono::minutes>(data.at("time")); // 12 * 60 + 30 min
``` ```
toml11 defines its own datetime classes. `local_date` and `local_datetime` are assumed to be in the local timezone when
You can see the definitions in [toml/datetime.hpp](toml/datetime.hpp). they are converted into `time_point`. On the other hand, `offset_datetime` only
uses the offset part of the data and it does not take local timezone into account.
To contain datetime data, toml11 defines its own datetime types.
For more detail, you can see the definitions in [toml/datetime.hpp](toml/datetime.hpp).
## Getting with a fallback ## Getting with a fallback
@@ -1456,8 +1495,6 @@ This feature is introduced to make it easy to write a custom serializer.
Because `std::chrono::system_clock::time_point` is a __time point__, Because `std::chrono::system_clock::time_point` is a __time point__,
not capable of representing a Local Time independent from a specific day. not capable of representing a Local Time independent from a specific day.
It is recommended to get `datetime`s as `std::chrono` classes through `toml::get`.
## Unreleased TOML features ## Unreleased TOML features
There are some unreleased features in toml-lang/toml:master. There are some unreleased features in toml-lang/toml:master.

View File

@@ -934,3 +934,35 @@ BOOST_AUTO_TEST_CASE(test_value_at)
BOOST_CHECK_THROW(v1.at(5), std::out_of_range); BOOST_CHECK_THROW(v1.at(5), std::out_of_range);
} }
} }
BOOST_AUTO_TEST_CASE(test_value_bracket)
{
{
toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}};
BOOST_TEST(v1["foo"].as_integer() == 42);
BOOST_TEST(v1["bar"].as_floating() == 3.14);
BOOST_TEST(v1["baz"].as_string() == "qux");
v1["qux"] = 54;
BOOST_TEST(v1["qux"].as_integer() == 54);
}
{
toml::value v1;
v1["foo"] = 42;
BOOST_TEST(v1.is_table());
BOOST_TEST(v1["foo"].as_integer() == 42);
}
{
toml::value v1{1,2,3,4,5};
BOOST_TEST(v1[0].as_integer() == 1);
BOOST_TEST(v1[1].as_integer() == 2);
BOOST_TEST(v1[2].as_integer() == 3);
BOOST_TEST(v1[3].as_integer() == 4);
BOOST_TEST(v1[4].as_integer() == 5);
BOOST_CHECK_THROW(v1["foo"], toml::type_error);
}
}

View File

@@ -45,6 +45,7 @@ inline std::string show_char(const char c)
buf.fill('\0'); buf.fill('\0');
const auto r = std::snprintf( const auto r = std::snprintf(
buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF); buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF);
(void) r; // Unused variable warning
assert(r == static_cast<int>(buf.size()) - 1); assert(r == static_cast<int>(buf.size()) - 1);
return std::string(buf.data()); return std::string(buf.data());
} }

View File

@@ -20,7 +20,7 @@ namespace toml
namespace detail namespace detail
{ {
// TODO: find more sophisticated way to handle this // TODO: find more sophisticated way to handle this
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || _POSIX_SOURCE #if _POSIX_C_SOURCE >= 1 || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
std::tm dst; std::tm dst;
@@ -28,6 +28,13 @@ inline std::tm localtime_s(const std::time_t* src)
if (!result) { throw std::runtime_error("localtime_r failed."); } if (!result) { throw std::runtime_error("localtime_r failed."); }
return dst; return dst;
} }
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_r(src, &dst);
if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst;
}
#elif _MSC_VER #elif _MSC_VER
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
@@ -36,13 +43,26 @@ inline std::tm localtime_s(const std::time_t* src)
if (result) { throw std::runtime_error("localtime_s failed."); } if (result) { throw std::runtime_error("localtime_s failed."); }
return dst; return dst;
} }
#else inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_s(&dst, src);
if (result) { throw std::runtime_error("gmtime_s failed."); }
return dst;
}
#else // fallback. not threadsafe
inline std::tm localtime_s(const std::time_t* src) inline std::tm localtime_s(const std::time_t* src)
{ {
const auto result = std::localtime(src); const auto result = std::localtime(src);
if (!result) { throw std::runtime_error("localtime failed."); } if (!result) { throw std::runtime_error("localtime failed."); }
return *result; return *result;
} }
inline std::tm gmtime_s(const std::time_t* src)
{
const auto result = std::gmtime(src);
if (!result) { throw std::runtime_error("gmtime failed."); }
return *result;
}
#endif #endif
} // detail } // detail
@@ -378,10 +398,31 @@ struct local_datetime
{ {
using internal_duration = using internal_duration =
typename std::chrono::system_clock::time_point::duration; typename std::chrono::system_clock::time_point::duration;
// Normally DST begins at A.M. 3 or 4. If we re-use conversion operator
// of local_date and local_time independently, the conversion fails if
// it is the day when DST begins or ends. Since local_date considers the
// time is 00:00 A.M. and local_time does not consider DST because it
// does not have any date information. We need to consider both date and
// time information at the same time to convert it correctly.
std::tm t;
t.tm_sec = static_cast<int>(this->time.second);
t.tm_min = static_cast<int>(this->time.minute);
t.tm_hour = static_cast<int>(this->time.hour);
t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(this->date.year) - 1900;
t.tm_wday = 0; // the value will be ignored
t.tm_yday = 0; // the value will be ignored
t.tm_isdst = -1;
// std::mktime returns date as local time zone. no conversion needed // std::mktime returns date as local time zone. no conversion needed
auto dt = std::chrono::system_clock::time_point(this->date); auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t));
dt += std::chrono::duration_cast<internal_duration>( dt += std::chrono::duration_cast<internal_duration>(
std::chrono::nanoseconds(this->time)); std::chrono::milliseconds(this->time.millisecond) +
std::chrono::microseconds(this->time.microsecond) +
std::chrono::nanoseconds (this->time.nanosecond));
return dt; return dt;
} }
@@ -447,40 +488,71 @@ struct offset_datetime
: date(dt.date), time(dt.time), offset(o) : date(dt.date), time(dt.time), offset(o)
{} {}
explicit offset_datetime(const local_datetime& ld) explicit offset_datetime(const local_datetime& ld)
: date(ld.date), time(ld.time), offset(get_local_offset()) : date(ld.date), time(ld.time), offset(get_local_offset(nullptr))
// use the current local timezone offset
{} {}
explicit offset_datetime(const std::chrono::system_clock::time_point& tp) explicit offset_datetime(const std::chrono::system_clock::time_point& tp)
: offset_datetime(local_datetime(tp)) : offset(0, 0) // use gmtime
{} {
const auto timet = std::chrono::system_clock::to_time_t(tp);
const auto tm = detail::gmtime_s(&timet);
this->date = local_date(tm);
this->time = local_time(tm);
}
explicit offset_datetime(const std::time_t& t) explicit offset_datetime(const std::time_t& t)
: offset_datetime(local_datetime(t)) : offset(0, 0) // use gmtime
{} {
const auto tm = detail::gmtime_s(&t);
this->date = local_date(tm);
this->time = local_time(tm);
}
explicit offset_datetime(const std::tm& t) explicit offset_datetime(const std::tm& t)
: offset_datetime(local_datetime(t)) : offset(0, 0) // assume gmtime
{} {
this->date = local_date(t);
this->time = local_time(t);
}
operator std::chrono::system_clock::time_point() const operator std::chrono::system_clock::time_point() const
{ {
// get date-time // get date-time
using internal_duration = using internal_duration =
typename std::chrono::system_clock::time_point::duration; typename std::chrono::system_clock::time_point::duration;
std::chrono::system_clock::time_point tp =
std::chrono::system_clock::time_point(this->date) +
std::chrono::duration_cast<internal_duration>(
std::chrono::nanoseconds(this->time));
// get date-time in UTC. let's say we are in +09:00 (JPN). // first, convert it to local date-time information in the same way as
// writing 12:00:00 in +09:00 means 03:00:00Z. to represent // local_datetime does. later we will use time_t to adjust time offset.
// 12:00:00Z, first we need to add +09:00. std::tm t;
const auto ofs = get_local_offset(); t.tm_sec = static_cast<int>(this->time.second);
t.tm_min = static_cast<int>(this->time.minute);
t.tm_hour = static_cast<int>(this->time.hour);
t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(this->date.year) - 1900;
t.tm_wday = 0; // the value will be ignored
t.tm_yday = 0; // the value will be ignored
t.tm_isdst = -1;
const std::time_t tp_loc = std::mktime(std::addressof(t));
auto tp = std::chrono::system_clock::from_time_t(tp_loc);
tp += std::chrono::duration_cast<internal_duration>(
std::chrono::milliseconds(this->time.millisecond) +
std::chrono::microseconds(this->time.microsecond) +
std::chrono::nanoseconds (this->time.nanosecond));
// Since mktime uses local time zone, it should be corrected.
// `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if
// we are in `+09:00` timezone. To represent `12:00:00Z` there, we need
// to add `+09:00` to `03:00:00Z`.
// Here, it uses the time_t converted from date-time info to handle
// daylight saving time.
const auto ofs = get_local_offset(std::addressof(tp_loc));
tp += std::chrono::hours (ofs.hour); tp += std::chrono::hours (ofs.hour);
tp += std::chrono::minutes(ofs.minute); tp += std::chrono::minutes(ofs.minute);
// here, tp represents 12:00:00 in UTC but we have offset information. // We got `12:00:00Z` by correcting local timezone applied by mktime.
// we need to subtract it. For example, let's say the input is // Then we will apply the offset. Let's say `12:00:00-08:00` is given.
// 12:00:00-08:00. now we have tp = 12:00:00Z as a result of the above // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`.
// conversion. But the actual time we need to return is 20:00:00Z // So we need to subtract the offset.
// because of -08:00.
tp -= std::chrono::minutes(this->offset); tp -= std::chrono::minutes(this->offset);
return tp; return tp;
} }
@@ -500,11 +572,10 @@ struct offset_datetime
private: private:
static time_offset get_local_offset() static time_offset get_local_offset(const std::time_t* tp)
{ {
// get current timezone // get local timezone with the same date-time information as mktime
const auto tmp1 = std::time(nullptr); const auto t = detail::localtime_s(tp);
const auto t = detail::localtime_s(&tmp1);
std::array<char, 6> buf; std::array<char, 6> buf;
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0

View File

@@ -105,7 +105,7 @@ struct has_into_toml_method
: decltype(has_into_toml_method_impl::check<T>(nullptr)){}; : decltype(has_into_toml_method_impl::check<T>(nullptr)){};
#ifdef __INTEL_COMPILER #ifdef __INTEL_COMPILER
#undef decltype(...) #undef decltype
#endif #endif
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@@ -1579,6 +1579,14 @@ class basic_value
{ {
return this->as_table().at(k); return this->as_table().at(k);
} }
value_type& operator[](const key& k)
{
if(this->is_uninitialized())
{
*this = table_type{};
}
return this->as_table()[k];
}
value_type& at(const std::size_t idx) value_type& at(const std::size_t idx)
{ {
@@ -1589,6 +1597,15 @@ class basic_value
return this->as_array().at(idx); return this->as_array().at(idx);
} }
value_type& operator[](const std::size_t idx) noexcept
{
return this->as_array(std::nothrow)[idx];
}
value_type const& operator[](const std::size_t idx) const noexcept
{
return this->as_array(std::nothrow)[idx];
}
source_location location() const source_location location() const
{ {
return source_location(this->region_info_.get()); return source_location(this->region_info_.get());