Compare commits

...

96 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
ToruNiina
c037913b2c doc: update link to the TOML spec 2020-09-20 19:24:48 +09:00
ToruNiina
6a328fe890 doc: recommend to set /Zc:__cplusplus 2020-09-20 18:07:58 +09:00
ToruNiina
7c18cbb1d9 doc: update section "contributors" 2020-09-19 20:35:37 +09:00
ToruNiina
ba7d49f452 test: use normal string literal
as a workaround for older version of gcc
2020-09-19 19:08:20 +09:00
ToruNiina
b0784ce286 test: in case of comment-before-comma 2020-09-19 18:24:23 +09:00
ToruNiina
670186fac7 Merge branch 'master' into allow-comment-before-comma 2020-09-19 18:10:45 +09:00
ToruNiina
5005998709 Merge branch 'master' into cpp20-mode-u8literal-workaround 2020-09-19 13:42:12 +09:00
ToruNiina
84fb703e04 ci: add utf-8 option to MSVC 2020-09-19 00:41:05 +09:00
ToruNiina
8c2560761b chore: enable to use __cplusplus on MSVC
related: https://github.com/ToruNiina/toml11/issues/112
2020-09-19 00:40:44 +09:00
ToruNiina
07ea5e52e2 ci: pass REQ_FS_LIB=ON in case of g++-8 & C++20 2020-09-16 22:16:20 +09:00
ToruNiina
d2b1e962c9 ci: add std=20 to some compilers on github actions 2020-09-16 21:28:19 +09:00
ToruNiina
528031012d test: add test for u8""_toml literals 2020-09-16 21:25:38 +09:00
ToruNiina
c205c762fe test: remove needless u8s from ascii characters 2020-09-16 21:25:04 +09:00
ToruNiina
a32cd6cb61 feat: enable to use u8""_toml literal in C++20 2020-09-16 21:24:03 +09:00
ToruNiina
38e113d2dc ci: set BUILD_TEST=ON on appveyor 2020-09-15 22:40:24 +09:00
Toru Niina
f15480ae4d Merge pull request #130 from MoAlyousef/master
Make toml11_BUILD_TEST Off by default
2020-09-15 22:28:34 +09:00
MoAlyousef
00bec8ae45 update Running Tests heading 2020-09-14 22:05:15 +03:00
MoAlyousef
d599edd1d4 make testing optional 2020-09-14 20:34:19 +03:00
MoAlyousef
a9534579c6 make testing optional 2020-09-14 20:25:38 +03:00
ToruNiina
c8ff302c94 test: add test for no-eof-newline cases 2020-09-14 16:39:05 +09:00
ToruNiina
003bc16c1b fix: skip the last zero in the file 2020-09-14 16:35:51 +09:00
Toru Niina
9132abc5c4 Merge pull request #127 from kenichiice/fix-include
Fix include path in README
2020-09-07 16:19:29 +09:00
OGAWA KenIchi
99d565bcc4 doc: fix include path
* see #72
2020-09-07 15:32:28 +09:00
ToruNiina
5f38127692 feat: allow comments before comma
replace ws by ws_comment_newline, as suggested.
discussed here: toml-lang/toml/issues/766
2020-08-16 11:03:58 +09:00
ToruNiina
3c3ebd88b4 feat: improve error message about invalid keys 2020-08-09 18:38:50 +09:00
ToruNiina
08f7ea9c56 refactor: remove extraneous whitespaces in errmsg 2020-08-09 18:38:21 +09:00
ToruNiina
cde29399f4 fix: use 1 in source_location as the default pos 2020-08-07 22:24:01 +09:00
ToruNiina
eec429e31b ci: add REQUIRE_FILESYSTEM_LIBRARY on CI 2020-08-06 16:35:49 +09:00
ToruNiina
79ddcaece6 chore: add CMake option to link with (std)c++fs 2020-08-06 16:29:24 +09:00
ToruNiina
8398b9a08b test: use array for char*
forgot to delete
2020-08-05 20:43:48 +09:00
ToruNiina
9c5abf0bfd test: check each overload compiles 2020-08-05 20:29:07 +09:00
ToruNiina
4fa94d45b3 fix: use const char* instead of &char[N]
to enable to pass char*, not only string literal
2020-08-04 20:08:58 +09:00
ToruNiina
46e84a9cc2 refactor: Merge branch 'refactor-region' 2020-07-31 12:45:52 +09:00
ToruNiina
4e6ae9a994 refactor: avoid string construct in format_ul 2020-07-30 16:11:35 +09:00
ToruNiina
f23c003d2f fix: add missing namespace specifier 2020-07-28 00:04:25 +09:00
ToruNiina
4b719f0806 refactor: use location() instead of get_region 2020-07-27 23:15:14 +09:00
ToruNiina
22ace027de refactor: rm template from detail::change_region 2020-07-27 23:04:24 +09:00
ToruNiina
bc219af5b5 refactor: use location() member instead of ctor 2020-07-27 23:03:33 +09:00
ToruNiina
68e8a31659 refactor: remove needless addressof() call 2020-07-27 23:00:40 +09:00
ToruNiina
32a5341d09 refactor: use source_location, not region_base* 2020-07-27 22:29:18 +09:00
ToruNiina
ce68f6f4c2 refactor: check (always-valid) ptr before deref 2020-07-27 21:32:35 +09:00
ToruNiina
e696aabd11 refactor: change internal interface to reduce code
to remove `std::addressof` calls, get_region(toml::value) now
returns a pointer to region.
2020-07-27 00:48:04 +09:00
ToruNiina
7fb93e2f54 fix: add missing explicit to detail::region 2020-07-27 00:20:26 +09:00
ToruNiina
19cc9a2edf refactor: remove template from detail::region 2020-07-25 22:01:34 +09:00
ToruNiina
72f5afb6af refactor: remove template from detail::location 2020-07-25 21:06:26 +09:00
ToruNiina
a8fa14d159 refactor: remove vec() method, use a constructor 2020-07-21 20:55:18 +09:00
ToruNiina
75999aa9ad refactor: add a constructor to location
By adding the constructor, vec() would not be not needed. But inserting
Container = std::string makes the constructor ambiguous, so it breaks
the current code.
2020-07-21 20:53:44 +09:00
ToruNiina
259da54edb refactor: always use vector<char> in location
`location` and `region` have a (shared_ptr to the) container of TOML
contents. Those take a template argument to allow both std::vector<char>
and std::string as an interanal container. But since those are internal
feature, i.e. it should not be used by a user directly, this template
can be removed by re-writing a parser a bit. This introduces a
complexity to toml11 error reporting system, so I'm removing this.
First, remove all the location<std::string> from the parser. Then the
template argument can be removed because everyone uses std::vector<char>
now.
2020-07-20 19:52:11 +09:00
ToruNiina
b461f363da refactor: add a method to reduce complexity later 2020-07-20 19:40:55 +09:00
ToruNiina
d43139a471 doc: update Contributors section 2020-07-19 19:50:02 +09:00
30 changed files with 1374 additions and 863 deletions

View File

@@ -9,10 +9,13 @@ jobs:
matrix:
# g++-4.8 and 4.9 are tested on Travis.CI.
compiler: ['g++-9', 'g++-8', 'g++-7', 'g++-6', 'g++-5']
standard: ['11', '14', '17']
standard: ['11', '14', '17', '20']
exclude:
- {compiler: 'g++-5', standard: '17'}
- {compiler: 'g++-6', standard: '17'}
- {compiler: 'g++-5', standard: '20'}
- {compiler: 'g++-6', standard: '20'}
- {compiler: 'g++-7', standard: '20'}
steps:
- name: Checkout
uses: actions/checkout@v2
@@ -31,7 +34,11 @@ jobs:
- name: Configure
run: |
mkdir build && cd build
cmake .. -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }}
if [[ "${{ matrix.compiler }}" == "g++-8" && ( "${{ matrix.standard }}" == "17" || "${{ matrix.standard }}" == "20" ) ]] ; then
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_REQUIRE_FILESYSTEM_LIBRARY=ON
else
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }}
fi
- name: Build
run: |
cd build && cmake --build .
@@ -43,11 +50,16 @@ jobs:
strategy:
matrix:
compiler: ['10', '9', '8', '7', '6.0', '5.0', '4.0', '3.9']
standard: ['11', '14', '17']
standard: ['11', '14', '17', '20']
exclude:
- {compiler: '3.9', standard: '17'}
- {compiler: '4.0', standard: '17'}
- {compiler: '5.0', standard: '17'}
- {compiler: '3.9', standard: '20'}
- {compiler: '4.0', standard: '20'}
- {compiler: '5.0', standard: '20'}
- {compiler: '6.0', standard: '20'}
- {compiler: '7', standard: '20'}
steps:
- name: Checkout
uses: actions/checkout@v2
@@ -66,31 +78,64 @@ jobs:
- name: Configure
run: |
mkdir build && cd build
cmake .. -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
run: |
cd build && cmake --build .
- name: Test
run: |
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:
runs-on: windows-2019
strategy:
matrix:
standard: ['11', '14', '17']
standard: ['11', '14', '17', '20']
config: ['Release', 'Debug']
steps:
- name: Checkout
uses: actions/checkout@v2
with:
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
- name: Configure
shell: cmd
run: |
file --mime-encoding tests/test_literals.cpp
mkdir build
cd build
cmake ../ -G "NMake Makefiles" -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
working-directory: ./build
run: |
@@ -98,6 +143,7 @@ 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

