Compare commits

...

46 Commits

Author SHA1 Message Date
ToruNiina
5e3f8f9105 chore: update version values 2021-03-25 22:43:37 +09:00
ToruNiina
17a15d3c18 doc: update contributor list and link in README 2021-03-25 22:33:05 +09:00
ToruNiina
42cc111b05 ci: activate linux/windows
confirmed that macos works.
2021-03-25 15:01:40 +09:00
ToruNiina
5e0ee32854 ci: trying to add macos to github actions [skip travis] [skip appveyor]
it is already listed in travis CI, but not in the GH actions
2021-03-25 14:53:28 +09:00
ToruNiina
2c5cc431fe ci: re-activate linux CI 2021-03-25 14:33:55 +09:00
ToruNiina
970f7cb36a ci: trying to update boost installation settings [skip travis] [skip appveyor] 2021-03-25 14:03:26 +09:00
ToruNiina
b924e70e3c feat: add a simple way to disable <filesystem>
As jwillikers pointed out in #150, there is a case where compiler
defines the corresponding feature test macro of <filesystem> but is
actually not available. The macro is a way to disable the feature
regardless of the status of feature test macro.
2021-03-25 11:44:11 +09:00
Toru Niina
7782258e68 Merge pull request #148 from sneakypete81/patch-1
Fix typo in error message
2021-01-31 14:26:02 +09:00
sneakypete81
08859c36d0 Fix typo in error message 2021-01-30 20:04:00 +00:00
ToruNiina
d3de136562 doc: simplity example code a bit 2021-01-25 17:25:29 +09:00
ToruNiina
43183e2ad1 Merge branch 'master' of github.com:ToruNiina/toml11 2020-12-29 18:54:58 +09:00
ToruNiina
e9144b41fb test: returning toml::value directly from into<T> 2020-12-29 18:53:10 +09:00
ToruNiina
2fb8793f1a doc: add document about basic_value and toml::into
related to #146.
2020-12-29 18:52:07 +09:00
Toru Niina
6c8a53915a Merge pull request #144 from amerry/sstream-include-fix
Add missing standard includes
2020-12-10 01:53:31 +09:00
Alex Merry
db2d33ca4b Add missing header for std::out_of_range exception
Failure seen on GCC 4.8.5 when including "toml/value.hpp".
2020-12-09 10:39:10 +00:00
Alex Merry
935da51769 Add missing include for ostringstream
Since region.hpp no longer includes <iostream> (but only <iomanip>),
source_location.hpp no longer includes a header that provides
std::ostringstream. Including <sstream> fixes this.
2020-12-09 10:19:07 +00:00
ToruNiina
be0d4bd0a9 fix: fix #141; Merge branch 'issue-141' 2020-11-05 00:01:41 +09:00
ToruNiina
9b472a6c72 fix: check it is empty before calling back 2020-11-04 23:24:59 +09:00
ToruNiina
1ead14589e fix: check if it is empty before calling back() 2020-11-04 23:24:02 +09:00
ToruNiina
b13065b1b5 fix: #142 Merge branch 'issue-142' 2020-11-03 21:05:03 +09:00
ToruNiina
a6581ee66b fix: an empty array is not an array of table 2020-11-03 20:34:01 +09:00
ToruNiina
0dafa7ee42 test: add case where a table should be inlined
array-of-table implicitly defines an array. If the array itself has a
comment, we need to format it explicitly.
2020-10-18 20:45:12 +09:00
ToruNiina
908b91079b fix: distinguish the comments and try to keep it
If a value has a comment, we need to try to write it explicitly.
2020-10-18 20:43:33 +09:00
ToruNiina
fce6ff317e refactor: distinguish the reason of failure 2020-10-18 18:36:05 +09:00
ToruNiina
fd50b11523 refactor: add write_comments() 2020-10-18 18:35:56 +09:00
ToruNiina
9090b8273c refactor: move array-of-table stuff to a function 2020-10-18 17:20:06 +09:00
ToruNiina
382e3dc3ab refactor: use serializer::is_array_of_tables 2020-10-14 22:27:29 +09:00
ToruNiina
f7bfcdd7aa fix: check all the elements in an array
while checking if the array is array-of-tables or not (heterogeneous
arrays are allowed, so there might be an array that has a table and
an integer at the same time)
2020-10-14 18:00:04 +09:00
ToruNiina
2e41a26785 Merge branch 'master' of github.com:ToruNiina/toml11 into master 2020-10-14 15:35:18 +09:00
ToruNiina
f3378f0ac1 fix: #131 distinguish implicitly declared array 2020-10-14 15:32:08 +09:00
ToruNiina
12ee73d6a9 ci: suppress some of the combinations in CI
clang-7 with C++20 fails with the same reason, 'undefined reference to
std::allocator<char>::(de)allocate'.
2020-10-14 00:38:46 +09:00
ToruNiina
503baf52ed ci: suppress clang 6 + cxx20
Since the main branch that passed the same check 9 days ago also fails
with clang-6 and C++20 because of the same error, "undefined reference
to allocator_traits<char>::allocate". It could be a change in upstream
and since others (e.g. gcc) works well, I suppress the setting at this
moment.
2020-10-14 00:05:55 +09:00
ToruNiina
2deb75052c ci: use the same version of clang
I don't think it resolves the problem, undefined reference to
'std::allocator<char>::deallocate(char*, unsigned long)', though
2020-10-13 23:37:52 +09:00
ToruNiina
290dca3d67 test: add test for comment duplication 2020-10-13 22:04:28 +09:00
ToruNiina
f283a257d2 Revert "quick temporary patch for comment dup"
This reverts commit a6d38c1ec0.
Since the problem is solved, we don't need this patch any more.
2020-10-13 22:02:32 +09:00
ToruNiina
3d86f3a4e1 fix: avoid comment duplication in array of tables 2020-10-13 21:59:46 +09:00
ToruNiina
dc5a8069a9 refactor: require comments while construction
Note: at this commit, the code would not compile.
2020-10-13 21:58:08 +09:00
Toru Niina
4f31b90665 Merge pull request #136 from chronoxor/master
Fixed: Compile toml11 with MinGW cause error in <filesystem> #135
2020-10-04 18:53:34 +09:00
Ivan Shynkarenka
5d8c573357 Fixed: Compile toml11 with MinGW cause error in <filesystem> #136 2020-10-03 23:16:58 +03:00
Ivan Shynkarenka
6e1e5ccd84 Fixed: Compile toml11 with MinGW cause error in <filesystem> #136 2020-10-03 23:06:47 +03:00
Ivan Shynkarenka
f2d9fd1d1f Fixed: Compile toml11 with MinGW cause error in <filesystem> #136 2020-10-03 22:36:59 +03:00
Ivan Shynkarenka
97c8cbdaf5 Fixed: Compile toml11 with MinGW cause error in <filesystem> #135 2020-10-02 19:10:04 +03:00
ToruNiina
05ceb5ae79 fix: workaround for error around SFINAE in MSVC
avoid lambda with template argument
2020-09-29 02:26:16 +09:00
ToruNiina
96cfdb260a fix: update version in macro and cmake 2020-09-29 01:41:38 +09:00
ToruNiina
0fec125688 feat: remove default value from internal src 2020-09-29 01:40:49 +09:00
ToruNiina
a6d38c1ec0 fix: add a quick temporary patch for comment dup
first aid for #131
2020-09-22 17:36:24 +09:00
15 changed files with 460 additions and 184 deletions

