Compare commits

...

146 Commits

Author SHA1 Message Date
ToruNiina
c3cb22a789 chore: fix example of err msg by re-running sample 2019-03-21 18:12:35 +09:00
ToruNiina
5aebd6b562 fix: restore the back compat of format_error
the following code was okay in the last release
```
toml::format_error("[test]", v, "test", {"hint1", "hint2"})
```
but was not okay in the current master. This commit fixes this.

cons: By this, the number of values to show is limited upto 3.
2019-03-20 20:46:22 +09:00
ToruNiina
4c13085b35 fix: add stream operator for toml::table 2019-03-20 19:30:08 +09:00
ToruNiina
8709e8a14e chore: fix incorrect syntax highlight in README 2019-03-20 19:29:03 +09:00
ToruNiina
9eea46ec01 chore: fix typoes and broken links in README 2019-03-20 12:06:55 +09:00
ToruNiina
2e9f937c43 chore: update README 2019-03-20 11:53:03 +09:00
Toru Niina
65b10b6537 Merge pull request #46 from ToruNiina/toml-literal
feat: add ""_toml literal
2019-03-20 10:12:56 +09:00
ToruNiina
b51a8d5966 fix: add missing include file in test code 2019-03-20 00:58:58 +09:00
Toru Niina
55e3d70869 Merge pull request #47 from ToruNiina/format-table
`toml::format` and top-level table
2019-03-20 00:49:59 +09:00
ToruNiina
20ba57e389 fix: add missing const specifier to some of get()s 2019-03-20 00:37:13 +09:00
ToruNiina
39bc3c64fe test: add test for ""_toml literals 2019-03-20 00:36:46 +09:00
ToruNiina
40ccf1d912 feat: add argument to control top-level inlinization 2019-03-19 23:25:26 +09:00
ToruNiina
982ae36428 feat: add ""_toml literal 2019-03-19 21:34:57 +09:00
ToruNiina
d6714ec450 feat: detect value type and format as a file
in toml::format
2019-03-19 21:24:51 +09:00
ToruNiina
773c3816be ci: remove needless confirmation on CI 2019-03-19 19:39:45 +09:00
Toru Niina
1b417ddc7a Merge pull request #45 from ToruNiina/get_or
fix `get_or` and add `find_or`
2019-03-19 01:41:21 +09:00
ToruNiina
7a0ecf977d feat: add find_or(table, key, fallback)
get_or(value, fallback) is still ok, but get_or(table, key, fallback)
is now deprecated.
2019-03-18 17:44:03 +09:00
ToruNiina
aade704411 refactor: remove needless overload of get_or 2019-03-18 17:10:18 +09:00
ToruNiina
ca3f6102ef fix: correctly resolve overloads of get_or 2019-03-18 16:44:36 +09:00
ToruNiina
4a58b629ce feat: add a way to check arg is "string literal" 2019-03-18 16:31:12 +09:00
ToruNiina
3adba237b8 feat: enable to show message for deprecated() 2019-03-18 16:28:27 +09:00
Toru Niina
ccf03d9291 Merge pull request #44 from ToruNiina/test-link
test: add test for multiple translation unit
2019-03-18 15:20:04 +09:00
Toru Niina
30ae90ebd5 Merge pull request #43 from ToruNiina/hotfix
fix: incorrect move in constructor and return type deducing in toml::visit
2019-03-18 14:08:23 +09:00
ToruNiina
d5369c3429 test: add test for multiple translation unit 2019-03-18 12:39:58 +09:00
Toru Niina
48f2f0555d Merge pull request #42 from ToruNiina/test-suite
run toml-test suite
2019-03-18 12:33:48 +09:00
ToruNiina
f40fd12e25 refactor: add and rewrite comments 2019-03-18 11:09:12 +09:00
ToruNiina
65c2c3c238 fix: correctly deduce return value of visitor 2019-03-18 10:53:04 +09:00
ToruNiina
891a61a5e3 fix: do not move array element without checking 2019-03-18 02:05:55 +09:00
ToruNiina
1e6f30f6fa chore: update README.md 2019-03-18 01:50:23 +09:00
ToruNiina
02346a3126 Merge branch 'master' into test-suite 2019-03-18 01:40:17 +09:00
Toru Niina
1908f18e95 Merge pull request #41 from ToruNiina/hotfix
fix: simplify and correct the format of timezone
2019-03-18 01:39:11 +09:00
ToruNiina
3bfa7f09ba test: use the test suite in the effective way
add tests/check_toml_test.cpp to compare json object
2019-03-18 01:36:43 +09:00
ToruNiina
243f43fafd Merge branch 'master' into hotfix 2019-03-17 21:16:37 +09:00
ToruNiina
66e27a94b6 fix: simplify and correct the format of timezone 2019-03-17 21:14:17 +09:00
ToruNiina
227688ec63 ci: make result clearer a bit 2019-03-17 19:36:23 +09:00
ToruNiina
e761a503c0 ci: fix silly mistake in circleci script 2019-03-17 19:27:58 +09:00
ToruNiina
209ad79a8f ci: fix config file of circleci 2019-03-17 19:26:22 +09:00
ToruNiina
cdf209d7f6 ci: show the status on CI 2019-03-17 19:23:52 +09:00
ToruNiina
77ab391885 ci: fix name of directory and add test script 2019-03-17 19:20:24 +09:00
ToruNiina
6628fe5ace test: add language agnostic toml-test 2019-03-17 19:12:13 +09:00
Toru Niina
f3e3000d45 Merge pull request #40 from ToruNiina/remove-to-toml
refactor: remove to_toml and related tests
2019-03-17 13:12:30 +09:00
Toru Niina
f7380c6e32 Merge pull request #39 from ToruNiina/throw-incorrect-unicode
Throw syntax_error when parser encounter an incorrect utf-8 codepoint
2019-03-17 13:12:16 +09:00
Toru Niina
d86870e038 Merge pull request #38 from ToruNiina/get-any-type
extended conversions
2019-03-17 13:11:59 +09:00
Toru Niina
0908806915 Merge pull request #33 from ToruNiina/is-something
add `is_boolean` and other stuffs like that
2019-03-16 23:55:01 +09:00
ToruNiina
d17c192681 refactor: remove to_toml and related tests 2019-03-16 17:05:58 +09:00
ToruNiina
cad8f51256 doc: add explanation of conversions to README 2019-03-16 16:56:37 +09:00
ToruNiina
43014c6619 fix: remove redefined default template argument 2019-03-16 16:24:10 +09:00
ToruNiina
30a41aa710 fix: use older style in BOOST_TEST 2019-03-16 16:15:01 +09:00
ToruNiina
04bfeba3f2 merge branch master into get-any-type 2019-03-16 15:58:18 +09:00
ToruNiina
190636b791 fix: support getting a container of external types 2019-03-16 15:52:22 +09:00
ToruNiina
31e450f9af test: add test for from/into based conversions 2019-03-16 15:46:21 +09:00
ToruNiina
b1b72a94a8 feat: support conversion with external types 2019-03-16 14:44:04 +09:00
ToruNiina
6929bcdf78 feat: add from<T> and into<T> 2019-03-16 14:27:05 +09:00
ToruNiina
fd063af7ce refactor: make include guard style uniform 2019-03-16 14:19:47 +09:00
ToruNiina
df6dcbc4ed feat: check a class has from/into_toml member fn
to support better serialization
2019-03-16 14:16:31 +09:00
ToruNiina
9b8db6a225 fix: remove extraneous null character after float
the bug was introduced by snprintf
2019-03-15 19:30:36 +09:00
ToruNiina
76863cb27f refactor: simplify branches about utf8 codepoint 2019-03-15 17:48:47 +09:00
ToruNiina
514df99e40 feat: consider invalid UTF-8 as syntax_error
the following codepoints are considered to be a syntax_error
- [0xD800, 0xDFFF]
- larger than 0x10FFFF
2019-03-15 17:39:31 +09:00
ToruNiina
055353a460 chore: merge branch 'master' into is-something 2019-03-15 17:25:17 +09:00
Toru Niina
9eb4008d6d Merge pull request #37 from ToruNiina/to-toml-deprecated
feat: mark to_toml as deprecated
2019-03-15 17:12:45 +09:00
ToruNiina
a04544637b feat: mark to_toml as deprecated
because the constructor of `toml::value()` supports all the stuff that
are supported by `to_toml`.
2019-03-15 14:29:32 +09:00
Toru Niina
4c7dc17b78 Merge pull request #36 from ToruNiina/refactor-format-underline
refactor: remove redundant function overload
2019-03-15 13:41:16 +09:00
ToruNiina
59aaaab436 test: add test to check format_error compiles 2019-03-15 12:40:01 +09:00
ToruNiina
61dfa4a2dc feat: format any number of values into an err msg
```cpp
toml::format_error("[error] message", v1, "v1", v2, "v2", ...);
```
2019-03-15 12:38:37 +09:00
ToruNiina
ca337a1110 chore: merge branch 'master' into travis-ci 2019-03-14 23:02:04 +09:00
ToruNiina
510e10de95 ci: test numerous compilers on CI 2019-03-14 22:58:44 +09:00
ToruNiina
0babe8d589 fix: use format_underline for N regions everywhere 2019-03-14 00:59:10 +09:00
ToruNiina
5b2ce26721 refactor: remove redundant function
since N region format_underline() has been implemented, overloads for 1
and 2 region(s) are not needed.
2019-03-14 00:56:35 +09:00
Toru Niina
db4d99cd4f Merge pull request #35 from ToruNiina/suppress-warning
fix: suppress warning about sign-unsign comparison
2019-03-13 15:01:51 +09:00
ToruNiina
74ceceef73 fix: suppress warning about sign-unsign comparison
The solution is not ideal, but it's okay at the line
2019-03-13 14:03:04 +09:00
Toru Niina
360e890cc0 Merge pull request #34 from ToruNiina/consider-locale
fix: use snprintf instead of stringstream
2019-03-13 09:56:32 +09:00
ToruNiina
46b35870c5 style: remove needless type casting 2019-03-13 01:17:27 +09:00
ToruNiina
dddcecb034 fix: use snprintf instead of stringstream
to avoid the effect of locale
2019-03-12 23:37:46 +09:00
ToruNiina
c4c416e8b2 doc: add is_* function to README 2019-03-12 22:18:25 +09:00
ToruNiina
6693ec78f4 test: add test for toml::value::is_something() 2019-03-12 20:44:27 +09:00
ToruNiina
dc112bd6c1 feat: add is_[boolean|integer|...]() member func
it is an alias to is<toml::value_t::[Boolean|Integer|...]>
2019-03-12 20:43:07 +09:00
ToruNiina
084e82a8a9 chore: update README 2019-03-07 14:09:02 +09:00
Toru Niina
f5079a7892 Merge pull request #32 from ToruNiina/allow-deeper-table-before
Allow deeper table before
2019-03-06 12:03:14 +09:00
ToruNiina
d90ffb63c6 Merge branch 'master' into allow-deeper-table-before 2019-03-05 23:27:11 +09:00
ToruNiina
b0ed122214 fix: allow deeper table appeared before
allow the following toml file.
```toml
[a.b.c]
d = 10
[a]
e = 2.718
```
2019-03-05 23:25:25 +09:00
ToruNiina
d88521d63c feat: enable to change region of value
To allow the following toml file, we need to replace the region after
the more precise region is found.
```toml
[a.b.c]
d = 42
[a]
e = 2.71
```
If the precise region (here, [a]) is found, the region of `a` should be
`[a]`, not `[a.b.c]`. After `[a]` is defined, toml does not allow to
write `[a]` twice. To check it, we need to replace the region of values
to the precise one.
2019-03-04 15:01:28 +09:00
ToruNiina
2accc9d22c fix: diagnose, but not throw for unicode error
in 2.0.x and 2.1.0, README says "it shows warning" for invalid unicode
codepoints. So far, this library just show an error message in stderr
for this case. It is not good to change the behavior fatal in the next
minor release, 2.1.1, that includes patches and improved error msgs.
I will make it throw syntax_error after 2.2.0 for invalid unicode
codepoints. For now, I will keep it to be "warning".
2019-03-03 18:56:45 +09:00
Toru Niina
363927f489 Merge pull request #31 from ToruNiina/err-msg-inhomogenous-array
improve error messages for invalid arrays
2019-03-02 23:07:29 +09:00
ToruNiina
ae793fb631 feat: improve error message for invalid array 2019-03-02 17:56:16 +09:00
ToruNiina
944b83642a feat: make location to inherit region_base
To generate error message, it is better to have the same interface.
Also, location can be considered as a region having only one character.
2019-03-02 17:52:00 +09:00
Toru Niina
5a8e5dee73 Merge pull request #30 from ToruNiina/hotfix
small patches for parser
2019-03-02 16:11:31 +09:00
ToruNiina
7f870d5861 fix: diagnose invalid UTF-8 codepoints 2019-03-02 01:57:05 +09:00
ToruNiina
536b23dc84 fix: allow empty table in the middle of a file 2019-03-01 22:53:16 +09:00
ToruNiina
0c9806e99f fix: diagnose key after [table.key] pattern
the following is not a valid toml format.
```
[table] key = "value"
```
this commit enables to diagnose that pattern.
2019-03-01 22:37:52 +09:00
ToruNiina
5a92932019 fix: disallow invalid escape sequence 2019-03-01 22:13:32 +09:00
ToruNiina
e929d2f00f fix: allow empty input file (to be an empty table) 2019-02-27 12:30:57 +09:00
Toru Niina
1e1e4c06e8 Merge pull request #29 from ToruNiina/err-msg-dotted-key
Error message for getting values corresponds to dotted keys
2019-02-27 12:21:03 +09:00
ToruNiina
d0726db473 chore: merge branch master into err-msg-dotted-key 2019-02-27 01:26:36 +09:00
Toru Niina
4cdc15a824 Merge pull request #28 from xaxousis/master
Add location to error string in parse_* functions
2019-02-27 01:25:52 +09:00
ToruNiina
b36fdf2f54 refactor: remove internal fn not needed any more
The function was needed to copy region information from value to value,
for a useful error message. Because of the last few commits, the region
information about keys are passed to insert_nested_keys that requires
the function which is removed. And it turned out that the function is no
longer required. It is originally a workaround, so I removed it.
2019-02-27 01:07:00 +09:00
ToruNiina
73ba6b385f feat: use key-region to represent table
use x.y.z = 42
    ~~~ here as the region of the table `x.y`
2019-02-27 01:02:39 +09:00
ToruNiina
30d1639aa4 refactor: return region info from parse_kvpair 2019-02-27 00:21:05 +09:00
Quentin Khan
d82814fc86 Add location to error string in parse_* functions 2019-02-26 14:56:58 +01:00
ToruNiina
2220efd682 chore: show appvayor status of master branch
other branches might be unstable, so they might fail. It is good to show
the status of the stable branch, rather than the experimental branches.
2019-02-26 00:26:04 +09:00
ToruNiina
679b365cf7 feat: get region info when parsing keys
Error messages related to dotted keys looks weird. like:
 1 | a.b.c = 42
   |         ~~ in this table
The underlined token is not a table. This should be like the following.
 1 | a.b.c = 42
   | ~~~ in this table
To implement this, the region information is needed when the keys are
read. This commit add this functionality, though currently the region
information is not used yet.
2019-02-26 00:17:28 +09:00
ToruNiina
83bf83b6dd style: add braces to if and remove additional else 2019-02-19 02:56:15 +09:00
ToruNiina
321364c7c2 fix: format char in an error message correctly 2019-02-19 02:46:48 +09:00
ToruNiina
d8707d5867 chore: fix README 2019-02-17 00:22:19 +09:00
ToruNiina
2dd0a78c52 fix: reset stream width before printing
without this, the first line of the serialized result becomes too wide
2019-02-16 23:55:19 +09:00
Toru Niina
d7b8c3c78f Merge pull request #18 from ToruNiina/threadsafe-localtime
add threadsafe localtime_(s|r)
2019-02-16 23:28:48 +09:00
Toru Niina
2f0148a2df Merge pull request #26 from ToruNiina/serialize
add serializer
2019-02-16 23:27:05 +09:00
ToruNiina
4accc29984 chore: update README 2019-02-14 16:47:15 +09:00
ToruNiina
19b9af2494 Merge branch 'master' into serialize 2019-02-14 16:34:45 +09:00
ToruNiina
0aa50e9439 style: just add newlines to README 2019-02-14 16:26:48 +09:00
ToruNiina
a00a906482 fix: add comma at correct position 2019-02-14 16:17:32 +09:00
ToruNiina
19ad7d7c96 fix: remove needless empty line from serialization 2019-02-14 16:17:04 +09:00
ToruNiina
251e55da42 fix: don't ignore std::setw(0) 2019-02-14 15:49:27 +09:00
ToruNiina
32f1b2060a fix: avoid width overflow 2019-02-14 15:49:13 +09:00
ToruNiina
b1c54532df feat: improve array serialization
- make multiline array more clean
- short-circuit for empty array
2019-02-14 15:48:05 +09:00
ToruNiina
38c67f16e8 fix: initialize float precition correctly 2019-02-14 15:47:00 +09:00
ToruNiina
24aefc52a1 test: set width in test_serialize 2019-02-14 15:46:12 +09:00
ToruNiina
ba8c205253 fix: change CRLF into LF before comparison 2019-02-13 23:48:53 +09:00
ToruNiina
31193d99ba Merge branch 'master' into serialize 2019-02-13 23:16:39 +09:00
ToruNiina
c4aecc8e4b chore: update README badges 2019-02-13 22:36:29 +09:00
Toru Niina
60c81d06a0 Merge pull request #25 from ToruNiina/hotfix
fix: open file as binary-mode #16
2019-02-13 21:14:15 +09:00
ToruNiina
46569da231 fix: avoid auto-conversion while making test case 2019-02-13 19:51:54 +09:00
ToruNiina
5e20a8ff16 fix: add scope to the test case to flush 2019-02-13 19:26:52 +09:00
ToruNiina
dd9319245e fix: open file as binary-mode #16
to avoid inconsistency between file size (obtained by tellg) and the
size of the actual contents that would be read later
2019-02-13 19:18:09 +09:00
ToruNiina
4bbe42d105 test: add test_serialize_file 2019-02-13 13:51:36 +09:00
ToruNiina
5bdc022627 fix: correctly serialize quoted keys 2019-02-13 13:51:08 +09:00
ToruNiina
41e354f1ee supress warnings while skipping switch-cases 2019-02-13 13:50:33 +09:00
ToruNiina
d1c76709b0 add serializer #23 2019-02-13 13:37:58 +09:00
ToruNiina
64774a8db0 add toml::visit to use it in serializer 2019-02-13 13:36:55 +09:00
ToruNiina
53f6b8268b fix: compare offset_datetime correctly 2019-02-13 13:34:26 +09:00
ToruNiina
32dcc35918 move return_type_of_t from result to traits 2019-02-13 13:34:03 +09:00
ToruNiina
8c3854b28b update README 2019-01-31 15:37:25 +09:00
Toru Niina
75af9c79df Merge pull request #22 from xaxousis/master
Fix multiple definition error
2019-01-31 01:34:33 +09:00
Quentin Khan
1dfe32acd8 Fix multiple definition error 2019-01-30 17:06:23 +01:00
Toru Niina
5dfdbe4bff Merge pull request #20 from ToruNiina/format-error
add an extra parameter `hints` to format_error
2018-12-27 20:34:53 +09:00
Toru Niina
4584eeb57a Merge pull request #19 from ToruNiina/find-default-type
add default template arg to toml::find
2018-12-27 20:34:36 +09:00
ToruNiina
aa67069387 move hints to the internal function 2018-12-27 16:32:20 +09:00
ToruNiina
ee3424ad51 add an extra parameter hints to format_error 2018-12-27 16:26:23 +09:00
ToruNiina
17def14ab6 add default template arg to toml::find
in most of the use cases, toml::value is used (to show error message).
2018-12-27 15:58:50 +09:00
ToruNiina
51dd3abcae remove one branch by preprocessor
since localtime in windows is already thread-safe, there are no need to
change the function.
2018-12-26 13:38:01 +09:00
ToruNiina
825b2c30a1 add threadsafe localtime_(s|r) 2018-12-25 22:40:52 +09:00
Toru Niina
b5b8830c29 Merge pull request #17 from ToruNiina/hotfix
fix the error with BOM and end of file w/o newline
2018-12-24 16:37:10 +09:00
ToruNiina
87a5c844c2 add test cases for the end-of-file problems 2018-12-24 16:02:32 +09:00
ToruNiina
11c7ee4501 fix the case of file w/o newline at the end
toml::parse failed with the file that contains whitespace or comment at
the end of file without newline. this commit fixes the error.
2018-12-24 16:00:33 +09:00
ToruNiina
d24a188d4c fix the error while reading BOM.
remove possible UB because of the use-after-move.
2018-12-24 15:06:26 +09:00
Toru Niina
29876221f8 Merge pull request #15 from ToruNiina/performance
speedup by removing needless format_underline
2018-12-23 18:30:19 +09:00
ToruNiina
7c03c446fe speedup by removing needless format_underline
drastical speedup for long toml files
2018-12-23 15:22:12 +09:00
38 changed files with 3942 additions and 1021 deletions

59
.circleci/config.yml Normal file
View File