View File

@@ -101,7 +101,7 @@ matrix:
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=17
env: COMPILER="g++-8" CXX_STANDARD=17 REQUIRE_FILESYSTEM_LIBRARY=ON
addons:
apt:
sources:
@@ -113,7 +113,7 @@ matrix:
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=17 TOML_HEAD=ON
env: COMPILER="g++-8" CXX_STANDARD=17 TOML_HEAD=ON REQUIRE_FILESYSTEM_LIBRARY=ON
addons:
apt:
sources:
@@ -237,7 +237,7 @@ matrix:
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=17
env: COMPILER="clang++-8" CXX_STANDARD=17 REQUIRE_FILESYSTEM_LIBRARY=ON
addons:
apt:
sources:
@@ -251,7 +251,7 @@ matrix:
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=17 TOML_HEAD=ON
env: COMPILER="clang++-8" CXX_STANDARD=17 TOML_HEAD=ON REQUIRE_FILESYSTEM_LIBRARY=ON
addons:
apt:
sources:
@@ -321,11 +321,17 @@ script:
export WITH_UBSAN="OFF"
fi
- echo "WITH_UBSAN = ${WITH_UBSAN}"
- echo "REQUIRE_FILESYSTEM_LIBRARY = ${REQUIRE_FILESYSTEM_LIBRARY}"
- |
if [[ "${REQUIRE_FILESYSTEM_LIBRARY}" != "ON" ]]; then
export REQUIRE_FILESYSTEM_LIBRARY="OFF"
fi
- echo "REQUIRE_FILESYSTEM_LIBRARY = ${REQUIRE_FILESYSTEM_LIBRARY}"
- cmake --version
- mkdir build
- cd build
- echo "COMPILER = ${COMPILER}"
- echo "CXX_STANDARD = ${CXX_STANDARD}"
- cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD -DTOML11_USE_UNRELEASED_TOML_FEATURES=${TOML_HEAD} -Dtoml11_TEST_WITH_ASAN=${WITH_ASAN} -Dtoml11_TEST_WITH_UBSAN=${WITH_UBSAN} ..
- cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD -DTOML11_REQUIRE_FILESYSTEM_LIBRARY=${REQUIRE_FILESYSTEM_LIBRARY} -Dtoml11_BUILD_TEST=ON -DTOML11_USE_UNRELEASED_TOML_FEATURES=${TOML_HEAD} -Dtoml11_TEST_WITH_ASAN=${WITH_ASAN} -Dtoml11_TEST_WITH_UBSAN=${WITH_UBSAN} ..
- make
- ctest --output-on-failure

View File

@@ -1,9 +1,9 @@
cmake_minimum_required(VERSION 3.1)
enable_testing()
project(toml11 VERSION 3.5.0)
project(toml11 VERSION 3.6.1)
option(toml11_BUILD_TEST "Build toml tests" ON)
option(toml11_BUILD_TEST "Build toml tests" OFF)
option(toml11_TEST_WITH_ASAN "use LLVM address sanitizer" OFF)
option(toml11_TEST_WITH_UBSAN "use LLVM undefined behavior sanitizer" OFF)
@@ -37,6 +37,11 @@ else()
endif()
endif()
if(MSVC)
add_definitions("/Zc:__cplusplus") # define __cplusplus value correctly
add_definitions("/utf-8") # enable to use u8"" literal
endif()
# Set some common directories
include(GNUInstallDirs)
set(toml11_install_cmake_dir ${CMAKE_INSTALL_LIBDIR}/cmake/toml11)

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.
- It is compatible to the latest version of [TOML v1.0.0-rc.1](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v1.0.0-rc.1.md).
- 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 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.
@@ -28,6 +28,10 @@ toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C
int main()
{
// ```toml
// title = "an example toml file"
// nums = [3, 1, 4, 1, 5]
// ```
auto data = toml::parse("example.toml");
// 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");
// 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
@@ -95,7 +99,7 @@ int main()
Just include the file after adding it to the include path.
```cpp
#include <toml11/toml.hpp> // that's all! now you can use it.
#include <toml.hpp> // that's all! now you can use it.
#include <iostream>
int main()
@@ -110,6 +114,8 @@ int main()
The convenient way is to add this repository as a git-submodule or to install
it in your system by CMake.
Note for MSVC: We recommend to set `/Zc:__cplusplus` to detect C++ version correctly.
## Decoding a toml file
To parse a toml file, the only thing you have to do is
@@ -1301,9 +1307,9 @@ struct foo
double b;
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
@@ -1330,9 +1336,9 @@ namespace toml
template<>
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
@@ -1344,6 +1350,27 @@ toml::value v(f);
Any type that can be converted to `toml::value`, e.g. `int`, `toml::table` and
`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
When you encounter an error after you read the toml value, you may want to
@@ -1798,7 +1825,7 @@ for automating test set fetching!).
```sh
$ mkdir build
$ cd build
$ cmake ..
$ cmake .. -Dtoml11_BUILD_TEST=ON
$ make
$ make test
```
@@ -1836,6 +1863,23 @@ I appreciate the help of the contributors who introduced the great feature to th
- Fixed feature test macro to suppress -Wundef
- Use cache variables in CMakeLists.txt
- Automate test set fetching, update and refactor CMakeLists.txt
- Scott McCaskill
- Parse 9 digits (nanoseconds) of fractional seconds in a `local_time`
- Shu Wang (@halfelf)
- fix "Finding a value in an array" example in README
- @maass-tv and @SeverinLeonhardt
- Fix MSVC warning C4866
- OGAWA KenIchi (@kenichiice)
- Fix include path in README
- Mohammed Alyousef (@MoAlyousef)
- 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

View File

@@ -17,7 +17,7 @@ build_script:
- cd C:\toml11
- mkdir build
- cd build
- cmake -G"%generator%" -DBOOST_ROOT=C:/Libraries/boost_1_69_0 ..
- cmake -G"%generator%" -DBOOST_ROOT=C:/Libraries/boost_1_69_0 -Dtoml11_BUILD_TEST=ON ..
- cmake --build . --config "%configuration%"
- file --mime-encoding tests/toml/tests/hard_example_unicode.toml

View File