View File

@@ -58,6 +58,8 @@ jobs:
- {compiler: '3.9', standard: '20'} - {compiler: '3.9', standard: '20'}
- {compiler: '4.0', standard: '20'} - {compiler: '4.0', standard: '20'}
- {compiler: '5.0', standard: '20'} - {compiler: '5.0', standard: '20'}
- {compiler: '6.0', standard: '20'}
- {compiler: '7', standard: '20'}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -76,13 +78,38 @@ jobs:
- name: Configure - name: Configure
run: | run: |
mkdir build && cd build mkdir build && cd build
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_C_COMPILER=clang-${{ matrix.compiler }} -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }}
- name: Build - name: Build
run: | run: |
cd build && cmake --build . cd build && cmake --build .
- name: Test - name: Test
run: | run: |
cd build && ctest --output-on-failure cd build && ctest --output-on-failure
build-osx:
runs-on: macos-10.15
strategy:
matrix:
standard: ['11', '14', '17', '20']
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Install
run: |
brew install boost
- name: Configure
run: |
mkdir build && cd build
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }}
- name: Build
run: |
cd build && cmake --build .
- name: Test
run: |
cd build && ctest --output-on-failure
build-windows-msvc: build-windows-msvc:
runs-on: windows-2019 runs-on: windows-2019
strategy: strategy:
@@ -94,6 +121,13 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
submodules: true submodules: true
- name: Install
run: |
(New-Object System.Net.WebClient).DownloadFile("https://github.com/actions/boost-versions/releases/download/1.72.0-20200608.4/boost-1.72.0-win32-msvc14.2-x86_64.tar.gz", "$env:TEMP\\boost.tar.gz")
7z.exe x "$env:TEMP\\boost.tar.gz" -o"$env:TEMP\\boostArchive" -y | Out-Null
7z.exe x "$env:TEMP\\boostArchive" -o"$env:TEMP\\boost" -y | Out-Null
Push-Location -Path "$env:TEMP\\boost"
Invoke-Expression .\\setup.ps1
- uses: ilammy/msvc-dev-cmd@v1 - uses: ilammy/msvc-dev-cmd@v1
- name: Configure - name: Configure
shell: cmd shell: cmd
@@ -101,7 +135,7 @@ jobs:
file --mime-encoding tests/test_literals.cpp file --mime-encoding tests/test_literals.cpp
mkdir build mkdir build
cd build cd build
cmake ../ -G "NMake Makefiles" -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DBoost_ADDITIONAL_VERSIONS=1.72.0 -DBoost_USE_MULTITHREADED=ON -DBoost_ARCHITECTURE=-x64 -DBoost_NO_BOOST_CMAKE=ON -DBOOST_ROOT=%BOOST_ROOT_1_72_0% cmake ../ -G "NMake Makefiles" -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DBoost_NO_BOOST_CMAKE=ON -DBOOST_ROOT="C:\\hostedtoolcache\\windows\\Boost\\1.72.0\\x86_64"
- name: Build - name: Build
working-directory: ./build working-directory: ./build
run: | run: |

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.1) cmake_minimum_required(VERSION 3.1)
enable_testing() enable_testing()
project(toml11 VERSION 3.5.0) project(toml11 VERSION 3.6.1)
option(toml11_BUILD_TEST "Build toml tests" OFF) option(toml11_BUILD_TEST "Build toml tests" OFF)
option(toml11_TEST_WITH_ASAN "use LLVM address sanitizer" OFF) option(toml11_TEST_WITH_ASAN "use LLVM address sanitizer" OFF)

View File