@@ -0,0 +1,59 @@
version: 2.1
jobs:
test_suite:
environment:
- GOPATH: /home/circleci/go
docker:
- image: circleci/golang:1.9
steps:
- checkout
- run:
command: |
g++ --version
cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Werror -I../ check_toml_test.cpp -o check_toml_test
go get github.com/BurntSushi/toml-test
$GOPATH/bin/toml-test ./check_toml_test
output_result:
docker:
- image: circleci/buildpack-deps:bionic
steps:
- checkout
- run:
command: |
g++ --version
cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Werror -I../ check.cpp -o check
git clone https://github.com/BurntSushi/toml-test.git
cp check toml-test/tests/invalid
cp check toml-test/tests/valid
cd toml-test/tests/invalid
for f in $(ls ./*.toml);
do echo "==> ${f}";
cat ${f};
echo "---------------------------------------";
./check ${f} invalid;
if [ $? -ne 0 ] ; then
exit 1
fi
echo "=======================================";
done
cd ../valid
for f in $(ls ./*.toml);
do echo "==> ${f}";
cat ${f};
echo "---------------------------------------";
./check ${f} valid;
if [ $? -ne 0 ] ; then
exit 1
fi
echo "=======================================";
done
workflows:
version: 2.1
test:
jobs:
- test_suite
- output_result

View File

@@ -12,8 +12,39 @@ matrix:
- ubuntu-toolchain-r-test
packages:
- g++-5
- build-essential
- cmake
- libboost-all-dev
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-6"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-6
- libboost-all-dev
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-7"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-7
- libboost-all-dev
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-8
- libboost-all-dev
- os: linux
language: cpp
@@ -26,8 +57,66 @@ matrix:
- llvm-toolchain-precise-3.7
packages:
- clang-3.7
- build-essential
- cmake
- libboost-all-dev
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-4.0"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-4.0
packages:
- clang-4.0
- libboost-all-dev
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-5.0"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-5.0
packages:
- clang-5.0
- libboost-all-dev
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-6.0"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-6.0
packages:
- clang-6.0
- libboost-all-dev
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-7"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-7
packages:
- clang-7
- libboost-all-dev
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-8
packages:
- clang-8
- libboost-all-dev
- os: osx
language: cpp

View File

@@ -1,59 +0,0 @@
### encoding user's data
You can encode your data to toml format.
```cpp
const toml::value integer(1);
const toml::value array{3.1, 3.14, 3.141, 3.1415};
const toml::value table{{"answer", 42}, {"pi", 3.14}, {"string", "foobar"}};
std::cout << toml::format("integer", integer) << std::endl;
std::cout << toml::format("array", array) << std::endl;
std::cout << toml::format("table", table) << std::endl;
```
this program will output as below.
```toml
integer = 1
array = [3.1, 3.14, 3.141, 3.1415]
[table]
answer = 42
pi = 3.14
string = "foobar"
```
Without key name, you can make string formatted as toml.
```cpp
const std::string integer_ = toml::format(integer); // "1"
const std::string array_ = toml::format(array); // "[3.1, 3.14, 3.141, 3.1415]"
const std::string table_ = toml::format(table); // "answer = 42\npi=3.14\nstring=foobar"
```
### inlinize
You can make `toml::Table` inline.
```cpp
const toml::value table{{"answer", 42}, {"pi", 3.14}, {"string", "foobar"}};
// if the inline-table format length is less than 80, the table will be inlined
std::cout << toml::format("table", table, toml::make_inline(80)) << std::endl;
// In any case, the table will be inlined.
std::cout << toml::format("table", table, toml::forceinline) << std::endl;
```
```toml
table = {answer = 42, pi = 3.14, string = "foobar"}
```
And there are some stream manipulators for toml format.
```cpp
const toml::value table{{"answer", 42}, {"pi", 3.14}, {"string", "foobar"}};
// if the inline-table format length is less than 80, the table will be inlined
std::cout << toml::make_inline(80) << table << std::endl;
// In any case, the table will be inlined.
std::cout << toml::forceinline << table << std::endl;
```

674
README.md
View File

@@ -1,20 +1,53 @@
toml11
======
[![Build Status](https://travis-ci.org/ToruNiina/toml11.svg?branch=master)](https://travis-ci.org/ToruNiina/toml11)
[![Build status](https://ci.appveyor.com/api/projects/status/m2n08a926asvg5mg?svg=true)](https://ci.appveyor.com/project/ToruNiina/toml11)
[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
[![Build Status on TravisCI](https://travis-ci.org/ToruNiina/toml11.svg?branch=master)](https://travis-ci.org/ToruNiina/toml11)
[![Build status on Appveyor](https://ci.appveyor.com/api/projects/status/m2n08a926asvg5mg/branch/master?svg=true)](https://ci.appveyor.com/project/ToruNiina/toml11/branch/master)
[![Build status on CircleCI](https://circleci.com/gh/ToruNiina/toml11/tree/master.svg?style=svg)](https://circleci.com/gh/ToruNiina/toml11/tree/master)
[![Version](https://img.shields.io/github/release/ToruNiina/toml11.svg?style=flat)](https://github.com/ToruNiina/toml11/releases)
[![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136)
c++11 header-only toml parser depending only on c++ standard library.
toml11 is a C++11 header-only toml parser/encoder depending only on C++ standard library.
compatible to the latest version of [TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md) after version 2.0.0.
compatible to the latest version of
[TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md)
after version 2.0.0.
Are you looking for pre-C++11 compatible toml parser? Try [Boost.toml](https://github.com/ToruNiina/Boost.toml)! It has almost the same functionality as this library and works with C++98 & Boost.
It passes [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test).
Not only the test suite itself, a TOML reader/encoder also runs on [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
You can see the error messages about invalid files and serialization results of valid files at
[CircleCI](https://circleci.com/gh/ToruNiina/toml11).
## How to use
## Table of Contents
## Installation
- [Integration](#integration)
- [Decoding a toml file](#decoding-a-toml-file)
- [In the case of syntax error](#in-the-case-of-syntax-error)
- [Getting a toml value](#getting-a-toml-value)
- [In the case of type error](#in-the-case-of-type-error)
- [Getting an array](#getting-an-array)
- [Getting a table](#getting-a-table)
- [Dotted keys](#dotted-keys)
- [Getting an array of tables](#getting-an-array-of-tables)
- [Cost of conversion](#cost-of-conversion)
- [Getting datetime and its variants](#getting-datetime-and-its-variants)
- [Getting with a fallback](#getting-with-a-fallback)
- [Expecting conversion](#expecting-conversion)
- [Finding a value from a table](#finding-a-value-from-a-table)
- [Checking value type](#checking-value-type)
- [Visiting a toml::value](#visiting-a-tomlvalue)
- [TOML literal](#toml-literal)
- [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types)
- [Invalid UTF-8 Codepoints](#invalid-utf-8-codepoints)
- [Formatting user-defined error messages](#formatting-user-defined-error-messages)
- [Serializing TOML data](#serializing-toml-data)
- [Underlying types](#underlying-types)
- [Running Tests](#running-tests)
- [Contributors](#contributors)
- [Licensing Terms](#licensing-terms)
## Integration
Just include the file after adding it to the include path.
@@ -29,32 +62,40 @@ int main()
}
```
### Decoding toml file
## Decoding a toml file
The only thing you have to do is to pass a filename to the `toml::parse` function.
To parse a toml file, the only thing you have to do is
to pass a filename to the `toml::parse` function.
```cpp
const std::string fname("sample.toml");
const toml::table data = toml::parse(fname);
```
In the case of file open error, it will throw `std::runtime_error`.
If it encounters a file open error, it will throw `std::runtime_error`.
You can also pass a `stream` to the `toml::parse` function after checking the status.
You can also pass a `std::istream` to the `toml::parse` function.
To show a filename in an error message, it is recommended to pass the filename
with the stream.
```cpp
std::ifstream ifs("sample.toml");
std::ifstream ifs("sample.toml", std::ios_base::binary);
assert(ifs.good());
const auto data = toml::parse(ifs /*, "filename" (optional)*/);
const auto data = toml::parse(ifs, /*optional*/ "sample.toml");
```
To show a better error message, it is recommended to pass a filename with `istream`. See also [in the case of syntax error](#in-the-case-of-syntax-error) and [passing invalid type to toml::get](#passing-invalid-type-to-tomlget).
Note that on Windows, if a file is opened in text-mode, CRLF ("\r\n") will
automatically be converted to LF ("\n") and this causes inconsistency between
file size and the contents that would be read. This causes weird error.
To use a file stream with `toml::parse` on Windows, don't forget to open it
in binary mode.
### In the case of syntax error
If there is a syntax error in a toml file, `toml::parse` will throw `toml::syntax_error`.
toml11 now has clean and informative error messages inspired by Rust and it looks like the following (comment after hash sign are actually not shown).
toml11 has clean and informative error messages inspired by Rust and
it looks like the following.
```console
terminate called after throwing an instance of 'toml::syntax_error'
@@ -64,7 +105,8 @@ terminate called after throwing an instance of 'toml::syntax_error'
| ^------ expected newline, but got '='. # error reason
```
If you (mistakenly) duplicate tables and got an error, you may want to see where the other is. toml11 shows both at the same time.
If you (mistakenly) duplicate tables and got an error, it is helpful to see
where they are. toml11 shows both at the same time like the following.
```console
terminate called after throwing an instance of 'toml::syntax_error'
@@ -77,13 +119,17 @@ terminate called after throwing an instance of 'toml::syntax_error'
| ~~~~~~~ table defined twice
```
Since the error message generation is generally a difficult task, the current status is not ideal. toml11 needs your help. If you encounter a weird error message, please let us know and contribute to improve the quality!
Since the error message generation is generally a difficult task, the current
status is not ideal. If you encounter a weird error message, please let us know
and contribute to improve the quality!
### Getting a toml value
## Getting a toml value
After parsing successfully, you can obtain the values from the result of `toml::parse` (here, `data`) using `toml::get` function.
After parsing successfully, you can obtain the values from the result of
`toml::parse` using `toml::get` function.
```toml
# sample.toml
answer = 42
pi = 3.14
numbers = [1,2,3]
@@ -93,6 +139,7 @@ key = "value"
```
``` cpp
const auto data = toml::parse("sample.toml");
const auto answer = toml::get<std::int64_t >(data.at("answer"));
const auto pi = toml::get<double >(data.at("pi"));
const auto numbers = toml::get<std::vector<int>>(data.at("numbers"));
@@ -101,18 +148,26 @@ const auto tab = toml::get<toml::Table>(data.at("tab"));
const auto key = toml::get<std::string>( tab.at("key"));
```
When you pass an exact TOML type that does not require type conversion, `toml::get` returns also a reference through which you can modify the content.
When you pass an exact TOML type that does not require type conversion,
`toml::get` returns a reference through which you can modify the content.
```cpp
toml::get<toml::integer>(data["answer"]) = 6 * 9;
auto data = toml::parse("sample.toml");
auto& answer = toml::get<toml::integer>(data["answer"]); // get reference
answer = 6 * 9; // write to data.answer
std::cout << toml::get<int>(data.at("answer")) << std::endl; // 54
```
If the specified type requires conversion, you can't take a reference to the value. See also [underlying types](#underlying-types).
If the specified type requires conversion, you can't take a reference to the value.
See also [underlying types](#underlying-types).
#### Passing invalid type to toml::get
NOTE: To enable to get a reference, conversions between Float and Integer are not supported.
If you choose the invalid type, `toml::type_error` will be thrown. Similar to the `syntax_error`, toml11 also displays informative error messages. The error message when you choose `int` to get `string` value would be like this.
### In the case of type error
If you pass an invalid type to `toml::get`, `toml::type_error` will be thrown.
Similar to the case of syntax error, toml11 also displays clean error messages.
The error message when you choose `int` to get `string` value would be like this.
```console
terminate called after throwing an instance of 'toml::type_error'
@@ -122,13 +177,21 @@ terminate called after throwing an instance of 'toml::type_error'
| ~~~~~~~~~~~~~~ the actual type is string
```
NOTE: In order to show this kind of error message, all the toml values have 1 shared_ptr that points the corresponding byte sequence and 2 iterators that point the range. It is recommended to destruct all the `toml::value` classes after configuring your application to save memory resources.
NOTE: In order to show this kind of error message, all the toml values have
pointers to represent its range in a file. The entire contents of a file is
shared by `toml::value`s and remains on the heap memory. It is recommended to
destruct all the `toml::value` classes after configuring your application
if you have a large TOML file compared to the memory resource.
### Getting arrays
## Getting an array
You can set any kind of `container` class to obtain a `toml::array` except for `map`-like classes.
You can get any kind of `container` class from a `toml::array`
except for `map`-like classes.
``` cpp
// # sample.toml
// numbers = [1,2,3]
const auto vc = toml::get<std::vector<int> >(data.at("numbers"));
const auto ls = toml::get<std::list<int> >(data.at("numbers"));
const auto dq = toml::get<std::deque<int> >(data.at("numbers"));
@@ -137,16 +200,16 @@ const auto ar = toml::get<std::array<int, 3>>(data.at("numbers"));
// it will throw toml::type_error because std::array is not resizable.
```
Surprisingly, you can also get a `toml::array` as `std::pair` and `std::tuple.`
Surprisingly, you can also get `std::pair`s and `std::tuple`s from `toml::array`.
```cpp
const auto tp = toml::get<std::tuple<short, int, unsigned int>>(data.at("numbers"));
```
The case when you need this functionality is to get an array of arrays.
This functionality is helpful when you have the following toml file.
```toml
aofa = [[1,2,3], ["foo", "bar", "baz"]] # toml allows this
array_of_arrays = [[1, 2, 3], ["foo", "bar", "baz"]] # toml allows this
```
What is the corresponding C++ type? Obviously, it is a `std::pair` of `std::vector`s.
@@ -154,23 +217,28 @@ What is the corresponding C++ type? Obviously, it is a `std::pair` of `std::vect
```cpp
const auto aofa = toml::get<
std::pair<std::vector<int>, std::vector<std::string>>
>(data.at("aofa"));
>(data.at("array_of_arrays"));
```
If you don't know what the type is inside the array, you can use `toml::array`, which is a `std::vector` of `toml::value`, instead.
If you don't know the type of the elements, you can use `toml::array`,
which is a `std::vector` of `toml::value`, instead.
```cpp
const auto aofa = toml::get<toml::array>(data.at("aofa"));
const auto first = toml::get<toml::array>(aofa.at(0));
const auto aofa = toml::get<toml::array>(data.at("array_of_arrays"));
const auto first = toml::get<std::vector<int>>(aofa.at(0));
```
See also [expecting conversion](#expecting-conversion) and [checking-value-type](#checking-value-type).
See also [expecting conversion](#expecting-conversion)
and [checking-value-type](#checking-value-type).
### Getting tables
## Getting a table
`toml::table` is a key component of this library, which is an alias of a `std::unordered_map` from `toml::key (a.k.a. std::string)` to `toml::value`. `toml::parse` returns this as a result.
`toml::table` is a key component of this library, which is an alias of
a `std::unordered_map` from `toml::key (a.k.a. std::string)` to `toml::value`.
`toml::parse` returns this.
Since it is just an alias of `std::unordered_map`, it has all the functionalities that `std::unordered_map` has, e.g. `operator[]`, `count`, and `find`.
Since it is just an alias of `std::unordered_map`, it has all the functionalities
that `std::unordered_map` has, e.g. `operator[]`, `count`, and `find`.
```cpp
toml::table data = toml::parse("example.toml");
@@ -180,7 +248,8 @@ if(data.count("title") != 0)
}
```
When all the values of the table have the same type, toml11 allows you to convert a `toml::table` to a `map` that contains the convertible type.
When all the values of the table have the same type, toml11 allows you to
convert a `toml::table` to a `map` that contains the convertible type.
```toml
[tab]
@@ -189,14 +258,16 @@ key2 = "bar" # toml String
```
```cpp
const auto data = toml::parse("sample.toml");
const auto tab = toml::get<std::map<std::string, std::string>>(data.at("tab"));
std::cout << tab["key1"] << std::endl; // foo
std::cout << tab["key2"] << std::endl; // bar
```
### Dotted keys
## Dotted keys
TOML v0.5.0 has a new feature named "dotted keys". You can chain keys to represent the structure of the data.
TOML v0.5.0 has a new feature named "dotted keys".
You can chain keys to represent the structure of the data.
```toml
physical.color = "orange"
@@ -218,38 +289,50 @@ const auto physical = toml::get<toml::table>(data.at("physical"));
const auto color = toml::get<std::string>(physical.at("color"));
```
### An array of tables
## Getting an array of tables
An array of tables is just an array of tables. You can get it completely in the same way as the other arrays and tables.
An array of tables is just an array of tables.
You can get it completely in the same way as the other arrays and tables.
```toml
array_of_inline_table = [{key = "value1"}, {key = "value2"}, {key = "value3"}]
# sample.toml
array_of_inline_tables = [{key = "value1"}, {key = "value2"}, {key = "value3"}]
[[array_of_table]]
[[array_of_tables]]
key = "value4"
[[array_of_table]]
[[array_of_tables]]
key = "value5"
[[array_of_table]]
[[array_of_tables]]
key = "value6"
```
```cpp
const auto aot1 = toml::get<std::vector<toml::table>>(data.at("array_of_inline_table"));
const auto aot2 = toml::get<std::vector<toml::table>>(data.at("array_of_table"));
const auto data = toml::parse("sample.toml");
const auto aot1 = toml::get<std::vector<toml::table>>(data.at("array_of_inline_tables"));
const auto aot2 = toml::get<std::vector<toml::table>>(data.at("array_of_tables"));
```
### Cost of conversion
## Cost of conversion
Although `toml::get` is convenient, it has additional copy-cost because it copies data contained in `toml::value` to the user-specified type. Of course in some case this overhead is not ignorable.
Although `toml::get` is convenient, it has additional copy-cost because
it copies data contained in `toml::value` to the user-specified type.
Of course in some cases this overhead is not ignorable.
By passing the exact types, `toml::get` returns reference that has nealy zero overhead.
```cpp
// the following code constructs a std::vector.
// it requires heap allocation for vector and element conversion.
const auto array = toml::get<std::vector<int>>(data.at("foo"));
```
By passing the exact types, `toml::get` returns reference that has no overhead.
``` cpp
const auto& tab = toml::get<toml::array>(data.at("tab"));
const auto& numbers = toml::get<toml::table>(data.at("numbers"));
```
Unfortunately, in this case you need to call `toml::get` each time you access to the element of `toml::array` because `toml::array` is an array of `toml::value`.
In this case you need to call `toml::get` each time you access to
the element of `toml::array` because `toml::array` is an array of `toml::value`.
```cpp
const auto& num0 = toml::get<toml::integer>(numbers.at(0));
@@ -257,34 +340,64 @@ const auto& num1 = toml::get<toml::integer>(numbers.at(1));
const auto& num2 = toml::get<toml::integer>(numbers.at(2));
```
### Datetime and its variants
## Getting datetime and its variants
TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`, `local_datetime`, and `offset_datetime`. With toml11, you can convert `local_time` to your favorite `std::chrono::duration` and others to `std::chrono::system_clock::time_point`.
TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`,
`local_datetime`, and `offset_datetime`.
Since `local_date`, `local_datetime`, and `offset_datetime` represent a time
point, you can convert them to `std::chrono::system_clock::time_point`.
Contrary, `local_time` does not represents a time point because they lack a
date information, but it can be converted to `std::chrono::duration` that
represents a duration from the beginning of the day, `00:00:00.000`.
```toml
time = 12:30:00
date = 2018-12-23
time = 12:30:00
l_dt = 2018-12-23T12:30:00
o_dt = 2018-12-23T12:30:00+09:30
```
```cpp
const auto dur = toml::get<std::chrono::minutes>(data.at("time")); // 12 * 60 + 30 min
const auto tp = toml::get<std::chrono::system_clock::time_point>(data.at("date"));
const auto data = toml::parse("sample.toml");
const auto date = toml::get<std::chrono::system_clock::time_point>(data.at("date"));
const auto l_dt = toml::get<std::chrono::system_clock::time_point>(data.at("l_dt"));
const auto o_dt = toml::get<std::chrono::system_clock::time_point>(data.at("o_dt"));
const auto time = toml::get<std::chrono::minutes>(data.at("time")); // 12 * 60 + 30 min
```
### Getting with a fallback
toml11 contains datetime as its own struct.
You can see the definitions in [toml/datetime.hpp](toml/datetime.hpp).
`toml::get_or` returns a default value if `toml::get<T>` failed.
## Getting with a fallback
`toml::get_or` returns a default value if `toml::get<T>` failed.
```cpp
toml::table data; // empty table!
const auto value = toml::get_or(data, "key", 42); // value => int 42.
toml::value v("foo"); // v contains String
const int value = toml::get_or(v, 42); // conversion fails. it returns 42.
```
`toml::get_or` automatically deduces what type you want to get from the default value you passed.
`toml::get_or` automatically deduces what type you want to get from
the default value you passed.
### Expecting conversion
To get a reference through this function, take care about the default value.
By using `toml::expect`, you will get your expected value or an error message without throwing `toml::type_error`.
```cpp
toml::value v("foo"); // v contains String
toml::integer& i = toml::get_or(v, 42); // does not work because binding `42`
// to `integer&` is invalid
toml::integer opt = 42;
toml::integer& i = toml::get_or(v, opt); // this works.
```
## Expecting conversion
By using `toml::expect`, you will get your expected value or an error message
without throwing `toml::type_error`.
```cpp
const auto value = toml::expect<std::string>(data.at("title"));
@@ -305,23 +418,26 @@ const auto value = toml::expect<int>(data.at("number"))
}).unwrap_or(/*default value =*/ 3.14);
```
### Finding value from table
## Finding a value from a table
toml11 provides utility function to find a value from `toml::table`. Of course, you can do this in your own way with `toml::get` because it just searches an `unordered_map` and returns a value if it exists.
toml11 provides utility function to find a value from `toml::table`.
Of course, you can do this in your own way with `toml::get` because
it just searches an `unordered_map` and returns a value if it exists.
```cpp
const auto data = toml::parse("example.toml");
const auto num = toml::find<int>(data, "num", /*for err msg*/"example.toml");
```
If the value does not exist, it throws `std::out_of_range` with informative error message.
If the value does not exist, it throws `std::out_of_range` with an error message.
```console
terminate called after throwing an instance of 'std::out_of_range'
what(): [error] key "num" not found in example.toml
```
You can use this with a `toml::value` that is expected to be a `toml::table`. It automatically casts the value to table.
You can use this with a `toml::value` that is expected to be a `toml::table`.
It automatically casts the value to table.
```cpp
const auto data = toml::parse("example.toml");
@@ -331,7 +447,9 @@ const auto num = toml::find<int>(data.at("table"), "num");
// num = 42
```
In this case, because the value `data.at("table")` knows the locatoin of itself, you don't need to pass where you find the value. `toml::find` will show you a great error message.
In this case, because the value `data.at("table")` knows the locatoin of itself,
you don't need to pass where you find the value.
`toml::find` will show you an error message including table location.
```console
terminate called after throwing an instance of 'std::out_of_range'
@@ -343,66 +461,279 @@ terminate called after throwing an instance of 'std::out_of_range'
If it's not a `toml::table`, the same error as "invalid type" would be thrown.
### Checking value type
There is another utility function, `toml::find_or`.
It is almost same as `toml::find`, but returns a default value if the value is
not found or has a different type, like `toml::get_or`.
When you don't know the exact type of toml-value, you can get `enum` type from `toml::value`.
```cpp
const auto data = toml::parse("example.toml");
const auto num = toml::find_or(data.at("table"), "num", 42);
```
## Checking value type
You can check what type of value does `toml::value` contains by `is_*` function.
```cpp
toml::value v = /* ... */;
if(v.is_integer() && toml::get<int>(v) == 42)
{
std::cout << "value is 42" << std::endl;
}
```
The complete list of the functions is below.
```cpp
const toml::value v(/*...*/);
v.is_boolean();
v.is_integer();
v.is_float();
v.is_string();
v.is_offset_datetime();
v.is_local_datetime();
v.is_local_date();
v.is_local_time();
v.is_array();
v.is_table();
v.is_uninitialized();
```
Also, you can get `enum class` value from `toml::value`.
```cpp
switch(data.at("something").type())
{
case toml::value_t::Integer: /* do some stuff */; break;
case toml::value_t::Float : /* do some stuff */; break;
case toml::value_t::String : /* do some stuff */; break;
case toml::value_t::Integer: /*do some stuff*/ ; break;
case toml::value_t::Float : /*do some stuff*/ ; break;
case toml::value_t::String : /*do some stuff*/ ; break;
default : throw std::runtime_error(
"unexpected type : " + toml::stringize(data.at("something").type()));
}
```
### Fill only the matched value
The complete list of the `enum`s can be found in the section
[underlying types](#underlying-types).
The more sophisticated way is using `toml::from_toml` and `std::tie`.
The `enum`s can be used as a parameter of `toml::value::is` function like the following.
```cpp
toml::table data{{"something", toml::value("foo")}};
int i = 0;
double d = 0.;
std::string s;
toml::from_toml(std::tie(i, d, s), data.at("something"));
std::cout << i << ", " << d << ", " << s << std::endl; // 0, 0, foo
toml::value v = /* ... */;
if(v.is(toml::value_t::Boolean)) // ...
```
Here, only matched value will be filled. The others are left intact after calling `from_toml`.
It should be noted that `toml::from_toml` returns as usual even if there are no matched type.
## Visiting a toml::value
`from_toml` can be used also for single type.
toml11 provides `toml::visit` to apply a function to `toml::value` in the
same way as `std::variant`.
```cpp
int i = 0;
toml::from_toml(i, data.at("something"));
const toml::value v(3.14);
toml::visit([](const auto& val) -> void {
std::cout << val << std::endl;
}, v);
```
### Sanitizing UTF-8 codepoints
The function object that would be passed to `toml::visit` must be able to
recieve all the possible TOML types. Also, the result types should be the same
each other.
toml11 shows warning if a value of an escape sequence used to represent unicode character exceeds the unicode range.
## TOML literal
toml11 supports `"..."_toml` literal.
It accept both a bare value and a file content.
```cpp
using namespace toml::literals::toml_literals;
// `_toml` can convert a bare value without key
const toml::value v = u8"0xDEADBEEF"_toml;
// v is an Integer value containing 0xDEADBEEF.
// raw string literal (`R"(...)"` is useful for this purpose)
const toml::value t = u8R"(
title = "this is TOML literal"
[table]
key = "value"
)"_toml;
// the literal will be parsed and the result will be contained in t
```
The literal function is defined in the same way as the standard library literals
such as `std::literals::string_literals::operator""s`.
```cpp
namespace toml
{
inline namespace literals
{
inline namespace toml_literals
{
toml::value operator""_toml(const char* str, std::size_t len);
} // toml_literals
} // literals
} // toml
```
Access to the operator can be gained with `using namespace toml::literals;`,
`using namespace toml::toml_literals`, and `using namespace toml::literals::toml_literals`.
## Conversion between toml value and arbitrary types
You can also use `toml::get` and other related functions with the types you defined
after you implement some stuff.
```cpp
namespace ext
{
struct foo
{
int a;
double b;
std::string c;
};
} // ext
const auto data = toml::parse("example.toml");
const foo f = toml::get<ext::foo>(data.at("foo"));
```
There are 2 ways to use `toml::get` with the types that you defined.
The first one is to implement `from_toml(const toml::value&)` member function.
```cpp
namespace ext
{
struct foo
{
int a;
double b;
std::string c;
void from_toml(const toml::value& v)
{
this->a = toml::find<int >(v, "a");
this->b = toml::find<double >(v, "b");
this->c = toml::find<std::string>(v, "c");
return;
}
};
} // ext
```
In this way, because `toml::get` first constructs `foo` without arguments,
the type should be default-constructible.
The second is to implement specialization of `toml::from` for your type.
```cpp
namespace ext
{
struct foo
{
int a;
double b;
std::string c;
};
} // ext
namespace toml
{
template<>
struct from<ext::foo>
{
ext::foo from_toml(const toml::value& v)
{
ext::foo f;
f.a = toml::find<int >(v, "a");
f.b = toml::find<double >(v, "b");
f.c = toml::find<std::string>(v, "c");
return f;
}
};
} // toml
```
In this way, since the conversion function is defined outside of the class,
you can add conversion between `toml::value` and classes defined in another library.
Note that you cannot implement both of the functions because the overload
resolution of `toml::get` will be ambiguous.
----
The opposite direction is also supported in a similar way. You can directly
pass your type to `toml::value`'s constructor by introducing `into_toml` or
`toml::into<T>`.
```cpp
namespace ext
{
struct foo
{
int a;
double b;
std::string c;
toml::table into_toml() const // you need to mark it const.
{
return toml::table{{"a", this->a}, {"b", this->b}, {"c", this->c}};
}
};
} // ext
ext::foo f{42, 3.14, "foobar"};
toml::value v(f);
```
The definition of `toml::into<T>` is similar to `toml::from<T>`.
```cpp
namespace ext
{
struct foo
{
int a;
double b;
std::string c;
};
} // ext
namespace toml
{
template<>
struct into<ext::foo>
{
toml::table into_toml(const ext::foo& f)
{
return toml::table{{"a", f.a}, {"b", f.b}, {"c", f.c}};
}
};
} // toml
ext::foo f{42, 3.14, "foobar"};
toml::value v(f);
```
Any type that can be converted to `toml::value`, e.g. `toml::table`, `toml::array`,
is okay to return from `into_toml`.
## Invalid UTF-8 codepoints
toml11 throws `syntax_error` if a value of an escape sequence
representing unicode character is not a valid UTF-8 codepoint.
```console
[warning] input codepoint (0011FFFF) is too large to decode as a unicode character. The result may not be able to render to your screen.
--> example.toml
3 | exceeds_unicode = "\U0011FFFF example"
| ~~~~~~~~~ should be in [0x00..0x10FFFF]
what(): [error] toml::read_utf8_codepoint: input codepoint is too large.
--> utf8.toml
1 | exceeds_unicode = "\U0011FFFF example"
| ^--------- should be in [0x00..0x10FFFF]
```
Also, toml11 throws `std::domain_error` if the code point exceeds the range that can be represented by utf-8.
```console
terminate called after throwing an instance of 'std::range_error'
what(): [error] input codepoint (0020FFFF) is too large to encode as utf-8.
--> example.toml
3 | exceeds_utf8 = "\U0020FFFF example"
| ~~~~~~~~~ should be in [0x00..0x10FFFF]
```
### Formatting your error
## Formatting user-defined error messages
When you encounter an error after you read the toml value, you may want to
show the error with the value.
@@ -455,6 +786,102 @@ you will get an error message like this.
| ~~ maximum number here
```
## Serializing TOML data
toml11 (after v2.1.0) enables you to serialize data into toml format.
```cpp
const auto data = toml::table{{"foo", 42}, {"bar", "baz"}};
std::cout << data << std::endl;
// bar = "baz"
// foo = 42
```
toml11 automatically makes a small table and small array inline.
You can specify the width to make them inline by `std::setw` for streams.
```cpp
const auto data = toml::table{
{"qux", toml::table{{"foo", 42}, {"bar", "baz"}}},
{"quux", toml::array{"small", "array", "of", "strings"}},
{"foobar", toml::array{"this", "array", "of", "strings", "is", "too", "long",
"to", "print", "into", "single", "line", "isn't", "it?"}},
};
// the threshold becomes 80.
std::cout << std::setw(80) << data << std::endl;
// foobar = [
// "this","array","of","strings","is","too","long","to","print","into",
// "single","line","isn't","it?",
// ]
// quux = ["small","array","of","strings"]
// qux = {bar="baz",foo=42}
// the width is 0. nothing become inline.
std::cout << std::setw(0) << data << std::endl;
// foobar = [
// "this",
// ... (snip)
// "it?",
// ]
// quux = [
// "small",
// "array",
// "of",
// "strings",
// ]
// [qux]
// bar = "baz"
// foo = 42
```
It is recommended to set width before printing data. Some I/O functions changes
width to 0, and it makes all the stuff (including `toml::array`) multiline.
The resulting files becomes too long.
To control the precision of floating point numbers, you need to pass
`std::setprecision` to stream.
```cpp
const auto data = toml::table{
{"pi", 3.141592653589793},
{"e", 2.718281828459045}
};
std::cout << std::setprecision(17) << data << std::endl;
// e = 2.7182818284590451
// pi = 3.1415926535897931
std::cout << std::setprecision( 7) << data << std::endl;
// e = 2.718282
// pi = 3.141593
```
There is another way to format toml values, `toml::format()`.
It returns `std::string` that represents a value.
```cpp
const toml::value v{{"a", 42}};
const std::string fmt = toml::format(v);
// a = 42
```
Note that since `toml::format` formats a value, the resulting string may lack
the key value.
```cpp
const toml::value v{3.14};
const std::string fmt = toml::format(v);
// 3.14
```
To control the width and precision, `toml::format` receives optional second and
third arguments to set them. By default, the witdh is 80 and the precision is
`std::numeric_limits<double>::max_digit10`.
```cpp
const auto serial = toml::format(data, /*width = */ 0, /*prec = */ 17);
```
## Underlying types
The toml types (can be used as `toml::*` in this library) and corresponding `enum` names are listed in the table below.
@@ -468,23 +895,48 @@ The toml types (can be used as `toml::*` in this library) and corresponding `enu
| LocalDate | `toml::local_date` | `toml::value_t::LocalDate` |
| LocalTime | `toml::local_time` | `toml::value_t::LocalTime` |
| LocalDatetime | `toml::local_datetime` | `toml::value_t::LocalDatetime` |
| OffsetDatetime | `toml::offset_datetime` | `toml::value_t::offsetDatetime` |
| OffsetDatetime | `toml::offset_datetime` | `toml::value_t::OffsetDatetime` |
| Array | `std::vector<toml::value>` | `toml::value_t::Array` |
| Table | `std::unordered_map<std::string, toml::key>` | `toml::value_t::Table` |
| Table | `std::unordered_map<toml::key, toml::value>` | `toml::value_t::Table` |
`toml::string` is effectively the same as `std::string` but has an additional flag that represents a kind of a string, `string_t::basic` and `string_t::literal`. Although `std::string` is not an exact toml type, still you can get a reference that points to internal `std::string` by using `toml::get<std::string>()` for convenience.
`toml::string` is effectively the same as `std::string` but has an additional
flag that represents a kind of a string, `string_t::basic` and `string_t::literal`.
Although `std::string` is not an exact toml type, still you can get a reference
that points to internal `std::string` by using `toml::get<std::string>()` for convenience.
`Datetime` variants are `struct` that are defined in this library. Because `std::chrono::system_clock::time_point` is a __time point__, not capable of representing a Local Time independent from a specific day.
`Datetime` variants are `struct` that are defined in this library.
Because `std::chrono::system_clock::time_point` is a __time point__,
not capable of representing a Local Time independent from a specific day.
It is recommended to get `Datetime`s as `std::chrono` classes through `toml::get`.
## Running Tests
To run test codes, you need to clone toml-lang/toml repository under `build/` directory
because some of the test codes read a file in the repository.
```sh
$ mkdir build
$ cd build
$ git clone https://github.com/toml-lang/toml.git
$ cmake ..
$ make
$ make test
```
To run the language agnostic test suite, you need to compile
`tests/check_toml_test.cpp` and pass it to the tester.
## Contributors
I thank the contributor for providing great feature to this repository.
I appreciate the help of the contributors who introduced the great feature to this library.
- Guillaume Fraux (@Luthaf)
- Windows support and CI on Appvayor
- Intel Compiler support
- Quentin Khan (@xaxousis)
- Found & Fixed a bug around ODR
- Improved error messages for invaild keys to show the location where the parser fails
## Licensing terms

View File

@@ -20,13 +20,16 @@ set(TEST_NAMES
test_parse_inline_table
test_parse_key
test_parse_table_key
test_literals
test_get
test_get_related_func
test_to_toml
test_from_toml
test_parse_file
test_serialize_file
test_parse_unicode
test_error_detection
test_format_error
test_extended_conversions
)
CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL)
@@ -96,3 +99,8 @@ 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
test_multiple_translation_unit_2.cpp)

41
tests/check.cpp Normal file
View File

@@ -0,0 +1,41 @@
#include "toml.hpp"
#include <iostream>
#include <iomanip>
int main(int argc, char **argv)
{
if(argc != 3)
{
std::cerr << "usage: ./check [filename] [valid|invalid]" << std::endl;
return 1;
}
const std::string file_kind(argv[2]);
try
{
const auto data = toml::parse(argv[1]);
std::cout << std::setprecision(16) << std::setw(80) << data;
if(file_kind == "valid")
{
return 0;
}
else
{
return 1;
}
}
catch(const toml::syntax_error& err)
{
std::cout << "what(): " << err.what() << std::endl;
if(file_kind == "invalid")
{
return 0;
}
else
{
return 1;
}
}
return 127;
}

142
tests/check_toml_test.cpp Normal file
View File

@@ -0,0 +1,142 @@
#include "toml.hpp"
#include <iostream>
#include <iomanip>
struct json_serializer
{
void operator()(toml::boolean v)
{
std::cout << "{\"type\":\"bool\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(toml::integer v)
{
std::cout << "{\"type\":\"integer\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(toml::floating v)
{
std::cout << "{\"type\":\"float\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(const toml::string& v)
{
// since toml11 automatically convert string to multiline string that is
// valid only in TOML, we need to format the string to make it valid in
// JSON.
std::cout << "{\"type\":\"string\",\"value\":\""
<< this->escape_string(v.str) << "\"}";
return ;
}
void operator()(const toml::local_time& v)
{
std::cout << "{\"type\":\"local_time\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(const toml::local_date& v)
{
std::cout << "{\"type\":\"local_date\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(const toml::local_datetime& v)
{
std::cout << "{\"type\":\"local_datetime\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(const toml::offset_datetime& v)
{
std::cout << "{\"type\":\"datetime\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(const toml::array& v)
{
if(!v.empty() && v.front().is_table())
{
std::cout << '[';
bool is_first = true;
for(const auto& elem : v)
{
if(!is_first) {std::cout << ", ";}
is_first = false;
toml::visit(*this, elem);
}
std::cout << ']';
}
else
{
std::cout << "{\"type\":\"array\",\"value\":[";
bool is_first = true;
for(const auto& elem : v)
{
if(!is_first) {std::cout << ", ";}
is_first = false;
toml::visit(*this, elem);
}
std::cout << "]}";
}
return ;
}
void operator()(const toml::table& v)
{
std::cout << '{';
bool is_first = true;
for(const auto& elem : v)
{
if(!is_first) {std::cout << ", ";}
is_first = false;
std::cout << toml::format(toml::string(elem.first),
std::numeric_limits<std::size_t>::max());
std::cout << ':';
toml::visit(*this, elem.second);
}
std::cout << '}';
return ;
}
std::string escape_string(const std::string& s) const
{
std::string retval;
for(const char c : s)
{
switch(c)
{
case '\\': {retval += "\\\\"; break;}
case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;}
case '\n': {retval += "\\n"; break;}
case '\r': {retval += "\\r"; break;}
default : {retval += c; break;}
}
}
return retval;
}
};
int main()
{
try
{
std::vector<char> buf;
std::cin.peek();
while(!std::cin.eof())
{
buf.push_back(std::cin.get());
std::cin.peek();
}
std::string bufstr(buf.begin(), buf.end());
std::istringstream ss(bufstr);
const auto data = toml::parse(ss);
std::cout << std::setprecision(std::numeric_limits<double>::max_digits10);
toml::visit(json_serializer(), data);
return 0;
}
catch(const toml::syntax_error& err)
{
std::cout << "what(): " << err.what() << std::endl;
return 1;
}
}

View File

@@ -0,0 +1,116 @@
#define BOOST_TEST_MODULE "test_extended_conversions"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
namespace extlib
{
struct foo
{
int a;
std::string b;
};
struct bar
{
int a;
std::string b;
void from_toml(const toml::value& v)
{
this->a = toml::find<int>(v, "a");
this->b = toml::find<std::string>(v, "b");
return ;
}
toml::table into_toml() const
{
return toml::table{{"a", this->a}, {"b", this->b}};
}
};
} // extlib
namespace toml
{
template<>
struct from<extlib::foo>
{
static extlib::foo from_toml(const toml::value& v)
{
return extlib::foo{toml::find<int>(v, "a"), toml::find<std::string>(v, "b")};
}
};
template<>
struct into<extlib::foo>
{
static toml::table into_toml(const extlib::foo& f)
{
return toml::table{{"a", f.a}, {"b", f.b}};
}
};
} // toml
BOOST_AUTO_TEST_CASE(test_conversion_by_member_methods)
{
const toml::value v{{"a", 42}, {"b", "baz"}};
const auto foo = toml::get<extlib::foo>(v);
BOOST_CHECK_EQUAL(foo.a, 42);
BOOST_CHECK_EQUAL(foo.b, "baz");
const toml::value v2(foo);
BOOST_CHECK_EQUAL(v, v2);
}
BOOST_AUTO_TEST_CASE(test_conversion_by_specialization)
{
const toml::value v{{"a", 42}, {"b", "baz"}};
const auto bar = toml::get<extlib::bar>(v);
BOOST_CHECK_EQUAL(bar.a, 42);
BOOST_CHECK_EQUAL(bar.b, "baz");
const toml::value v2(bar);
BOOST_CHECK_EQUAL(v, v2);
}
BOOST_AUTO_TEST_CASE(test_recursive_conversion)
{
const toml::value v{
toml::table{{"a", 42}, {"b", "baz"}},
toml::table{{"a", 43}, {"b", "qux"}},
toml::table{{"a", 44}, {"b", "quux"}},
toml::table{{"a", 45}, {"b", "foobar"}},
};
const auto foos = toml::get<std::vector<extlib::foo>>(v);
BOOST_CHECK_EQUAL(foos.size() , 4ul);
BOOST_CHECK_EQUAL(foos.at(0).a , 42);
BOOST_CHECK_EQUAL(foos.at(1).a , 43);
BOOST_CHECK_EQUAL(foos.at(2).a , 44);
BOOST_CHECK_EQUAL(foos.at(3).a , 45);
BOOST_CHECK_EQUAL(foos.at(0).b , "baz");
BOOST_CHECK_EQUAL(foos.at(1).b , "qux");
BOOST_CHECK_EQUAL(foos.at(2).b , "quux");
BOOST_CHECK_EQUAL(foos.at(3).b , "foobar");
const auto bars = toml::get<std::vector<extlib::bar>>(v);
BOOST_CHECK_EQUAL(bars.size() , 4ul);
BOOST_CHECK_EQUAL(bars.at(0).a , 42);
BOOST_CHECK_EQUAL(bars.at(1).a , 43);
BOOST_CHECK_EQUAL(bars.at(2).a , 44);
BOOST_CHECK_EQUAL(bars.at(3).a , 45);
BOOST_CHECK_EQUAL(bars.at(0).b , "baz");
BOOST_CHECK_EQUAL(bars.at(1).b , "qux");
BOOST_CHECK_EQUAL(bars.at(2).b , "quux");
BOOST_CHECK_EQUAL(bars.at(3).b , "foobar");
}

View File

@@ -0,0 +1,75 @@
#define BOOST_TEST_MODULE "test_value"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
// to check it successfully compiles. it does not check the formatted string.
BOOST_AUTO_TEST_CASE(test_1_value)
{
toml::value val(42);
{
const std::string pretty_error =
toml::format_error("[error] test error", val, "this is a value");
std::cout << pretty_error << std::endl;
}
{
const std::string pretty_error =
toml::format_error("[error] test error", val, "this is a value",
{"this is a hint"});
std::cout << pretty_error << std::endl;
}
}
BOOST_AUTO_TEST_CASE(test_2_values)
{
toml::value v1(42);
toml::value v2(3.14);
{
const std::string pretty_error =
toml::format_error("[error] test error with two values",
v1, "this is the answer",
v2, "this is the pi");
std::cout << pretty_error << std::endl;
}
{
const std::string pretty_error =
toml::format_error("[error] test error with two values",
v1, "this is the answer",
v2, "this is the pi",
{"hint"});
std::cout << pretty_error << std::endl;
}
}
BOOST_AUTO_TEST_CASE(test_3_values)
{
toml::value v1(42);
toml::value v2(3.14);
toml::value v3("foo");
{
const std::string pretty_error =
toml::format_error("[error] test error with two values",
v1, "this is the answer",
v2, "this is the pi",
v3, "this is a meta-syntactic variable");
std::cout << pretty_error << std::endl;
}
{
const std::string pretty_error =
toml::format_error("[error] test error with two values",
v1, "this is the answer",
v2, "this is the pi",
v3, "this is a meta-syntactic variable",
{"hint 1", "hint 2"});
std::cout << pretty_error << std::endl;
}
}

View File

@@ -45,21 +45,194 @@ BOOST_AUTO_TEST_CASE(test_find)
BOOST_AUTO_TEST_CASE(test_get_or)
{
{
toml::table v{{"num", 42}};
BOOST_CHECK_EQUAL(42, toml::get_or<int>(v, "num", 0));
BOOST_CHECK_EQUAL(0, toml::get_or<int>(v, "foo", 0));
}
{
toml::value v = toml::table{{"num", 42}};
BOOST_CHECK_EQUAL(42, toml::get_or<int>(v, "num", 0));
BOOST_CHECK_EQUAL(0, toml::get_or<int>(v, "foo", 0));
}
// requires conversion int -> uint
{
toml::value v1(42);
toml::value v2(3.14);
BOOST_CHECK_EQUAL(42, toml::get_or<int>(v1, 0));
BOOST_CHECK_EQUAL(0, toml::get_or<int>(v2, 0));
BOOST_CHECK_EQUAL(42u, toml::get_or(v1, 0u));
BOOST_CHECK_EQUAL(0u, toml::get_or(v2, 0u));
}
// exact toml type
{
toml::value v1(42);
toml::value v2(3.14);
toml::integer opt(0);
BOOST_CHECK_EQUAL(42, toml::get_or(v1, opt));
BOOST_CHECK_EQUAL(0, toml::get_or(v2, opt));
toml::value v3("foobar");
toml::string s("bazqux");
BOOST_CHECK_EQUAL("foobar", toml::get_or(v3, s));
BOOST_CHECK_EQUAL("bazqux", toml::get_or(v1, s));
}
// std::string
{
toml::value v1("foobar");
toml::value v2(42);
std::string s1("bazqux");
const std::string s2("bazqux");
BOOST_CHECK_EQUAL("foobar", toml::get_or(v1, s1));
BOOST_CHECK_EQUAL("bazqux", toml::get_or(v2, s1));
std::string& v1r = toml::get_or(v1, s1);
std::string& s1r = toml::get_or(v2, s1);
BOOST_CHECK_EQUAL("foobar", v1r);
BOOST_CHECK_EQUAL("bazqux", s1r);
BOOST_CHECK_EQUAL("foobar", toml::get_or(v1, s2));
BOOST_CHECK_EQUAL("bazqux", toml::get_or(v2, s2));
BOOST_CHECK_EQUAL("foobar", toml::get_or(v1, std::move(s1)));
BOOST_CHECK_EQUAL("bazqux", toml::get_or(v2, std::move(s1)));
}
// string literal
{
toml::value v1("foobar");
toml::value v2(42);
BOOST_CHECK_EQUAL("foobar", toml::get_or(v1, "bazqux"));
BOOST_CHECK_EQUAL("bazqux", toml::get_or(v2, "bazqux"));
const char* lit = "bazqux";
BOOST_CHECK_EQUAL("foobar", toml::get_or(v1, lit));
BOOST_CHECK_EQUAL("bazqux", toml::get_or(v2, lit));
}
}
BOOST_AUTO_TEST_CASE(test_find_or)
{
// ========================================================================
// pass toml::value
//
// requires conversion int -> uint
{
toml::table v{{"num", 42}};
BOOST_CHECK_EQUAL(42u, toml::find_or(v, "num", 0u));
BOOST_CHECK_EQUAL(0u, toml::find_or(v, "foo", 0u));
}
// exact toml type
{
toml::table v1{{"key", 42 }};
toml::table v2{{"key", 3.14}};
toml::table v3{{"not", "key"}};
toml::integer opt(0);
BOOST_CHECK_EQUAL(42, toml::find_or(v1, "key", opt));
BOOST_CHECK_EQUAL(0, toml::find_or(v2, "key", opt));
BOOST_CHECK_EQUAL(0, toml::find_or(v3, "key", opt));
toml::table v4{{"str", "foobar"}};
toml::string s("bazqux");
BOOST_CHECK_EQUAL("foobar", toml::find_or(v4, "str", s));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v1, "str", s));
}
// std::string
{
toml::table v1{{"key", "foobar"}};
toml::table v2{{"key", 42}};
std::string s1("bazqux");
const std::string s2("bazqux");
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", s1));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", s1));
std::string& v1r = toml::find_or(v1, "key", s1);
std::string& s1r = toml::find_or(v2, "key", s1);
BOOST_CHECK_EQUAL("foobar", v1r);
BOOST_CHECK_EQUAL("bazqux", s1r);
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", s2));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", s2));
BOOST_CHECK_EQUAL("foobar", toml::find_or(std::move(v1), "key", std::move(s1)));
s1 = "bazqux"; // restoring moved value
BOOST_CHECK_EQUAL("bazqux", toml::find_or(std::move(v2), "key", std::move(s1)));
}
// string literal
{
toml::table v1{{"key", "foobar"}};
toml::table v2{{"key",42}};
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", "bazqux"));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", "bazqux"));
const char* lit = "bazqux";
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", lit));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", lit));
}
// ========================================================================
// pass toml::value
//
// requires conversion int -> uint
{
toml::table v = toml::table{{"num", 42}};
BOOST_CHECK_EQUAL(42u, toml::find_or(v, "num", 0u));
BOOST_CHECK_EQUAL(0u, toml::find_or(v, "foo", 0u));
}
// exact toml type
{
toml::value v1 = toml::table{{"key", 42 }};
toml::value v2 = toml::table{{"key", 3.14}};
toml::value v3 = toml::table{{"not", "key"}};
BOOST_CHECK_EQUAL(42, toml::find_or(v1, "key", toml::integer(0)));
BOOST_CHECK_EQUAL( 0, toml::find_or(v2, "key", toml::integer(0)));
BOOST_CHECK_EQUAL( 0, toml::find_or(v3, "key", toml::integer(0)));
toml::value v4 = toml::table{{"str", "foobar"}};
toml::string s("bazqux");
BOOST_CHECK_EQUAL("foobar", toml::find_or(v4, "str", s));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v1, "str", s));
}
// std::string
{
toml::value v1 = toml::table{{"key", "foobar"}};
toml::value v2 = toml::table{{"key", 42}};
std::string s1("bazqux");
const std::string s2("bazqux");
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", s1));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", s1));
std::string& v1r = toml::find_or(v1, "key", s1);
std::string& s1r = toml::find_or(v2, "key", s1);
BOOST_CHECK_EQUAL("foobar", v1r);
BOOST_CHECK_EQUAL("bazqux", s1r);
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", s2));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", s2));
BOOST_CHECK_EQUAL("foobar", toml::find_or(std::move(v1), "key", std::move(s1)));
s1 = "bazqux"; // restoring moved value
BOOST_CHECK_EQUAL("bazqux", toml::find_or(std::move(v2), "key", std::move(s1)));
}
// string literal
{
toml::value v1 = toml::table{{"key", "foobar"}};
toml::value v2 = toml::table{{"key",42}};
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", "bazqux"));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", "bazqux"));
const char* lit = "bazqux";
BOOST_CHECK_EQUAL("foobar", toml::find_or(v1, "key", lit));
BOOST_CHECK_EQUAL("bazqux", toml::find_or(v2, "key", lit));
}
}

129
tests/test_literals.cpp Normal file
View File

@@ -0,0 +1,129 @@
#define BOOST_TEST_MODULE "test_literals"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
#include <map>
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 = u8R"(
a = 42
b = "baz"
)"_toml;
BOOST_CHECK_EQUAL(r, v);
}
{
const toml::value r{
{"c", 3.14},
{"table", toml::table{{"a", 42}, {"b", "baz"}}}
};
const toml::value v = u8R"(
c = 3.14
[table]
a = 42
b = "baz"
)"_toml;
BOOST_CHECK_EQUAL(r, v);
}
}
BOOST_AUTO_TEST_CASE(test_value_as_literal)
{
using namespace toml::literals::toml_literals;
{
const toml::value v1 = u8"true"_toml;
const toml::value v2 = u8"false"_toml;
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK(v2.is_boolean());
BOOST_CHECK(toml::get<bool>(v1));
BOOST_CHECK(!toml::get<bool>(v2));
}
{
const toml::value v1 = u8"123_456"_toml;
const toml::value v2 = u8"0b0010"_toml;
const toml::value v3 = u8"0xDEADBEEF"_toml;
BOOST_CHECK(v1.is_integer());
BOOST_CHECK(v2.is_integer());
BOOST_CHECK(v3.is_integer());
BOOST_CHECK_EQUAL(toml::get<toml::integer>(v1), 123456);
BOOST_CHECK_EQUAL(toml::get<toml::integer>(v2), 2);
BOOST_CHECK_EQUAL(toml::get<toml::integer>(v3), 0xDEADBEEF);
}
{
const toml::value v1 = u8"3.1415"_toml;
const toml::value v2 = u8"6.02e+23"_toml;
BOOST_CHECK(v1.is_float());
BOOST_CHECK(v2.is_float());
BOOST_CHECK_CLOSE(toml::get<double>(v1), 3.1415, 0.00001);
BOOST_CHECK_CLOSE(toml::get<double>(v2), 6.02e23, 0.0001);
}
{
const toml::value v1 = u8R"("foo")"_toml;
const toml::value v2 = u8R"('foo')"_toml;
const toml::value v3 = u8R"("""foo""")"_toml;
const toml::value v4 = u8R"('''foo''')"_toml;
BOOST_CHECK(v1.is_string());
BOOST_CHECK(v2.is_string());
BOOST_CHECK(v3.is_string());
BOOST_CHECK(v4.is_string());
BOOST_CHECK_EQUAL(toml::get<std::string>(v1), "foo");
BOOST_CHECK_EQUAL(toml::get<std::string>(v2), "foo");
BOOST_CHECK_EQUAL(toml::get<std::string>(v3), "foo");
BOOST_CHECK_EQUAL(toml::get<std::string>(v4), "foo");
}
{
const toml::value v1 = u8R"([1,2,3])"_toml;
BOOST_CHECK(v1.is_array());
BOOST_CHECK((toml::get<std::vector<int>>(v1) == std::vector<int>{1,2,3}));
}
{
const toml::value v1 = u8R"({a = 42})"_toml;
BOOST_CHECK(v1.is_table());
BOOST_CHECK((toml::get<std::map<std::string,int>>(v1) ==
std::map<std::string,int>{{"a", 42}}));
}
{
const toml::value v1 = u8"1979-05-27"_toml;
BOOST_CHECK(v1.is_local_date());
BOOST_CHECK_EQUAL(toml::get<toml::local_date>(v1),
toml::local_date(1979, toml::month_t::May, 27));
}
{
const toml::value v1 = u8"12:00:00"_toml;
BOOST_CHECK(v1.is_local_time());
BOOST_CHECK(toml::get<std::chrono::hours>(v1) == std::chrono::hours(12));
}
{
const toml::value v1 = u8"1979-05-27T07:32:00"_toml;
BOOST_CHECK(v1.is_local_datetime());
BOOST_CHECK_EQUAL(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_CHECK(v1.is_offset_datetime());
BOOST_CHECK_EQUAL(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)));
}
}

View File

@@ -0,0 +1,11 @@
#include <toml.hpp>
int read_a(const toml::table&);
int main()
{
const std::string content("a = 0");
std::istringstream iss(content);
const auto data = toml::parse(iss, "test_multiple_translation_unit.toml");
return read_a(data);
}

View File

@@ -0,0 +1,6 @@
#include <toml.hpp>
int read_a(const toml::table& t)
{
return toml::get<int>(t.at("a"));
}

View File

@@ -194,3 +194,504 @@ BOOST_AUTO_TEST_CASE(test_hard_example)
BOOST_CHECK(toml::get<std::vector<std::string>>(bit.at("multi_line_array")) ==
expected_multi_line_array);
}
// ---------------------------------------------------------------------------
// after here, the test codes generate the content of a file.
BOOST_AUTO_TEST_CASE(test_file_with_BOM)
{
{
const std::string table(
"\xEF\xBB\xBF" // BOM
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss, "test_file_with_BOM.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"\xEF\xBB\xBF" // BOM
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
);
{
std::ofstream ofs("tmp.toml");
ofs << table;
}
const auto data = toml::parse("tmp.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"\xEF\xBB\xBF" // BOM
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss, "test_file_with_BOM_CRLF.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"\xEF\xBB\xBF" // BOM
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
);
{
// with text-mode, "\n" is converted to "\r\n" and the resulting
// value will be "\r\r\n". To avoid the additional "\r", use binary
// mode.
std::ofstream ofs("tmp.toml", std::ios_base::binary);
ofs.write(table.data(), table.size());
}
const auto data = toml::parse("tmp.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
}
BOOST_AUTO_TEST_CASE(test_file_without_newline_at_the_end_of_file)
{
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\""
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_file_without_newline_at_the_end_of_file.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\""
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_file_without_newline_at_the_end_of_file_CRLF.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\" # comment"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_file_without_newline_at_the_end_of_file_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\" # comment"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_file_without_newline_at_the_end_of_file_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\" \t"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_file_without_newline_at_the_end_of_file_ws.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\" \t"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_file_without_newline_at_the_end_of_file_ws.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
}
BOOST_AUTO_TEST_CASE(test_files_end_with_comment)
{
// comment w/o newline
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"# comment"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"# comment\n"
"# one more comment"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
// comment w/ newline
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"# comment\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"# comment\n"
"# one more comment\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
// CRLF version
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"# comment"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"# comment\r\n"
"# one more comment"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"# comment\r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"# comment\r\n"
"# one more comment\r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_comment.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
}
BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines)
{
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"\n"
"\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
// with whitespaces
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
" \n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
" \n"
" \n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"\n"
" \n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
" \n"
"\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
// with whitespaces but no newline
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
" "
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
// CRLF
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"\r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"\r\n"
"\r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
// with whitespaces
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
" \r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
"\r\n"
" \r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
" \r\n"
"\r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
" \r\n"
" \r\n"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
{
const std::string table(
"key = \"value\"\r\n"
"[table]\r\n"
"key = \"value\"\r\n"
" "
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_CHECK_EQUAL(toml::get <std::string>(data.at("key")), "value");
BOOST_CHECK_EQUAL(toml::find<std::string>(data.at("table"), "key"), "value");
}
}

View File

@@ -13,23 +13,23 @@ using namespace detail;
BOOST_AUTO_TEST_CASE(test_bare_key)
{
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "barekey", std::vector<key>(1, "barekey"));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "bare-key", std::vector<key>(1, "bare-key"));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "bare_key", std::vector<key>(1, "bare_key"));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "1234", std::vector<key>(1, "1234"));
TOML11_TEST_PARSE_EQUAL(parse_key, "barekey", std::vector<key>(1, "barekey"));
TOML11_TEST_PARSE_EQUAL(parse_key, "bare-key", std::vector<key>(1, "bare-key"));
TOML11_TEST_PARSE_EQUAL(parse_key, "bare_key", std::vector<key>(1, "bare_key"));
TOML11_TEST_PARSE_EQUAL(parse_key, "1234", std::vector<key>(1, "1234"));
}
BOOST_AUTO_TEST_CASE(test_quoted_key)
{
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"127.0.0.1\"", std::vector<key>(1, "127.0.0.1" ));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"character encoding\"", std::vector<key>(1, "character encoding"));
TOML11_TEST_PARSE_EQUAL(parse_key, "\"127.0.0.1\"", std::vector<key>(1, "127.0.0.1" ));
TOML11_TEST_PARSE_EQUAL(parse_key, "\"character encoding\"", std::vector<key>(1, "character encoding"));
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", std::vector<key>(1, "\xCA\x8E\xC7\x9D\xCA\x9E"));
TOML11_TEST_PARSE_EQUAL(parse_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", std::vector<key>(1, "\xCA\x8E\xC7\x9D\xCA\x9E"));
#else
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"ʎǝʞ\"", std::vector<key>(1, "ʎǝʞ" ));
TOML11_TEST_PARSE_EQUAL(parse_key, "\"ʎǝʞ\"", std::vector<key>(1, "ʎǝʞ" ));
#endif
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "'key2'", std::vector<key>(1, "key2" ));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "'quoted \"value\"'", std::vector<key>(1, "quoted \"value\"" ));
TOML11_TEST_PARSE_EQUAL(parse_key, "'key2'", std::vector<key>(1, "key2" ));
TOML11_TEST_PARSE_EQUAL(parse_key, "'quoted \"value\"'", std::vector<key>(1, "quoted \"value\"" ));
}
BOOST_AUTO_TEST_CASE(test_dotted_key)
@@ -38,13 +38,13 @@ BOOST_AUTO_TEST_CASE(test_dotted_key)
std::vector<key> keys(2);
keys[0] = "physical";
keys[1] = "color";
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "physical.color", keys);
TOML11_TEST_PARSE_EQUAL(parse_key, "physical.color", keys);
}
{
std::vector<key> keys(2);
keys[0] = "physical";
keys[1] = "shape";
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "physical.shape", keys);
TOML11_TEST_PARSE_EQUAL(parse_key, "physical.shape", keys);
}
{
std::vector<key> keys(4);
@@ -52,12 +52,12 @@ BOOST_AUTO_TEST_CASE(test_dotted_key)
keys[1] = "y";
keys[2] = "z";
keys[3] = "w";
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "x.y.z.w", keys);
TOML11_TEST_PARSE_EQUAL(parse_key, "x.y.z.w", keys);
}
{
std::vector<key> keys(2);
keys[0] = "site";
keys[1] = "google.com";
TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "site.\"google.com\"", keys);
TOML11_TEST_PARSE_EQUAL(parse_key, "site.\"google.com\"", keys);
}
}

View File

@@ -0,0 +1,54 @@
#define BOOST_TEST_MODULE "test_serialize_file"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
#include <iostream>
#include <fstream>
BOOST_AUTO_TEST_CASE(test_example)
{
const auto data = toml::parse("toml/tests/example.toml");
{
std::ofstream ofs("tmp1.toml");
ofs << std::setw(80) << data;
}
auto serialized = toml::parse("tmp1.toml");
{
auto& owner = toml::get<toml::table>(serialized.at("owner"));
auto& bio = toml::get<std::string>(owner.at("bio"));
const auto CR = std::find(bio.begin(), bio.end(), '\r');
if(CR != bio.end())
{
bio.erase(CR);
}
}
BOOST_CHECK(data == serialized);
}
BOOST_AUTO_TEST_CASE(test_fruit)
{
const auto data = toml::parse("toml/tests/fruit.toml");
{
std::ofstream ofs("tmp2.toml");
ofs << std::setw(80) << data;
}
const auto serialized = toml::parse("tmp2.toml");
BOOST_CHECK(data == serialized);
}
BOOST_AUTO_TEST_CASE(test_hard_example)
{
const auto data = toml::parse("toml/tests/hard_example.toml");
{
std::ofstream ofs("tmp3.toml");
ofs << std::setw(80) << data;
}
const auto serialized = toml::parse("tmp3.toml");
BOOST_CHECK(data == serialized);
}

View File

@@ -1,189 +0,0 @@
#define BOOST_TEST_MODULE "test_to_toml"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
#include <map>
#include <list>
BOOST_AUTO_TEST_CASE(test_value_boolean)
{
toml::value v1 = toml::to_toml(true);
toml::value v2 = toml::to_toml(false);
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean);
BOOST_CHECK(v1.is(toml::value_t::Boolean));
BOOST_CHECK(v2.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v2.is<toml::Boolean>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), false);
}
BOOST_AUTO_TEST_CASE(test_value_integer)
{
toml::value v1 = toml::to_toml(-42);
toml::value v2 = toml::to_toml(42u);
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer);
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Integer);
BOOST_CHECK(v1.is(toml::value_t::Integer));
BOOST_CHECK(v2.is(toml::value_t::Integer));
BOOST_CHECK(v1.is<toml::Integer>());
BOOST_CHECK(v2.is<toml::Integer>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Integer>(), -42);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Integer>(), 42u);
}
BOOST_AUTO_TEST_CASE(test_value_float)
{
toml::value v1 = toml::to_toml(3.14);
toml::value v2 = toml::to_toml(3.14f);
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Float);
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float);
BOOST_CHECK(v1.is(toml::value_t::Float));
BOOST_CHECK(v2.is(toml::value_t::Float));
BOOST_CHECK(v1.is<toml::Float>());
BOOST_CHECK(v2.is<toml::Float>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Float>(), 3.14);
BOOST_CHECK_CLOSE_FRACTION(v2.cast<toml::value_t::Float>(), 3.14, 1e-2);
}
BOOST_AUTO_TEST_CASE(test_value_string)
{
toml::value v1 = toml::to_toml(std::string("foo"));
toml::value v2 = toml::to_toml(std::string("foo"), toml::string_t::literal);
toml::value v3 = toml::to_toml("foo");
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::String);
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::String);
BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String);
BOOST_CHECK(v1.is(toml::value_t::String));
BOOST_CHECK(v2.is(toml::value_t::String));
BOOST_CHECK(v3.is(toml::value_t::String));
BOOST_CHECK(v1.is<toml::String>());
BOOST_CHECK(v2.is<toml::String>());
BOOST_CHECK(v3.is<toml::String>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::String>(), "foo");
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::String>(), "foo");
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::String>(), "foo");
}
BOOST_AUTO_TEST_CASE(test_value_local_date)
{
toml::value v1 = toml::to_toml(toml::local_date(2018, toml::month_t::Jan, 31));
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate);
BOOST_CHECK(v1.is(toml::value_t::LocalDate));
BOOST_CHECK(v1.is<toml::LocalDate>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDate>(),
toml::local_date(2018, toml::month_t::Jan, 31));
}
BOOST_AUTO_TEST_CASE(test_value_local_time)
{
toml::value v1 = toml::to_toml(toml::local_time(12, 30, 45));
toml::value v2 = toml::to_toml(std::chrono::hours(12) + std::chrono::minutes(30) +
std::chrono::seconds(45));
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime);
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalTime);
BOOST_CHECK(v1.is(toml::value_t::LocalTime));
BOOST_CHECK(v2.is(toml::value_t::LocalTime));
BOOST_CHECK(v1.is<toml::LocalTime>());
BOOST_CHECK(v2.is<toml::LocalTime>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalTime>(),
toml::local_time(12, 30, 45));
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::LocalTime>(),
toml::local_time(12, 30, 45));
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalTime>(),
v2.cast<toml::value_t::LocalTime>());
}
BOOST_AUTO_TEST_CASE(test_value_local_datetime)
{
toml::value v1 = toml::to_toml(toml::local_datetime(
toml::local_date(2018, toml::month_t::Jan, 31),
toml::local_time(12, 30, 45)
));
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime);
BOOST_CHECK(v1.is(toml::value_t::LocalDatetime));
BOOST_CHECK(v1.is<toml::LocalDatetime>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDatetime>(),
toml::local_datetime(
toml::local_date(2018, toml::month_t::Jan, 31),
toml::local_time(12, 30, 45)));
}
BOOST_AUTO_TEST_CASE(test_value_offset_datetime)
{
toml::value v1 = toml::to_toml(toml::offset_datetime(
toml::local_date(2018, toml::month_t::Jan, 31),
toml::local_time(12, 30, 45),
toml::time_offset(9, 0)
));
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime);
BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime));
BOOST_CHECK(v1.is<toml::OffsetDatetime>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::OffsetDatetime>(),
toml::offset_datetime(
toml::local_date(2018, toml::month_t::Jan, 31),
toml::local_time(12, 30, 45),
toml::time_offset(9, 0)
));
}
BOOST_AUTO_TEST_CASE(test_value_array)
{
std::vector<int> v{1,2,3,4,5};
toml::value v1 = toml::to_toml(v);
toml::value v2 = toml::to_toml(6,7,8,9,0);
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array);
BOOST_CHECK(v1.is(toml::value_t::Array));
BOOST_CHECK(v1.is<toml::Array>());
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array);
BOOST_CHECK(v2.is(toml::value_t::Array));
BOOST_CHECK(v2.is<toml::Array>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 1);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 2);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(2).cast<toml::value_t::Integer>(), 3);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(3).cast<toml::value_t::Integer>(), 4);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(4).cast<toml::value_t::Integer>(), 5);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 6);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 7);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(2).cast<toml::value_t::Integer>(), 8);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(3).cast<toml::value_t::Integer>(), 9);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Array>().at(4).cast<toml::value_t::Integer>(), 0);
}
BOOST_AUTO_TEST_CASE(test_value_table)
{
toml::value v1 = toml::to_toml({{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}});
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table);
BOOST_CHECK(v1.is(toml::value_t::Table));
BOOST_CHECK(v1.is<toml::Table>());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("foo").cast<toml::value_t::Integer>(), 42);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("bar").cast<toml::value_t::Float>(), 3.14);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("baz").cast<toml::value_t::String>().str, "qux");
}

View File

@@ -20,6 +20,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK(v2.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v2.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK(v2.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), false);
@@ -33,6 +35,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK(v2.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v2.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK(v2.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), false);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), true);
@@ -48,6 +52,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK(v4.is(toml::value_t::Boolean));
BOOST_CHECK(v3.is<toml::Boolean>());
BOOST_CHECK(v4.is<toml::Boolean>());
BOOST_CHECK(v3.is_boolean());
BOOST_CHECK(v4.is_boolean());
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Boolean>(), false);
BOOST_CHECK_EQUAL(v4.cast<toml::value_t::Boolean>(), true);
@@ -61,6 +67,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK(v6.is(toml::value_t::Boolean));
BOOST_CHECK(v5.is<toml::Boolean>());
BOOST_CHECK(v6.is<toml::Boolean>());
BOOST_CHECK(v3.is_boolean());
BOOST_CHECK(v4.is_boolean());
BOOST_CHECK_EQUAL(v5.cast<toml::value_t::Boolean>(), false);
BOOST_CHECK_EQUAL(v6.cast<toml::value_t::Boolean>(), true);
@@ -74,6 +82,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean)
BOOST_CHECK(v2.is(toml::value_t::Float));
BOOST_CHECK(v1.is<toml::Integer>());
BOOST_CHECK(v2.is<toml::Float>());
BOOST_CHECK(v1.is_integer());
BOOST_CHECK(v2.is_float());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Integer>(), 42);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Float>(), 3.14);
@@ -90,6 +100,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK(v2.is(toml::value_t::Integer));
BOOST_CHECK(v1.is<toml::Integer>());
BOOST_CHECK(v2.is<toml::Integer>());
BOOST_CHECK(v1.is_integer());
BOOST_CHECK(v2.is_integer());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Integer>(), -42);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Integer>(), 42u);
@@ -103,6 +115,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK(v2.is(toml::value_t::Integer));
BOOST_CHECK(v1.is<toml::Integer>());
BOOST_CHECK(v2.is<toml::Integer>());
BOOST_CHECK(v1.is_integer());
BOOST_CHECK(v2.is_integer());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Integer>(), 54);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Integer>(), -54);
@@ -118,6 +132,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK(v4.is(toml::value_t::Integer));
BOOST_CHECK(v3.is<toml::Integer>());
BOOST_CHECK(v4.is<toml::Integer>());
BOOST_CHECK(v3.is_integer());
BOOST_CHECK(v4.is_integer());
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Integer>(), 54);
BOOST_CHECK_EQUAL(v4.cast<toml::value_t::Integer>(), -54);
@@ -131,6 +147,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK(v6.is(toml::value_t::Integer));
BOOST_CHECK(v5.is<toml::Integer>());
BOOST_CHECK(v6.is<toml::Integer>());
BOOST_CHECK(v5.is_integer());
BOOST_CHECK(v6.is_integer());
BOOST_CHECK_EQUAL(v5.cast<toml::value_t::Integer>(), 54);
BOOST_CHECK_EQUAL(v6.cast<toml::value_t::Integer>(), -54);
@@ -144,6 +162,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer)
BOOST_CHECK(v2.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v2.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK(v2.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), false);
@@ -160,6 +180,8 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v2.is(toml::value_t::Float));
BOOST_CHECK(v1.is<toml::Float>());
BOOST_CHECK(v2.is<toml::Float>());
BOOST_CHECK(v1.is_float());
BOOST_CHECK(v2.is_float());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Float>(), 3.14);
BOOST_CHECK_CLOSE_FRACTION(v2.cast<toml::value_t::Float>(), 3.14, 1e-2);
@@ -173,6 +195,8 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v2.is(toml::value_t::Float));
BOOST_CHECK(v1.is<toml::Float>());
BOOST_CHECK(v2.is<toml::Float>());
BOOST_CHECK(v1.is_float());
BOOST_CHECK(v2.is_float());
BOOST_CHECK_CLOSE_FRACTION(v1.cast<toml::value_t::Float>(), 2.718, 1e-3);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Float>(), 2.718);
@@ -188,6 +212,8 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v4.is(toml::value_t::Float));
BOOST_CHECK(v3.is<toml::Float>());
BOOST_CHECK(v4.is<toml::Float>());
BOOST_CHECK(v3.is_float());
BOOST_CHECK(v4.is_float());
BOOST_CHECK_CLOSE_FRACTION(v3.cast<toml::value_t::Float>(), 2.718, 1e-3);
BOOST_CHECK_EQUAL(v4.cast<toml::value_t::Float>(), 2.718);
@@ -201,6 +227,8 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v6.is(toml::value_t::Float));
BOOST_CHECK(v5.is<toml::Float>());
BOOST_CHECK(v6.is<toml::Float>());
BOOST_CHECK(v5.is_float());
BOOST_CHECK(v6.is_float());
BOOST_CHECK_CLOSE_FRACTION(v5.cast<toml::value_t::Float>(), 2.718, 1e-3);
BOOST_CHECK_EQUAL(v6.cast<toml::value_t::Float>(), 2.718);
@@ -214,6 +242,8 @@ BOOST_AUTO_TEST_CASE(test_value_float)
BOOST_CHECK(v2.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v2.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK(v2.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), false);
@@ -234,6 +264,9 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK(v1.is<toml::String>());
BOOST_CHECK(v2.is<toml::String>());
BOOST_CHECK(v3.is<toml::String>());
BOOST_CHECK(v1.is_string());
BOOST_CHECK(v2.is_string());
BOOST_CHECK(v3.is_string());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::String>(), "foo");
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::String>(), "foo");
@@ -249,9 +282,9 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK(v1.is(toml::value_t::String));
BOOST_CHECK(v2.is(toml::value_t::String));
BOOST_CHECK(v3.is(toml::value_t::String));
BOOST_CHECK(v1.is<toml::String>());
BOOST_CHECK(v2.is<toml::String>());
BOOST_CHECK(v3.is<toml::String>());
BOOST_CHECK(v1.is_string());
BOOST_CHECK(v2.is_string());
BOOST_CHECK(v3.is_string());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::String>(), "bar");
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::String>(), "bar");
@@ -273,6 +306,9 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK(v4.is<toml::String>());
BOOST_CHECK(v5.is<toml::String>());
BOOST_CHECK(v6.is<toml::String>());
BOOST_CHECK(v4.is_string());
BOOST_CHECK(v5.is_string());
BOOST_CHECK(v6.is_string());
BOOST_CHECK_EQUAL(v4.cast<toml::value_t::String>(), "bar");
BOOST_CHECK_EQUAL(v5.cast<toml::value_t::String>(), "bar");
@@ -291,6 +327,9 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK(v4.is<toml::String>());
BOOST_CHECK(v5.is<toml::String>());
BOOST_CHECK(v6.is<toml::String>());
BOOST_CHECK(v4.is_string());
BOOST_CHECK(v5.is_string());
BOOST_CHECK(v6.is_string());
BOOST_CHECK_EQUAL(v4.cast<toml::value_t::String>(), "baz");
BOOST_CHECK_EQUAL(v5.cast<toml::value_t::String>(), "baz");
@@ -309,6 +348,9 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v2.is<toml::Boolean>());
BOOST_CHECK(v3.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK(v2.is_boolean());
BOOST_CHECK(v3.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::Boolean>(), true);
@@ -322,6 +364,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate);
BOOST_CHECK(v1.is(toml::value_t::LocalDate));
BOOST_CHECK(v1.is<toml::LocalDate>());
BOOST_CHECK(v1.is_local_date());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDate>(),
toml::local_date(2018, toml::month_t::Jan, 31));
@@ -331,6 +374,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate);
BOOST_CHECK(v1.is(toml::value_t::LocalDate));
BOOST_CHECK(v1.is<toml::LocalDate>());
BOOST_CHECK(v1.is_local_date());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDate>(),
toml::local_date(2018, toml::month_t::Apr, 1));
@@ -341,6 +385,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date)
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDate);
BOOST_CHECK(v2.is(toml::value_t::LocalDate));
BOOST_CHECK(v2.is<toml::LocalDate>());
BOOST_CHECK(v2.is_local_date());
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::LocalDate>(),
toml::local_date(2018, toml::month_t::Apr, 1));
@@ -349,6 +394,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
BOOST_CHECK(v1.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
}
@@ -364,6 +410,8 @@ BOOST_AUTO_TEST_CASE(test_value_local_time)
BOOST_CHECK(v2.is(toml::value_t::LocalTime));
BOOST_CHECK(v1.is<toml::LocalTime>());
BOOST_CHECK(v2.is<toml::LocalTime>());
BOOST_CHECK(v1.is_local_time());
BOOST_CHECK(v2.is_local_time());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalTime>(),
toml::local_time(12, 30, 45));
@@ -377,6 +425,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_time)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime);
BOOST_CHECK(v1.is(toml::value_t::LocalTime));
BOOST_CHECK(v1.is<toml::LocalTime>());
BOOST_CHECK(v1.is_local_time());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalTime>(),
toml::local_time(1, 30, 0, 100, 0));
@@ -386,6 +435,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_time)
BOOST_CHECK_EQUAL(v3.type(), toml::value_t::LocalTime);
BOOST_CHECK(v3.is(toml::value_t::LocalTime));
BOOST_CHECK(v3.is<toml::LocalTime>());
BOOST_CHECK(v3.is_local_time());
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::LocalTime>(),
toml::local_time(1, 30, 0, 100, 0));
@@ -394,6 +444,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_time)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
BOOST_CHECK(v1.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
}
@@ -407,6 +458,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime);
BOOST_CHECK(v1.is(toml::value_t::LocalDatetime));
BOOST_CHECK(v1.is<toml::LocalDatetime>());
BOOST_CHECK(v1.is_local_datetime());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDatetime>(),
toml::local_datetime(
@@ -420,6 +472,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime);
BOOST_CHECK(v1.is(toml::value_t::LocalDatetime));
BOOST_CHECK(v1.is<toml::LocalDatetime>());
BOOST_CHECK(v1.is_local_datetime());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::LocalDatetime>(),
toml::local_datetime(
@@ -432,6 +485,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime)
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDatetime);
BOOST_CHECK(v2.is(toml::value_t::LocalDatetime));
BOOST_CHECK(v2.is<toml::LocalDatetime>());
BOOST_CHECK(v2.is_local_datetime());
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::LocalDatetime>(),
toml::local_datetime(
@@ -442,6 +496,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
BOOST_CHECK(v1.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
}
@@ -456,6 +511,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime);
BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime));
BOOST_CHECK(v1.is<toml::OffsetDatetime>());
BOOST_CHECK(v1.is_offset_datetime());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::OffsetDatetime>(),
toml::offset_datetime(
@@ -472,6 +528,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime);
BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime));
BOOST_CHECK(v1.is<toml::OffsetDatetime>());
BOOST_CHECK(v1.is_offset_datetime());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::OffsetDatetime>(),
toml::offset_datetime(
@@ -485,6 +542,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime)
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::OffsetDatetime);
BOOST_CHECK(v2.is(toml::value_t::OffsetDatetime));
BOOST_CHECK(v2.is<toml::OffsetDatetime>());
BOOST_CHECK(v2.is_offset_datetime());
BOOST_CHECK_EQUAL(v2.cast<toml::value_t::OffsetDatetime>(),
toml::offset_datetime(
@@ -495,6 +553,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
BOOST_CHECK(v1.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
}
@@ -507,10 +566,12 @@ BOOST_AUTO_TEST_CASE(test_value_array)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array);
BOOST_CHECK(v1.is(toml::value_t::Array));
BOOST_CHECK(v1.is<toml::Array>());
BOOST_CHECK(v1.is_array());
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array);
BOOST_CHECK(v2.is(toml::value_t::Array));
BOOST_CHECK(v2.is<toml::Array>());
BOOST_CHECK(v2.is_array());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 1);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 2);
@@ -530,10 +591,12 @@ BOOST_AUTO_TEST_CASE(test_value_array)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array);
BOOST_CHECK(v1.is(toml::value_t::Array));
BOOST_CHECK(v1.is<toml::Array>());
BOOST_CHECK(v1.is_array());
BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array);
BOOST_CHECK(v2.is(toml::value_t::Array));
BOOST_CHECK(v2.is<toml::Array>());
BOOST_CHECK(v2.is_array());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 6);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 7);
@@ -553,6 +616,7 @@ BOOST_AUTO_TEST_CASE(test_value_array)
BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Array);
BOOST_CHECK(v3.is(toml::value_t::Array));
BOOST_CHECK(v3.is<toml::Array>());
BOOST_CHECK(v3.is_array());
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Array>().at(0).cast<toml::value_t::Integer>(), 6);
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Array>().at(1).cast<toml::value_t::Integer>(), 7);
@@ -564,6 +628,7 @@ BOOST_AUTO_TEST_CASE(test_value_array)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
BOOST_CHECK(v1.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
}
@@ -574,6 +639,7 @@ BOOST_AUTO_TEST_CASE(test_value_table)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table);
BOOST_CHECK(v1.is(toml::value_t::Table));
BOOST_CHECK(v1.is<toml::Table>());
BOOST_CHECK(v1.is_table());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("foo").cast<toml::value_t::Integer>(), 42);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("bar").cast<toml::value_t::Float>(), 3.14);
@@ -584,6 +650,7 @@ BOOST_AUTO_TEST_CASE(test_value_table)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table);
BOOST_CHECK(v1.is(toml::value_t::Table));
BOOST_CHECK(v1.is<toml::Table>());
BOOST_CHECK(v1.is_table());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("foo").cast<toml::value_t::Float>(), 2.71);
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Table>().at("bar").cast<toml::value_t::Integer>(), 54);
@@ -595,6 +662,7 @@ BOOST_AUTO_TEST_CASE(test_value_table)
BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Table);
BOOST_CHECK(v3.is(toml::value_t::Table));
BOOST_CHECK(v3.is<toml::Table>());
BOOST_CHECK(v3.is_table());
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Table>().at("foo").cast<toml::value_t::Float>(), 2.71);
BOOST_CHECK_EQUAL(v3.cast<toml::value_t::Table>().at("bar").cast<toml::value_t::Integer>(), 54);
@@ -604,5 +672,13 @@ BOOST_AUTO_TEST_CASE(test_value_table)
BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean);
BOOST_CHECK(v1.is(toml::value_t::Boolean));
BOOST_CHECK(v1.is<toml::Boolean>());
BOOST_CHECK(v1.is_boolean());
BOOST_CHECK_EQUAL(v1.cast<toml::value_t::Boolean>(), true);
}
BOOST_AUTO_TEST_CASE(test_value_empty)
{
toml::value v1;
BOOST_CHECK(v1.is_uninitialized());
BOOST_CHECK(v1.is(toml::value_t::Empty));
}

View File

@@ -34,7 +34,8 @@
#endif
#include "toml/parser.hpp"
#include "toml/to_toml.hpp"
#include "toml/literal.hpp"
#include "toml/serializer.hpp"
#include "toml/from_toml.hpp"
#include "toml/get.hpp"

View File

@@ -39,7 +39,7 @@ inline std::string show_char(const char c)
else
{
std::ostringstream oss;
oss << std::hex << std::setfill('0') << std::setw(2) << "0x"
oss << "0x" << std::hex << std::setfill('0') << std::setw(2)
<< static_cast<int>(c);
return oss.str();
}

View File

@@ -1,7 +1,7 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_DATETIME
#define TOML11_DATETIME
#ifndef TOML11_DATETIME_HPP
#define TOML11_DATETIME_HPP
#include <chrono>
#include <tuple>
#include <array>
@@ -14,6 +14,36 @@
namespace toml
{
// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is
// provided in the absolutely same purpose, but C++11 is actually not compatible
// with C11. We need to dispatch the function depending on the OS.
namespace detail
{
// TODO: find more sophisticated way to handle this
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || _POSIX_SOURCE
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_r(src, &dst);
if(!result)
{
throw std::runtime_error("localtime_r failed.");
}
return dst;
}
#else
// XXX: On Windows, std::localtime is thread-safe because they uses thread-local
// storage to store the instance of std::tm. On the other platforms, it may not
// be thread-safe.
inline std::tm localtime_s(const std::time_t* src)
{
const auto result = std::localtime(src);
if(!result) {throw std::runtime_error("localtime failed.");}
return *result;
}
#endif
} // detail
enum class month_t : std::int8_t
{
Jan = 0,
@@ -50,10 +80,8 @@ struct local_date
explicit local_date(const std::chrono::system_clock::time_point& tp)
{
const auto t = std::chrono::system_clock::to_time_t(tp);
const auto tmp = std::localtime(&t); //XXX: not threadsafe!
assert(tmp); // if std::localtime fails, tmp is nullptr
const std::tm time = *tmp;
const auto t = std::chrono::system_clock::to_time_t(tp);
const auto time = detail::localtime_s(&t);
*this = local_date(time);
}
@@ -304,17 +332,10 @@ operator<<(std::basic_ostream<charT, traits>& os, const time_offset& offset)
os << 'Z';
return os;
}
if(static_cast<int>(offset.hour) * static_cast<int>(offset.minute) < 0)
{
const int min = static_cast<int>(offset.hour) * 60 + offset.minute;
if(min < 0){os << '-';} else {os << '+';}
os << std::setfill('0') << std::setw(2) << min / 60 << ':';
os << std::setfill('0') << std::setw(2) << min % 60;
return os;
}
if(offset.hour < 0){os << '-';} else {os << '+';}
os << std::setfill('0') << std::setw(2) << static_cast<int>(offset.hour) << ':';
os << std::setfill('0') << std::setw(2) << static_cast<int>(offset.minute);
int minute = static_cast<int>(offset.hour) * 60 + offset.minute;
if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';}
os << std::setfill('0') << std::setw(2) << minute / 60 << ':';
os << std::setfill('0') << std::setw(2) << minute % 60;
return os;
}
@@ -330,10 +351,8 @@ struct local_datetime
explicit local_datetime(const std::chrono::system_clock::time_point& tp)
{
const auto t = std::chrono::system_clock::to_time_t(tp);
const auto tmp = std::localtime(&t); //XXX: not threadsafe!
assert(tmp); // if std::localtime fails, tmp is nullptr
std::tm time = detail::localtime_s(&t);
std::tm time = *tmp;
this->date = local_date(time);
this->time = local_time(time);
@@ -482,10 +501,8 @@ struct offset_datetime
static time_offset get_local_offset()
{
// get current timezone
const auto tmp1 = std::time(nullptr);
const auto tmp2 = std::localtime(&tmp1); // XXX not threadsafe!
assert(tmp2);
std::tm t = *tmp2;
const auto tmp1 = std::time(nullptr);
const auto t = detail::localtime_s(&tmp1);
std::array<char, 6> buf;
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0

View File

@@ -1,7 +1,7 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_EXCEPTION
#define TOML11_EXCEPTION
#ifndef TOML11_EXCEPTION_HPP
#define TOML11_EXCEPTION_HPP
#include <stdexcept>
#include <string>

20
toml/from.hpp Normal file
View File

@@ -0,0 +1,20 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_FROM_HPP
#define TOML11_FROM_HPP
#include "traits.hpp"
namespace toml
{
template<typename T>
struct from;
// {
// static T from_toml(const toml::value& v)
// {
// // User-defined conversions ...
// }
// };
} // toml
#endif // TOML11_FROM_HPP

View File

@@ -1,7 +1,7 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_FROM_TOML
#define TOML11_FROM_TOML
#ifndef TOML11_FROM_TOML_HPP
#define TOML11_FROM_TOML_HPP
#include "get.hpp"
namespace toml

View File

@@ -1,7 +1,8 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_GET
#define TOML11_GET
#ifndef TOML11_GET_HPP
#define TOML11_GET_HPP
#include "from.hpp"
#include "result.hpp"
#include "value.hpp"
#include <algorithm>
@@ -112,7 +113,7 @@ inline std::string get(value&& v)
template<typename T, typename std::enable_if<
detail::is_chrono_duration<T>::value, std::nullptr_t>::type = nullptr>
inline T get(value& v)
inline T get(const value& v)
{
return std::chrono::duration_cast<T>(
std::chrono::nanoseconds(v.cast<value_t::LocalTime>()));
@@ -124,7 +125,7 @@ inline T get(value& v)
template<typename T, typename std::enable_if<
std::is_same<std::chrono::system_clock::time_point, T>::value,
std::nullptr_t>::type = nullptr>
inline T get(value& v)
inline T get(const value& v)
{
switch(v.type())
{
@@ -173,6 +174,20 @@ template<typename T, typename std::enable_if<detail::conjunction<
>::value, std::nullptr_t>::type = nullptr>
T get(const toml::value& v);
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>, // not a toml::value
detail::has_from_toml_method<T>, // but has from_toml(toml::value) memfn
std::is_default_constructible<T> // and default constructible
>::value, std::nullptr_t>::type = nullptr>
T get(const toml::value& v);
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>> // not a toml::value
>::value, std::nullptr_t>::type = nullptr,
std::size_t = sizeof(::toml::from<T>) // and has from<T> specialization
>
T get(const toml::value& v);
// ============================================================================
// array-like types; most likely STL container, like std::vector, etc.
@@ -210,8 +225,9 @@ T get(const value& v)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[erorr] toml::get specified container size is ", container.size(),
" but there are ", ar.size(), " elements in toml array."),
detail::get_region(v), "here"));
" but there are ", ar.size(), " elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
}));
}
std::transform(ar.cbegin(), ar.cend(), container.begin(),
[](const value& x){return ::toml::get<value_type>(x);});
@@ -233,7 +249,9 @@ T get(const value& v)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[erorr] toml::get specified std::pair but there are ", ar.size(),
" elements in toml array."), detail::get_region(v), "here"));
" elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
}));
}
return std::make_pair(::toml::get<first_type >(ar.at(0)),
::toml::get<second_type>(ar.at(1)));
@@ -264,7 +282,9 @@ T get(const value& v)
throw std::out_of_range(detail::format_underline(concat_to_string(
"[erorr] toml::get specified std::tuple with ",
std::tuple_size<T>::value, "elements, but there are ", ar.size(),
" elements in toml array."), detail::get_region(v), "here"));
" elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
}));
}
return detail::get_tuple_impl<T>(ar,
detail::make_index_sequence<std::tuple_size<T>::value>{});
@@ -292,10 +312,33 @@ T get(const toml::value& v)
return map;
}
// ============================================================================
// user-defined, but compatible types.
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>, // not a toml::value
detail::has_from_toml_method<T>, // but has from_toml(toml::value) memfn
std::is_default_constructible<T> // and default constructible
>::value, std::nullptr_t>::type>
T get(const toml::value& v)
{
T ud;
ud.from_toml(v);
return ud;
}
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>> // not a toml::value
>::value, std::nullptr_t>::type, std::size_t> // and has from<T>
T get(const toml::value& v)
{
return ::toml::from<T>::from_toml(v);
}
// ============================================================================
// find and get
template<typename T>
template<typename T = ::toml::value>
decltype(::toml::get<T>(std::declval<const ::toml::value&>()))
find(const toml::table& tab, const toml::key& ky,
std::string tablename = "unknown table")
@@ -307,7 +350,7 @@ find(const toml::table& tab, const toml::key& ky,
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T>
template<typename T = ::toml::value>
decltype(::toml::get<T>(std::declval<::toml::value&>()))
find(toml::table& tab, const toml::key& ky,
std::string tablename = "unknown table")
@@ -319,7 +362,7 @@ find(toml::table& tab, const toml::key& ky,
}
return ::toml::get<T>(tab[ky]);
}
template<typename T>
template<typename T = ::toml::value>
decltype(::toml::get<T>(std::declval<::toml::value&&>()))
find(toml::table&& tab, const toml::key& ky,
std::string tablename = "unknown table")
@@ -332,7 +375,7 @@ find(toml::table&& tab, const toml::key& ky,
return ::toml::get<T>(std::move(tab[ky]));
}
template<typename T>
template<typename T = ::toml::value>
decltype(::toml::get<T>(std::declval<const ::toml::value&>()))
find(const toml::value& v, const toml::key& ky)
{
@@ -340,12 +383,13 @@ find(const toml::value& v, const toml::key& ky)
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), detail::get_region(v),
"in this table"));
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T>
template<typename T = ::toml::value>
decltype(::toml::get<T>(std::declval<::toml::value&>()))
find(toml::value& v, const toml::key& ky)
{
@@ -353,12 +397,13 @@ find(toml::value& v, const toml::key& ky)
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), detail::get_region(v),
"in this table"));
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(tab.at(ky));
}
template<typename T>
template<typename T = ::toml::value>
decltype(::toml::get<T>(std::declval<::toml::value&&>()))
find(toml::value&& v, const toml::key& ky)
{
@@ -366,21 +411,23 @@ find(toml::value&& v, const toml::key& ky)
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[error] key \"", ky, "\" not found"), detail::get_region(v),
"in this table"));
"[error] key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
}
return ::toml::get<T>(std::move(tab[ky]));
}
// ============================================================================
// get_or
// get_or(value, fallback)
template<typename T>
decltype(::toml::get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(
std::declval<const toml::value&>()))
get_or(const toml::value& v, T&& opt)
// ----------------------------------------------------------------------------
// specialization for the exact toml types (return type becomes lvalue ref)
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T const& get_or(const toml::value& v, const T& opt)
{
try
{
@@ -389,14 +436,12 @@ get_or(const toml::value& v, T&& opt)
}
catch(...)
{
return std::forward<T>(opt);
return opt;
}
}
template<typename T>
decltype(::toml::get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(
std::declval<toml::value&>()))
get_or(toml::value& v, T&& opt)
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T& get_or(toml::value& v, T& opt)
{
try
{
@@ -405,77 +450,333 @@ get_or(toml::value& v, T&& opt)
}
catch(...)
{
return std::forward<T>(opt);
return opt;
}
}
template<typename T>
decltype(::toml::get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(
std::declval<toml::value&&>()))
get_or(toml::value&& v, T&& opt)
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T&& get_or(toml::value&& v, T&& opt)
{
try
{
return get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(std::move(v));
typename std::remove_reference<T>::type>::type>(v);
}
catch(...)
{
return std::forward<T>(opt);
return opt;
}
}
// ----------------------------------------------------------------------------
// specialization for std::string (return type becomes lvalue ref)
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string const& get_or(const toml::value& v, const T& opt)
{
try
{
return get<std::string>(v);
}
catch(...)
{
return opt;
}
}
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string& get_or(toml::value& v, T& opt)
{
try
{
return get<std::string>(v);
}
catch(...)
{
return opt;
}
}
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string get_or(toml::value&& v, T&& opt)
{
try
{
return get<std::string>(v);
}
catch(...)
{
return opt;
}
}
template<typename T, typename std::enable_if<
detail::is_string_literal<typename std::remove_reference<T>::type>::value,
std::nullptr_t>::type = nullptr>
std::string get_or(const toml::value& v, T&& opt)
{
try
{
return get<std::string>(v);
}
catch(...)
{
return std::string(opt);
}
}
// ----------------------------------------------------------------------------
// others (require type conversion and return type cannot be lvalue reference)
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>,
detail::negation<std::is_same<T, std::string>>,
detail::negation<detail::is_string_literal<typename std::remove_reference<T>::type>>
>::value, std::nullptr_t>::type = nullptr>
T get_or(const toml::value& v, T&& opt)
{
try
{
return get<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>(v);
}
catch(...)
{
return opt;
}
}
// ===========================================================================
// get_or(table, key, fallback)
//
// DEPRECATED: use find_or instead.
template<typename T>
TOML11_MARK_AS_DEPRECATED("use toml::find_or(table, key, opt) instead.")
auto get_or(const toml::table& tab, const toml::key& ky, T&& opt)
-> decltype(get_or(std::declval<value const&>(), std::forward<T>(opt)))
{
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(tab.at(ky), std::forward<T>(opt));
}
template<typename T>
TOML11_MARK_AS_DEPRECATED("use toml::find_or(table, key, opt) instead.")
auto get_or(toml::table& tab, const toml::key& ky, T&& opt)
-> decltype(get_or(std::declval<value&>(), std::forward<T>(opt)))
{
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(tab[ky], std::forward<T>(opt));
}
template<typename T>
TOML11_MARK_AS_DEPRECATED("use toml::find_or(table, key, opt) instead.")
auto get_or(toml::table&& tab, const toml::key& ky, T&& opt)
-> decltype(get_or(std::declval<value&&>(), std::forward<T>(opt)))
{
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(std::move(tab[ky]), std::forward<T>(opt));
}
template<typename T>
TOML11_MARK_AS_DEPRECATED("use toml::find_or(value, key, opt) instead.")
auto get_or(const toml::value& v, const toml::key& ky, T&& opt)
-> decltype(get_or(std::declval<value const&>(), std::forward<T>(opt)))
{
if(v.type() != toml::value_t::Table){return std::forward<T>(opt);}
if(!v.is_table()) {return opt;}
const auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(tab.at(ky), std::forward<T>(opt));
}
template<typename T>
TOML11_MARK_AS_DEPRECATED("use toml::find_or(value, key, opt) instead.")
auto get_or(toml::value& v, const toml::key& ky, T&& opt)
-> decltype(get_or(std::declval<value&>(), std::forward<T>(opt)))
{
if(v.type() != toml::value_t::Table){return std::forward<T>(opt);}
if(!v.is_table()) {return opt;}
auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(tab[ky], std::forward<T>(opt));
}
template<typename T>
TOML11_MARK_AS_DEPRECATED("use toml::find_or(value, key, opt) instead.")
auto get_or(toml::value&& v, const toml::key& ky, T&& opt)
-> decltype(get_or(std::declval<value&&>(), std::forward<T>(opt)))
{
if(v.type() != toml::value_t::Table){return std::forward<T>(opt);}
if(!v.is_table()) {return opt;}
auto tab = toml::get<toml::table>(std::move(v));
if(tab.count(ky) == 0) {return std::forward<T>(opt);}
if(tab.count(ky) == 0) {return opt;}
return ::toml::get_or(std::move(tab[ky]), std::forward<T>(opt));
}
// ===========================================================================
// find_or(value, key, fallback)
// ---------------------------------------------------------------------------
// exact types (return type can be a reference)
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T const& find_or(const toml::value& v, const toml::key& ky, const T& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T& find_or(toml::value& v, const toml::key& ky, T& opt)
{
if(!v.is_table()) {return opt;}
auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return opt;}
return get_or(tab[ky], opt);
}
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T&& find_or(toml::value&& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return opt;}
auto tab = toml::get<toml::table>(std::move(v));
if(tab.count(ky) == 0) {return opt;}
return get_or(std::move(tab[ky]), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// std::string (return type can be a reference)
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string const& find_or(const toml::value& v, const toml::key& ky, const T& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string& find_or(toml::value& v, const toml::key& ky, T& opt)
{
if(!v.is_table()) {return opt;}
auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return opt;}
return get_or(tab[ky], opt);
}
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string find_or(toml::value&& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return opt;}
auto tab = toml::get<toml::table>(std::move(v));
if(tab.count(ky) == 0) {return opt;}
return get_or(std::move(tab[ky]), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// string literal (deduced as std::string)
template<typename T, typename std::enable_if<
detail::is_string_literal<typename std::remove_reference<T>::type>::value,
std::nullptr_t>::type = nullptr>
std::string find_or(const toml::value& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return std::string(opt);}
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// others (require type conversion and return type cannot be lvalue reference)
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>,
detail::negation<std::is_same<T, std::string>>,
detail::negation<detail::is_string_literal<typename std::remove_reference<T>::type>>
>::value, std::nullptr_t>::type = nullptr>
T find_or(const toml::value& v, const toml::key& ky, T&& opt)
{
if(!v.is_table()) {return opt;}
const auto& tab = toml::get<toml::table>(v);
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ===========================================================================
// find_or(table, key, opt)
// ---------------------------------------------------------------------------
// exact types (return type can be a reference)
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T const& find_or(const toml::table& tab, const toml::key& ky, const T& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T& find_or(toml::table& tab, const toml::key& ky, T& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(tab[ky], opt);
}
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T>::value, std::nullptr_t>::type = nullptr>
T&& find_or(toml::table&& tab, const toml::key& ky, T&& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(std::move(tab[ky]), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// std::string (return type can be a reference)
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string const& find_or(const toml::table& tab, const toml::key& ky, const T& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), opt);
}
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string& find_or(toml::table& tab, const toml::key& ky, T& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(tab[ky], opt);
}
template<typename T, typename std::enable_if<
std::is_same<T, std::string>::value, std::nullptr_t>::type = nullptr>
std::string find_or(toml::table&& tab, const toml::key& ky, T&& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(std::move(tab[ky]), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// string literal (deduced as std::string)
template<typename T, typename std::enable_if<
detail::is_string_literal<typename std::remove_reference<T>::type>::value,
std::nullptr_t>::type = nullptr>
std::string find_or(const toml::table& tab, const toml::key& ky, T&& opt)
{
if(tab.count(ky) == 0) {return std::string(opt);}
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// others (require type conversion and return type cannot be lvalue reference)
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>,
detail::negation<std::is_same<T, std::string>>,
detail::negation<detail::is_string_literal<typename std::remove_reference<T>::type>>
>::value, std::nullptr_t>::type = nullptr>
T find_or(const toml::table& tab, const toml::key& ky, T&& opt)
{
if(tab.count(ky) == 0) {return opt;}
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ============================================================================
// expect

20
toml/into.hpp Normal file
View File

@@ -0,0 +1,20 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_INTO_HPP
#define TOML11_INTO_HPP
#include "traits.hpp"
namespace toml
{
template<typename T>
struct into;
// {
// static toml::value into_toml(const T& user_defined_type)
// {
// // User-defined conversions ...
// }
// };
} // toml
#endif // TOML11_INTO_HPP

View File

@@ -124,9 +124,9 @@ using lex_escape_unicode_short = sequence<character<'u'>,
using lex_escape_unicode_long = sequence<character<'U'>,
repeat<lex_hex_dig, exactly<8>>>;
using lex_escape_seq_char = either<character<'"'>, character<'\\'>,
character<'/'>, character<'b'>,
character<'f'>, character<'n'>,
character<'r'>, character<'t'>,
character<'b'>, character<'f'>,
character<'n'>, character<'r'>,
character<'t'>,
lex_escape_unicode_short,
lex_escape_unicode_long
>;

55
toml/literal.hpp Normal file
View File

@@ -0,0 +1,55 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_LITERAL_HPP
#define TOML11_LITERAL_HPP
#include "parser.hpp"
namespace toml
{
inline namespace literals
{
inline namespace toml_literals
{
inline ::toml::value operator""_toml(const char* str, std::size_t len)
{
::toml::detail::location<std::vector<char>>
loc(/* filename = */ std::string("TOML literal encoded in a C++ code"),
/* contents = */ std::vector<char>(str, str + len));
// 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>,
::toml::detail::maybe<::toml::detail::lex_comment>,
::toml::detail::lex_newline
>, ::toml::detail::at_least<1>>;
skip_line::invoke(loc);
// if there are some whitespaces before a value, skip them.
using skip_ws = ::toml::detail::repeat<
::toml::detail::lex_ws, ::toml::detail::at_least<1>>;
skip_ws::invoke(loc);
// literal may be a bare value. try them first.
if(auto data = ::toml::detail::parse_value(loc))
{
return data.unwrap();
}
// literal is a TOML file (i.e. multiline table).
if(auto data = ::toml::detail::parse_toml_file(loc))
{
loc.iter() = loc.begin(); // rollback to the top of the literal
return ::toml::value(std::move(data.unwrap()),
::toml::detail::region<std::vector<char>>(std::move(loc)));
}
else // none of them.
{
throw ::toml::syntax_error(data.unwrap_err());
}
}
} // toml_literals
} // literals
} // toml
#endif//TOML11_LITERAL_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_REGION_H
#define TOML11_REGION_H
#ifndef TOML11_REGION_HPP
#define TOML11_REGION_HPP
#include "exception.hpp"
#include <memory>
#include <vector>
@@ -15,7 +15,7 @@ namespace toml
namespace detail
{
// helper function to avoid std::string(0, 'c')
// helper function to avoid std::string(0, 'c') or std::string(iter, iter)
template<typename Iterator>
std::string make_string(Iterator first, Iterator last)
{
@@ -28,47 +28,7 @@ inline std::string make_string(std::size_t len, char c)
return std::string(len, c);
}
// location in a container, normally in a file content.
// shared_ptr points the resource that the iter points.
// it can be used not only for resource handling, but also error message.
template<typename Container>
struct location
{
static_assert(std::is_same<char, typename Container::value_type>::value,"");
using const_iterator = typename Container::const_iterator;
using source_ptr = std::shared_ptr<const Container>;
location(std::string name, Container cont)
: source_(std::make_shared<Container>(std::move(cont))),
source_name_(std::move(name)), iter_(source_->cbegin())
{}
location(const location&) = default;
location(location&&) = default;
location& operator=(const location&) = default;
location& operator=(location&&) = default;
~location() = default;
const_iterator& iter() noexcept {return iter_;}
const_iterator iter() const noexcept {return iter_;}
const_iterator begin() const noexcept {return source_->cbegin();}
const_iterator end() const noexcept {return source_->cend();}
source_ptr const& source() const& noexcept {return source_;}
source_ptr&& source() && noexcept {return std::move(source_);}
std::string const& name() const noexcept {return source_name_;}
private:
source_ptr source_;
std::string source_name_;
const_iterator iter_;
};
// region in a container, normally in a file content.
// shared_ptr points the resource that the iter points.
// combinators returns this.
// region_base is a base class of location and region that are defined below.
// it will be used to generate better error messages.
struct region_base
{
@@ -86,12 +46,96 @@ struct region_base
virtual std::string line() const {return std::string("unknown line");}
virtual std::string line_num() const {return std::string("?");}
virtual std::size_t before() const noexcept {return 0;}
// length of the region
virtual std::size_t size() const noexcept {return 0;}
// number of characters in the line before the region
virtual std::size_t before() const noexcept {return 0;}
// number of characters in the line after the region
virtual std::size_t after() const noexcept {return 0;}
};
// location represents a position in a container, which contains a file content.
// it can be considered as a region that contains only one character.
//
// it contains pointer to the file content and iterator that points the current
// location.
template<typename Container>
struct location final : public region_base
{
static_assert(std::is_same<char, typename Container::value_type>::value,"");
using const_iterator = typename Container::const_iterator;
using source_ptr = std::shared_ptr<const Container>;
location(std::string name, Container cont)
: source_(std::make_shared<Container>(std::move(cont))),
source_name_(std::move(name)), iter_(source_->cbegin())
{}
location(const location&) = default;
location(location&&) = default;
location& operator=(const location&) = default;
location& operator=(location&&) = default;
~location() = default;
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
const_iterator& iter() noexcept {return iter_;}
const_iterator iter() const noexcept {return iter_;}
const_iterator begin() const noexcept {return source_->cbegin();}
const_iterator end() const noexcept {return source_->cend();}
std::string str() const override {return make_string(1, *this->iter());}
std::string name() const override {return source_name_;}
std::string line_num() const override
{
return std::to_string(1+std::count(this->begin(), this->iter(), '\n'));
}
std::string line() const override
{
return make_string(this->line_begin(), this->line_end());
}
const_iterator line_begin() const noexcept
{
using reverse_iterator = std::reverse_iterator<const_iterator>;
return std::find(reverse_iterator(this->iter()),
reverse_iterator(this->begin()), '\n').base();
}
const_iterator line_end() const noexcept
{
return std::find(this->iter(), this->end(), '\n');
}
// location is always points a character. so the size is 1.
std::size_t size() const noexcept override
{
return 1u;
}
std::size_t before() const noexcept override
{
return std::distance(this->line_begin(), this->iter());
}
std::size_t after() const noexcept override
{
return std::distance(this->iter(), this->line_end());
}
source_ptr const& source() const& noexcept {return source_;}
source_ptr&& source() && noexcept {return std::move(source_);}
private:
source_ptr source_;
std::string source_name_;
const_iterator iter_;
};
// region represents a range in a container, which contains a file content.
//
// 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
{
@@ -200,96 +244,67 @@ struct region final : public region_base
// to show a better error message.
inline std::string format_underline(const std::string& message,
const region_base& reg, const std::string& comment_for_underline,
std::vector<std::pair<region_base const*, std::string>> reg_com,
std::vector<std::string> helps = {})
{
assert(!reg_com.empty());
#ifdef _WIN32
const auto newline = "\r\n";
#else
const char newline = '\n';
#endif
const auto line = reg.line();
const auto line_number = reg.line_num();
std::string retval;
retval += message;
retval += newline;
retval += " --> ";
retval += reg.name();
retval += newline;
retval += ' ';
retval += line_number;
retval += " | ";
retval += line;
retval += newline;
retval += make_string(line_number.size() + 1, ' ');
retval += " | ";
retval += make_string(reg.before(), ' ');
retval += make_string(reg.size(), '~');
retval += ' ';
retval += comment_for_underline;
if(helps.size() != 0)
{
retval += newline;
retval += make_string(line_number.size() + 1, ' ');
retval += " | ";
for(const auto help : helps)
const auto line_num_width = 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)
{
retval += newline;
retval += "Hint: ";
retval += help;
return lhs.first->line_num().size() < rhs.first->line_num().size();
}
}
return retval;
}
// to show a better error message.
inline std::string format_underline(const std::string& message,
const region_base& reg1, const std::string& comment_for_underline1,
const region_base& reg2, const std::string& comment_for_underline2,
std::vector<std::string> helps = {})
{
#ifdef _WIN32
const auto newline = "\r\n";
#else
const char newline = '\n';
#endif
const auto line1 = reg1.line();
const auto line_number1 = reg1.line_num();
const auto line2 = reg2.line();
const auto line_number2 = reg2.line_num();
const auto line_num_width =
std::max(line_number1.size(), line_number2.size());
)->first->line_num().size();
std::ostringstream retval;
retval << message << newline;
retval << " --> " << reg1.name() << newline;
// ---------------------------------------
retval << ' ' << std::setw(line_num_width) << line_number1;
retval << " | " << line1 << newline;
retval << make_string(line_num_width + 1, ' ');
retval << " | ";
retval << make_string(reg1.before(), ' ');
retval << make_string(reg1.size(), '~');
retval << ' ';
retval << comment_for_underline1 << newline;
// ---------------------------------------
if(reg2.name() != reg1.name())
for(std::size_t i=0; i<reg_com.size(); ++i)
{
retval << " --> " << reg2.name() << newline;
if(i!=0 && reg_com.at(i-1).first->name() == reg_com.at(i).first->name())
{
retval << newline << " ..." << newline;
}
else
{
if(i != 0) {retval << newline;}
retval << " --> " << reg_com.at(i).first->name() << newline;
}
const region_base* const reg = reg_com.at(i).first;
const std::string& comment = reg_com.at(i).second;
retval << ' ' << std::setw(line_num_width) << reg->line_num();
retval << " | " << reg->line() << newline;
retval << make_string(line_num_width + 1, ' ');
retval << " | " << make_string(reg->before(), ' ');
if(reg->size() == 1)
{
// invalid
// ^------
retval << '^';
retval << make_string(reg->after(), '-');
}
else
{
// invalid
// ~~~~~~~
retval << make_string(reg->size(), '~');
}
retval << ' ';
retval << comment;
}
else
{
retval << " ..." << newline;
}
retval << ' ' << std::setw(line_num_width) << line_number2;
retval << " | " << line2 << newline;
retval << make_string(line_num_width + 1, ' ');
retval << " | ";
retval << make_string(reg2.before(), ' ');
retval << make_string(reg2.size(), '~');
retval << ' ';
retval << comment_for_underline2;
if(helps.size() != 0)
{
retval << newline;
@@ -305,62 +320,6 @@ inline std::string format_underline(const std::string& message,
return retval.str();
}
// to show a better error message.
template<typename Container>
std::string
format_underline(const std::string& message, const location<Container>& loc,
const std::string& comment_for_underline,
std::vector<std::string> helps = {})
{
#ifdef _WIN32
const auto newline = "\r\n";
#else
const char newline = '\n';
#endif
using const_iterator = typename location<Container>::const_iterator;
using reverse_iterator = std::reverse_iterator<const_iterator>;
const auto line_begin = std::find(reverse_iterator(loc.iter()),
reverse_iterator(loc.begin()),
'\n').base();
const auto line_end = std::find(loc.iter(), loc.end(), '\n');
const auto line_number = std::to_string(
1 + std::count(loc.begin(), loc.iter(), '\n'));
std::string retval;
retval += message;
retval += newline;
retval += " --> ";
retval += loc.name();
retval += newline;
retval += ' ';
retval += line_number;
retval += " | ";
retval += make_string(line_begin, line_end);
retval += newline;
retval += make_string(line_number.size() + 1, ' ');
retval += " | ";
retval += make_string(std::distance(line_begin, loc.iter()),' ');
retval += '^';
retval += make_string(std::distance(loc.iter(), line_end), '-');
retval += ' ';
retval += comment_for_underline;
if(helps.size() != 0)
{
retval += newline;
retval += make_string(line_number.size() + 1, ' ');
retval += " | ";
for(const auto help : helps)
{
retval += newline;
retval += "Hint: ";
retval += help;
}
}
return retval;
}
} // detail
} // toml
#endif// TOML11_REGION_H

View File

@@ -1,7 +1,8 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_RESULT_H
#define TOML11_RESULT_H
#ifndef TOML11_RESULT_HPP
#define TOML11_RESULT_HPP
#include "traits.hpp"
#include <type_traits>
#include <stdexcept>
#include <utility>
@@ -13,19 +14,6 @@
namespace toml
{
#if __cplusplus >= 201703L
template<typename F, typename ... Args>
using return_type_of_t = std::invoke_result_t<F, Args...>;
#else
// result_of is deprecated after C++17
template<typename F, typename ... Args>
using return_type_of_t = typename std::result_of<F(Args...)>::type;
#endif
template<typename T>
struct success
{
@@ -441,21 +429,21 @@ struct result
// F: T -> U
// retval: result<U, E>
template<typename F>
result<return_type_of_t<F, value_type&>, error_type>
result<detail::return_type_of_t<F, value_type&>, error_type>
map(F&& f) &
{
if(this->is_ok()){return ok(f(this->as_ok()));}
return err(this->as_err());
}
template<typename F>
result<return_type_of_t<F, value_type const&>, error_type>
result<detail::return_type_of_t<F, value_type const&>, error_type>
map(F&& f) const&
{
if(this->is_ok()){return ok(f(this->as_ok()));}
return err(this->as_err());
}
template<typename F>
result<return_type_of_t<F, value_type &&>, error_type>
result<detail::return_type_of_t<F, value_type &&>, error_type>
map(F&& f) &&
{
if(this->is_ok()){return ok(f(std::move(this->as_ok())));}
@@ -466,21 +454,21 @@ struct result
// F: E -> F
// retval: result<T, F>
template<typename F>
result<value_type, return_type_of_t<F, error_type&>>
result<value_type, detail::return_type_of_t<F, error_type&>>
map_err(F&& f) &
{
if(this->is_err()){return err(f(this->as_err()));}
return ok(this->as_ok());
}
template<typename F>
result<value_type, return_type_of_t<F, error_type const&>>
result<value_type, detail::return_type_of_t<F, error_type const&>>
map_err(F&& f) const&
{
if(this->is_err()){return err(f(this->as_err()));}
return ok(this->as_ok());
}
template<typename F>
result<value_type, return_type_of_t<F, error_type&&>>
result<value_type, detail::return_type_of_t<F, error_type&&>>
map_err(F&& f) &&
{
if(this->is_err()){return err(f(std::move(this->as_err())));}
@@ -491,21 +479,21 @@ struct result
// F: T -> U
// retval: U
template<typename F, typename U>
return_type_of_t<F, value_type&>
detail::return_type_of_t<F, value_type&>
map_or_else(F&& f, U&& opt) &
{
if(this->is_err()){return std::forward<U>(opt);}
return f(this->as_ok());
}
template<typename F, typename U>
return_type_of_t<F, value_type const&>
detail::return_type_of_t<F, value_type const&>
map_or_else(F&& f, U&& opt) const&
{
if(this->is_err()){return std::forward<U>(opt);}
return f(this->as_ok());
}
template<typename F, typename U>
return_type_of_t<F, value_type&&>
detail::return_type_of_t<F, value_type&&>
map_or_else(F&& f, U&& opt) &&
{
if(this->is_err()){return std::forward<U>(opt);}
@@ -516,21 +504,21 @@ struct result
// F: E -> U
// retval: U
template<typename F, typename U>
return_type_of_t<F, error_type&>
detail::return_type_of_t<F, error_type&>
map_err_or_else(F&& f, U&& opt) &
{
if(this->is_ok()){return std::forward<U>(opt);}
return f(this->as_err());
}
template<typename F, typename U>
return_type_of_t<F, error_type const&>
detail::return_type_of_t<F, error_type const&>
map_err_or_else(F&& f, U&& opt) const&
{
if(this->is_ok()){return std::forward<U>(opt);}
return f(this->as_err());
}
template<typename F, typename U>
return_type_of_t<F, error_type&&>
detail::return_type_of_t<F, error_type&&>
map_err_or_else(F&& f, U&& opt) &&
{
if(this->is_ok()){return std::forward<U>(opt);}
@@ -542,21 +530,21 @@ struct result
// toml::err(error_type) should be convertible to U.
// normally, type U is another result<S, F> and E is convertible to F
template<typename F>
return_type_of_t<F, value_type&>
detail::return_type_of_t<F, value_type&>
and_then(F&& f) &
{
if(this->is_ok()){return f(this->as_ok());}
return err(this->as_err());
}
template<typename F>
return_type_of_t<F, value_type const&>
detail::return_type_of_t<F, value_type const&>
and_then(F&& f) const&
{
if(this->is_ok()){return f(this->as_ok());}
return err(this->as_err());
}
template<typename F>
return_type_of_t<F, value_type&&>
detail::return_type_of_t<F, value_type&&>
and_then(F&& f) &&
{
if(this->is_ok()){return f(std::move(this->as_ok()));}
@@ -568,21 +556,21 @@ struct result
// toml::ok(value_type) should be convertible to U.
// normally, type U is another result<S, F> and T is convertible to S
template<typename F>
return_type_of_t<F, error_type&>
detail::return_type_of_t<F, error_type&>
or_else(F&& f) &
{
if(this->is_err()){return f(this->as_err());}
return ok(this->as_ok());
}
template<typename F>
return_type_of_t<F, error_type const&>
detail::return_type_of_t<F, error_type const&>
or_else(F&& f) const&
{
if(this->is_err()){return f(this->as_err());}
return ok(this->as_ok());
}
template<typename F>
return_type_of_t<F, error_type&&>
detail::return_type_of_t<F, error_type&&>
or_else(F&& f) &&
{
if(this->is_err()){return f(std::move(this->as_err()));}

527
toml/serializer.hpp Normal file
View File

@@ -0,0 +1,527 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_SERIALIZER_HPP
#define TOML11_SERIALIZER_HPP
#include "value.hpp"
#include "lexer.hpp"
#include <limits>
#include <cstdio>
namespace toml
{
struct serializer
{
serializer(const std::size_t w = 80,
const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
const bool can_be_inlined = false,
std::vector<toml::key> ks = {})
: can_be_inlined_(can_be_inlined), float_prec_(float_prec), width_(w),
keys_(std::move(ks))
{}
~serializer() = default;
std::string operator()(const toml::boolean& b) const
{
return b ? "true" : "false";
}
std::string operator()(const integer i) const
{
return std::to_string(i);
}
std::string operator()(const toml::floating f) const
{
const auto fmt = "%.*g";
const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f);
std::vector<char> buf(bsz + 1, '\0'); // +1 for null character(\0)
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
{
token += '0';
}
const auto e = std::find_if(token.cbegin(), token.cend(),
[](const char c) -> bool {
return c == 'E' || c == 'e';
});
if(e == token.cend())
{
return token; // there is no exponent part. just return it.
}
// zero-prefix in an exponent is not allowed in TOML.
// remove it if it exists.
bool sign_exists = false;
std::size_t zero_prefix = 0;
for(auto iter = std::next(e), iend = token.cend(); iter != iend; ++iter)
{
if(*iter == '+' || *iter == '-'){sign_exists = true; continue;}
if(*iter == '0'){zero_prefix += 1;}
else {break;}
}
if(zero_prefix != 0)
{
const auto offset = std::distance(token.cbegin(), e) +
(sign_exists ? 2 : 1);
token.erase(offset, zero_prefix);
}
return token;
}
std::string operator()(const string& s) const
{
if(s.kind == string_t::basic)
{
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend())
{
// if linefeed is contained, make it multiline-string.
const std::string open("\"\"\"\n");
const std::string close("\\\n\"\"\"");
return open + this->escape_ml_basic_string(s.str) + close;
}
// no linefeed. try to make it oneline-string.
std::string oneline = this->escape_basic_string(s.str);
if(oneline.size() + 2 < width_ || width_ < 2)
{
const std::string quote("\"");
return quote + oneline + quote;
}
// the line is too long compared to the specified width.
// split it into multiple lines.
std::string token("\"\"\"\n");
while(!oneline.empty())
{
if(oneline.size() < width_)
{
token += oneline;
oneline.clear();
}
else if(oneline.at(width_-2) == '\\')
{
token += oneline.substr(0, width_-2);
token += "\\\n";
oneline.erase(0, width_-2);
}
else
{
token += oneline.substr(0, width_-1);
token += "\\\n";
oneline.erase(0, width_-1);
}
}
return token + std::string("\\\n\"\"\"");
}
else // the string `s` is literal-string.
{
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
{
const std::string open("'''\n");
const std::string close("'''");
return open + s.str + close;
}
else
{
const std::string quote("'");
return quote + s.str + quote;
}
}
}
std::string operator()(const local_date& d) const
{
std::ostringstream oss;
oss << d;
return oss.str();
}
std::string operator()(const local_time& t) const
{
std::ostringstream oss;
oss << t;
return oss.str();
}
std::string operator()(const local_datetime& dt) const
{
std::ostringstream oss;
oss << dt;
return oss.str();
}
std::string operator()(const offset_datetime& odt) const
{
std::ostringstream oss;
oss << odt;
return oss.str();
}
std::string operator()(const array& v) const
{
if(!v.empty() && v.front().is(value_t::Table))// v is an array of tables
{
// if it's not inlined, we need to add `[[table.key]]`.
// but if it can be inlined, we need `table.key = [...]`.
if(this->can_be_inlined_)
{
std::string token;
if(!keys_.empty())
{
token += this->serialize_key(keys_.back());
token += " = ";
}
bool width_exceeds = false;
token += "[\n";
for(const auto& item : v)
{
const auto t =
this->make_inline_table(item.cast<value_t::Table>());
if(t.size() + 1 > width_ || // +1 for the last comma {...},
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
{
width_exceeds = true;
break;
}
token += t;
token += ",\n";
}
if(!width_exceeds)
{
token += "]\n";
return token;
}
// if width_exceeds, serialize it as [[array.of.tables]].
}
std::string token;
for(const auto& item : v)
{
token += "[[";
token += this->serialize_dotted_key(keys_);
token += "]]\n";
token += this->make_multiline_table(item.cast<value_t::Table>());
}
return token;
}
if(v.empty())
{
return std::string("[]");
}
// not an array of tables. normal array. first, try to make it inline.
{
const auto inl = this->make_inline_array(v);
if(inl.size() < this->width_ &&
std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend())
{
return inl;
}
}
// if the length exceeds this->width_, print multiline array
std::string token;
std::string current_line;
token += "[\n";
for(const auto& item : v)
{
auto next_elem = toml::visit(*this, item);
// newline between array-value and comma is not allowed
if(next_elem.back() == '\n'){next_elem.pop_back();}
if(current_line.size() + next_elem.size() + 1 < this->width_)
{
current_line += next_elem;
current_line += ',';
}
else if(current_line.empty())
{
// the next elem cannot be within the width.
token += next_elem;
token += ",\n";
// keep current line empty
}
else // current_line has some tokens and it exceeds width
{
assert(current_line.back() == ',');
token += current_line;
token += '\n';
current_line = next_elem;
current_line += ',';
}
}
if(!current_line.empty())
{
if(current_line.back() != '\n') {current_line += '\n';}
token += current_line;
}
token += "]\n";
return token;
}
std::string operator()(const table& v) const
{
if(this->can_be_inlined_)
{
std::string token;
if(!this->keys_.empty())
{
token += this->serialize_key(this->keys_.back());
token += " = ";
}
token += this->make_inline_table(v);
if(token.size() < this->width_)
{
return token;
}
}
std::string token;
if(!keys_.empty())
{
token += '[';
token += this->serialize_dotted_key(keys_);
token += "]\n";
}
token += this->make_multiline_table(v);
return token;
}
private:
std::string serialize_key(const toml::key& key) const
{
detail::location<toml::key> loc(key, key);
detail::lex_unquoted_key::invoke(loc);
if(loc.iter() == loc.end())
{
return key; // all the tokens are consumed. the key is unquoted-key.
}
std::string token("\"");
token += this->escape_basic_string(key);
token += "\"";
return token;
}
std::string serialize_dotted_key(const std::vector<toml::key>& keys) const
{
std::string token;
if(keys.empty()){return token;}
for(const auto& k : keys)
{
token += this->serialize_key(k);
token += '.';
}
token.erase(token.size() - 1, 1); // remove trailing `.`
return token;
}
std::string escape_basic_string(const std::string& s) const
{
//XXX assuming `s` is a valid utf-8 sequence.
std::string retval;
for(const char c : s)
{
switch(c)
{
case '\\': {retval += "\\\\"; break;}
case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;}
case '\n': {retval += "\\n"; break;}
case '\r': {retval += "\\r"; break;}
default : {retval += c; break;}
}
}
return retval;
}
std::string escape_ml_basic_string(const std::string& s) const
{
std::string retval;
for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i)
{
switch(*i)
{
case '\\': {retval += "\\\\"; break;}
case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;}
case '\n': {retval += "\n"; break;}
case '\r':
{
if(std::next(i) != e && *std::next(i) == '\n')
{
retval += "\r\n";
++i;
}
else
{
retval += "\\r";
}
break;
}
default: {retval += *i; break;}
}
}
return retval;
}
std::string make_inline_array(const array& v) const
{
std::string token;
token += '[';
bool is_first = true;
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 += ']';
return token;
}
std::string make_inline_table(const table& v) const
{
assert(this->can_be_inlined_);
std::string token;
token += '{';
bool is_first = true;
for(const auto& kv : v)
{
// in inline tables, trailing comma is not allowed (toml-lang #569).
if(is_first) {is_first = false;} else {token += ',';}
token += this->serialize_key(kv.first);
token += '=';
token += visit(serializer(std::numeric_limits<std::size_t>::max(),
this->float_prec_, true), kv.second);
}
token += '}';
return token;
}
std::string make_multiline_table(const table& v) const
{
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]
for(const auto kv : v)
{
if(kv.second.is(value_t::Table) || is_array_of_tables(kv.second))
{
continue;
}
const auto key_and_sep = this->serialize_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);
if(token.back() != '\n')
{
token += '\n';
}
}
// normal tables / array of tables
// after multiline table appeared, the other tables cannot be inline
// because the table would be assigned into the table.
// [foo]
// ...
// bar = {...} # <- bar will be a member of [foo].
bool multiline_table_printed = false;
for(const auto& kv : v)
{
if(!kv.second.is(value_t::Table) && !is_array_of_tables(kv.second))
{
continue; // other stuff are already serialized. skip them.
}
std::vector<toml::key> ks(this->keys_);
ks.push_back(kv.first);
auto tmp = visit(serializer(
this->width_, this->float_prec_, !multiline_table_printed, ks),
kv.second);
if((!multiline_table_printed) &&
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend())
{
multiline_table_printed = true;
}
else
{
// still inline tables only.
tmp += '\n';
}
token += tmp;
}
return token;
}
bool is_array_of_tables(const value& v) const
{
if(!v.is(value_t::Array)) {return false;}
const auto& a = v.cast<value_t::Array>();
return !a.empty() && a.front().is(value_t::Table);
}
private:
bool can_be_inlined_;
int float_prec_;
std::size_t width_;
std::vector<toml::key> keys_;
};
inline std::string
format(const value& v, std::size_t w = 80,
int fprec = std::numeric_limits<toml::floating>::max_digits10,
bool force_inline = false)
{
// if value is a table, it is considered to be a root object.
// the root object can't be an inline table. so pass false. otherwise, true.
return visit(serializer(w, fprec, (!v.is_table()) || force_inline), v);
}
inline std::string
format(const table& t, std::size_t w = 80,
int fprec = std::numeric_limits<toml::floating>::max_digits10,
bool force_inline = false)
{
return serializer(w, fprec, force_inline)(t);
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const value& v)
{
// get status of std::setw().
const std::size_t w = os.width();
const int fprec = os.precision();
os.width(0);
// the root object can't be an inline table. so pass `false`.
os << visit(serializer(w, fprec, false), v);
return os;
}
template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const table& v)
{
// get status of std::setw().
const std::size_t w = os.width();
const int fprec = os.precision();
os.width(0);
// the root object can't be an inline table. so pass `false`.
os << serializer(w, fprec, false)(v);
return os;
}
} // toml
#endif// TOML11_SERIALIZER_HPP

View File

@@ -1,7 +1,7 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_STRING_H
#define TOML11_STRING_H
#ifndef TOML11_STRING_HPP
#define TOML11_STRING_HPP
#include <string>
#include <cstdint>

View File

@@ -1,43 +0,0 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_TO_TOML
#define TOML11_TO_TOML
#include "value.hpp"
namespace toml
{
template<typename T>
inline value to_toml(T&& x)
{
return value(std::forward<T>(x));
}
template<typename T>
inline value to_toml(T&& x, string_t kind)
{
return value(std::forward<T>(x), kind);
}
inline value to_toml(local_date d, local_time t)
{
return value(local_datetime(d, t));
}
inline value to_toml(local_date d, local_time t, time_offset ofs)
{
return value(offset_datetime(d, t, ofs));
}
template<typename ... Ts>
inline value to_toml(Ts&& ... xs)
{
return value(toml::array{toml::value(std::forward<Ts>(xs)) ... });
}
inline value to_toml(std::initializer_list<std::pair<std::string, toml::value>> xs)
{
return value(toml::table(xs.begin(), xs.end()));
}
} // toml
#endif // TOML11_TO_TOML

View File

@@ -1,7 +1,7 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_TRAITS
#define TOML11_TRAITS
#ifndef TOML11_TRAITS_HPP
#define TOML11_TRAITS_HPP
#include <type_traits>
#include <utility>
#include <chrono>
@@ -9,12 +9,18 @@
namespace toml
{
class value; // forward decl
namespace detail
{
template<typename T>
using unwrap_t = typename std::decay<T>::type;
// ---------------------------------------------------------------------------
// check whether type T is a kind of container/map class
struct has_iterator_impl
{
template<typename T> static std::true_type check(typename T::iterator*);
@@ -42,6 +48,22 @@ struct has_resize_method_impl
template<typename T> static std::false_type check(...);
};
struct has_from_toml_method_impl
{
template<typename T>
static std::true_type check(
decltype(std::declval<T>().from_toml(std::declval<::toml::value>()))*);
template<typename T>
static std::false_type check(...);
};
struct has_into_toml_method_impl
{
template<typename T>
static std::true_type check(decltype(std::declval<T>().into_toml())*);
template<typename T>
static std::false_type check(...);
};
/// Intel C++ compiler can not use decltype in parent class declaration, here
/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076
#ifdef __INTEL_COMPILER
@@ -59,10 +81,21 @@ struct has_mapped_type : decltype(has_mapped_type_impl::check<T>(nullptr)){};
template<typename T>
struct has_resize_method : decltype(has_resize_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_from_toml_method
: decltype(has_from_toml_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_into_toml_method
: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
#ifdef __INTEL_COMPILER
#undef decltype(...)
#endif
// ---------------------------------------------------------------------------
// C++17 and/or/not
template<typename ...> struct conjunction : std::true_type{};
template<typename T> struct conjunction<T> : T{};
template<typename T, typename ... Ts>
@@ -80,6 +113,9 @@ struct disjunction<T, Ts...> :
template<typename T>
struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{};
// ---------------------------------------------------------------------------
// normal type checker
template<typename T> struct is_std_pair : std::false_type{};
template<typename T1, typename T2>
struct is_std_pair<std::pair<T1, T2>> : std::true_type{};
@@ -92,7 +128,9 @@ template<typename T> struct is_chrono_duration: std::false_type{};
template<typename Rep, typename Period>
struct is_chrono_duration<std::chrono::duration<Rep, Period>>: std::true_type{};
// to use toml::get<std::tuple<T1, T2, ...>> in C++11
// ---------------------------------------------------------------------------
// C++14 index_sequence
template<std::size_t ... Ns> struct index_sequence{};
template<typename IS, std::size_t N> struct push_back_index_sequence{};
@@ -116,6 +154,36 @@ struct index_sequence_maker<0>
template<std::size_t N>
using make_index_sequence = typename index_sequence_maker<N-1>::type;
// ---------------------------------------------------------------------------
// return_type_of_t
#if __cplusplus >= 201703L
template<typename F, typename ... Args>
using return_type_of_t = std::invoke_result_t<F, Args...>;
#else
// result_of is deprecated after C++17
template<typename F, typename ... Args>
using return_type_of_t = typename std::result_of<F(Args...)>::type;
#endif
// ---------------------------------------------------------------------------
// is_string_literal
//
// to use this, pass `typename remove_reference<T>::type` to T.
template<typename T>
struct is_string_literal:
disjunction<
std::is_same<const char*, T>,
conjunction<
std::is_array<T>,
std::is_same<const char, typename std::remove_extent<T>::type>
>
>{};
}// detail
}//toml
#endif // TOML_TRAITS

View File

@@ -1,7 +1,7 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_TYPES_H
#define TOML11_TYPES_H
#ifndef TOML11_TYPES_HPP
#define TOML11_TYPES_HPP
#include "datetime.hpp"
#include "string.hpp"
#include "traits.hpp"
@@ -137,17 +137,6 @@ template<> struct toml_value_t<LocalDate >{static constexpr value_t value =
template<> struct toml_value_t<LocalTime >{static constexpr value_t value = value_t::LocalTime ;};
template<> struct toml_value_t<Array >{static constexpr value_t value = value_t::Array ;};
template<> struct toml_value_t<Table >{static constexpr value_t value = value_t::Table ;};
template<typename T> constexpr value_t toml_value_t<T>::value;
constexpr value_t toml_value_t<Boolean >::value;
constexpr value_t toml_value_t<Integer >::value;
constexpr value_t toml_value_t<Float >::value;
constexpr value_t toml_value_t<String >::value;
constexpr value_t toml_value_t<OffsetDatetime>::value;
constexpr value_t toml_value_t<LocalDatetime >::value;
constexpr value_t toml_value_t<LocalDate >::value;
constexpr value_t toml_value_t<LocalTime >::value;
constexpr value_t toml_value_t<Array >::value;
constexpr value_t toml_value_t<Table >::value;
template<typename T>
struct is_exact_toml_type : disjunction<

View File

@@ -1,12 +1,22 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_UTILITY
#define TOML11_UTILITY
#ifndef TOML11_UTILITY_HPP
#define TOML11_UTILITY_HPP
#include "traits.hpp"
#include <utility>
#include <memory>
#include <sstream>
#if __cplusplus >= 201402L
# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]]
#elif defined(__GNUC__)
# define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg)))
#elif defined(_MSC_VER)
# define TOML11_MARK_AS_DEPRECATED(msg) __declspec(deprecated(msg))
#else
# define TOML11_MARK_AS_DEPRECATED
#endif
namespace toml
{
@@ -29,8 +39,9 @@ inline void resize_impl(T& container, std::size_t N, std::true_type)
template<typename T>
inline void resize_impl(T& container, std::size_t N, std::false_type)
{
if(container.size() >= N) return;
else throw std::invalid_argument("not resizable type");
if(container.size() >= N) {return;}
throw std::invalid_argument("not resizable type");
}
} // detail
@@ -38,8 +49,9 @@ inline void resize_impl(T& container, std::size_t N, std::false_type)
template<typename T>
inline void resize(T& container, std::size_t N)
{
if(container.size() == N) return;
else return detail::resize_impl(container, N, detail::has_resize_method<T>());
if(container.size() == N) {return;}
return detail::resize_impl(container, N, detail::has_resize_method<T>());
}
namespace detail
@@ -73,7 +85,5 @@ T from_string(const std::string& str, U&& opt)
return v;
}
}// toml
#endif // TOML11_UTILITY

View File

@@ -1,8 +1,9 @@
// Copyright Toru Niina 2017.
// Distributed under the MIT License.
#ifndef TOML11_VALUE
#define TOML11_VALUE
#ifndef TOML11_VALUE_HPP
#define TOML11_VALUE_HPP
#include "traits.hpp"
#include "into.hpp"
#include "utility.hpp"
#include "exception.hpp"
#include "storage.hpp"
@@ -21,8 +22,8 @@ namespace detail
{
// to show error messages. not recommended for users.
region_base const& get_region(const value&);
// ditto.
void assign_keeping_region(value&, value);
template<typename Region>
void change_region(value&, Region&&);
}// detail
template<typename T>
@@ -473,7 +474,7 @@ class value
region_info_(std::make_shared<region_base>(region_base{}))
{
array ary; ary.reserve(list.size());
for(auto& elem : list) {ary.emplace_back(std::move(elem));}
for(const auto& elem : list) {ary.emplace_back(elem);}
assigner(this->array_, std::move(ary));
}
template<typename T, typename std::enable_if<detail::is_container<T>::value,
@@ -485,7 +486,7 @@ class value
this->region_info_ = std::make_shared<region_base>(region_base{});
array ary; ary.reserve(list.size());
for(auto& elem : list) {ary.emplace_back(std::move(elem));}
for(const auto& elem : list) {ary.emplace_back(elem);}
assigner(this->array_, std::move(ary));
return *this;
}
@@ -533,10 +534,62 @@ class value
return *this;
}
// user-defined =========================================================
// convert using into_toml() method -------------------------------------
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>, // not a toml::value
detail::has_into_toml_method<T> // but has `into_toml` method
>::value, std::nullptr_t>::type = nullptr>
value(const T& ud): value(ud.into_toml()) {}
template<typename T, typename std::enable_if<detail::conjunction<
detail::negation<detail::is_exact_toml_type<T>>, // not a toml::value
detail::has_into_toml_method<T> // but has `into_toml` method
>::value, std::nullptr_t>::type = nullptr>
value& operator=(const T& ud)
{
*this = ud.into_toml();
return *this;
}
// convert using into<T> struct -----------------------------------------
template<typename T, typename std::enable_if<
detail::negation<detail::is_exact_toml_type<T>>::value,
std::nullptr_t>::type = nullptr,
std::size_t S = sizeof(::toml::into<T>)>
value(const T& ud): value(::toml::into<T>::into_toml(ud)) {}
template<typename T, typename std::enable_if<
detail::negation<detail::is_exact_toml_type<T>>::value,
std::nullptr_t>::type = nullptr,
std::size_t S = sizeof(::toml::into<T>)>
value& operator=(const T& ud)
{
*this = ::toml::into<T>::into_toml(ud);
return *this;
}
// type checking and casting ============================================
template<typename T>
bool is() const noexcept {return value_traits<T>::type_index == this->type_;}
bool is(value_t t) const noexcept {return t == this->type_;}
bool is_uninitialized() const noexcept {return this->is(value_t::Empty );}
bool is_boolean() const noexcept {return this->is(value_t::Boolean );}
bool is_integer() const noexcept {return this->is(value_t::Integer );}
bool is_float() const noexcept {return this->is(value_t::Float );}
bool is_string() const noexcept {return this->is(value_t::String );}
bool is_offset_datetime() const noexcept {return this->is(value_t::OffsetDatetime);}
bool is_local_datetime() const noexcept {return this->is(value_t::LocalDatetime );}
bool is_local_date() const noexcept {return this->is(value_t::LocalDate );}
bool is_local_time() const noexcept {return this->is(value_t::LocalTime );}
bool is_array() const noexcept {return this->is(value_t::Array );}
bool is_table() const noexcept {return this->is(value_t::Table );}
value_t type() const {return type_;}
template<value_t T>
@@ -562,8 +615,8 @@ class value
// for error messages
friend region_base const& detail::get_region(const value&);
// to see why it's here, see detail::insert_nested_key.
friend void detail::assign_keeping_region(value&, value);
template<typename Region>
friend void detail::change_region(value&, Region&&);
template<value_t T>
struct switch_cast;
@@ -599,35 +652,21 @@ inline region_base const& get_region(const value& v)
{
return *(v.region_info_);
}
// If we keep region information after assigning another toml::* types, the
// error message become different from the actual value contained.
// To avoid this kind of confusing phenomena, the default assigners clear the
// old region_info_. But this functionality is actually needed deep inside of
// parser, so if you want to see the usecase, see toml::detail::insert_nested_key
// defined in toml/parser.hpp.
inline void assign_keeping_region(value& v, value other)
template<typename Region>
void change_region(value& v, Region&& reg)
{
v.cleanup(); // this keeps region info
// keep region_info_ intact
v.type_ = other.type();
switch(v.type())
{
case value_t::Boolean : ::toml::value::assigner(v.boolean_ , other.boolean_ ); break;
case value_t::Integer : ::toml::value::assigner(v.integer_ , other.integer_ ); break;
case value_t::Float : ::toml::value::assigner(v.floating_ , other.floating_ ); break;
case value_t::String : ::toml::value::assigner(v.string_ , other.string_ ); break;
case value_t::OffsetDatetime: ::toml::value::assigner(v.offset_datetime_, other.offset_datetime_); break;
case value_t::LocalDatetime : ::toml::value::assigner(v.local_datetime_ , other.local_datetime_ ); break;
case value_t::LocalDate : ::toml::value::assigner(v.local_date_ , other.local_date_ ); break;
case value_t::LocalTime : ::toml::value::assigner(v.local_time_ , other.local_time_ ); break;
case value_t::Array : ::toml::value::assigner(v.array_ , other.array_ ); break;
case value_t::Table : ::toml::value::assigner(v.table_ , other.table_ ); break;
default: break;
}
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;
return;
}
}// detail
}// detail
template<> struct value::switch_cast<value_t::Boolean>
{
@@ -695,9 +734,11 @@ typename detail::toml_default_type<T>::type& value::cast() &
{
if(T != this->type_)
{
throw type_error(format_underline(concat_to_string(
"[error] toml::value bad_cast to ", T), *region_info_,
concat_to_string("the actual type is ", this->type_)));
throw type_error(detail::format_underline(concat_to_string(
"[error] toml::value bad_cast to ", T), {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}));
}
return switch_cast<T>::invoke(*this);
}
@@ -706,9 +747,11 @@ typename detail::toml_default_type<T>::type const& value::cast() const&
{
if(T != this->type_)
{
throw type_error(format_underline(concat_to_string(
"[error] toml::value bad_cast to ", T), *region_info_,
concat_to_string("the actual type is ", this->type_)));
throw type_error(detail::format_underline(concat_to_string(
"[error] toml::value bad_cast to ", T), {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}));
}
return switch_cast<T>::invoke(*this);
}
@@ -717,9 +760,11 @@ typename detail::toml_default_type<T>::type&& value::cast() &&
{
if(T != this->type_)
{
throw type_error(format_underline(concat_to_string(
"[error] toml::value bad_cast to ", T), *region_info_,
concat_to_string("the actual type is ", this->type_)));
throw type_error(detail::format_underline(concat_to_string(
"[error] toml::value bad_cast to ", T), {
{this->region_info_.get(),
concat_to_string("the actual type is ", this->type_)}
}));
}
return switch_cast<T>::invoke(std::move(*this));
}
@@ -767,6 +812,8 @@ inline bool operator<(const toml::value& lhs, const toml::value& rhs)
return lhs.cast<value_t::Float >() < rhs.cast<value_t::Float >();
case value_t::String :
return lhs.cast<value_t::String >() < rhs.cast<value_t::String >();
case value_t::OffsetDatetime:
return lhs.cast<value_t::OffsetDatetime>() < rhs.cast<value_t::OffsetDatetime>();
case value_t::LocalDatetime:
return lhs.cast<value_t::LocalDatetime>() < rhs.cast<value_t::LocalDatetime>();
case value_t::LocalDate:
@@ -801,17 +848,111 @@ inline bool operator>=(const toml::value& lhs, const toml::value& rhs)
}
inline std::string format_error(const std::string& err_msg,
const toml::value& v, const std::string& comment)
const toml::value& v, const std::string& comment,
std::vector<std::string> hints = {})
{
return detail::format_underline(err_msg, detail::get_region(v), comment);
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));
}
inline std::string format_error(const std::string& err_msg,
const toml::value& v1, const std::string& comment1,
const toml::value& v2, const std::string& comment2)
const toml::value& v2, const std::string& comment2,
std::vector<std::string> hints = {})
{
return detail::format_underline(err_msg, detail::get_region(v1), comment1,
detail::get_region(v2), comment2);
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::move(hints));
}
inline std::string format_error(const std::string& err_msg,
const toml::value& v1, const std::string& comment1,
const toml::value& v2, const std::string& comment2,
const toml::value& v3, const std::string& comment3,
std::vector<std::string> hints = {})
{
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}
}, std::move(hints));
}
template<typename Visitor>
detail::return_type_of_t<Visitor, const toml::boolean&>
visit(Visitor&& visitor, const toml::value& v)
{
switch(v.type())
{
case value_t::Boolean : {return visitor(v.cast<value_t::Boolean >());}
case value_t::Integer : {return visitor(v.cast<value_t::Integer >());}
case value_t::Float : {return visitor(v.cast<value_t::Float >());}
case value_t::String : {return visitor(v.cast<value_t::String >());}
case value_t::OffsetDatetime: {return visitor(v.cast<value_t::OffsetDatetime>());}
case value_t::LocalDatetime : {return visitor(v.cast<value_t::LocalDatetime >());}
case value_t::LocalDate : {return visitor(v.cast<value_t::LocalDate >());}
case value_t::LocalTime : {return visitor(v.cast<value_t::LocalTime >());}
case value_t::Array : {return visitor(v.cast<value_t::Array >());}
case value_t::Table : {return visitor(v.cast<value_t::Table >());}
case value_t::Empty : break;
case value_t::Unknown : break;
default: break;
}
throw std::runtime_error(format_error("[error] toml::visit: toml::value "
"does not have any valid value.", v, "here"));
}
template<typename Visitor>
detail::return_type_of_t<Visitor, toml::boolean&>
visit(Visitor&& visitor, toml::value& v)
{
switch(v.type())
{
case value_t::Boolean : {return visitor(v.cast<value_t::Boolean >());}
case value_t::Integer : {return visitor(v.cast<value_t::Integer >());}
case value_t::Float : {return visitor(v.cast<value_t::Float >());}
case value_t::String : {return visitor(v.cast<value_t::String >());}
case value_t::OffsetDatetime: {return visitor(v.cast<value_t::OffsetDatetime>());}
case value_t::LocalDatetime : {return visitor(v.cast<value_t::LocalDatetime >());}
case value_t::LocalDate : {return visitor(v.cast<value_t::LocalDate >());}
case value_t::LocalTime : {return visitor(v.cast<value_t::LocalTime >());}
case value_t::Array : {return visitor(v.cast<value_t::Array >());}
case value_t::Table : {return visitor(v.cast<value_t::Table >());}
case value_t::Empty : break;
case value_t::Unknown : break;
default: break;
}
throw std::runtime_error(format_error("[error] toml::visit: toml::value "
"does not have any valid value.", v, "here"));
}
template<typename Visitor>
detail::return_type_of_t<Visitor, toml::boolean&&>
visit(Visitor&& visitor, toml::value&& v)
{
switch(v.type())
{
case value_t::Boolean : {return visitor(std::move(v.cast<value_t::Boolean >()));}
case value_t::Integer : {return visitor(std::move(v.cast<value_t::Integer >()));}
case value_t::Float : {return visitor(std::move(v.cast<value_t::Float >()));}
case value_t::String : {return visitor(std::move(v.cast<value_t::String >()));}
case value_t::OffsetDatetime: {return visitor(std::move(v.cast<value_t::OffsetDatetime>()));}
case value_t::LocalDatetime : {return visitor(std::move(v.cast<value_t::LocalDatetime >()));}
case value_t::LocalDate : {return visitor(std::move(v.cast<value_t::LocalDate >()));}
case value_t::LocalTime : {return visitor(std::move(v.cast<value_t::LocalTime >()));}
case value_t::Array : {return visitor(std::move(v.cast<value_t::Array >()));}
case value_t::Table : {return visitor(std::move(v.cast<value_t::Table >()));}
case value_t::Empty : break;
case value_t::Unknown : break;
default: break;
}
throw std::runtime_error(format_error("[error] toml::visit: toml::value "
"does not have any valid value.", v, "here"));
}
}// toml