@@ -150,9 +150,46 @@ find_package(Boost COMPONENTS unit_test_framework REQUIRED)
add_definitions(-DBOOST_TEST_DYN_LINK)
add_definitions(-DUNITTEST_FRAMEWORK_LIBRARY_EXIST)
# check which standard library implementation is used. If libstdc++ is used,
# it will fail to compile. It compiles if libc++ is used.
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("
#include <cstddef>
#ifdef __GLIBCXX__
static_assert(false);
#endif
int main() {
return 0;
}" TOML11_WITH_LIBCXX_LIBRARY)
# LLVM 8 requires -lc++fs if compiled with libc++ to use <filesystem>.
# LLVM 9+ does not require any special library.
# GCC 8 requires -lstdc++fs. GCC 9+ does not require it.
#
# Yes, we can check the version of the compiler used in the current build
# directly in CMake. But, in most cases, clang build uses libstdc++ as the
# standard library implementation and it makes the condition complicated.
# In many environment, the default installed C++ compiler is GCC and libstdc++
# is installed along with it. In most build on such an environment, even if we
# chose clang as the C++ compiler, still libstdc++ is used. Checking default
# gcc version makes the condition complicated.
# The purpose of this file is to compile tests. We know the environment on which
# the tests run. We can set this option and, I think, it is easier and better.
option(TOML11_REQUIRE_FILESYSTEM_LIBRARY "need to link -lstdc++fs or -lc++fs" OFF)
foreach(TEST_NAME ${TEST_NAMES})
add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
target_link_libraries(${TEST_NAME} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} toml11::toml11)
# to compile tests with <filesystem>...
if(TOML11_REQUIRE_FILESYSTEM_LIBRARY)
if(TOML11_WITH_LIBCXX_LIBRARY)
target_link_libraries(${TEST_NAME} "c++fs")
else()
target_link_libraries(${TEST_NAME} "stdc++fs")
endif()
endif()
target_include_directories(${TEST_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
@@ -178,6 +215,7 @@ foreach(TEST_NAME ${TEST_NAMES})
endif()
endforeach(TEST_NAME)
# this test is to check it compiles. it will not run
add_executable(test_multiple_translation_unit
test_multiple_translation_unit_1.cpp

View File

@@ -11,7 +11,7 @@
BOOST_AUTO_TEST_CASE(test_comment_before)
{
{
const std::string file = u8R"(
const std::string file = R"(
# comment for a.
a = 42
# comment for b.
@@ -24,12 +24,12 @@ BOOST_AUTO_TEST_CASE(test_comment_before)
const auto& b = toml::find(v, "b");
BOOST_TEST(a.comments().size() == 1u);
BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(b.comments().size() == 1u);
BOOST_TEST(b.comments().front() == u8" comment for b.");
BOOST_TEST(b.comments().front() == " comment for b.");
}
{
const std::string file = u8R"(
const std::string file = R"(
# comment for a.
# another comment for a.
a = 42
@@ -45,18 +45,18 @@ BOOST_AUTO_TEST_CASE(test_comment_before)
const auto& b = toml::find(v, "b");
BOOST_TEST(a.comments().size() == 2u);
BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(a.comments().back() == u8" another comment for a.");
BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(a.comments().back() == " another comment for a.");
BOOST_TEST(b.comments().size() == 2u);
BOOST_TEST(b.comments().front() == u8" comment for b.");
BOOST_TEST(b.comments().back() == u8" also comment for b.");
BOOST_TEST(b.comments().front() == " comment for b.");
BOOST_TEST(b.comments().back() == " also comment for b.");
}
}
BOOST_AUTO_TEST_CASE(test_comment_inline)
{
{
const std::string file = u8R"(
const std::string file = R"(
a = 42 # comment for a.
b = "baz" # comment for b.
)";
@@ -68,12 +68,12 @@ BOOST_AUTO_TEST_CASE(test_comment_inline)
const auto& b = toml::find(v, "b");
BOOST_TEST(a.comments().size() == 1u);
BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(b.comments().size() == 1u);
BOOST_TEST(b.comments().front() == u8" comment for b.");
BOOST_TEST(b.comments().front() == " comment for b.");
}
{
const std::string file = u8R"(
const std::string file = R"(
a = [
42,
] # comment for a.
@@ -90,18 +90,18 @@ BOOST_AUTO_TEST_CASE(test_comment_inline)
const auto& b0 = b.as_array().at(0);
BOOST_TEST(a.comments().size() == 1u);
BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(b.comments().size() == 1u);
BOOST_TEST(b.comments().front() == u8" this is a comment for b.");
BOOST_TEST(b.comments().front() == " this is a comment for b.");
BOOST_TEST(b0.comments().size() == 1u);
BOOST_TEST(b0.comments().front() == u8" this is not a comment for b, but \"bar\"");
BOOST_TEST(b0.comments().front() == " this is not a comment for b, but \"bar\"");
}
}
BOOST_AUTO_TEST_CASE(test_comment_both)
{
{
const std::string file = u8R"(
const std::string file = R"(
# comment for a.
a = 42 # inline comment for a.
# comment for b.
@@ -122,25 +122,62 @@ BOOST_AUTO_TEST_CASE(test_comment_both)
const auto& c0 = c.as_array().at(0);
BOOST_TEST(a.comments().size() == 2u);
BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(a.comments().back() == u8" inline comment for a.");
BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(a.comments().back() == " inline comment for a.");
BOOST_TEST(b.comments().size() == 2u);
BOOST_TEST(b.comments().front() == u8" comment for b.");
BOOST_TEST(b.comments().back() == u8" inline comment for b.");
BOOST_TEST(b.comments().front() == " comment for b.");
BOOST_TEST(b.comments().back() == " inline comment for b.");
BOOST_TEST(c.comments().size() == 2u);
BOOST_TEST(c.comments().front() == u8" comment for c.");
BOOST_TEST(c.comments().back() == u8" another comment for c.");
BOOST_TEST(c.comments().front() == " comment for c.");
BOOST_TEST(c.comments().back() == " another comment for c.");
BOOST_TEST(c0.comments().size() == 2u);
BOOST_TEST(c0.comments().front() == u8" comment for the first element.");
BOOST_TEST(c0.comments().back() == u8" this also.");
BOOST_TEST(c0.comments().front() == " comment for the first element.");
BOOST_TEST(c0.comments().back() == " this also.");
}
}
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)
{
const std::string file = u8R"(
const std::string file = R"(
# comment for a.
a = 42 # inline comment for a.
# comment for b.

View File

@@ -70,9 +70,9 @@ struct from<extlib::foo>
template<>
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

@@ -8,7 +8,7 @@
do { \
const std::string token (tkn); \
const std::string expected(expct); \
toml::detail::location<std::string> loc("test", token); \
toml::detail::location loc("test", token); \
const auto result = lxr::invoke(loc); \
BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \
@@ -28,7 +28,7 @@ do { \
#define TOML11_TEST_LEX_REJECT(lxr, tkn) \
do { \
const std::string token (tkn); \
toml::detail::location<std::string> loc("test", token); \
toml::detail::location loc("test", token); \
const auto result = lxr::invoke(loc); \
BOOST_TEST(result.is_err()); \
const bool loc_same = (loc.begin() == loc.iter()); \

View File

@@ -18,12 +18,11 @@ BOOST_AUTO_TEST_CASE(test_quoted_key)
{
TOML11_TEST_LEX_ACCEPT(lex_key, "\"127.0.0.1\"", "\"127.0.0.1\"");
TOML11_TEST_LEX_ACCEPT(lex_key, "\"character encoding\"", "\"character encoding\"");
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
// UTF-8 codepoint of characters that looks like "key" written upside down
TOML11_TEST_LEX_ACCEPT(lex_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"",
"\"\xCA\x8E\xC7\x9D\xCA\x9E\"");
#else
TOML11_TEST_LEX_ACCEPT(lex_key, u8"\"ʎǝʞ\"", u8"\"ʎǝʞ\"");
#endif
TOML11_TEST_LEX_ACCEPT(lex_key, "'key2'", "'key2'");
TOML11_TEST_LEX_ACCEPT(lex_key, "'quoted \"value\"'", "'quoted \"value\"'");
}

View File

@@ -31,15 +31,9 @@ BOOST_AUTO_TEST_CASE(test_basic_string)
"\"192.168.1.1\"",
"\"192.168.1.1\"");
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\xE4\xB8\xAD\xE5\x9B\xBD\"",
"\"\xE4\xB8\xAD\xE5\x9B\xBD\"");
#else
TOML11_TEST_LEX_ACCEPT(lex_string,
u8"\"中国\"",
u8"\"中国\"");
#endif
"\"\xE4\xB8\xAD\xE5\x9B\xBD\"", // UTF-8 string (means "China" in
"\"\xE4\xB8\xAD\xE5\x9B\xBD\""); // Chinese characters)
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"You'll hate me after this - #\"",

View File

@@ -12,6 +12,170 @@ BOOST_AUTO_TEST_CASE(test_file_as_literal)
{
using namespace toml::literals::toml_literals;
{
const toml::value r{{"a", 42}, {"b", "baz"}};
const toml::value v = R"(
a = 42
b = "baz"
)"_toml;
BOOST_TEST(r == v);
}
{
const toml::value r{
{"c", 3.14},
{"table", toml::table{{"a", 42}, {"b", "baz"}}}
};
const toml::value v = R"(
c = 3.14
[table]
a = 42
b = "baz"
)"_toml;
BOOST_TEST(r == v);
}
{
const toml::value r{
{"table", toml::table{{"a", 42}, {"b", "baz"}}}
};
const toml::value v = R"(
[table]
a = 42
b = "baz"
)"_toml;
BOOST_TEST(r == v);
}
{
const toml::value r{
{"array_of_tables", toml::array{toml::table{}}}
};
const toml::value v = R"(
[[array_of_tables]]
)"_toml;
BOOST_TEST(r == v);
}
}
BOOST_AUTO_TEST_CASE(test_value_as_literal)
{
using namespace toml::literals::toml_literals;
{
const toml::value v1 = "true"_toml;
const toml::value v2 = "false"_toml;
BOOST_TEST(v1.is_boolean());
BOOST_TEST(v2.is_boolean());
BOOST_TEST(toml::get<bool>(v1));
BOOST_TEST(!toml::get<bool>(v2));
}
{
const toml::value v1 = "123_456"_toml;
const toml::value v2 = "0b0010"_toml;
const toml::value v3 = "0xDEADBEEF"_toml;
BOOST_TEST(v1.is_integer());
BOOST_TEST(v2.is_integer());
BOOST_TEST(v3.is_integer());
BOOST_TEST(toml::get<toml::integer>(v1) == 123456);
BOOST_TEST(toml::get<toml::integer>(v2) == 2);
BOOST_TEST(toml::get<toml::integer>(v3) == 0xDEADBEEF);
}
{
const toml::value v1 = "3.1415"_toml;
const toml::value v2 = "6.02e+23"_toml;
BOOST_TEST(v1.is_floating());
BOOST_TEST(v2.is_floating());
BOOST_TEST(toml::get<double>(v1) == 3.1415, boost::test_tools::tolerance(0.00001));
BOOST_TEST(toml::get<double>(v2) == 6.02e23, boost::test_tools::tolerance(0.0001));
}
{
const toml::value v1 = R"("foo")"_toml;
const toml::value v2 = R"('foo')"_toml;
const toml::value v3 = R"("""foo""")"_toml;
const toml::value v4 = R"('''foo''')"_toml;
BOOST_TEST(v1.is_string());
BOOST_TEST(v2.is_string());
BOOST_TEST(v3.is_string());
BOOST_TEST(v4.is_string());
BOOST_TEST(toml::get<std::string>(v1) == "foo");
BOOST_TEST(toml::get<std::string>(v2) == "foo");
BOOST_TEST(toml::get<std::string>(v3) == "foo");
BOOST_TEST(toml::get<std::string>(v4) == "foo");
}
{
{
const toml::value v1 = R"([1,2,3])"_toml;
BOOST_TEST(v1.is_array());
const bool result = (toml::get<std::vector<int>>(v1) == std::vector<int>{1,2,3});
BOOST_TEST(result);
}
{
const toml::value v2 = R"([1,])"_toml;
BOOST_TEST(v2.is_array());
const bool result = (toml::get<std::vector<int>>(v2) == std::vector<int>{1});
BOOST_TEST(result);
}
{
const toml::value v3 = R"([[1,]])"_toml;
BOOST_TEST(v3.is_array());
const bool result = (toml::get<std::vector<int>>(toml::get<toml::array>(v3).front()) == std::vector<int>{1});
BOOST_TEST(result);
}
{
const toml::value v4 = R"([[1],])"_toml;
BOOST_TEST(v4.is_array());
const bool result = (toml::get<std::vector<int>>(toml::get<toml::array>(v4).front()) == std::vector<int>{1});
BOOST_TEST(result);
}
}
{
const toml::value v1 = R"({a = 42})"_toml;
BOOST_TEST(v1.is_table());
const bool result = toml::get<std::map<std::string,int>>(v1) ==
std::map<std::string,int>{{"a", 42}};
BOOST_TEST(result);
}
{
const toml::value v1 = "1979-05-27"_toml;
BOOST_TEST(v1.is_local_date());
BOOST_TEST(toml::get<toml::local_date>(v1) ==
toml::local_date(1979, toml::month_t::May, 27));
}
{
const toml::value v1 = "12:00:00"_toml;
BOOST_TEST(v1.is_local_time());
const bool result = toml::get<std::chrono::hours>(v1) == std::chrono::hours(12);
BOOST_TEST(result);
}
{
const toml::value v1 = "1979-05-27T07:32:00"_toml;
BOOST_TEST(v1.is_local_datetime());
BOOST_TEST(toml::get<toml::local_datetime>(v1) ==
toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27),
toml::local_time(7, 32, 0)));
}
{
const toml::value v1 = "1979-05-27T07:32:00Z"_toml;
BOOST_TEST(v1.is_offset_datetime());
BOOST_TEST(toml::get<toml::offset_datetime>(v1) ==
toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27),
toml::local_time(7, 32, 0), toml::time_offset(0, 0)));
}
}
BOOST_AUTO_TEST_CASE(test_file_as_u8_literal)
{
using namespace toml::literals::toml_literals;
{
const toml::value r{{"a", 42}, {"b", "baz"}};
const toml::value v = u8R"(
@@ -59,7 +223,7 @@ BOOST_AUTO_TEST_CASE(test_file_as_literal)
}
}
BOOST_AUTO_TEST_CASE(test_value_as_literal)
BOOST_AUTO_TEST_CASE(test_value_as_u8_literal)
{
using namespace toml::literals::toml_literals;
@@ -98,15 +262,18 @@ BOOST_AUTO_TEST_CASE(test_value_as_literal)
const toml::value v2 = u8R"('foo')"_toml;
const toml::value v3 = u8R"("""foo""")"_toml;
const toml::value v4 = u8R"('''foo''')"_toml;
const toml::value v5 = u8R"("")"_toml;
BOOST_TEST(v1.is_string());
BOOST_TEST(v2.is_string());
BOOST_TEST(v3.is_string());
BOOST_TEST(v4.is_string());
BOOST_TEST(v5.is_string());
BOOST_TEST(toml::get<std::string>(v1) == "foo");
BOOST_TEST(toml::get<std::string>(v2) == "foo");
BOOST_TEST(toml::get<std::string>(v3) == "foo");
BOOST_TEST(toml::get<std::string>(v4) == "foo");
BOOST_TEST(toml::get<std::string>(v5) == "\xE3\x81\xB2\xE3\x82\x89\xE3\x81\x8C\xE3\x81\xAA");
}
{
{
@@ -164,7 +331,7 @@ BOOST_AUTO_TEST_CASE(test_value_as_literal)
toml::local_time(7, 32, 0)));
}
{
const toml::value v1 = "1979-05-27T07:32:00Z"_toml;
const toml::value v1 = u8"1979-05-27T07:32:00Z"_toml;
BOOST_TEST(v1.is_offset_datetime());
BOOST_TEST(toml::get<toml::offset_datetime>(v1) ==
toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27),

View File

@@ -172,3 +172,18 @@ BOOST_AUTO_TEST_CASE(test_heterogeneous_array)
}
#endif
}
BOOST_AUTO_TEST_CASE(test_comments_after_comma)
{
{
array a;
a.push_back("foo");
a.push_back("bar");
a.push_back("baz");
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"[ \"foo\" # comment\n"
", \"bar\" # comment\n"
", \"baz\" # comment\n"
"]", toml::value(a));
}
}

View File

@@ -7,7 +7,7 @@
#define TOML11_TEST_PARSE_EQUAL(psr, tkn, expct) \
do { \
const std::string token(tkn); \
toml::detail::location<std::string> loc("test", token); \
toml::detail::location loc("test", token); \
const auto result = psr(loc); \
BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \
@@ -23,7 +23,7 @@ do { \
#define TOML11_TEST_PARSE_EQUAL_VALUE(psr, tkn, expct) \
do { \
const std::string token(tkn); \
toml::detail::location<std::string> loc("test", token); \
toml::detail::location loc("test", token); \
const auto result = psr(loc); \
BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \

View File

@@ -779,6 +779,22 @@ BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines)
BOOST_TEST(toml::find<std::string>(toml::find(data, "table"), "key") == "value");
}
// without newline
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"a = 0"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_TEST(toml::find<std::string>(data, "key") == "value");
BOOST_TEST(toml::find<std::string>(toml::find(data, "table"), "key") == "value");
}
// CRLF
@@ -889,3 +905,41 @@ BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines)
}
}
BOOST_AUTO_TEST_CASE(test_parse_function_compiles)
{
// toml::parse("");
const auto string_literal = toml::parse("toml/tests/example.toml");
BOOST_TEST_MESSAGE("string_literal");
const char* fname_cstring = "toml/tests/example.toml";
// toml::parse(const char*);
const auto cstring = toml::parse(fname_cstring);
BOOST_TEST_MESSAGE("const char*");
// toml::parse(char*);
std::array<char, 24> fname_char_ptr;
std::strncpy(fname_char_ptr.data(), fname_cstring, 24);
const auto char_ptr = toml::parse(fname_char_ptr.data());
BOOST_TEST_MESSAGE("char*");
// toml::parse(const std::string&);
const std::string fname_string("toml/tests/example.toml");
const auto string = toml::parse(fname_string);
std::string fname_string_mut("toml/tests/example.toml");
// toml::parse(std::string&);
const auto string_mutref = toml::parse(fname_string_mut);
// toml::parse(std::string&&);
const auto string_rref = toml::parse(std::move(fname_string_mut));
BOOST_TEST_MESSAGE("strings");
#ifdef TOML11_HAS_STD_FILESYSTEM
const std::filesystem::path fname_path(fname_string.begin(), fname_string.end());
const auto filesystem_path = toml::parse(fname_path);
BOOST_TEST_MESSAGE("path");
#endif
}

View File

@@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
{
{
const std::string token("inf");
toml::detail::location<std::string> loc("test", token);
toml::detail::location loc("test", token);
const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isinf(r.unwrap().first));
@@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
}
{
const std::string token("+inf");
toml::detail::location<std::string> loc("test", token);
toml::detail::location loc("test", token);
const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isinf(r.unwrap().first));
@@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
}
{
const std::string token("-inf");
toml::detail::location<std::string> loc("test", token);
toml::detail::location loc("test", token);
const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isinf(r.unwrap().first));
@@ -156,21 +156,21 @@ BOOST_AUTO_TEST_CASE(test_nan)
{
{
const std::string token("nan");
toml::detail::location<std::string> loc("test", token);
toml::detail::location loc("test", token);
const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isnan(r.unwrap().first));
}
{
const std::string token("+nan");
toml::detail::location<std::string> loc("test", token);
toml::detail::location loc("test", token);
const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isnan(r.unwrap().first));
}
{
const std::string token("-nan");
toml::detail::location<std::string> loc("test", token);
toml::detail::location loc("test", token);
const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isnan(r.unwrap().first));

View File

@@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(test_normal_table)
"key2 = 42\n"
"key3 = 3.14\n"
);
location<std::string> loc("test", table);
location loc("test", table);
const auto result = toml::detail::parse_ml_table<toml::value>(loc);
BOOST_TEST(result.is_ok());
@@ -36,7 +36,7 @@ BOOST_AUTO_TEST_CASE(test_nested_table)
"a.b = \"value\"\n"
"a.c.d = 42\n"
);
location<std::string> loc("test", table);
location loc("test", table);
const auto result = toml::detail::parse_ml_table<toml::value>(loc);
BOOST_TEST(result.is_ok());

View File

@@ -9,7 +9,6 @@
#include <iostream>
#include <fstream>
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
BOOST_AUTO_TEST_CASE(test_hard_example_unicode)
{
const auto data = toml::parse("toml/tests/hard_example_unicode.toml");
@@ -40,35 +39,3 @@ BOOST_AUTO_TEST_CASE(test_hard_example_unicode)
BOOST_CHECK(toml::get<std::vector<std::string>>(bit.at("multi_line_array")) ==
expected_multi_line_array);
}
#else
BOOST_AUTO_TEST_CASE(test_hard_example_unicode)
{
const auto data = toml::parse("toml/tests/hard_example_unicode.toml");
const auto the = toml::find<toml::table>(data, "the");
BOOST_TEST(toml::get<std::string>(the.at("test_string")) ==
std::string(u8"Ýôú' λáƭè ₥è áƒƭèř ƭλïƨ - #"));
const auto hard = toml::get<toml::table>(the.at("hard"));
const std::vector<std::string> expected_the_hard_test_array{"] ", " # "};
BOOST_CHECK(toml::get<std::vector<std::string>>(hard.at("test_array")) ==
expected_the_hard_test_array);
const std::vector<std::string> expected_the_hard_test_array2{
std::string(u8"Tèƨƭ #11 ]ƥřôƲèδ ƭλáƭ"),
std::string(u8"Éжƥèřï₥èñƭ #9 ωáƨ á ƨúççèƨƨ")};
BOOST_CHECK(toml::get<std::vector<std::string>>(hard.at("test_array2")) ==
expected_the_hard_test_array2);
BOOST_TEST(toml::get<std::string>(hard.at("another_test_string")) ==
std::string(u8"§á₥è ƭλïñϱ, βúƭ ωïƭλ á ƨƭřïñϱ #"));
BOOST_TEST(toml::get<std::string>(hard.at("harder_test_string")) ==
std::string(u8" Âñδ ωλèñ \"'ƨ ářè ïñ ƭλè ƨƭřïñϱ, áℓôñϱ ωïƭλ # \""));
const auto bit = toml::get<toml::table>(hard.at(std::string(u8"βïƭ#")));
BOOST_TEST(toml::get<std::string>(bit.at(std::string(u8"ωλáƭ?"))) ==
std::string(u8"Ýôú δôñ'ƭ ƭλïñƙ ƨô₥è úƨèř ωôñ'ƭ δô ƭλáƭ?"));
const std::vector<std::string> expected_multi_line_array{"]"};
BOOST_CHECK(toml::get<std::vector<std::string>>(bit.at("multi_line_array")) ==
expected_multi_line_array);
}
#endif

View File

@@ -10,6 +10,7 @@
#include <map>
#include <iostream>
#include <fstream>
#include <sstream>
template<typename Comment,
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));
}
}
// 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

@@ -5,7 +5,7 @@
int main()
{
using namespace toml::literals::toml_literals;
const auto data = u8R"(windows = "defines min and max as a macro")"_toml;
const auto data = R"(windows = "defines min and max as a macro")"_toml;
std::cout << toml::find<std::string>(data, "windows") << std::endl;
return 0;

View File

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

View File

@@ -58,13 +58,9 @@ struct character
{
static constexpr char target = C;
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter();
@@ -75,7 +71,7 @@ struct character
}
loc.advance(); // update location
return ok(region<Cont>(loc, first, loc.iter()));
return ok(region(loc, first, loc.iter()));
}
};
template<char C>
@@ -91,13 +87,9 @@ struct in_range
static constexpr char upper = Up;
static constexpr char lower = Low;
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter();
@@ -108,7 +100,7 @@ struct in_range
}
loc.advance();
return ok(region<Cont>(loc, first, loc.iter()));
return ok(region(loc, first, loc.iter()));
}
};
template<char L, char U> constexpr char in_range<L, U>::upper;
@@ -119,13 +111,9 @@ template<char L, char U> constexpr char in_range<L, U>::lower;
template<typename Combinator>
struct exclude
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return none();}
auto first = loc.iter();
@@ -136,7 +124,7 @@ struct exclude
return none();
}
loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
return ok(region<Cont>(loc, first, loc.iter()));
return ok(region(loc, first, loc.iter()));
}
};
@@ -144,19 +132,15 @@ struct exclude
template<typename Combinator>
struct maybe
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
const auto rslt = Combinator::invoke(loc);
if(rslt.is_ok())
{
return rslt;
}
return ok(region<Cont>(loc));
return ok(region(loc));
}
};
@@ -166,13 +150,9 @@ struct sequence;
template<typename Head, typename ... Tail>
struct sequence<Head, Tail...>
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
const auto first = loc.iter();
const auto rslt = Head::invoke(loc);
if(rslt.is_err())
@@ -184,9 +164,9 @@ struct sequence<Head, Tail...>
}
// called from the above function only, recursively.
template<typename Cont, typename Iterator>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc, region<Cont> reg, Iterator first)
template<typename Iterator>
static result<region, none_t>
invoke(location& loc, region reg, Iterator first)
{
const auto rslt = Head::invoke(loc);
if(rslt.is_err())
@@ -203,9 +183,9 @@ template<typename Head>
struct sequence<Head>
{
// would be called from sequence<T ...>::invoke only.
template<typename Cont, typename Iterator>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc, region<Cont> reg, Iterator first)
template<typename Iterator>
static result<region, none_t>
invoke(location& loc, region reg, Iterator first)
{
const auto rslt = Head::invoke(loc);
if(rslt.is_err())
@@ -224,13 +204,9 @@ struct either;
template<typename Head, typename ... Tail>
struct either<Head, Tail...>
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
const auto rslt = Head::invoke(loc);
if(rslt.is_ok()) {return rslt;}
return either<Tail...>::invoke(loc);
@@ -239,12 +215,9 @@ struct either<Head, Tail...>
template<typename Head>
struct either<Head>
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
return Head::invoke(loc);
}
};
@@ -259,11 +232,10 @@ struct unlimited{};
template<typename T, std::size_t N>
struct repeat<T, exactly<N>>
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
region<Cont> retval(loc);
region retval(loc);
const auto first = loc.iter();
for(std::size_t i=0; i<N; ++i)
{
@@ -282,11 +254,10 @@ struct repeat<T, exactly<N>>
template<typename T, std::size_t N>
struct repeat<T, at_least<N>>
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
region<Cont> retval(loc);
region retval(loc);
const auto first = loc.iter();
for(std::size_t i=0; i<N; ++i)
@@ -314,11 +285,10 @@ struct repeat<T, at_least<N>>
template<typename T>
struct repeat<T, unlimited>
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
region<Cont> retval(loc);
region retval(loc);
while(true)
{
auto rslt = T::invoke(loc);

View File

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

View File

@@ -189,8 +189,7 @@ get(const basic_value<C, M, V>& v)
{
throw type_error(detail::format_underline("toml::value: "
"bad_cast to std::chrono::system_clock::time_point", {
{std::addressof(detail::get_region(v)),
concat_to_string("the actual type is ", v.type())}
{v.location(), concat_to_string("the actual type is ", v.type())}
}), v.location());
}
}
@@ -336,11 +335,13 @@ get(const basic_value<C, M, V>& v)
throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified container size is ", container.size(),
" but there are ", ar.size(), " elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
{v.location(), "here"}
}));
}
std::transform(ar.cbegin(), ar.cend(), container.begin(),
[](const value& x){return ::toml::get<value_type>(x);});
for(std::size_t i=0; i<ar.size(); ++i)
{
container[i] = ::toml::get<value_type>(ar[i]);
}
return container;
}
@@ -360,9 +361,7 @@ get(const basic_value<C, M, V>& v)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified std::pair but there are ", ar.size(),
" elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
}));
" elements in toml array."), {{v.location(), "here"}}));
}
return std::make_pair(::toml::get<first_type >(ar.at(0)),
::toml::get<second_type>(ar.at(1)));
@@ -392,9 +391,7 @@ get(const basic_value<C, M, V>& v)
throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified std::tuple with ",
std::tuple_size<T>::value, " elements, but there are ", ar.size(),
" elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
}));
" elements in toml array."), {{v.location(), "here"}}));
}
return detail::get_tuple_impl<T>(ar,
detail::make_index_sequence<std::tuple_size<T>::value>{});
@@ -511,9 +508,7 @@ find(const basic_value<C, M, V>& v, const std::size_t idx)
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {
{std::addressof(detail::get_region(v)), "in this array"}
}));
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ary.at(idx);
}
@@ -525,9 +520,7 @@ basic_value<C, M, V>& find(basic_value<C, M, V>& v, const std::size_t idx)
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {
{std::addressof(detail::get_region(v)), "in this array"}
}));
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ary.at(idx);
}
@@ -539,9 +532,7 @@ basic_value<C, M, V> find(basic_value<C, M, V>&& v, const std::size_t idx)
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {
{std::addressof(detail::get_region(v)), "in this array"}
}));
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return basic_value<C, M, V>(std::move(ary.at(idx)));
}
@@ -599,9 +590,7 @@ find(const basic_value<C, M, V>& v, const std::size_t idx)
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {
{std::addressof(detail::get_region(v)), "in this array"}
}));
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ::toml::get<T>(ary.at(idx));
}
@@ -614,9 +603,7 @@ find(basic_value<C, M, V>& v, const std::size_t idx)
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {
{std::addressof(detail::get_region(v)), "in this array"}
}));
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ::toml::get<T>(ary.at(idx));
}
@@ -629,9 +616,7 @@ find(basic_value<C, M, V>&& v, const std::size_t idx)
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {
{std::addressof(detail::get_region(v)), "in this array"}
}));
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ::toml::get<T>(std::move(ary.at(idx)));
}

View File

@@ -11,12 +11,12 @@ inline namespace literals
inline namespace toml_literals
{
inline ::toml::value operator"" _toml(const char* str, std::size_t len)
// implementation
inline ::toml::basic_value<::toml::discard_comments, std::unordered_map, std::vector>
literal_internal_impl(::toml::detail::location loc)
{
::toml::detail::location<std::vector<char>>
loc(/* filename = */ std::string("TOML literal encoded in a C++ code"),
/* contents = */ std::vector<char>(str, str + len));
using value_type = ::toml::basic_value<
::toml::discard_comments, std::unordered_map, std::vector>;
// if there are some comments or empty lines, skip them.
using skip_line = ::toml::detail::repeat<toml::detail::sequence<
::toml::detail::maybe<::toml::detail::lex_ws>,
@@ -53,7 +53,7 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len)
// 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(auto data = ::toml::detail::parse_value<::toml::value>(loc))
if(auto data = ::toml::detail::parse_value<value_type>(loc))
{
return data.unwrap();
}
@@ -70,17 +70,42 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len)
// It is a valid toml file.
// 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();
}
else // none of them.
{
throw ::toml::syntax_error(data.unwrap_err(),
source_location(std::addressof(loc)));
throw ::toml::syntax_error(data.unwrap_err(), source_location(loc));
}
}
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(
std::string("TOML literal encoded in a C++ code"),
std::vector<char>(str, str + len));
return literal_internal_impl(std::move(loc));
}
// value of __cplusplus in C++2a/20 mode is not fixed yet along compilers.
// So here we use the feature test macro for `char8_t` itself.
#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
// NOT compatible to char
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(
std::string("TOML literal encoded in a C++ code"),
std::vector<char>(reinterpret_cast<const char*>(str),
reinterpret_cast<const char*>(str) + len));
return literal_internal_impl(std::move(loc));
}
#endif
} // toml_literals
} // literals
} // toml

File diff suppressed because it is too large Load Diff

View File

@@ -67,22 +67,21 @@ struct region_base
//
// it contains pointer to the file content and iterator that points the current
// location.
template<typename Container>
struct location final : public region_base
{
using const_iterator = typename Container::const_iterator;
using const_iterator = typename std::vector<char>::const_iterator;
using difference_type = typename const_iterator::difference_type;
using source_ptr = std::shared_ptr<const Container>;
using source_ptr = std::shared_ptr<const std::vector<char>>;
static_assert(std::is_same<char, typename Container::value_type>::value,"");
static_assert(std::is_same<std::random_access_iterator_tag,
typename std::iterator_traits<const_iterator>::iterator_category>::value,
"container should be randomly accessible");
location(std::string name, Container cont)
: source_(std::make_shared<Container>(std::move(cont))), line_number_(1),
source_name_(std::move(name)), iter_(source_->cbegin())
location(std::string name, std::vector<char> cont)
: source_(std::make_shared<std::vector<char>>(std::move(cont))),
line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin())
{}
location(std::string name, const std::string& cont)
: source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())),
line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin())
{}
location(const location&) = default;
location(location&&) = default;
location& operator=(const location&) = default;
@@ -195,33 +194,27 @@ struct location final : public region_base
//
// it contains pointer to the file content and iterator that points the first
// and last location.
template<typename Container>
struct region final : public region_base
{
using const_iterator = typename Container::const_iterator;
using source_ptr = std::shared_ptr<const Container>;
static_assert(std::is_same<char, typename Container::value_type>::value,"");
static_assert(std::is_same<std::random_access_iterator_tag,
typename std::iterator_traits<const_iterator>::iterator_category>::value,
"container should be randomly accessible");
using const_iterator = typename std::vector<char>::const_iterator;
using source_ptr = std::shared_ptr<const std::vector<char>>;
// delete default constructor. source_ never be null.
region() = delete;
region(const location<Container>& loc)
explicit region(const location& loc)
: source_(loc.source()), source_name_(loc.name()),
first_(loc.iter()), last_(loc.iter())
{}
region(location<Container>&& loc)
explicit region(location&& loc)
: source_(loc.source()), source_name_(loc.name()),
first_(loc.iter()), last_(loc.iter())
{}
region(const location<Container>& loc, const_iterator f, const_iterator l)
region(const location& loc, const_iterator f, const_iterator l)
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
{}
region(location<Container>&& loc, const_iterator f, const_iterator l)
region(location&& loc, const_iterator f, const_iterator l)
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
{}
@@ -351,7 +344,7 @@ struct region final : public region_base
{
// unwrap the first '#' by std::next.
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));
}
else
@@ -404,7 +397,7 @@ struct region final : public region_base
{
// unwrap the first '#' by std::next.
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));
}
}
@@ -419,107 +412,6 @@ struct region final : public region_base
const_iterator first_, last_;
};
// to show a better error message.
inline std::string format_underline(const std::string& message,
const std::vector<std::pair<region_base const*, std::string>>& reg_com,
const std::vector<std::string>& helps = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
{
assert(!reg_com.empty());
const auto line_num_width = static_cast<int>(std::max_element(
reg_com.begin(), reg_com.end(),
[](std::pair<region_base const*, std::string> const& lhs,
std::pair<region_base const*, std::string> const& rhs)
{
return lhs.first->line_num().size() < rhs.first->line_num().size();
}
)->first->line_num().size());
std::ostringstream retval;
if(colorize)
{
retval << color::colorize; // turn on ANSI color
}
// XXX
// Here, before `colorize` support, it does not output `[error]` prefix
// automatically. So some user may output it manually and this change may
// duplicate the prefix. To avoid it, check the first 7 characters and
// if it is "[error]", it removes that part from the message shown.
if(message.size() > 7 && message.substr(0, 7) == "[error]")
{
retval << color::bold << color::red << "[error]" << color::reset
<< color::bold << message.substr(7) << color::reset << '\n';
}
else
{
retval << color::bold << color::red << "[error] " << color::reset
<< color::bold << message << color::reset << '\n';
}
for(auto iter = reg_com.begin(); iter != reg_com.end(); ++iter)
{
// if the filenames are the same, print "..."
if(iter != reg_com.begin() &&
std::prev(iter)->first->name() == iter->first->name())
{
retval << color::bold << color::blue << "\n ...\n" << color::reset;
}
else // if filename differs, print " --> filename.toml"
{
if(iter != reg_com.begin()) {retval << '\n';}
retval << color::bold << color::blue << " --> " << color::reset
<< iter->first->name() << '\n';
// add one almost-empty line for readability
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ')
<< color::bold << color::blue << " | " << color::reset << '\n';
}
const region_base* const reg = iter->first;
const std::string& comment = iter->second;
retval << ' ' << color::bold << color::blue << std::setw(line_num_width)
<< std::right << reg->line_num() << " | " << color::reset
<< reg->line() << '\n';
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ')
<< color::bold << color::blue << " | " << color::reset
<< make_string(reg->before(), ' ');
if(reg->size() == 1)
{
// invalid
// ^------
retval << color::bold << color::red
<< '^' << make_string(reg->after(), '-') << color::reset;
}
else
{
// invalid
// ~~~~~~~
const auto underline_len = (std::min)(reg->size(), reg->line().size());
retval << color::bold << color::red
<< make_string(underline_len, '~') << color::reset;
}
retval << ' ';
retval << comment;
}
if(!helps.empty())
{
retval << '\n';
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ');
retval << color::bold << color::blue << " | " << color::reset;
for(const auto& help : helps)
{
retval << color::bold << "\nHint: " << color::reset;
retval << help;
}
}
return retval.str();
}
} // detail
} // toml
#endif// TOML11_REGION_H

View File

@@ -29,7 +29,7 @@ std::basic_string<charT, traits, Alloc>
format_key(const std::basic_string<charT, traits, Alloc>& key)
{
// check the key can be a bare (unquoted) key
detail::location<toml::key> loc(key, key);
detail::location loc(key, std::vector<char>(key.begin(), key.end()));
detail::lex_unquoted_key::invoke(loc);
if(loc.iter() == loc.end())
{
@@ -97,8 +97,10 @@ struct serializer
const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
const bool can_be_inlined = 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),
value_has_comment_(value_has_comment && !no_comment),
float_prec_(float_prec), width_(w), keys_(std::move(ks))
{}
~serializer() = default;
@@ -120,7 +122,7 @@ struct serializer
std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f);
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';
}
@@ -244,92 +246,18 @@ struct serializer
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())
{
return std::string("[]");
}
if(this->is_array_of_tables(v))
{
return make_array_of_tables(v);
}
// not an array of tables. normal array.
// 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);
if(inl.size() < this->width_ &&
@@ -350,7 +278,7 @@ struct serializer
token += "[\n";
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.
// e.g. the following is not allowed.
@@ -376,7 +304,7 @@ struct serializer
token += '\n';
}
token += toml::visit(*this, item);
if(token.back() == '\n') {token.pop_back();}
if(!token.empty() && token.back() == '\n') {token.pop_back();}
token += ",\n";
continue;
}
@@ -384,7 +312,7 @@ struct serializer
next_elem += toml::visit(*this, item);
// 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.size() + next_elem.size() + 1 < this->width_)
@@ -411,7 +339,10 @@ struct serializer
}
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 += "]\n";
@@ -557,8 +488,10 @@ struct serializer
for(const auto& item : v)
{
if(is_first) {is_first = false;} else {token += ',';}
token += visit(serializer((std::numeric_limits<std::size_t>::max)(),
this->float_prec_, true), item);
token += visit(serializer(
(std::numeric_limits<std::size_t>::max)(), this->float_prec_,
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !item.comments().empty()), item);
}
token += ']';
return token;
@@ -577,8 +510,10 @@ struct serializer
if(is_first) {is_first = false;} else {token += ',';}
token += format_key(kv.first);
token += '=';
token += visit(serializer((std::numeric_limits<std::size_t>::max)(),
this->float_prec_, true), kv.second);
token += visit(serializer(
(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 += '}';
return token;
@@ -588,8 +523,16 @@ struct serializer
{
std::string token;
// print non-table stuff first. because after printing [foo.bar], the
// remaining non-table values will be assigned into [foo.bar], not [foo]
// print non-table elements first.
// ```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)
{
if(kv.second.is_table() || is_array_of_tables(kv.second))
@@ -597,21 +540,16 @@ struct serializer
continue;
}
if(!kv.second.comments().empty() && !no_comment_)
{
for(const auto& c : kv.second.comments())
{
token += '#';
token += c;
token += '\n';
}
}
token += write_comments(kv.second);
const auto key_and_sep = format_key(kv.first) + " = ";
const auto residual_width = (this->width_ > key_and_sep.size()) ?
this->width_ - key_and_sep.size() : 0;
token += key_and_sep;
token += visit(serializer(residual_width, this->float_prec_, true),
kv.second);
token += visit(serializer(residual_width, this->float_prec_,
/*can be inlined*/ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
if(token.back() != '\n')
{
token += '\n';
@@ -637,8 +575,8 @@ struct serializer
ks.push_back(kv.first);
auto tmp = visit(serializer(this->width_, this->float_prec_,
!multiline_table_printed, this->no_comment_, ks),
kv.second);
!multiline_table_printed, this->no_comment_, ks,
/*has_comment*/ !kv.second.comments().empty()), kv.second);
if((!multiline_table_printed) &&
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend())
@@ -651,31 +589,142 @@ struct serializer
tmp += '\n';
}
if(!kv.second.comments().empty() && !no_comment_)
{
for(const auto& c : kv.second.comments())
{
token += '#';
token += c;
token += '\n';
}
}
token += write_comments(kv.second);
token += tmp;
}
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
{
if(!v.is_array()) {return false;}
const auto& a = v.as_array();
return !a.empty() && a.front().is_table();
if(!v.is_array() || v.as_array().empty()) {return false;}
return is_array_of_tables(v.as_array());
}
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:
bool can_be_inlined_;
bool no_comment_;
bool value_has_comment_;
int float_prec_;
std::size_t width_;
std::vector<toml::key> keys_;

View File

@@ -3,6 +3,7 @@
#ifndef TOML11_SOURCE_LOCATION_HPP
#define TOML11_SOURCE_LOCATION_HPP
#include <cstdint>
#include <sstream>
#include "region.hpp"
@@ -39,12 +40,12 @@ struct source_location
public:
source_location()
: line_num_(0), column_num_(0), region_size_(0),
: line_num_(1), column_num_(1), region_size_(1),
file_name_("unknown file"), line_str_("")
{}
explicit source_location(const detail::region_base* reg)
: line_num_(0), column_num_(0), region_size_(0),
: line_num_(1), column_num_(1), region_size_(1),
file_name_("unknown file"), line_str_("")
{
if(reg)
@@ -61,6 +62,21 @@ struct source_location
}
}
explicit source_location(const detail::region& reg)
: line_num_(static_cast<std::uint_least32_t>(std::stoul(reg.line_num()))),
column_num_(static_cast<std::uint_least32_t>(reg.before() + 1)),
region_size_(static_cast<std::uint_least32_t>(reg.size())),
file_name_(reg.name()),
line_str_ (reg.line())
{}
explicit source_location(const detail::location& loc)
: line_num_(static_cast<std::uint_least32_t>(std::stoul(loc.line_num()))),
column_num_(static_cast<std::uint_least32_t>(loc.before() + 1)),
region_size_(static_cast<std::uint_least32_t>(loc.size())),
file_name_(loc.name()),
line_str_ (loc.line())
{}
~source_location() = default;
source_location(source_location const&) = default;
source_location(source_location &&) = default;
@@ -83,5 +99,135 @@ struct source_location
std::string line_str_;
};
namespace detail
{
// internal error message generation.
inline std::string format_underline(const std::string& message,
const std::vector<std::pair<source_location, std::string>>& loc_com,
const std::vector<std::string>& helps = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
{
std::size_t line_num_width = 0;
for(const auto& lc : loc_com)
{
std::uint_least32_t line = lc.first.line();
std::size_t digit = 0;
while(line != 0)
{
line /= 10;
digit += 1;
}
line_num_width = (std::max)(line_num_width, digit);
}
// 1 is the minimum width
line_num_width = std::max<std::size_t>(line_num_width, 1);
std::ostringstream retval;
if(colorize)
{
retval << color::colorize; // turn on ANSI color
}
// XXX
// Here, before `colorize` support, it does not output `[error]` prefix
// automatically. So some user may output it manually and this change may
// duplicate the prefix. To avoid it, check the first 7 characters and
// if it is "[error]", it removes that part from the message shown.
if(message.size() > 7 && message.substr(0, 7) == "[error]")
{
retval << color::bold << color::red << "[error]" << color::reset
<< color::bold << message.substr(7) << color::reset << '\n';
}
else
{
retval << color::bold << color::red << "[error] " << color::reset
<< color::bold << message << color::reset << '\n';
}
const auto format_one_location = [line_num_width]
(std::ostringstream& oss,
const source_location& loc, const std::string& comment) -> void
{
oss << ' ' << color::bold << color::blue
<< std::setw(static_cast<int>(line_num_width))
<< std::right << loc.line() << " | " << color::reset
<< loc.line_str() << '\n';
oss << make_string(line_num_width + 1, ' ')
<< color::bold << color::blue << " | " << color::reset
<< make_string(loc.column()-1 /*1-origin*/, ' ');
if(loc.region() == 1)
{
// invalid
// ^------
oss << color::bold << color::red << "^---" << color::reset;
}
else
{
// invalid
// ~~~~~~~
const auto underline_len = (std::min)(
static_cast<std::size_t>(loc.region()), loc.line_str().size());
oss << color::bold << color::red
<< make_string(underline_len, '~') << color::reset;
}
oss << ' ';
oss << comment;
return;
};
assert(!loc_com.empty());
// --> example.toml
// |
retval << color::bold << color::blue << " --> " << color::reset
<< loc_com.front().first.file_name() << '\n';
retval << make_string(line_num_width + 1, ' ')
<< color::bold << color::blue << " |\n" << color::reset;
// 1 | key value
// | ^--- missing =
format_one_location(retval, loc_com.front().first, loc_com.front().second);
// process the rest of the locations
for(std::size_t i=1; i<loc_com.size(); ++i)
{
const auto& prev = loc_com.at(i-1);
const auto& curr = loc_com.at(i);
retval << '\n';
// if the filenames are the same, print "..."
if(prev.first.file_name() == curr.first.file_name())
{
retval << color::bold << color::blue << " ...\n" << color::reset;
}
else // if filename differs, print " --> filename.toml" again
{
retval << color::bold << color::blue << " --> " << color::reset
<< curr.first.file_name() << '\n';
retval << make_string(line_num_width + 1, ' ')
<< color::bold << color::blue << " |\n" << color::reset;
}
format_one_location(retval, curr.first, curr.second);
}
if(!helps.empty())
{
retval << '\n';
retval << make_string(line_num_width + 1, ' ');
retval << color::bold << color::blue << " |" << color::reset;
for(const auto& help : helps)
{
retval << color::bold << "\nHint: " << color::reset;
retval << help;
}
}
return retval.str();
}
} // detail
} // toml
#endif// TOML11_SOURCE_LOCATION_HPP

View File

@@ -22,21 +22,15 @@ namespace detail
// to show error messages. not recommended for users.
template<typename Value>
inline region_base const& get_region(const Value& v)
inline region_base const* get_region(const Value& v)
{
return *(v.region_info_);
return v.region_info_.get();
}
template<typename Value, typename Region>
void change_region(Value& v, Region&& reg)
template<typename Value>
void change_region(Value& v, region reg)
{
using region_type = typename std::remove_reference<
typename std::remove_cv<Region>::type
>::type;
std::shared_ptr<region_base> new_reg =
std::make_shared<region_type>(std::forward<region_type>(reg));
v.region_info_ = new_reg;
v.region_info_ = std::make_shared<region>(std::move(reg));
return;
}
@@ -46,8 +40,7 @@ throw_bad_cast(const std::string& funcname, value_t actual, const Value& v)
{
throw type_error(detail::format_underline(
concat_to_string(funcname, "bad_cast to ", Expected), {
{std::addressof(get_region(v)),
concat_to_string("the actual type is ", actual)}
{v.location(), concat_to_string("the actual type is ", actual)}
}), v.location());
}
@@ -74,8 +67,8 @@ throw_key_not_found_error(const Value& v, const key& ky)
// It actually points to the top-level table at the first character,
// not `[table]`. But it is too confusing. To avoid the confusion, the error
// message should explicitly say "key not found in the top-level table".
const auto& reg = get_region(v);
if(reg.line_num() == "1" && reg.size() == 1)
const auto loc = v.location();
if(loc.line() == 1 && loc.region() == 1)
{
// Here it assumes that top-level table starts at the first character.
// The region corresponds to the top-level table will be generated at
@@ -111,16 +104,14 @@ throw_key_not_found_error(const Value& v, const key& ky)
//
throw std::out_of_range(format_underline(concat_to_string(
"key \"", ky, "\" not found in the top-level table"), {
{std::addressof(reg), "the top-level table starts here"}
{loc, "the top-level table starts here"}
}));
}
else
{
// normal table.
throw std::out_of_range(format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(reg), "in this table"}
}));
"key \"", ky, "\" not found"), { {loc, "in this table"} }));
}
}
@@ -1056,96 +1047,97 @@ class basic_value
//
// Those constructors take detail::region that contains parse result.
template<typename Container>
basic_value(boolean b, detail::region<Container> reg)
basic_value(boolean b, detail::region reg, std::vector<std::string> cm)
: type_(value_t::boolean),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(std::move(cm))
{
assigner(this->boolean_, b);
}
template<typename T, typename Container, typename std::enable_if<
template<typename T, typename std::enable_if<
detail::conjunction<
std::is_integral<T>, detail::negation<std::is_same<T, boolean>>
>::value, std::nullptr_t>::type = nullptr>
basic_value(T i, detail::region<Container> reg)
basic_value(T i, detail::region reg, std::vector<std::string> cm)
: type_(value_t::integer),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(std::move(cm))
{
assigner(this->integer_, static_cast<integer>(i));
}
template<typename T, typename Container, typename std::enable_if<
template<typename T, typename std::enable_if<
std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
basic_value(T f, detail::region<Container> reg)
basic_value(T f, detail::region reg, std::vector<std::string> cm)
: type_(value_t::floating),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(std::move(cm))
{
assigner(this->floating_, static_cast<floating>(f));
}
template<typename Container>
basic_value(toml::string s, detail::region<Container> reg)
basic_value(toml::string s, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::string),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(std::move(cm))
{
assigner(this->string_, std::move(s));
}
template<typename Container>
basic_value(const local_date& ld, detail::region<Container> reg)
basic_value(const local_date& ld, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::local_date),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(std::move(cm))
{
assigner(this->local_date_, ld);
}
template<typename Container>
basic_value(const local_time& lt, detail::region<Container> reg)
basic_value(const local_time& lt, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::local_time),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(std::move(cm))
{
assigner(this->local_time_, lt);
}
template<typename Container>
basic_value(const local_datetime& ldt, detail::region<Container> reg)
basic_value(const local_datetime& ldt, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::local_datetime),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(std::move(cm))
{
assigner(this->local_datetime_, ldt);
}
template<typename Container>
basic_value(const offset_datetime& odt, detail::region<Container> reg)
basic_value(const offset_datetime& odt, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::offset_datetime),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(std::move(cm))
{
assigner(this->offset_datetime_, odt);
}
template<typename Container>
basic_value(const array_type& ary, detail::region<Container> reg)
basic_value(const array_type& ary, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::array),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(std::move(cm))
{
assigner(this->array_, ary);
}
template<typename Container>
basic_value(const table_type& tab, detail::region<Container> reg)
basic_value(const table_type& tab, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::table),
region_info_(std::make_shared<detail::region<Container>>(std::move(reg))),
comments_(region_info_->comments())
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(std::move(cm))
{
assigner(this->table_, tab);
}
template<typename T, typename Container, typename std::enable_if<
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T, value_type>::value,
std::nullptr_t>::type = nullptr>
basic_value(std::pair<T, detail::region<Container>> parse_result)
: basic_value(std::move(parse_result.first), std::move(parse_result.second))
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),
std::move(comments))
{}
// type checking and casting ============================================
@@ -1578,9 +1570,9 @@ class basic_value
{
throw std::out_of_range(detail::format_underline(
"toml::value::at(idx): no element corresponding to the index", {
{this->region_info_.get(),
concat_to_string("the length is ", this->as_array(std::nothrow).size(),
", and the specified index is ", idx)}
{this->location(), concat_to_string("the length is ",
this->as_array(std::nothrow).size(),
", and the specified index is ", idx)}
}));
}
return this->as_array().at(idx);
@@ -1596,9 +1588,9 @@ class basic_value
{
throw std::out_of_range(detail::format_underline(
"toml::value::at(idx): no element corresponding to the index", {
{this->region_info_.get(),
concat_to_string("the length is ", this->as_array(std::nothrow).size(),
", and the specified index is ", idx)}
{this->location(), concat_to_string("the length is ",
this->as_array(std::nothrow).size(),
", and the specified index is ", idx)}
}));
}
return this->as_array(std::nothrow).at(idx);
@@ -1668,7 +1660,7 @@ class basic_value
{
throw type_error(detail::format_underline(
"toml::value::size(): bad_cast to container types", {
{this->region_info_.get(),
{this->location(),
concat_to_string("the actual type is ", this->type_)}
}), this->location());
}
@@ -1718,10 +1710,10 @@ class basic_value
// for error messages
template<typename Value>
friend region_base const& detail::get_region(const Value& v);
friend region_base const* detail::get_region(const Value& v);
template<typename Value, typename Region>
friend void detail::change_region(Value& v, Region&& reg);
template<typename Value>
friend void detail::change_region(Value& v, detail::region reg);
private:
@@ -1926,10 +1918,8 @@ inline std::string format_error(const std::string& err_msg,
std::vector<std::string> hints = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
{
return detail::format_underline(err_msg,
std::vector<std::pair<detail::region_base const*, std::string>>{
{std::addressof(detail::get_region(v)), comment}
}, std::move(hints), colorize);
return detail::format_underline(err_msg, {{v.location(), comment}},
std::move(hints), colorize);
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
@@ -1939,10 +1929,8 @@ inline std::string format_error(const std::string& err_msg,
std::vector<std::string> hints = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
{
return detail::format_underline(err_msg,
std::vector<std::pair<detail::region_base const*, std::string>>{
{std::addressof(detail::get_region(v1)), comment1},
{std::addressof(detail::get_region(v2)), comment2}
return detail::format_underline(err_msg, {
{v1.location(), comment1}, {v2.location(), comment2}
}, std::move(hints), colorize);
}
@@ -1954,11 +1942,8 @@ inline std::string format_error(const std::string& err_msg,
std::vector<std::string> hints = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
{
return detail::format_underline(err_msg,
std::vector<std::pair<detail::region_base const*, std::string>>{
{std::addressof(detail::get_region(v1)), comment1},
{std::addressof(detail::get_region(v2)), comment2},
{std::addressof(detail::get_region(v3)), comment3}
return detail::format_underline(err_msg, {{v1.location(), comment1},
{v2.location(), comment2}, {v3.location(), comment3}
}, std::move(hints), colorize);
}