@@ -11,7 +11,7 @@ toml11
toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C++ standard library. toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C++ standard library.
- It is compatible to the latest version of [TOML v1.0.0-rc.2](https://toml.io/en/v1.0.0-rc.2). - It is compatible to the latest version of [TOML v1.0.0](https://toml.io/en/v1.0.0).
- It is one of the most TOML standard compliant libraries, tested with [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test). - It is one of the most TOML standard compliant libraries, tested with [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test).
- It shows highly informative error messages. You can see the error messages about invalid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11). - It shows highly informative error messages. You can see the error messages about invalid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
- It has configurable container. You can use any random-access containers and key-value maps as backend containers. - It has configurable container. You can use any random-access containers and key-value maps as backend containers.
@@ -28,6 +28,10 @@ toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C
int main() int main()
{ {
// ```toml
// title = "an example toml file"
// nums = [3, 1, 4, 1, 5]
// ```
auto data = toml::parse("example.toml"); auto data = toml::parse("example.toml");
// find a value with the specified type from a table // find a value with the specified type from a table
@@ -37,9 +41,9 @@ int main()
std::vector<int> nums = toml::find<std::vector<int>>(data, "nums"); std::vector<int> nums = toml::find<std::vector<int>>(data, "nums");
// access with STL-like manner // access with STL-like manner
if(not data.at("a").contains("b")) if(not data.contains("foo"))
{ {
data["a"]["b"] = "c"; data["foo"] = "bar";
} }
// pass a fallback // pass a fallback
@@ -1303,9 +1307,9 @@ struct foo
double b; double b;
std::string c; std::string c;
toml::table into_toml() const // you need to mark it const. toml::value into_toml() const // you need to mark it const.
{ {
return toml::table{{"a", this->a}, {"b", this->b}, {"c", this->c}}; return toml::value{{"a", this->a}, {"b", this->b}, {"c", this->c}};
} }
}; };
} // ext } // ext
@@ -1332,9 +1336,9 @@ namespace toml
template<> template<>
struct into<ext::foo> struct into<ext::foo>
{ {
static toml::table into_toml(const ext::foo& f) static toml::value into_toml(const ext::foo& f)
{ {
return toml::table{{"a", f.a}, {"b", f.b}, {"c", f.c}}; return toml::value{{"a", f.a}, {"b", f.b}, {"c", f.c}};
} }
}; };
} // toml } // toml
@@ -1346,6 +1350,27 @@ toml::value v(f);
Any type that can be converted to `toml::value`, e.g. `int`, `toml::table` and Any type that can be converted to `toml::value`, e.g. `int`, `toml::table` and
`toml::array` are okay to return from `into_toml`. `toml::array` are okay to return from `into_toml`.
You can also return a custom `toml::basic_value` from `toml::into`.
```cpp
namespace toml
{
template<>
struct into<ext::foo>
{
static toml::basic_value<toml::preserve_comments> into_toml(const ext::foo& f)
{
toml::basic_value<toml::preserve_comments> v{{"a", f.a}, {"b", f.b}, {"c", f.c}};
v.comments().push_back(" comment");
return v;
}
};
} // toml
```
But note that, if this `basic_value` would be assigned into other `toml::value`
that discards `comments`, the comments would be dropped.
## Formatting user-defined error messages ## Formatting user-defined error messages
When you encounter an error after you read the toml value, you may want to When you encounter an error after you read the toml value, you may want to
@@ -1848,6 +1873,12 @@ I appreciate the help of the contributors who introduced the great feature to th
- Fix include path in README - Fix include path in README
- Mohammed Alyousef (@MoAlyousef) - Mohammed Alyousef (@MoAlyousef)
- Made testing optional in CMake - Made testing optional in CMake
- Ivan Shynkarenka (@chronoxor)
- Fix compilation error in `<filesystem>` with MinGW
- Alex Merry (@amerry)
- Add missing include files
- sneakypete81 (@sneakypete81)
- Fix typo in error message
## Licensing terms ## Licensing terms

View File

@@ -138,6 +138,43 @@ BOOST_AUTO_TEST_CASE(test_comment_both)
} }
} }
BOOST_AUTO_TEST_CASE(test_comments_on_implicit_values)
{
{
const std::string file = R"(
# comment for the first element of array-of-tables.
[[array-of-tables]]
foo = "bar"
)";
std::istringstream iss(file);
const auto v = toml::parse<toml::preserve_comments>(iss);
const auto aot = toml::find(v, "array-of-tables");
const auto elm = aot.at(0);
BOOST_TEST(aot.comments().empty());
BOOST_TEST(elm.comments().size() == 1);
BOOST_TEST(elm.comments().front() == " comment for the first element of array-of-tables.");
}
{
const std::string file = R"(
# comment for the array itself
array-of-tables = [
# comment for the first element of array-of-tables.
{foo = "bar"}
]
)";
std::istringstream iss(file);
const auto v = toml::parse<toml::preserve_comments>(iss);
const auto aot = toml::find(v, "array-of-tables");
const auto elm = aot.at(0);
BOOST_TEST(aot.comments().size() == 1);
BOOST_TEST(aot.comments().front() == " comment for the array itself");
BOOST_TEST(elm.comments().size() == 1);
BOOST_TEST(elm.comments().front() == " comment for the first element of array-of-tables.");
}
}
BOOST_AUTO_TEST_CASE(test_discard_comment) BOOST_AUTO_TEST_CASE(test_discard_comment)
{ {
const std::string file = R"( const std::string file = R"(

View File

@@ -70,9 +70,9 @@ struct from<extlib::foo>
template<> template<>
struct into<extlib::foo> struct into<extlib::foo>
{ {
static toml::table into_toml(const extlib::foo& f) static toml::value into_toml(const extlib::foo& f)
{ {
return toml::table{{"a", f.a}, {"b", f.b}}; return toml::value{{"a", f.a}, {"b", f.b}};
} }
}; };

View File

@@ -10,6 +10,7 @@
#include <map> #include <map>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <sstream>
template<typename Comment, template<typename Comment,
template<typename ...> class Table, template<typename ...> class Table,
@@ -303,3 +304,60 @@ BOOST_AUTO_TEST_CASE(test_format_key)
BOOST_TEST("\"special-chars-\\\\-\\\"-\\b-\\f-\\r-\\n-\\t\"" == toml::format_key(key)); BOOST_TEST("\"special-chars-\\\\-\\\"-\\b-\\f-\\r-\\n-\\t\"" == toml::format_key(key));
} }
} }
// In toml11, an implicitly-defined value does not have any comments.
// So, in the following file,
// ```toml
// # comment
// [[array-of-tables]]
// foo = "bar"
// ```
// The array named "array-of-tables" does not have the comment, but the first
// element of the array has. That means that, the above file is equivalent to
// the following.
// ```toml
// array-of-tables = [
// # comment
// {foo = "bar"},
// ]
// ```
// If the array itself has a comment (value_has_comment_ == true), we should try
// to make it inline.
// ```toml
// # comment about array
// array-of-tables = [
// # comment about table element
// {foo = "bar"}
// ]
// ```
// If it is formatted as a multiline table, the two comments becomes
// indistinguishable.
// ```toml
// # comment about array
// # comment about table element
// [[array-of-tables]]
// foo = "bar"
// ```
// So we need to try to make it inline, and it force-inlines regardless
// of the line width limit.
// It may fail if the element of a table has comment. In that case,
// the array-of-tables will be formatted as a multiline table.
BOOST_AUTO_TEST_CASE(test_distinguish_comment)
{
const std::string str = R"(# comment about array itself
array_of_table = [
# comment about the first element (table)
{key = "value"},
])";
std::istringstream iss(str);
const auto data = toml::parse<toml::preserve_comments>(iss);
const auto serialized = toml::format(data, /*width = */ 0);
std::istringstream reparse(serialized);
const auto parsed = toml::parse<toml::preserve_comments>(reparse);
BOOST_TEST(parsed.at("array_of_table").comments().size() == 1u);
BOOST_TEST(parsed.at("array_of_table").comments().front() == " comment about array itself");
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)");
}

