mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-09-18 10:28:09 +08:00
Merge branch 'master' into colorize-err-msg
This commit is contained in:
45
README.md
45
README.md
@@ -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
|
||||
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
|
||||
|
||||
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`.
|
||||
|
||||
```toml
|
||||
# sample.toml
|
||||
date = 2018-12-23
|
||||
time = 12: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
|
||||
```
|
||||
|
||||
toml11 defines its own datetime classes.
|
||||
You can see the definitions in [toml/datetime.hpp](toml/datetime.hpp).
|
||||
`local_date` and `local_datetime` are assumed to be in the local timezone when
|
||||
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
|
||||
|
||||
@@ -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__,
|
||||
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
|
||||
|
||||
There are some unreleased features in toml-lang/toml:master.
|
||||
|
@@ -934,3 +934,35 @@ BOOST_AUTO_TEST_CASE(test_value_at)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@@ -45,6 +45,7 @@ inline std::string show_char(const char c)
|
||||
buf.fill('\0');
|
||||
const auto r = std::snprintf(
|
||||
buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF);
|
||||
(void) r; // Unused variable warning
|
||||
assert(r == static_cast<int>(buf.size()) - 1);
|
||||
return std::string(buf.data());
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ namespace toml
|
||||
namespace detail
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
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."); }
|
||||
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
|
||||
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."); }
|
||||
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)
|
||||
{
|
||||
const auto result = std::localtime(src);
|
||||
if (!result) { throw std::runtime_error("localtime failed."); }
|
||||
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
|
||||
} // detail
|
||||
|
||||
@@ -378,10 +398,31 @@ struct local_datetime
|
||||
{
|
||||
using internal_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
|
||||
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>(
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -447,40 +488,71 @@ struct offset_datetime
|
||||
: date(dt.date), time(dt.time), offset(o)
|
||||
{}
|
||||
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)
|
||||
: 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)
|
||||
: 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)
|
||||
: 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
|
||||
{
|
||||
// get date-time
|
||||
using internal_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).
|
||||
// writing 12:00:00 in +09:00 means 03:00:00Z. to represent
|
||||
// 12:00:00Z, first we need to add +09:00.
|
||||
const auto ofs = get_local_offset();
|
||||
// first, convert it to local date-time information in the same way as
|
||||
// local_datetime does. later we will use time_t to adjust time offset.
|
||||
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;
|
||||
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::minutes(ofs.minute);
|
||||
|
||||
// here, tp represents 12:00:00 in UTC but we have offset information.
|
||||
// we need to subtract it. For example, let's say the input is
|
||||
// 12:00:00-08:00. now we have tp = 12:00:00Z as a result of the above
|
||||
// conversion. But the actual time we need to return is 20:00:00Z
|
||||
// because of -08:00.
|
||||
// We got `12:00:00Z` by correcting local timezone applied by mktime.
|
||||
// Then we will apply the offset. Let's say `12:00:00-08:00` is given.
|
||||
// And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`.
|
||||
// So we need to subtract the offset.
|
||||
tp -= std::chrono::minutes(this->offset);
|
||||
return tp;
|
||||
}
|
||||
@@ -500,11 +572,10 @@ struct offset_datetime
|
||||
|
||||
private:
|
||||
|
||||
static time_offset get_local_offset()
|
||||
static time_offset get_local_offset(const std::time_t* tp)
|
||||
{
|
||||
// get current timezone
|
||||
const auto tmp1 = std::time(nullptr);
|
||||
const auto t = detail::localtime_s(&tmp1);
|
||||
// get local timezone with the same date-time information as mktime
|
||||
const auto t = detail::localtime_s(tp);
|
||||
|
||||
std::array<char, 6> buf;
|
||||
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0
|
||||
|
@@ -105,7 +105,7 @@ struct has_into_toml_method
|
||||
: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
|
||||
|
||||
#ifdef __INTEL_COMPILER
|
||||
#undef decltype(...)
|
||||
#undef decltype
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
@@ -1579,6 +1579,14 @@ class basic_value
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -1589,6 +1597,15 @@ class basic_value
|
||||
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
|
||||
{
|
||||
return source_location(this->region_info_.get());
|
||||
|
Reference in New Issue
Block a user