View File

@@ -34,8 +34,8 @@
#endif #endif
#define TOML11_VERSION_MAJOR 3 #define TOML11_VERSION_MAJOR 3
#define TOML11_VERSION_MINOR 5 #define TOML11_VERSION_MINOR 6
#define TOML11_VERSION_PATCH 0 #define TOML11_VERSION_PATCH 1
#include "toml/parser.hpp" #include "toml/parser.hpp"
#include "toml/literal.hpp" #include "toml/literal.hpp"

View File

@@ -4,6 +4,7 @@
#define TOML11_COMMENTS_HPP #define TOML11_COMMENTS_HPP
#include <initializer_list> #include <initializer_list>
#include <iterator> #include <iterator>
#include <stdexcept>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>

View File

@@ -338,8 +338,10 @@ get(const basic_value<C, M, V>& v)
{v.location(), "here"} {v.location(), "here"}
})); }));
} }
std::transform(ar.cbegin(), ar.cend(), container.begin(), for(std::size_t i=0; i<ar.size(); ++i)
[](const value& x){return ::toml::get<value_type>(x);}); {
container[i] = ::toml::get<value_type>(ar[i]);
}
return container; return container;
} }

View File

@@ -12,8 +12,11 @@ inline namespace toml_literals
{ {
// implementation // implementation
inline ::toml::value literal_internal_impl(::toml::detail::location loc) inline ::toml::basic_value<::toml::discard_comments, std::unordered_map, std::vector>
literal_internal_impl(::toml::detail::location loc)
{ {
using value_type = ::toml::basic_value<
::toml::discard_comments, std::unordered_map, std::vector>;
// if there are some comments or empty lines, skip them. // if there are some comments or empty lines, skip them.
using skip_line = ::toml::detail::repeat<toml::detail::sequence< using skip_line = ::toml::detail::repeat<toml::detail::sequence<
::toml::detail::maybe<::toml::detail::lex_ws>, ::toml::detail::maybe<::toml::detail::lex_ws>,
@@ -50,7 +53,7 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
// If it is neither a table-key or a array-of-table-key, it may be a value. // If it is neither a table-key or a array-of-table-key, it may be a value.
if(!is_table_key && !is_aots_key) if(!is_table_key && !is_aots_key)
{ {
if(auto data = ::toml::detail::parse_value<::toml::value>(loc)) if(auto data = ::toml::detail::parse_value<value_type>(loc))
{ {
return data.unwrap(); return data.unwrap();
} }
@@ -67,7 +70,7 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
// It is a valid toml file. // It is a valid toml file.
// It should be parsed as if we parse a file with this content. // It should be parsed as if we parse a file with this content.
if(auto data = ::toml::detail::parse_toml_file<::toml::value>(loc)) if(auto data = ::toml::detail::parse_toml_file<value_type>(loc))
{ {
return data.unwrap(); return data.unwrap();
} }
@@ -78,7 +81,8 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
} }
inline ::toml::value operator"" _toml(const char* str, std::size_t len) inline ::toml::basic_value<::toml::discard_comments, std::unordered_map, std::vector>
operator"" _toml(const char* str, std::size_t len)
{ {
::toml::detail::location loc( ::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"), std::string("TOML literal encoded in a C++ code"),
@@ -91,7 +95,8 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len)
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L #if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
// value of u8"" literal has been changed from char to char8_t and char8_t is // value of u8"" literal has been changed from char to char8_t and char8_t is
// NOT compatible to char // NOT compatible to char
inline ::toml::value operator"" _toml(const char8_t* str, std::size_t len) inline ::toml::basic_value<::toml::discard_comments, std::unordered_map, std::vector>
operator"" _toml(const char8_t* str, std::size_t len)
{ {
::toml::detail::location loc( ::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"), std::string("TOML literal encoded in a C++ code"),

View File

@@ -13,12 +13,14 @@
#include "types.hpp" #include "types.hpp"
#include "value.hpp" #include "value.hpp"
#if __cplusplus >= 201703L #ifndef TOML11_DISABLE_STD_FILESYSTEM
#ifdef __cpp_lib_filesystem
#if __has_include(<filesystem>) #if __has_include(<filesystem>)
#define TOML11_HAS_STD_FILESYSTEM #define TOML11_HAS_STD_FILESYSTEM
#include <filesystem> #include <filesystem>
#endif // has_include(<string_view>) #endif // has_include(<string_view>)
#endif // cplusplus >= C++17 #endif // __cpp_lib_filesystem
#endif // TOML11_DISABLE_STD_FILESYSTEM
namespace toml namespace toml
{ {
@@ -930,7 +932,7 @@ parse_key(location& loc)
return ok(std::make_pair(std::vector<key>(1, smpl.unwrap().first), return ok(std::make_pair(std::vector<key>(1, smpl.unwrap().first),
smpl.unwrap().second)); smpl.unwrap().second));
} }
return err(format_underline("toml::parse_key: an invalid key appeaed.", return err(format_underline("toml::parse_key: an invalid key appeared.",
{{source_location(loc), "is not a valid key"}}, { {{source_location(loc), "is not a valid key"}}, {
"bare keys : non-empty strings composed only of [A-Za-z0-9_-].", "bare keys : non-empty strings composed only of [A-Za-z0-9_-].",
"quoted keys: same as \"basic strings\" or 'literal strings'.", "quoted keys: same as \"basic strings\" or 'literal strings'.",
@@ -1315,7 +1317,41 @@ insert_nested_key(typename Value::table_type& root, const Value& v,
} }
else // if not, we need to create the array of table else // if not, we need to create the array of table
{ {
value_type aot(array_type(1, v), key_reg); // XXX: Consider the following array of tables.
// ```toml
// # This is a comment.
// [[aot]]
// foo = "bar"
// ```
// Here, the comment is for `aot`. But here, actually two
// values are defined. An array that contains tables, named
// `aot`, and the 0th element of the `aot`, `{foo = "bar"}`.
// Those two are different from each other. But both of them
// points to the same portion of the TOML file, `[[aot]]`,
// so `key_reg.comments()` returns `# This is a comment`.
// If it is assigned as a comment of `aot` defined here, the
// comment will be duplicated. Both the `aot` itself and
// the 0-th element will have the same comment. This causes
// "duplication of the same comments" bug when the data is
// serialized.
// Next, consider the following.
// ```toml
// # comment 1
// aot = [
// # coment 2
// {foo = "bar"},
// ]
// ```
// In this case, we can distinguish those two comments. So
// here we need to add "comment 1" to the `aot` and
// "comment 2" to the 0th element of that.
// To distinguish those two, we check the key region.
std::vector<std::string> comments{/* empty by default */};
if(key_reg.str().substr(0, 2) != "[[")
{
comments = key_reg.comments();
}
value_type aot(array_type(1, v), key_reg, std::move(comments));
tab->insert(std::make_pair(k, aot)); tab->insert(std::make_pair(k, aot));
return ok(true); return ok(true);
} }
@@ -1384,7 +1420,8 @@ insert_nested_key(typename Value::table_type& root, const Value& v,
// [x.y.z] // [x.y.z]
if(tab->count(k) == 0) if(tab->count(k) == 0)
{ {
(*tab)[k] = value_type(table_type{}, key_reg); // a table that is defined implicitly doesn't have any comments.
(*tab)[k] = value_type(table_type{}, key_reg, {/*no comment*/});
} }
// type checking... // type checking...
@@ -1674,11 +1711,24 @@ inline result<value_t, std::string> guess_value_type(const location& loc)
} }
} }
template<typename Value, typename T>
result<Value, std::string>
parse_value_helper(result<std::pair<T, region>, std::string> rslt)
{
if(rslt.is_ok())
{
auto comments = rslt.as_ok().second.comments();
return ok(Value(std::move(rslt.as_ok()), std::move(comments)));
}
else
{
return err(std::move(rslt.as_err()));
}
}
template<typename Value> template<typename Value>
result<Value, std::string> parse_value(location& loc) result<Value, std::string> parse_value(location& loc)
{ {
using value_type = Value;
const auto first = loc.iter(); const auto first = loc.iter();
if(first == loc.end()) if(first == loc.end())
{ {
@@ -1691,18 +1741,19 @@ result<Value, std::string> parse_value(location& loc)
{ {
return err(type.unwrap_err()); return err(type.unwrap_err());
} }
switch(type.unwrap()) switch(type.unwrap())
{ {
case value_t::boolean : {return parse_boolean(loc); } case value_t::boolean : {return parse_value_helper<Value>(parse_boolean(loc) );}
case value_t::integer : {return parse_integer(loc); } case value_t::integer : {return parse_value_helper<Value>(parse_integer(loc) );}
case value_t::floating : {return parse_floating(loc); } case value_t::floating : {return parse_value_helper<Value>(parse_floating(loc) );}
case value_t::string : {return parse_string(loc); } case value_t::string : {return parse_value_helper<Value>(parse_string(loc) );}
case value_t::offset_datetime: {return parse_offset_datetime(loc);} case value_t::offset_datetime: {return parse_value_helper<Value>(parse_offset_datetime(loc) );}
case value_t::local_datetime : {return parse_local_datetime(loc); } case value_t::local_datetime : {return parse_value_helper<Value>(parse_local_datetime(loc) );}
case value_t::local_date : {return parse_local_date(loc); } case value_t::local_date : {return parse_value_helper<Value>(parse_local_date(loc) );}
case value_t::local_time : {return parse_local_time(loc); } case value_t::local_time : {return parse_value_helper<Value>(parse_local_time(loc) );}
case value_t::array : {return parse_array<value_type>(loc); } case value_t::array : {return parse_value_helper<Value>(parse_array<Value>(loc) );}
case value_t::table : {return parse_inline_table<value_type>(loc);} case value_t::table : {return parse_value_helper<Value>(parse_inline_table<Value>(loc));}
default: default:
{ {
const auto msg = format_underline("toml::parse_value: " const auto msg = format_underline("toml::parse_value: "
@@ -1993,7 +2044,7 @@ result<Value, std::string> parse_toml_file(location& loc)
const auto& reg = tk.second; const auto& reg = tk.second;
const auto inserted = insert_nested_key(data, const auto inserted = insert_nested_key(data,
value_type(tab.unwrap(), reg), value_type(tab.unwrap(), reg, reg.comments()),
keys.begin(), keys.end(), reg, keys.begin(), keys.end(), reg,
/*is_array_of_table=*/ true); /*is_array_of_table=*/ true);
if(!inserted) {return err(inserted.unwrap_err());} if(!inserted) {return err(inserted.unwrap_err());}
@@ -2010,7 +2061,8 @@ result<Value, std::string> parse_toml_file(location& loc)
const auto& reg = tk.second; const auto& reg = tk.second;
const auto inserted = insert_nested_key(data, const auto inserted = insert_nested_key(data,
value_type(tab.unwrap(), reg), keys.begin(), keys.end(), reg); value_type(tab.unwrap(), reg, reg.comments()),
keys.begin(), keys.end(), reg);
if(!inserted) {return err(inserted.unwrap_err());} if(!inserted) {return err(inserted.unwrap_err());}
continue; continue;
@@ -2019,10 +2071,7 @@ result<Value, std::string> parse_toml_file(location& loc)
"unknown line appeared", {{source_location(loc), "unknown format"}})); "unknown line appeared", {{source_location(loc), "unknown format"}}));
} }
Value v(std::move(data), file); return ok(Value(std::move(data), file, comments));
v.comments() = comments;
return ok(std::move(v));
} }
} // detail } // detail

View File

@@ -344,7 +344,7 @@ struct region final : public region_base
{ {
// unwrap the first '#' by std::next. // unwrap the first '#' by std::next.
auto str = make_string(std::next(comment_found), iter); auto str = make_string(std::next(comment_found), iter);
if(str.back() == '\r') {str.pop_back();} if(!str.empty() && str.back() == '\r') {str.pop_back();}
com.push_back(std::move(str)); com.push_back(std::move(str));
} }
else else
@@ -397,7 +397,7 @@ struct region final : public region_base
{ {
// unwrap the first '#' by std::next. // unwrap the first '#' by std::next.
auto str = make_string(std::next(comment_found), this->line_end()); auto str = make_string(std::next(comment_found), this->line_end());
if(str.back() == '\r') {str.pop_back();} if(!str.empty() && str.back() == '\r') {str.pop_back();}
com.push_back(std::move(str)); com.push_back(std::move(str));
} }
} }

View File

@@ -97,8 +97,10 @@ struct serializer
const int float_prec = std::numeric_limits<toml::floating>::max_digits10, const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
const bool can_be_inlined = false, const bool can_be_inlined = false,
const bool no_comment = false, const bool no_comment = false,
std::vector<toml::key> ks = {}) std::vector<toml::key> ks = {},
const bool value_has_comment = false)
: can_be_inlined_(can_be_inlined), no_comment_(no_comment), : can_be_inlined_(can_be_inlined), no_comment_(no_comment),
value_has_comment_(value_has_comment && !no_comment),
float_prec_(float_prec), width_(w), keys_(std::move(ks)) float_prec_(float_prec), width_(w), keys_(std::move(ks))
{} {}
~serializer() = default; ~serializer() = default;
@@ -120,7 +122,7 @@ struct serializer
std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f);
std::string token(buf.begin(), std::prev(buf.end())); std::string token(buf.begin(), std::prev(buf.end()));
if(token.back() == '.') // 1. => 1.0 if(!token.empty() && token.back() == '.') // 1. => 1.0
{ {
token += '0'; token += '0';
} }
@@ -244,92 +246,18 @@ struct serializer
std::string operator()(const array_type& v) const std::string operator()(const array_type& v) const
{ {
if(!v.empty() && v.front().is_table())// v is an array of tables
{
// if it's not inlined, we need to add `[[table.key]]`.
// but if it can be inlined,
// ```
// table.key = [
// {...},
// # comment
// {...},
// ]
// ```
if(this->can_be_inlined_)
{
std::string token;
if(!keys_.empty())
{
token += format_key(keys_.back());
token += " = ";
}
bool failed = false;
token += "[\n";
for(const auto& item : v)
{
// if an element of the table has a comment, the table
// cannot be inlined.
if(this->has_comment_inside(item.as_table()))
{
failed = true;
break;
}
if(!no_comment_)
{
for(const auto& c : item.comments())
{
token += '#';
token += c;
token += '\n';
}
}
const auto t = this->make_inline_table(item.as_table());
if(t.size() + 1 > width_ || // +1 for the last comma {...},
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
{
failed = true;
break;
}
token += t;
token += ",\n";
}
if(!failed)
{
token += "]\n";
return token;
}
// if failed, serialize them as [[array.of.tables]].
}
std::string token;
for(const auto& item : v)
{
if(!no_comment_)
{
for(const auto& c : item.comments())
{
token += '#';
token += c;
token += '\n';
}
}
token += "[[";
token += format_keys(keys_);
token += "]]\n";
token += this->make_multiline_table(item.as_table());
}
return token;
}
if(v.empty()) if(v.empty())
{ {
return std::string("[]"); return std::string("[]");
} }
if(this->is_array_of_tables(v))
{
return make_array_of_tables(v);
}
// not an array of tables. normal array. // not an array of tables. normal array.
// first, try to make it inline if none of the elements have a comment. // first, try to make it inline if none of the elements have a comment.
if(!this->has_comment_inside(v)) if( ! this->has_comment_inside(v))
{ {
const auto inl = this->make_inline_array(v); const auto inl = this->make_inline_array(v);
if(inl.size() < this->width_ && if(inl.size() < this->width_ &&
@@ -350,7 +278,7 @@ struct serializer
token += "[\n"; token += "[\n";
for(const auto& item : v) for(const auto& item : v)
{ {
if(!item.comments().empty() && !no_comment_) if( ! item.comments().empty() && !no_comment_)
{ {
// if comment exists, the element must be the only element in the line. // if comment exists, the element must be the only element in the line.
// e.g. the following is not allowed. // e.g. the following is not allowed.
@@ -376,7 +304,7 @@ struct serializer
token += '\n'; token += '\n';
} }
token += toml::visit(*this, item); token += toml::visit(*this, item);
if(token.back() == '\n') {token.pop_back();} if(!token.empty() && token.back() == '\n') {token.pop_back();}
token += ",\n"; token += ",\n";
continue; continue;
} }
@@ -384,7 +312,7 @@ struct serializer
next_elem += toml::visit(*this, item); next_elem += toml::visit(*this, item);
// comma before newline. // comma before newline.
if(next_elem.back() == '\n') {next_elem.pop_back();} if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();}
// if current line does not exceeds the width limit, continue. // if current line does not exceeds the width limit, continue.
if(current_line.size() + next_elem.size() + 1 < this->width_) if(current_line.size() + next_elem.size() + 1 < this->width_)
@@ -411,7 +339,10 @@ struct serializer
} }
if(!current_line.empty()) if(!current_line.empty())
{ {
if(current_line.back() != '\n') {current_line += '\n';} if(!current_line.empty() && current_line.back() != '\n')
{
current_line += '\n';
}
token += current_line; token += current_line;
} }
token += "]\n"; token += "]\n";
@@ -557,8 +488,10 @@ struct serializer
for(const auto& item : v) for(const auto& item : v)
{ {
if(is_first) {is_first = false;} else {token += ',';} if(is_first) {is_first = false;} else {token += ',';}
token += visit(serializer((std::numeric_limits<std::size_t>::max)(), token += visit(serializer(
this->float_prec_, true), item); (std::numeric_limits<std::size_t>::max)(), this->float_prec_,
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !item.comments().empty()), item);
} }
token += ']'; token += ']';
return token; return token;
@@ -577,8 +510,10 @@ struct serializer
if(is_first) {is_first = false;} else {token += ',';} if(is_first) {is_first = false;} else {token += ',';}
token += format_key(kv.first); token += format_key(kv.first);
token += '='; token += '=';
token += visit(serializer((std::numeric_limits<std::size_t>::max)(), token += visit(serializer(
this->float_prec_, true), kv.second); (std::numeric_limits<std::size_t>::max)(), this->float_prec_,
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
} }
token += '}'; token += '}';
return token; return token;
@@ -588,8 +523,16 @@ struct serializer
{ {
std::string token; std::string token;
// print non-table stuff first. because after printing [foo.bar], the // print non-table elements first.
// remaining non-table values will be assigned into [foo.bar], not [foo] // ```toml
// [foo] # a table we're writing now here
// key = "value" # <- non-table element, "key"
// # ...
// [foo.bar] # <- table element, "bar"
// ```
// because after printing [foo.bar], the remaining non-table values will
// be assigned into [foo.bar], not [foo]. Those values should be printed
// earlier.
for(const auto& kv : v) for(const auto& kv : v)
{ {
if(kv.second.is_table() || is_array_of_tables(kv.second)) if(kv.second.is_table() || is_array_of_tables(kv.second))
@@ -597,21 +540,16 @@ struct serializer
continue; continue;
} }
if(!kv.second.comments().empty() && !no_comment_) token += write_comments(kv.second);
{
for(const auto& c : kv.second.comments())
{
token += '#';
token += c;
token += '\n';
}
}
const auto key_and_sep = format_key(kv.first) + " = "; const auto key_and_sep = format_key(kv.first) + " = ";
const auto residual_width = (this->width_ > key_and_sep.size()) ? const auto residual_width = (this->width_ > key_and_sep.size()) ?
this->width_ - key_and_sep.size() : 0; this->width_ - key_and_sep.size() : 0;
token += key_and_sep; token += key_and_sep;
token += visit(serializer(residual_width, this->float_prec_, true), token += visit(serializer(residual_width, this->float_prec_,
kv.second); /*can be inlined*/ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
if(token.back() != '\n') if(token.back() != '\n')
{ {
token += '\n'; token += '\n';
@@ -637,8 +575,8 @@ struct serializer
ks.push_back(kv.first); ks.push_back(kv.first);
auto tmp = visit(serializer(this->width_, this->float_prec_, auto tmp = visit(serializer(this->width_, this->float_prec_,
!multiline_table_printed, this->no_comment_, ks), !multiline_table_printed, this->no_comment_, ks,
kv.second); /*has_comment*/ !kv.second.comments().empty()), kv.second);
if((!multiline_table_printed) && if((!multiline_table_printed) &&
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend()) std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend())
@@ -651,31 +589,142 @@ struct serializer
tmp += '\n'; tmp += '\n';
} }
if(!kv.second.comments().empty() && !no_comment_) token += write_comments(kv.second);
{
for(const auto& c : kv.second.comments())
{
token += '#';
token += c;
token += '\n';
}
}
token += tmp; token += tmp;
} }
return token; return token;
} }
std::string make_array_of_tables(const array_type& v) const
{
// if it's not inlined, we need to add `[[table.key]]`.
// but if it can be inlined, we can format it as the following.
// ```
// table.key = [
// {...},
// # comment
// {...},
// ]
// ```
// This function checks if inlinization is possible or not, and then
// format the array-of-tables in a proper way.
//
// Note about comments:
//
// If the array itself has a comment (value_has_comment_ == true), we
// should try to make it inline.
// ```toml
// # comment about array
// array = [
// # comment about table element
// {of = "table"}
// ]
// ```
// If it is formatted as a multiline table, the two comments becomes
// indistinguishable.
// ```toml
// # comment about array
// # comment about table element
// [[array]]
// of = "table"
// ```
// So we need to try to make it inline, and it force-inlines regardless
// of the line width limit.
// It may fail if the element of a table has comment. In that case,
// the array-of-tables will be formatted as a multiline table.
if(this->can_be_inlined_ || this->value_has_comment_)
{
std::string token;
if(!keys_.empty())
{
token += format_key(keys_.back());
token += " = ";
}
bool failed = false;
token += "[\n";
for(const auto& item : v)
{
// if an element of the table has a comment, the table
// cannot be inlined.
if(this->has_comment_inside(item.as_table()))
{
failed = true;
break;
}
// write comments for the table itself
token += write_comments(item);
const auto t = this->make_inline_table(item.as_table());
if(t.size() + 1 > width_ || // +1 for the last comma {...},
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
{
// if the value itself has a comment, ignore the line width limit
if( ! this->value_has_comment_)
{
failed = true;
break;
}
}
token += t;
token += ",\n";
}
if( ! failed)
{
token += "]\n";
return token;
}
// if failed, serialize them as [[array.of.tables]].
}
std::string token;
for(const auto& item : v)
{
token += write_comments(item);
token += "[[";
token += format_keys(keys_);
token += "]]\n";
token += this->make_multiline_table(item.as_table());
}
return token;
}
std::string write_comments(const value_type& v) const
{
std::string retval;
if(this->no_comment_) {return retval;}
for(const auto& c : v.comments())
{
retval += '#';
retval += c;
retval += '\n';
}
return retval;
}
bool is_array_of_tables(const value_type& v) const bool is_array_of_tables(const value_type& v) const
{ {
if(!v.is_array()) {return false;} if(!v.is_array() || v.as_array().empty()) {return false;}
const auto& a = v.as_array(); return is_array_of_tables(v.as_array());
return !a.empty() && a.front().is_table(); }
bool is_array_of_tables(const array_type& v) const
{
// Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to
// check all the element in an array to check if the array is an array
// of tables.
return std::all_of(v.begin(), v.end(), [](const value_type& elem) {
return elem.is_table();
});
} }
private: private:
bool can_be_inlined_; bool can_be_inlined_;
bool no_comment_; bool no_comment_;
bool value_has_comment_;
int float_prec_; int float_prec_;
std::size_t width_; std::size_t width_;
std::vector<toml::key> keys_; std::vector<toml::key> keys_;

View File

@@ -3,6 +3,7 @@
#ifndef TOML11_SOURCE_LOCATION_HPP #ifndef TOML11_SOURCE_LOCATION_HPP
#define TOML11_SOURCE_LOCATION_HPP #define TOML11_SOURCE_LOCATION_HPP
#include <cstdint> #include <cstdint>
#include <sstream>
#include "region.hpp" #include "region.hpp"

View File

@@ -1047,10 +1047,10 @@ class basic_value
// //
// Those constructors take detail::region that contains parse result. // Those constructors take detail::region that contains parse result.
basic_value(boolean b, detail::region reg) basic_value(boolean b, detail::region reg, std::vector<std::string> cm)
: type_(value_t::boolean), : type_(value_t::boolean),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->boolean_, b); assigner(this->boolean_, b);
} }
@@ -1058,68 +1058,75 @@ class basic_value
detail::conjunction< detail::conjunction<
std::is_integral<T>, detail::negation<std::is_same<T, boolean>> std::is_integral<T>, detail::negation<std::is_same<T, boolean>>
>::value, std::nullptr_t>::type = nullptr> >::value, std::nullptr_t>::type = nullptr>
basic_value(T i, detail::region reg) basic_value(T i, detail::region reg, std::vector<std::string> cm)
: type_(value_t::integer), : type_(value_t::integer),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->integer_, static_cast<integer>(i)); assigner(this->integer_, static_cast<integer>(i));
} }
template<typename T, typename std::enable_if< template<typename T, typename std::enable_if<
std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr> std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
basic_value(T f, detail::region reg) basic_value(T f, detail::region reg, std::vector<std::string> cm)
: type_(value_t::floating), : type_(value_t::floating),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->floating_, static_cast<floating>(f)); assigner(this->floating_, static_cast<floating>(f));
} }
basic_value(toml::string s, detail::region reg) basic_value(toml::string s, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::string), : type_(value_t::string),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->string_, std::move(s)); assigner(this->string_, std::move(s));
} }
basic_value(const local_date& ld, detail::region reg) basic_value(const local_date& ld, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::local_date), : type_(value_t::local_date),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->local_date_, ld); assigner(this->local_date_, ld);
} }
basic_value(const local_time& lt, detail::region reg) basic_value(const local_time& lt, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::local_time), : type_(value_t::local_time),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->local_time_, lt); assigner(this->local_time_, lt);
} }
basic_value(const local_datetime& ldt, detail::region reg) basic_value(const local_datetime& ldt, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::local_datetime), : type_(value_t::local_datetime),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->local_datetime_, ldt); assigner(this->local_datetime_, ldt);
} }
basic_value(const offset_datetime& odt, detail::region reg) basic_value(const offset_datetime& odt, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::offset_datetime), : type_(value_t::offset_datetime),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->offset_datetime_, odt); assigner(this->offset_datetime_, odt);
} }
basic_value(const array_type& ary, detail::region reg) basic_value(const array_type& ary, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::array), : type_(value_t::array),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->array_, ary); assigner(this->array_, ary);
} }
basic_value(const table_type& tab, detail::region reg) basic_value(const table_type& tab, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::table), : type_(value_t::table),
region_info_(std::make_shared<detail::region>(std::move(reg))), region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments()) comments_(std::move(cm))
{ {
assigner(this->table_, tab); assigner(this->table_, tab);
} }
@@ -1127,8 +1134,10 @@ class basic_value
template<typename T, typename std::enable_if< template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T, value_type>::value, detail::is_exact_toml_type<T, value_type>::value,
std::nullptr_t>::type = nullptr> std::nullptr_t>::type = nullptr>
basic_value(std::pair<T, detail::region> parse_result) basic_value(std::pair<T, detail::region> parse_result, std::vector<std::string> comments)
: basic_value(std::move(parse_result.first), std::move(parse_result.second)) : basic_value(std::move(parse_result.first),
std::move(parse_result.second),
std::move(comments))
{} {}
// type checking and casting ============================================ // type checking and casting ============================================