Compare commits

..

458 Commits

Author SHA1 Message Date
Toru Niina
b389bbc4eb Merge pull request #242 from 0X1A/master
Fix issues with CMake package configuration when used with vcpkg #110
2024-06-12 00:46:15 +09:00
ToruNiina
5e4eb52f84 fix: add cxx_std to check_cxx_source_compiles
in macos, default version is 98 and it fails to compile boost test.
ci fails in the test branch because of apt timeout, but osx jobs works.
2024-06-11 23:47:49 +09:00
Alberto Corona
cbd596144d Fix issue with CMake package configuration #110 2024-05-01 18:57:49 -05:00
ToruNiina
85faca9cbe ci: update action checkout 2024-03-20 23:43:01 +09:00
ToruNiina
3509be6629 ci: add env var to avoid installation failure 2024-03-20 23:33:47 +09:00
ToruNiina
fb7b02f254 ci: remove old version of osx that takes time 2024-03-20 23:33:27 +09:00
ToruNiina
2466959cf9 chore: avoid false-positive ref life warning 2024-03-20 22:42:00 +09:00
ToruNiina
a76c5b385f refactor: replace mut-ref by take-and-move pattern 2024-03-20 22:40:46 +09:00
ToruNiina
52621a4fd8 feat: remove cxx_standard related stuff
fix #241
2024-03-20 22:38:56 +09:00
ToruNiina
64cd90637b refactor: move cmake component to where it's used 2024-03-20 22:35:03 +09:00
ToruNiina
d4a6fc7953 refactor: remove scripts for old version of cmake
now minimum_require ensures that version is not used
2024-03-20 22:34:02 +09:00
ToruNiina
d4eb5f3c9d chore: update patch version 2024-01-07 18:06:50 +09:00
ToruNiina
cc2e453b5b feat: remove strerror from errmsg in FILE* version
parse(FILE*) is a minor overload, but dispatching strerror takes too
much cost. Standard library version is not thread-safe, so some compiler
reports a warning. There are thread-safe versions defined in XSI, GNU,
and Windows. XSI/GNU versions can be detected by macros, but in some
cases, detection-by-macro written in the doc does not work.
Since errno can be obtained from the exception, users can call strerror
that is available in their env if needed. We can just report errno.
2024-01-07 10:48:17 +09:00
ToruNiina
8ed1a1d7e4 feat: update version 2024-01-06 02:20:21 +09:00
ToruNiina
04209b1540 doc: update contributors list 2024-01-06 01:58:51 +09:00
Toru Niina
1bb5284eb5 Merge pull request #233 from Esonhugh/master
fix issue #231
2024-01-05 01:39:23 +09:00
Toru Niina
7bd09245fa ci: disable macos-13-arm64 2024-01-05 00:00:25 +09:00
Toru Niina
1dd15ffa26 Merge pull request #234 from ToruNiina/limit-value-recursion
Limit value recursion
2024-01-04 22:32:01 +09:00
ToruNiina
1c2c710ec9 refactor: add comment 2024-01-04 21:18:47 +09:00
ToruNiina
db72451bcf test: update test to add rec start val 2024-01-04 20:47:46 +09:00
ToruNiina
8e214ec411 fix: limit value recursion in array/inl-table
to avoid parse_value/parse_array recursion
2024-01-04 20:45:35 +09:00
Esonhugh
655d76b828 update: test on github action with macos 13 and macos 13 arm64 image 2023-12-27 11:27:21 +08:00
Esonhugh
0db935b602 fix: let __DARWIN_C as an exception 2023-12-27 11:19:59 +08:00
Esonhugh
ed577df40a fix: strerror_r error handling toml/exception.hpp in macos 2023-12-27 11:11:23 +08:00
ToruNiina
01a0e93e5f ci: disable bad combination of compiler and stdlib 2023-12-23 23:32:39 +09:00
Toru Niina
510419fb6a Merge pull request #232 from DavidKorczynski/clusterfuzzlite
Add fuzzing by way of ClusterFuzzLite
2023-12-23 22:28:46 +09:00
David Korczynski
7d09cdf067 cflite: change branch name
Signed-off-by: David Korczynski <david@adalogics.com>
2023-12-23 03:00:51 -08:00
David Korczynski
828afc3b1e Add fuzzing by way of ClusterFuzzLite
Signed-off-by: David Korczynski <david@adalogics.com>
2023-12-23 01:12:21 -08:00
ToruNiina
c32a20e1ee fix: #226 use strerror_s/strerror_r if possible 2023-10-11 23:44:23 +09:00
ToruNiina
937a7c45fe feat: fill char buffer with null char
those funcs always return null-terminated string but just to make it
sure
2023-10-11 01:44:30 +09:00
ToruNiina
947c995189 fix: include array to use char buffer 2023-10-11 01:43:55 +09:00
ToruNiina
9b7b8908e8 fix: avoid evaluating undefined macro as zero
to suppress a warning
2023-10-11 01:24:41 +09:00
ToruNiina
22d22198ec feat: use thread-safe variant of strerror 2023-10-11 01:08:12 +09:00
ToruNiina
1beb391a43 ci: use default version of libboost on ubuntu 20 2023-10-11 00:14:53 +09:00
ToruNiina
dfc625f38d fix: #229 do not move temporary object 2023-10-10 23:49:51 +09:00
Toru Niina
22d96ed921 Merge pull request #230 from arp242/t
Escape control characters in keys
2023-10-10 23:21:43 +09:00
Martin Tournoij
d2937ff4e1 Escape control characters in keys
Previously a key like:

        "a\u0000\u0001b" = 1

Would get written with literal control characters, rather than escapes:

        "a<00><01>b" = 1

The "valid/key/quoted-unicode" test from toml-test would fail with this,
although it seems they're not run automatically(?)

Can also reproduce with something like:

        % cat test.cpp
        #include <toml.hpp>
        #include <iostream>

        int main()
        {
                const auto data  = toml::parse("test.toml");
                std::cout << data << "\n";
                return 0;
        }

        % cat test.toml
        "a\u0000\u0001b" = "a\u0000\u0001b"

        % c++ -I. test.cpp

        % ./a.out
        "ab" = "a\u0000\u0001b"

        % ./a.out | hexdump -C
        00000000  22 61 00 01 62 22 20 3d  20 22 61 5c 75 30 30 30  |"a..b" = "a\u000|
        00000010  30 5c 75 30 30 30 31 62  22 0a 0a                 |0\u0001b"..|
2023-10-10 09:03:33 +01:00
Toru Niina
087408a8fb Merge pull request #225 from kfirgollan/kfir/add_install_instructions
Add installation example with checkinstall and cmake
2023-07-31 01:22:32 +09:00
Kfir Gollan
2339b32258 Add installation example with checkinstall and cmake 2023-07-28 19:02:01 +00:00
Toru Niina
5cc79bbd7b Merge pull request #224 from offa/remove_travisci
Remove Travis CI config
2023-07-27 20:55:45 +09:00
Toru Niina
9323a315eb Merge pull request #223 from offa/cmake_update
Require CMake 3.5+
2023-07-27 20:55:05 +09:00
offa
85d880d84e Remove Travis CI config 2023-07-25 20:14:37 +02:00
offa
c44459dc47 Require CMake 3.5+ 2023-07-25 20:09:52 +02:00
ToruNiina
1340692442 fix #218: consider locale while serialization 2023-05-29 23:18:38 +09:00
ToruNiina
da3d5153d1 ci: install language pack to test locale 2023-05-28 23:37:53 +09:00
ToruNiina
af13c2867a test: add test case for serializer with locale 2023-05-28 18:47:06 +09:00
ToruNiina
327f6e7701 fix: set locale to C when writing numbers 2023-05-28 18:42:33 +09:00
ToruNiina
e36eabf216 feat: add get<T> overload with toml::value& 2023-05-27 00:16:17 +09:00
ToruNiina
40eb1d2213 chore: update ci runners to the latest 2023-04-29 02:16:40 +09:00
ToruNiina
2da3b67d02 doc #217: add description about C++17 feature 2023-04-24 22:36:08 +09:00
ToruNiina
0dcf07b774 doc: update contributor list 2023-04-24 22:34:49 +09:00
Toru Niina
d47fe788bc Merge pull request #214 from VestniK/raw_ptr_iter
Fix for case when vector iterator is raw pointer
2023-03-17 22:11:38 +09:00
Sergey Vidyuk
78ae165096 Fix for case when vector iterator is raw pointer
We are using patched libc++ which uses raw pointers for vector itrators
to improve code compilation speed. This commit fixed two compilation
issues in toml11:
 * location::const_iterator deinition assumes that vector const_iterator
   is struct or class type rather than raw pointer.
 * `const const_itetr foo()` triggers `-Wignored-qualifiers` for primitive
   types and `void` which breaks `-Wextra -Werror` compilation.
2023-03-16 23:02:19 +07:00
ToruNiina
86eefc7255 feat: update toml-test from v1.2.0 to v1.3.0 2023-02-13 00:44:15 +09:00
ToruNiina
51e5d845b0 fix: #213 allow long binary integer 2023-02-12 23:20:09 +09:00
ToruNiina
ce941c318b fix: prevent windows minmax macro 2023-02-12 20:27:14 +09:00
ToruNiina
fd969a679b test: check if a large bin ints are parsed 2023-02-12 19:03:59 +09:00
ToruNiina
51587338cd fix: avoid overflow at postproc of the last loop 2023-02-12 18:50:46 +09:00
ToruNiina
418bfe9117 fix: cast explicitly to avoid un/signed comparison 2023-02-12 16:44:18 +09:00
ToruNiina
15346114ef fix: allow long binary integer and leading zeros 2023-02-12 16:22:23 +09:00
ToruNiina
565f43c484 fix: #211 reopen table implicitly defined by aot 2023-02-12 15:55:20 +09:00
ToruNiina
f9b224c222 fix: reopening table implicitly defined by aot 2023-02-12 02:55:03 +09:00
Toru Niina
72789dca42 Merge pull request #210 from offa/action_update
Update checkout action to v3
2023-02-11 23:47:41 +09:00
Toru Niina
75daa2dde0 Merge pull request #208 from cxw42/issue199-nonutf-string
Fix address-sanitizer error when parsing literal strings having invalid UTF-8 characters
2023-02-11 23:46:53 +09:00
Toru Niina
ff48387677 Merge pull request #207 from cxw42/misc
Add .editorconfig; fix some error messages in the parser
2023-02-11 23:43:59 +09:00
offa
132aa17f97 Update checkout action to v3 2023-01-22 16:01:50 +01:00
Chris White
a2f884b11e fix: parse_ml_literal_string() properly issues invalid-utf8 errors
Fix the same out-of-bounds read as in parse_literal_string().
2023-01-14 18:32:40 -05:00
Chris White
e3639d2bbc fix: parse_literal_string() properly issues invalid-utf8 errors
When creating the inner iterator, make sure it points into the same
vector as the outer iterator.  Otherwise, attempts to reset the iterator
wind up causing it to read out-of-bounds.

Fixes #199.
2023-01-14 18:32:40 -05:00
Chris White
626b0e6b95 fix: Correct function names in error messages
- fix error messages that referred to the wrong functions.
- parse_key(): remove "detail::" from the only error message that had
  it, for consistency with the other error messages in that function
2023-01-14 18:24:17 -05:00
Chris White
0acd2b9c88 Add EditorConfig file
Four-space indents in C++ files; two-space indents in Markdown and YAML.
2023-01-14 18:18:05 -05:00
ToruNiina
22db720ad5 fix: #202 do not set CMAKE_CXX_STANDARD 2022-10-24 23:56:51 +09:00
ToruNiina
7ec3086f19 ci: update appveyor script 2022-10-24 22:55:43 +09:00
ToruNiina
4c4e82866e feat: CMAKE_CXX_STD should be given by the enduser 2022-10-24 22:42:31 +09:00
Toru Niina
41908b2cef Merge pull request #200 from ctcmkl/several-simple-patches
Several simple patches
2022-09-30 23:33:57 +09:00
Moritz Klammler
3f197c3cab Fix use-after-move in test_parse_function_compiles and refactor
My recent patch had introduced a conditional use-after-move bug into the
test_parse_function_compiles function.  This patch fixes that by
reworking the entire test case into a compile-time check.  In my
opinion, we're not loosing anything by not actually executing the code
(the result wasn't looked at anyway) and the code becomes much clearer
by omitting the argument-preparation fluff.
2022-09-29 19:22:25 +02:00
Moritz Klammler
e064a5c371 Avoid unnecessary copies of parser result
The 'result' class has unwrap() and unwrap_err() member functions
overloaded for const lvalue and rvalue *this to avoid an unnecessarily
copying the to-be unwrapped object of its containing object is going to
be discarded anyway.  Alas, the parse() function toml/parser.hpp file
stored the parse result in a local `const` variable so, although the
unwrap call would have been the last use of the object in each case, the
unnecessary copy would still be made.  This patch removes the `const`
and adds a std::move() to actually benefit from the already implemented
optimization.
2022-09-29 19:02:52 +02:00
Moritz Klammler
e86d7c3cd3 Remove excess blank lines at end of file 2022-09-29 19:02:52 +02:00
Moritz Klammler
10fd14f8b9 Consistent unit test header inclusion order
This patch consistently changes the inclusion order for unit test files
to the following:

 1. The header of the unit under test (using <> includes).
 2. The unit_test.hpp header (using "" includes).
 3. Any additional auxiliary test headers (using "" includes and sorted alphabetically).
 4. Additional system headers needed for the test (using <> includes and sorted alphabetically).
 5. Conditionally included system headers (using <> includes).

Putting the unit under test's header at the very beginning has the
advantage of also testing that the header is self-contained.  It also
makes it very quick to tell what unit is tested in this file.
2022-09-29 19:02:52 +02:00
Moritz Klammler
81c5ba9082 Define BOOST_TEST_MODULE in CMake
This removes one #define from each unit test file and ensures
consistency between file and module names.  This consistency, was not
strictly maintained before.  I hope that any discrepancies were
unintentional and that a 1:1 mapping is actually what is desired.

Since the definition is now done at one single place, it would be easy
to apply transformations like removing the 'test_' prefix or replacing
'_' with '-' if this should be desired.
2022-09-29 19:02:52 +02:00
Moritz Klammler
d7c04ed5ee Factor redundant test boilerplate out into unit_test.hpp helper 2022-09-29 19:02:52 +02:00
Moritz Klammler
b10348c576 More flexible https://github.com/toml-lang/toml handling
Instead of unconditionally attempting to clone from a fixed location
(GitHub) during the build / test process, honor the following two
configuration variables:

  TOML11_LANGSPEC_GIT_REPOSITORY
    Can be set to override the URL from which the repository is cloned.
    This allows using a local mirror, including file:// URLs for working
    offline or reducing network traffic.

  TOML11_LANGSPEC_SOURCE_DIR
    Can be set to configure the location at which the repository is
    expected.  If it already exists no download will be attempted.  This
    allows avoiding the additional git-clone(1) altogether and use an
    existing directory as-is.  This offers two new possibilities:
    (1) The same checkout can be reused for building multiple
    configurations (e.g. Debug versus Release) saving a little bit of
    time and disk space.
    (2) Experimental changes can easily be applied to the local source
    tree without having them destroyed by the build process.

In order for this flexible location to work, the unit tests which
attempt to read files from the repository had to be adjusted.  They now
honor an environment variable TOMLDIR which can be set to point to an
alternate root directory.

All defaults are set such that the previous behavior is maintained.

Instead of introducing the TOMLDIR environment variable, an alternative
solution would have been to set the WORKING_DIRECTORY of the tests to
the TOML11_LANGSPEC_SOURCE_DIR and leave the relative paths in these
tests hard-coded.  Alas, some tests also expect that they can /write/
into the current working directory which isn't desirable if it is
potentially pointing outside the build tree.  I personally prefer to
mount the source directory read-only and build in a fast tempfs, so this
would e a problem.  To be perfectly honest, I don't quite understand why
these tests need to write to the file system in the first place, though.
It seems to me that refactoring them to serialize to a std::ostrstream
instead of a std::ofstream would not only simplify but also speed up the
unit tests and avoid file system problems.  But there might have been a
hidden reason why actually using the real file system was considered
necessary for these tests, so I didn't went ahead with that change yet.
2022-09-29 19:02:52 +02:00
Moritz Klammler
ac949437f8 Avoid false negative Boost.Test detection due to -Werror
Depending on the CMake and Boost version, the -Werror flags that might
get added to CMAKE_CXX_FLAGS could adversely affect the feature
detection logic, leading to the wrong conclusion that Boost.Test isn't
usable altogether.  Performing these checks first and only afterwards
altering CMAKE_CXX_FLAGS avoids this issue.
2022-09-29 17:59:28 +02:00
Moritz Klammler
fe471bcbe9 Autodetect the best option to use Boost.Test
The conditional inclusion of either the library or the header-only
version of the Boost.Test header wasn't tremendously useful in practice
because the tests/CMakeLists.txt file would unconditionally add compile
definitions to basically fore dynamic linking.

This patch adds feature detection to the tests/CMakeLists.txt file to
determine whether to use dynamic linking, static linking or the
header-only version (in that order of preference, for best performance).

The automatic detection could be overridden if needed by defining the
TOML11_WITH_BOOST_TEST_{HEADER,STATIC,DYNAMIC}
variables on the CMake command line.

While we are at it, instead of having a conditional
#define BOOST_TEST_NO_LIB
in each unit test file, handle this once in the CMakeLists.txt file.
2022-09-29 17:59:28 +02:00
Moritz Klammler
5312b8eb0e Honor UNITTEST_FRAMEWORK_LIBRARY_EXIST in all unit test files
Most unit test files checked UNITTEST_FRAMEWORK_LIBRARY_EXIST and
adapted themselves accordingly to either use the header-only version or
link with the library.  Alas, eight files didn't so the project couldn't
really be tested with the header-only version of that Boost library.

This patch adds the missing pre-processor logic to the files that were
missing it.  The style of using no indentation after the '#' was
followed from the existing unit test files.  Some other files in this
repository do indent their pre-processor logic, though.

Since replicating the conditional in every file is kind of verbose,
tedious and, apparently, easily forgotten, I'm wondering whether
isolating that logic into a tiny little auxiliary header and then
unconditionally including that one in each unit test file would be a
good idea, though.
2022-09-29 17:59:28 +02:00
Moritz Klammler
cf8a977be2 Don't deliberately dereference the null pointer
This patch addresses a static analysis issue reported by Cppcheck 2.9
where several member functions of the toml::discard_comment class
defined in the toml/comments.hpp header were implemented to deliberately
dereference the null pointer returned unconditionally by the
always-empty container's data() member function.  This behavior wasn't
technically wrong because those functions all have as precondition that
the container is non-empty so they must never be called on an instance
of toml::discard_comment but we can still be more helpful without
adversely affecting code generation.  Instead of dereferencing the null
pointer, this patch has these functions call an inline private helper
function which is defined to invoke __builtin_unreachable() if available
"and then" throw an exception with a helpful error message.  Even at the
-O1 level, GCC will optimize the code under the assumption that the
function will never be called (i.e. no assembly is emitted), making
failure to ensure this undefined behavior exactly as if the null pointer
had been dereferenced.  However, static analysis will now understand the
programmer's intent and remain silent.  Furthermore, when using the -O0
or -Og levels, GCC won't optimize under this assumption so the exception
will be thrown and might be helpful for debugging.  Compilers that don't
have __builtin_unreachable() won't get any help in determining that the
function must not be called and will have to figure this out by
analyzing the calling code -- which really shouldn't exist in the first
place anyway as the whole point is that these functions must not be
called.
2022-09-29 17:59:28 +02:00
Moritz Klammler
8bb2c63a01 Don't compare iterators from potentially different containers
This patch addresses a static analysis issue reported by Cppcheck 2.9
where an assertion in the toml/region.hpp header would compare two
container's (that are known to be of type std::vector<char>) begin() and
end() iterators in order to verify that they are the same.  This
assertion either passes or invokes undefined behavior.  Which isn't
technically wrong because calling code must always ensure that
preconditions are met and assertions therefore pass anyway but it does
make the value that is added by having the assertion in the first place
marginal.  Fortunately, the assertion was easy to rewrite: Just compare
the container's address itself.  This is well-defined regardless of
whether the assertion will pass or fail.
2022-09-29 17:59:28 +02:00
Moritz Klammler
79c125b54f Initialize data members for defaulted c'tors
This patch addresses a static analysis issue reported by Cppcheck 2.9
where several classes in the toml/datetime.hpp header explicitly default
all their special member functions, including the default constructor,
but don't provide initializers for their data members.  This might or
might not have caused any observable surprising behavior but I agree
with Cppcheck on this one in that an explicitly defaulted default
constructor should be expected to initialize all data members.  So let's
do that.
2022-09-29 17:59:27 +02:00
ToruNiina
b02aaed4fc fix: use fs::path, not fs::path::string() result 2022-09-29 21:28:35 +09:00
ToruNiina
c2af975609 fix: add missing include file and specifiers 2022-09-29 20:14:58 +09:00
Toru Niina
c3a3423fb2 Merge pull request #193 from lukash/replace-fstream
Use C-style IO instead of ifstream for parsing
2022-09-24 00:17:52 +09:00
Lukáš Hrázký
6c2c804eff fix: Improve error handling of ifstream a bit
Set the exceptions mask so that exceptions are thrown when an I/O error
occurs. Also throw the same exception type when the opening fails.
2022-09-08 17:05:15 +02:00
Lukáš Hrázký
021d84623c chore: De-duplicate code for parse(std::filesystem::path) 2022-09-08 17:05:15 +02:00
Lukáš Hrázký
bf9c9d620d feat: Add a parse(FILE *) interface
The fstream classes are notorious for their non-existent error handling.

This adds a C-style fILE * IO (fopen(), etc.) alternative interface, so
that if a user needs reliable error handling, they can use that, albeit
more inconvenient, but more robust approach.
2022-09-08 17:05:11 +02:00
Toru Niina
bbdbae91eb Merge pull request #196 from Jajauma/AvoidCharStaticCasts
Avoid possible lexer truncation warnings
2022-08-17 23:47:10 +09:00
Jajauma's GitHub
72af7b48d3 Avoid possible lexer truncation warnings
Instead of static_cast calls that convert int to char, literals of type
char are now used directly with the value encoded via escape sequence.

The benefits are:
- code without static_cast is much more compact and expresses intent
better
- fixed value truncation warning on some compilers (e.g. C4309 on Visual
Studio 2017)
2022-08-13 10:30:18 +00:00
Toru Niina
c7627ff6a1 Merge pull request #194 from Helaxious/patch-1
Fix a typo ('optinoal' to 'optional')
2022-07-28 21:02:21 +09:00
helaxious
1c3c84e90a fixed a typo ('optinoal' to 'optional') 2022-07-27 17:58:47 -03:00
ToruNiina
1400dd223f fix: #192 quick fix by checking address 2022-07-01 01:09:22 +09:00
Lukáš Hrázký
594accf9a7 chore: Don't include fstream in lexer 2022-06-30 11:37:53 +02:00
ToruNiina
c1378cd3d1 ci: test older gcc/clang on older ubuntu 2022-06-25 01:18:32 +09:00
ToruNiina
728f73ea24 ci: update compilers available on the os 2022-06-24 23:31:52 +09:00
ToruNiina
4212985443 ci: update os used on ci to the current default 2022-06-22 22:06:11 +09:00
ToruNiina
8e95891af1 fix: report an error if a table is inserted to aot 2022-06-22 21:48:37 +09:00
ToruNiina
2a987ac9ea chore: update toml-test to the latest release 2022-06-22 21:28:31 +09:00
ToruNiina
9e1bfcc962 doc: merge contributions from the same person 2022-06-22 21:24:22 +09:00
ToruNiina
4a0df22548 doc: update contributor list 2022-06-14 23:31:58 +09:00
ToruNiina
1ba3be38d0 fix: point the beginning of value in err msg 2022-06-08 01:33:10 +09:00
ToruNiina
5be587bb68 fix #190: Merge branch 'throw-when-overflow' 2022-06-08 01:27:30 +09:00
ToruNiina
d6efdf7d9e test: check if an exception is thrown by overflow 2022-06-08 00:40:28 +09:00
ToruNiina
12d0dbc6f4 fix: throw if number cannot be read losslessly
it throws when strtoll and strtof that are internally called fail
2022-06-08 00:39:04 +09:00
Toru Niina
6d9e533cf1 Merge pull request #189 from muggenhor/fix/avoid-fname-copy
fix: don't force a copy of std::string fname when moving is an option
2022-06-04 20:49:33 +09:00
Giel van Schijndel
aff6f0f574 fix: don't force a copy of std::string fname when moving is an option
Taking this parameter by const reference forces us to copy it (because
we know we're going to store it). Taking it by r-value reference would
suggest that we _might_ take ownership over it and would also force the
user to make a copy if they wish to retain the original value.

Taking this parameter by value however clearly gives us ownership of its
content without forcing a copy if it's implicit conversion from
`const char*` or explicitly handed over to us by the user via std::move.
2022-06-02 14:39:10 +02:00
ToruNiina
25be97dc39 refactor: style update 2022-05-29 00:37:39 +09:00
Toru Niina
846abd9a49 Merge pull request #188 from ken-matsui/support-changing-color-mode-at-runtime
Support changing color mode at runtime
2022-05-27 22:49:56 +09:00
Ken Matsui
9086b1114f Support changing color mode at runtime 2022-05-27 00:01:28 +09:00
ToruNiina
e8f922a1b8 chore: use the latest release of toml-test on CI
The CI task that corresponds to toml-test HEAD is currently failing.
Using the latest stable release is safer.
2022-05-22 15:40:54 +09:00
Toru Niina
fdb228598d Merge pull request #187 from ken-matsui/support-opting-out-of-error-prefix
Support opting out of the default `[error]` prefix
2022-05-22 15:22:01 +09:00
Ken Matsui
c26aa013cd Support opting out of the default [error] prefix 2022-05-22 13:50:45 +09:00
ToruNiina
5924325652 doc: update unreleased TOML features section 2022-03-17 00:07:48 +09:00
ToruNiina
1836ddc129 test: test -DTOML11_USE_UNRELEASED_TOML_FEATURES 2022-03-16 22:46:21 +09:00
ToruNiina
1c82c7a1dc fix: move cmake option to toplevel cmakelists 2022-03-16 22:40:42 +09:00
ToruNiina
dde351ea40 feat: add escape sequence of ESC
as an unreleased feature
2022-03-16 22:39:52 +09:00
ToruNiina
dcfe39a783 chore: update version number 2022-03-13 01:44:29 +09:00
ToruNiina
2f07030d43 doc: update the list of contributors 2022-03-13 01:40:43 +09:00
ToruNiina
97cc0ef62b fix #166: reorder local/gmtime wrapper for MSVC 2022-03-13 00:20:00 +09:00
ToruNiina
0e80cabe65 Merge branch 'check-specialized-conversion' 2022-03-12 19:00:29 +09:00
ToruNiina
cd97dfcec1 Merge branch 'insertion-to-table-defined-by-dotted-key' 2022-03-12 19:00:07 +09:00
ToruNiina
e60442c6db fix: check if re-open dotkey by a table
like:
```
a.b.c = "foo"
[a.b] # this is invalid
d = "bar"
```
2022-03-12 18:22:01 +09:00
Toru Niina
d39fd88a17 Merge pull request #185 from ax3l/topic-installEmbed
CMake: Optional Install if Embedded
2022-03-07 23:12:33 +09:00
Axel Huebl
7fb8b84143 CMake: Optional Install if Embedded
When adding this library as embedded library with private
"target link", e.g., only used inside private source files, the
library does not need to be installed when the main project gets
installed.

This adds an additional option `toml11_INSTALL` similar to the
test-build control switch in order to skip installing headers and
CMake config files if requested.

Avoids using
```cmake
add_subdirectory(path/to/toml11 EXCLUDE_FROM_ALL)
```

which has further side-effects:
https://cmake.org/cmake/help/v3.0/command/add_subdirectory.html
2022-03-03 18:55:55 -08:00
ToruNiina
03259e2003 fix #177: check specific conversion function
when converting toml::value to array-like
2021-12-25 14:08:55 +09:00
ToruNiina
bf2384d8da fix #182: Merge branch 'workaround-msvc-cplusplus-macro' 2021-12-18 15:41:43 +09:00
ToruNiina
8ebf9c984b ci: check if it works w/o /Zc:__cplusplus 2021-12-18 14:23:58 +09:00
ToruNiina
7354e91924 fix: Allow MSVC to have old version 2021-12-18 00:36:20 +09:00
ToruNiina
4522070391 ci: check if MSVC 14 2015 (19.0.24241.7) (1900)
passes ci build if we skip _MSVC_LANG
2021-12-18 00:21:55 +09:00
ToruNiina
02fd8a577b feat: workaround __cplusplus problem on MSVC 2021-12-17 22:29:57 +09:00
ToruNiina
40777070ad fix: disallow dotkey and inline table migration 2021-12-16 22:16:24 +09:00
ToruNiina
cc1cc27613 fix: disallow merging dotted key and inline table
current code mistakenly allows the following TOML file.
```toml
a.b = 42       # table "a" is defined here, implicitly
a = {c = 3.14} # table "a" is overwritten here
```
But we need to allow the following (structually similar) TOML file.
```toml
a.b = 42   # table "a" is defined here, implicitly
a.c = 3.14 # table "a" is merged with {c = 3.14}
```
To distinguish those, we check whether the current table is defined as
an inline table or via dotted key. If the table we are inserting is
defined via dotted key, we accept it and merge the table. If the table
being inserted is defined as an inline table, then we report an error.
2021-12-16 01:11:47 +09:00
ToruNiina
75e297eb47 fix: Merge branch 'check-datetime'
Briefly check if a given date and time is valid
2021-12-15 22:35:56 +09:00
ToruNiina
0d0605e290 fix #181: Merge branch 'no-null-char' 2021-12-15 22:35:31 +09:00
ToruNiina
23f6c931e5 test: add valid/invalid datetime cases 2021-12-15 00:51:07 +09:00
ToruNiina
518e6d4ae2 feat: check date and time are valid or not 2021-12-15 00:31:41 +09:00
ToruNiina
db2aa55d20 fix: disallow null char at the end of input
since std::string and ""_toml literal actually does not include null
char, we don't need to check if the last char is null or not
2021-12-14 22:33:58 +09:00
ToruNiina
1b5107e5e3 fix #180: Merge branch 'linefeed-at-eof' 2021-12-11 23:13:42 +09:00
ToruNiina
2152c85cd1 test: add test_file_ends_without_lf 2021-12-10 23:46:47 +09:00
ToruNiina
2e4c7fb95e fix: line-feed is not required at the EOF 2021-12-10 23:39:54 +09:00
ToruNiina
2ddcfb4b61 doc: update constributors in README 2021-12-08 21:50:09 +09:00
Toru Niina
3f233d57be Merge pull request #178 from marascio/lrm-resolve-173-free-nonheap-object
Resolve g++ warning: free-nonheap-object
2021-12-05 19:46:38 +09:00
Louis R. Marascio
21732fce45 Resolve g++ warning: free-nonheap-object
As described in issue #173, this warning is raised on various platforms
and in various build types. For example, g++ 11 in release mode will
cause this warning to be raised. This change fixes this warning.
2021-12-01 14:43:43 -06:00
Toru Niina
1dc09d0332 Merge pull request #176 from GMLC-TDC/fix_warnings
Fix warnings
2021-11-25 00:13:36 +09:00
Philip Top
9c1708c988 Update toml/traits.hpp
Co-authored-by: Toru Niina <niina.toru.68u@gmail.com>
2021-11-24 06:40:09 -08:00
Philip Top
84f61f7dc8 match the cmake condition to upstream 2021-11-23 16:16:16 -08:00
Philip Top
eef7106fbe fix more warnings 2021-11-19 10:22:37 -08:00
Philip Top
a919190490 tweak the cmake a little 2021-11-19 08:47:18 -08:00
Philip Top
7f98740c9c update from changes in cmake standard 2021-11-19 08:43:04 -08:00
Philip Top
e08c579e36 fix a change in the key_value name from upstream merge 2021-11-19 08:43:03 -08:00
Philip Top
6dd44dc672 fix project issue with cmake 2021-11-19 08:41:24 -08:00
Philip Top
99e483c447 [ci skip] use a policy in the CMakeLists.txt for toml11 fix some more string_view errors 2021-11-19 08:41:00 -08:00
Philip Top
26a066ad07 skip ci
ci skip

update another string_view issue
2021-11-19 08:39:00 -08:00
Philip Top
acad8b1a61 add additional check for invocability 2021-11-19 08:37:02 -08:00
Philip Top
605cd8ef4a fix shadow and some undef warnings 2021-11-19 08:37:01 -08:00
ToruNiina
bcee9f25a2 fix: check if subtable key conflicts 2021-10-10 20:58:28 +09:00
Toru Niina
f2f2b44d87 Merge pull request #172 from estshorter/fix_readme
fix a compile error in a code example
2021-10-10 14:44:00 +09:00
estshorter
e40dfc28eb fix a compile error in a code example 2021-10-10 14:07:34 +09:00
Toru Niina
24284c70ee Merge pull request #171 from estshorter/issue_170
fix a compile warning C26478
2021-10-10 13:29:37 +09:00
estshorter
dced71224d fix a compile warning C26478 2021-10-09 11:12:58 +09:00
Toru Niina
177c09f43d Merge pull request #169 from ohdarling/fix_force_inline
fix: serializer has wrong constructor params order when format root object
2021-09-24 00:29:14 +09:00
ohdarling
e434c96b7f fix: serializer has wrong constructor params order when format root object 2021-09-22 11:38:42 +08:00
Toru Niina
fda0a2b9ab Merge pull request #167 from karl-nilsson/spelling
Spelling fixes
2021-08-30 01:01:27 +09:00
Karl Nilsson
3eee515ce1 Spelling fixes 2021-08-27 19:52:45 -04:00
ToruNiina
ca9e36a484 fix: avoid duplicated-branches in result
when both two types are trivially destructible, both branches of cleanup
function results in the same code...
2021-07-01 00:46:56 +09:00
ToruNiina
0858fbfced fix: avoid max macro expansion on Windows
in numeric_limits<T>::max
2021-06-30 01:43:27 +09:00
ToruNiina
fe240e1ffc ci: trying to run toml_test 2021-06-30 01:28:53 +09:00
ToruNiina
d9959fcdeb ci: trying to make go get work 2021-06-30 01:18:52 +09:00
ToruNiina
1d0b003312 ci: add a patch to avoid nan comparison 2021-06-30 01:18:42 +09:00
ToruNiina
0aa3773860 feat: add bare minimum utf8 seq validity check 2021-06-30 00:58:50 +09:00
ToruNiina
9745c0005f ci: fix setup of toml-test 2021-06-27 18:58:44 +09:00
ToruNiina
4adf36d9fd test: update typename in json for toml-test 2021-06-27 18:58:10 +09:00
ToruNiina
c72b27bb4b fix: escape control characters in a string 2021-06-27 18:57:20 +09:00
ToruNiina
be5ffaf662 feat: check if width == max before using ml-string 2021-06-27 18:56:57 +09:00
ToruNiina
47a2a3332b fix: use empty quoted string for empty key 2021-06-27 18:56:33 +09:00
ToruNiina
9d28afa012 fix: fix serialization of inf/nan 2021-06-27 18:56:05 +09:00
ToruNiina
f09bd5b035 feat: easy check for datetime 2021-06-27 18:54:55 +09:00
ToruNiina
0dc51f95d9 fix: disallow trailing comma in an inline table 2021-06-27 18:54:28 +09:00
ToruNiina
cf9e86a84f fix: disallow control characters
in basic/literal string and comment
2021-06-27 18:53:48 +09:00
ToruNiina
5190e5148b ci: update go version to 1.16 2021-06-27 16:32:49 +09:00
ToruNiina
45bd566f7a fix: serialization of array containing a table
table in a (hetero-) array should be force-inlined
2021-06-27 16:28:41 +09:00
ToruNiina
2c72329530 ci: remove needless flag and allow hetero array
the example of hetero array (that was not allowed in v0.5.0 but allowed
in v1.0.0-rc1) has been moved from invalid/ to valid/
2021-06-27 16:12:01 +09:00
ToruNiina
1b7ca8566b fix: out_of_range with malformed toml file #164 2021-06-27 15:58:40 +09:00
ToruNiina
647381020e chore: update version number 2021-05-27 10:14:29 +09:00
ToruNiina
f04cf596eb doc: update README 2021-05-26 23:16:03 +09:00
Toru Niina
c281539b26 Merge pull request #161 from cubiest/bugfix/empty_files_missing_filename_in_error
Preserve empty location for empty files
2021-05-26 12:28:38 +09:00
Oliver Kahrmann
58542d36be Preserve empty location for empty files
Without a region, error messages in exceptions are unable to print
a filename.

By retaining the location in a zero-length region and detecting this
when formatting the exception text it is possible to print the filename
and explicitly state that the file is completely empty.

Fixes #160
2021-05-25 20:52:33 +02:00
ToruNiina
c38079f7c0 fix: remove needless include file
that might cause compilation error
2021-05-25 21:40:41 +09:00
ToruNiina
0c4594f59a doc: add TOML11_PRESERVE_COMMENTS_BY_DEFAULT 2021-05-15 21:53:13 +09:00
ToruNiina
e73c98490b doc: add recursive find_or to README 2021-05-15 21:47:03 +09:00
ToruNiina
7b9a1abdb3 feat: add test_find_or_recursive 2021-05-15 20:51:43 +09:00
ToruNiina
891f68eab0 feat: support all &/const&/&& variants 2021-05-15 20:41:11 +09:00
ToruNiina
4b1df61142 Merge branch 'master' into recursive-find-or 2021-05-15 20:01:30 +09:00
ToruNiina
392a260db8 doc: write about precedence 2021-05-15 00:24:51 +09:00
ToruNiina
7339ce39d5 fix: #159 Merge branch 'conversion-precedence' 2021-05-14 20:10:03 +09:00
ToruNiina
287be5a575 ci: clang11 is too new to install it
without adding a new ppa
2021-05-14 18:25:29 +09:00
ToruNiina
798856946f ci: add new compilers
gcc 10, 11, clang 11
2021-05-14 18:19:44 +09:00
ToruNiina
07c1d10212 ci: avoid clang-9 + C++20 because it lacks <=>
And the operator<=> is used in the (GNU-) standard library
implementation installed by default.
Note: consider using libc++ library
2021-05-14 16:16:23 +09:00
ToruNiina
0ac3919e08 feat: from<T> and from_toml precede constructor
constructor sometimes has `template<T> ctor(const T&)` and it causes
ambiguity. To avoid it, from<T> and T.from_toml precedes any
constructor. But, to check the ambiguity between from<T> and from_toml,
they do not precede each other. If anyone define both from<T> and
from_toml, it causes compilation error.
2021-05-14 16:05:54 +09:00
ToruNiina
e622595426 fix: fix has_specialized_from/into
to avoid ambiguity
2021-05-14 16:01:43 +09:00
ToruNiina
72ee8caf09 refactor: use has_specialized_from<T>
to check if toml::from<T> exists for a specific T
2021-05-14 15:53:34 +09:00
ToruNiina
b6e2c6e235 feat: add detail::has_specialization_from/into 2021-05-14 15:46:00 +09:00
ToruNiina
c5a22b9d88 fix: #158 Merge branch 'gcc-wshadow'
The -Wshadow warning is avoided from the source code level
2021-05-11 00:08:32 +09:00
ToruNiina
7e90282175 fix: add region where -Wshadow is ignored on GCC 4 2021-05-10 23:00:30 +09:00
ToruNiina
b8291af42b fix: rename func args to avoid -Wshadow in GCC 4.x 2021-05-10 22:56:16 +09:00
ToruNiina
cd60045014 fix: gcc 7 introduces wshadow variants 2021-05-10 21:51:51 +09:00
ToruNiina
db0d9a024b test: add -Wshadow while compiling tests 2021-05-10 20:49:41 +09:00
ToruNiina
4acc563b28 feat: explicitly avoid -Wshadow=global in GCC 2021-05-10 20:49:20 +09:00
ToruNiina
dce50142e6 fix: avoid argname key to supress warning
about shadowing
2021-05-10 20:47:08 +09:00
ToruNiina
06e8b853ba test: add Wshadow=local 2021-05-04 17:50:14 +09:00
ToruNiina
e3092335aa Merge branch 'enable-to-change-default-comment-handling' 2021-04-30 20:28:38 +09:00
ToruNiina
31b9b79312 ci: suppress clang-8 / c++20 because of gcc header 2021-04-28 15:12:04 +09:00
ToruNiina
beb665ba58 test: explicitly specify if comments are preserved 2021-04-27 13:19:55 +09:00
ToruNiina
b51ef5e869 test: explicitly specify the comment preservation 2021-04-27 13:18:39 +09:00
ToruNiina
21ea4a348d test: explicitly specify template arguments
toml::value is an alias of default parameters, so we need to avoid
conflict of definitions between default and non-default parameters
2021-04-27 13:12:37 +09:00
ToruNiina
c4a803df50 test: add comment/no-comment cases to parse_array
When we add a macro to change the default comment preservation scheme,
some of the current tests that assume comments are discarded by defualt
fails. To make it more robust, we need to explicitly specify the comment
preservation scheme and add test cases for both of discard_ and
preserve_comments.
2021-04-27 13:05:03 +09:00
ToruNiina
c40e0dbd37 feat: use comment macro everywhere 2021-04-16 15:29:24 +09:00
ToruNiina
d90c26f9ac feat: add TOML11_PRESERVE_COMMENTS_BY_DEFAULT 2021-04-16 15:28:58 +09:00
ToruNiina
b592ddcca2 fix: add SFINAE to avoid incorrect matching 2021-04-14 13:09:51 +09:00
ToruNiina
5518b2b155 refactor: simplify last_one_in_pack meta func 2021-04-14 13:09:25 +09:00
ToruNiina
ba3d41d913 feat(#156): add find_or(value, keys..., opt) 2021-04-14 11:22:19 +09:00
ToruNiina
8bc09d552a fix(#139): Merge branch 'auto-conversion-macro' 2021-04-02 19:29:04 +09:00
ToruNiina
03c846dc9d doc: add conversion macro to README 2021-04-02 19:28:45 +09:00
ToruNiina
e658a0126c test: disable macro testing if the macro is diabled 2021-04-02 18:26:24 +09:00
ToruNiina
6e3967e26e ci: check compiler version detected by cmake 2021-04-02 18:24:17 +09:00
ToruNiina
db1f42b5da fix: enable to control macro definition 2021-04-02 17:21:25 +09:00
ToruNiina
c7d6d793cb ci: install compiler 2021-04-02 17:00:00 +09:00
ToruNiina
14c6430dda Merge branch 'master' into auto-conversion-macro 2021-04-02 16:25:41 +09:00
ToruNiina
b4bc704e6e fix: trying to workaround MSVC preprocessor 2021-04-02 15:39:23 +09:00
ToruNiina
3f6e873aba fix: merge branch 'uneven-spacing-between-tables' 2021-03-31 11:53:54 +09:00
ToruNiina
a3b8dd6787 fix(#152): add newline btw kv-pair and subtables 2021-03-31 10:52:18 +09:00
ToruNiina
c121492071 fix: uneven spacing between tables
related: issue #152
2021-03-29 17:48:03 +09:00
ToruNiina
5e3f8f9105 chore: update version values 2021-03-25 22:43:37 +09:00
ToruNiina
17a15d3c18 doc: update contributor list and link in README 2021-03-25 22:33:05 +09:00
ToruNiina
42cc111b05 ci: activate linux/windows
confirmed that macos works.
2021-03-25 15:01:40 +09:00
ToruNiina
5e0ee32854 ci: trying to add macos to github actions [skip travis] [skip appveyor]
it is already listed in travis CI, but not in the GH actions
2021-03-25 14:53:28 +09:00
ToruNiina
2c5cc431fe ci: re-activate linux CI 2021-03-25 14:33:55 +09:00
ToruNiina
970f7cb36a ci: trying to update boost installation settings [skip travis] [skip appveyor] 2021-03-25 14:03:26 +09:00
ToruNiina
b924e70e3c feat: add a simple way to disable <filesystem>
As jwillikers pointed out in #150, there is a case where compiler
defines the corresponding feature test macro of <filesystem> but is
actually not available. The macro is a way to disable the feature
regardless of the status of feature test macro.
2021-03-25 11:44:11 +09:00
Toru Niina
7782258e68 Merge pull request #148 from sneakypete81/patch-1
Fix typo in error message
2021-01-31 14:26:02 +09:00
sneakypete81
08859c36d0 Fix typo in error message 2021-01-30 20:04:00 +00:00
ToruNiina
d3de136562 doc: simplity example code a bit 2021-01-25 17:25:29 +09:00
ToruNiina
43183e2ad1 Merge branch 'master' of github.com:ToruNiina/toml11 2020-12-29 18:54:58 +09:00
ToruNiina
e9144b41fb test: returning toml::value directly from into<T> 2020-12-29 18:53:10 +09:00
ToruNiina
2fb8793f1a doc: add document about basic_value and toml::into
related to #146.
2020-12-29 18:52:07 +09:00
Toru Niina
6c8a53915a Merge pull request #144 from amerry/sstream-include-fix
Add missing standard includes
2020-12-10 01:53:31 +09:00
Alex Merry
db2d33ca4b Add missing header for std::out_of_range exception
Failure seen on GCC 4.8.5 when including "toml/value.hpp".
2020-12-09 10:39:10 +00:00
Alex Merry
935da51769 Add missing include for ostringstream
Since region.hpp no longer includes <iostream> (but only <iomanip>),
source_location.hpp no longer includes a header that provides
std::ostringstream. Including <sstream> fixes this.
2020-12-09 10:19:07 +00:00
ToruNiina
be0d4bd0a9 fix: fix #141; Merge branch 'issue-141' 2020-11-05 00:01:41 +09:00
ToruNiina
9b472a6c72 fix: check it is empty before calling back 2020-11-04 23:24:59 +09:00
ToruNiina
1ead14589e fix: check if it is empty before calling back() 2020-11-04 23:24:02 +09:00
ToruNiina
b13065b1b5 fix: #142 Merge branch 'issue-142' 2020-11-03 21:05:03 +09:00
ToruNiina
a6581ee66b fix: an empty array is not an array of table 2020-11-03 20:34:01 +09:00
ToruNiina
0dafa7ee42 test: add case where a table should be inlined
array-of-table implicitly defines an array. If the array itself has a
comment, we need to format it explicitly.
2020-10-18 20:45:12 +09:00
ToruNiina
908b91079b fix: distinguish the comments and try to keep it
If a value has a comment, we need to try to write it explicitly.
2020-10-18 20:43:33 +09:00
ToruNiina
fce6ff317e refactor: distinguish the reason of failure 2020-10-18 18:36:05 +09:00
ToruNiina
fd50b11523 refactor: add write_comments() 2020-10-18 18:35:56 +09:00
ToruNiina
9090b8273c refactor: move array-of-table stuff to a function 2020-10-18 17:20:06 +09:00
ToruNiina
bfae1ab86c test: add test for auto-generated conversion 2020-10-16 21:40:54 +09:00
ToruNiina
88882b523f feat: add a macro defines convertion automatically 2020-10-16 21:40:47 +09:00
ToruNiina
382e3dc3ab refactor: use serializer::is_array_of_tables 2020-10-14 22:27:29 +09:00
ToruNiina
f7bfcdd7aa fix: check all the elements in an array
while checking if the array is array-of-tables or not (heterogeneous
arrays are allowed, so there might be an array that has a table and
an integer at the same time)
2020-10-14 18:00:04 +09:00
ToruNiina
2e41a26785 Merge branch 'master' of github.com:ToruNiina/toml11 into master 2020-10-14 15:35:18 +09:00
ToruNiina
f3378f0ac1 fix: #131 distinguish implicitly declared array 2020-10-14 15:32:08 +09:00
ToruNiina
12ee73d6a9 ci: suppress some of the combinations in CI
clang-7 with C++20 fails with the same reason, 'undefined reference to
std::allocator<char>::(de)allocate'.
2020-10-14 00:38:46 +09:00
ToruNiina
503baf52ed ci: suppress clang 6 + cxx20
Since the main branch that passed the same check 9 days ago also fails
with clang-6 and C++20 because of the same error, "undefined reference
to allocator_traits<char>::allocate". It could be a change in upstream
and since others (e.g. gcc) works well, I suppress the setting at this
moment.
2020-10-14 00:05:55 +09:00
ToruNiina
2deb75052c ci: use the same version of clang
I don't think it resolves the problem, undefined reference to
'std::allocator<char>::deallocate(char*, unsigned long)', though
2020-10-13 23:37:52 +09:00
ToruNiina
290dca3d67 test: add test for comment duplication 2020-10-13 22:04:28 +09:00
ToruNiina
f283a257d2 Revert "quick temporary patch for comment dup"
This reverts commit a6d38c1ec0.
Since the problem is solved, we don't need this patch any more.
2020-10-13 22:02:32 +09:00
ToruNiina
3d86f3a4e1 fix: avoid comment duplication in array of tables 2020-10-13 21:59:46 +09:00
ToruNiina
dc5a8069a9 refactor: require comments while construction
Note: at this commit, the code would not compile.
2020-10-13 21:58:08 +09:00
Toru Niina
4f31b90665 Merge pull request #136 from chronoxor/master
Fixed: Compile toml11 with MinGW cause error in <filesystem> #135
2020-10-04 18:53:34 +09:00
Ivan Shynkarenka
5d8c573357 Fixed: Compile toml11 with MinGW cause error in <filesystem> #136 2020-10-03 23:16:58 +03:00
Ivan Shynkarenka
6e1e5ccd84 Fixed: Compile toml11 with MinGW cause error in <filesystem> #136 2020-10-03 23:06:47 +03:00
Ivan Shynkarenka
f2d9fd1d1f Fixed: Compile toml11 with MinGW cause error in <filesystem> #136 2020-10-03 22:36:59 +03:00
Ivan Shynkarenka
97c8cbdaf5 Fixed: Compile toml11 with MinGW cause error in <filesystem> #135 2020-10-02 19:10:04 +03:00
ToruNiina
05ceb5ae79 fix: workaround for error around SFINAE in MSVC
avoid lambda with template argument
2020-09-29 02:26:16 +09:00
ToruNiina
96cfdb260a fix: update version in macro and cmake 2020-09-29 01:41:38 +09:00
ToruNiina
0fec125688 feat: remove default value from internal src 2020-09-29 01:40:49 +09:00
ToruNiina
a6d38c1ec0 fix: add a quick temporary patch for comment dup
first aid for #131
2020-09-22 17:36:24 +09:00
ToruNiina
c037913b2c doc: update link to the TOML spec 2020-09-20 19:24:48 +09:00
ToruNiina
6a328fe890 doc: recommend to set /Zc:__cplusplus 2020-09-20 18:07:58 +09:00
ToruNiina
7c18cbb1d9 doc: update section "contributors" 2020-09-19 20:35:37 +09:00
ToruNiina
ba7d49f452 test: use normal string literal
as a workaround for older version of gcc
2020-09-19 19:08:20 +09:00
ToruNiina
b0784ce286 test: in case of comment-before-comma 2020-09-19 18:24:23 +09:00
ToruNiina
670186fac7 Merge branch 'master' into allow-comment-before-comma 2020-09-19 18:10:45 +09:00
ToruNiina
5005998709 Merge branch 'master' into cpp20-mode-u8literal-workaround 2020-09-19 13:42:12 +09:00
ToruNiina
84fb703e04 ci: add utf-8 option to MSVC 2020-09-19 00:41:05 +09:00
ToruNiina
8c2560761b chore: enable to use __cplusplus on MSVC
related: https://github.com/ToruNiina/toml11/issues/112
2020-09-19 00:40:44 +09:00
ToruNiina
07ea5e52e2 ci: pass REQ_FS_LIB=ON in case of g++-8 & C++20 2020-09-16 22:16:20 +09:00
ToruNiina
d2b1e962c9 ci: add std=20 to some compilers on github actions 2020-09-16 21:28:19 +09:00
ToruNiina
528031012d test: add test for u8""_toml literals 2020-09-16 21:25:38 +09:00
ToruNiina
c205c762fe test: remove needless u8s from ascii characters 2020-09-16 21:25:04 +09:00
ToruNiina
a32cd6cb61 feat: enable to use u8""_toml literal in C++20 2020-09-16 21:24:03 +09:00
ToruNiina
38e113d2dc ci: set BUILD_TEST=ON on appveyor 2020-09-15 22:40:24 +09:00
Toru Niina
f15480ae4d Merge pull request #130 from MoAlyousef/master
Make toml11_BUILD_TEST Off by default
2020-09-15 22:28:34 +09:00
MoAlyousef
00bec8ae45 update Running Tests heading 2020-09-14 22:05:15 +03:00
MoAlyousef
d599edd1d4 make testing optional 2020-09-14 20:34:19 +03:00
MoAlyousef
a9534579c6 make testing optional 2020-09-14 20:25:38 +03:00
ToruNiina
c8ff302c94 test: add test for no-eof-newline cases 2020-09-14 16:39:05 +09:00
ToruNiina
003bc16c1b fix: skip the last zero in the file 2020-09-14 16:35:51 +09:00
Toru Niina
9132abc5c4 Merge pull request #127 from kenichiice/fix-include
Fix include path in README
2020-09-07 16:19:29 +09:00
OGAWA KenIchi
99d565bcc4 doc: fix include path
* see #72
2020-09-07 15:32:28 +09:00
ToruNiina
5f38127692 feat: allow comments before comma
replace ws by ws_comment_newline, as suggested.
discussed here: toml-lang/toml/issues/766
2020-08-16 11:03:58 +09:00
ToruNiina
3c3ebd88b4 feat: improve error message about invalid keys 2020-08-09 18:38:50 +09:00
ToruNiina
08f7ea9c56 refactor: remove extraneous whitespaces in errmsg 2020-08-09 18:38:21 +09:00
ToruNiina
cde29399f4 fix: use 1 in source_location as the default pos 2020-08-07 22:24:01 +09:00
ToruNiina
eec429e31b ci: add REQUIRE_FILESYSTEM_LIBRARY on CI 2020-08-06 16:35:49 +09:00
ToruNiina
79ddcaece6 chore: add CMake option to link with (std)c++fs 2020-08-06 16:29:24 +09:00
ToruNiina
8398b9a08b test: use array for char*
forgot to delete
2020-08-05 20:43:48 +09:00
ToruNiina
9c5abf0bfd test: check each overload compiles 2020-08-05 20:29:07 +09:00
ToruNiina
4fa94d45b3 fix: use const char* instead of &char[N]
to enable to pass char*, not only string literal
2020-08-04 20:08:58 +09:00
ToruNiina
46e84a9cc2 refactor: Merge branch 'refactor-region' 2020-07-31 12:45:52 +09:00
ToruNiina
4e6ae9a994 refactor: avoid string construct in format_ul 2020-07-30 16:11:35 +09:00
ToruNiina
f23c003d2f fix: add missing namespace specifier 2020-07-28 00:04:25 +09:00
ToruNiina
4b719f0806 refactor: use location() instead of get_region 2020-07-27 23:15:14 +09:00
ToruNiina
22ace027de refactor: rm template from detail::change_region 2020-07-27 23:04:24 +09:00
ToruNiina
bc219af5b5 refactor: use location() member instead of ctor 2020-07-27 23:03:33 +09:00
ToruNiina
68e8a31659 refactor: remove needless addressof() call 2020-07-27 23:00:40 +09:00
ToruNiina
32a5341d09 refactor: use source_location, not region_base* 2020-07-27 22:29:18 +09:00
ToruNiina
ce68f6f4c2 refactor: check (always-valid) ptr before deref 2020-07-27 21:32:35 +09:00
ToruNiina
e696aabd11 refactor: change internal interface to reduce code
to remove `std::addressof` calls, get_region(toml::value) now
returns a pointer to region.
2020-07-27 00:48:04 +09:00
ToruNiina
7fb93e2f54 fix: add missing explicit to detail::region 2020-07-27 00:20:26 +09:00
ToruNiina
19cc9a2edf refactor: remove template from detail::region 2020-07-25 22:01:34 +09:00
ToruNiina
72f5afb6af refactor: remove template from detail::location 2020-07-25 21:06:26 +09:00
ToruNiina
a8fa14d159 refactor: remove vec() method, use a constructor 2020-07-21 20:55:18 +09:00
ToruNiina
75999aa9ad refactor: add a constructor to location
By adding the constructor, vec() would not be not needed. But inserting
Container = std::string makes the constructor ambiguous, so it breaks
the current code.
2020-07-21 20:53:44 +09:00
ToruNiina
259da54edb refactor: always use vector<char> in location
`location` and `region` have a (shared_ptr to the) container of TOML
contents. Those take a template argument to allow both std::vector<char>
and std::string as an interanal container. But since those are internal
feature, i.e. it should not be used by a user directly, this template
can be removed by re-writing a parser a bit. This introduces a
complexity to toml11 error reporting system, so I'm removing this.
First, remove all the location<std::string> from the parser. Then the
template argument can be removed because everyone uses std::vector<char>
now.
2020-07-20 19:52:11 +09:00
ToruNiina
b461f363da refactor: add a method to reduce complexity later 2020-07-20 19:40:55 +09:00
ToruNiina
d43139a471 doc: update Contributors section 2020-07-19 19:50:02 +09:00
ToruNiina
a344668fa2 doc: update version to 3.5.0 2020-07-19 19:12:18 +09:00
ToruNiina
25aa97a435 doc: add actions status badge to README 2020-07-19 19:09:47 +09:00
ToruNiina
af70d3dfed ci [skip ci]: add github actions workflow file 2020-07-19 17:13:39 +09:00
ToruNiina
8b5cfb4105 test: add missing binary flag to ifstream 2020-07-19 16:57:20 +09:00
ToruNiina
4e0624aa60 feat: make sure the last null is removed 2020-07-19 16:56:31 +09:00
ToruNiina
3ac2c065eb Merge branch 'reorder-headers' to master 2020-07-17 15:17:40 +09:00
ToruNiina
470f81dc94 fix: #123 merge branch 'windows-nominmax' 2020-07-10 20:55:53 +09:00
ToruNiina
93a9f2711c test: add windows.h test 2020-07-10 18:32:59 +09:00
ToruNiina
761e576991 fix: workaround for windows.h that defines min/max
related to #123
2020-07-10 15:07:13 +09:00
ToruNiina
e6e84714c5 Merge branch 'master' into reorder-headers 2020-07-10 00:06:22 +09:00
Toru Niina
1efc99e11c Merge pull request #121 from SeverinLeonhardt/fix_msvc_c4866
Fix MSVC warning C4866
2020-07-03 21:37:22 +09:00
Marius Maaß
92aa42a58e Fix MSVC warning C4866
This fixes the warning "compiler may not enforce left-to-right
evaluation order for call to" that is caused by Visual Studio if this is
compiled with a target of C++17.
2020-07-03 08:00:47 +02:00
ToruNiina
b1c9df8998 feat: reorder headers following google c++ style
related to: #115
2020-06-28 00:58:20 +09:00
ToruNiina
9633e5fe5a doc: add iteration examples into as_xxx section
related to #120
2020-06-21 14:11:26 +09:00
ToruNiina
2164fd39f7 doc: explain about the type of the top-level value
fix #120.
2020-06-21 14:04:20 +09:00
ToruNiina
c22a3fd227 feat: support parse(std::filesystem::path) #113 2020-06-07 15:11:48 +09:00
ToruNiina
57c6652360 Merge branch 'master' into std-filesystem 2020-06-06 17:25:26 +09:00
ToruNiina
defde33544 fix: avoid ambiguity in overload resolution
Since both `std::string` and `std::filesystem::path` can be convertible
from `const char &[N]` (like, `parse("file.toml")`), after adding
`parse(std::filesystem::path)`, the overload resolution of
`parse("file.toml")` becomes ambiguous. By adding `parse(...)` that
exactly matches to `parse("file.toml")`, we can remove this ambiguity.
2020-06-06 17:18:02 +09:00
ToruNiina
46ed051740 fix: pass path.string as a filename 2020-06-05 23:15:19 +09:00
ToruNiina
2963d9a25b feat: add std::filesystem::path support 2020-06-05 19:43:23 +09:00
Toru Niina
531f335417 Merge pull request #119 from halfelf/fix/readme_finding_value_in_table
fix: "Finding a value in an array" example in README
2020-05-20 00:27:33 +09:00
Shu Wang
f29f42277e fix: "Finding a value in an array" example in README 2020-05-18 13:53:48 +08:00
Toru Niina
b03cde566a Merge pull request #117 from usefulcat/master
when parsing a local_time, parse up to 9 digits worth (nanoseconds) o…
2020-05-11 13:36:23 +09:00
Scott McCaskill
57d4e196a3 when parsing a local_time, parse up to 9 digits worth (nanoseconds) of fractional seconds 2020-05-10 16:06:52 -05:00
ToruNiina
deb3ab6617 ci: add DISALLOW_HETEROGENEOUS_ARRAYS 2020-04-03 23:57:52 +09:00
ToruNiina
bf992e8f94 doc: update README for v1-rc1 2020-04-03 23:45:45 +09:00
ToruNiina
7c07f4382c ci: add DISALLOW_HETEROGENEOUS_ARRAYS to toml-test 2020-04-03 23:43:59 +09:00
ToruNiina
125f608fa5 feat: remove TOML11_UNRELEASED_FEATURES.
v1.0.0-rc.1 has been released
2020-04-03 23:42:58 +09:00
ToruNiina
4d0ed847f9 test: remove default ctor from test code 2020-03-30 15:04:51 +09:00
ToruNiina
79594709fe fix: don't use default ctor when converting to map 2020-03-30 15:02:26 +09:00
ToruNiina
55a738c11f Merge branch 'do-not-require-default-ctor-108'
fix #108.
2020-03-28 23:19:52 +09:00
ToruNiina
eebe1f87e6 fix: update cmake version 3.4.0 2020-03-28 17:59:10 +09:00
ToruNiina
95c3b5f538 feat: use push_back instead of resize 2020-03-27 18:06:26 +09:00
ToruNiina
e2790c9e7b test: remove test_resize and add test_try_reserve 2020-03-27 18:06:02 +09:00
ToruNiina
9b52dc0131 feat: remove resize and add try_reserve 2020-03-27 18:05:31 +09:00
ToruNiina
5212992f05 feat: add is_std_forward_list
std::forward_list does not have push_back, insert, or emplace but
push_front, insert_after, and emplace_after. We need to distinguish it
from other continers.
2020-03-27 18:02:37 +09:00
ToruNiina
fcd6e47500 feat: add meta funcs, has_reserve/push_back_method 2020-03-27 18:01:47 +09:00
ToruNiina
31826b55ce feat: avoid double checking in helper methods 2020-03-25 22:49:19 +09:00
ToruNiina
e3fc354e8d Merge branch 'shorten-switch-cast' 2020-03-24 22:43:09 +09:00
ToruNiina
ea87f92358 doc: update exception section in README (fix #107) 2020-03-23 20:57:36 +09:00
ToruNiina
c259456282 ci: fix Travis.CI OS X build 2020-03-22 20:40:02 +09:00
ToruNiina
d7662347f2 refactor: shorten switch_cast definition by macro 2020-03-21 17:44:23 +09:00
ToruNiina
5f5539d402 feat: throw informative error from value.at(...) 2020-03-21 17:09:04 +09:00
ToruNiina
c2151cab0b refactor: show func name in bad_cast from helpers 2020-03-21 17:06:34 +09:00
ToruNiina
653c87592c feat: enable to show function name in bad_cast 2020-03-21 17:04:05 +09:00
ToruNiina
bdf4e75122 refactor: move helper function from get to value 2020-03-21 16:57:12 +09:00
ToruNiina
60d23116ba Merge branch 'master' of github.com:ToruNiina/toml11 2020-03-13 14:38:33 +09:00
ToruNiina
af8cf9ddc5 refactor: remove redundant functions in serializer 2020-03-13 13:55:14 +09:00
ToruNiina
f125cca010 refactor: simplify serializer's template argument 2020-03-12 13:46:17 +09:00
ToruNiina
a20a2c0b80 doc: update README 2020-03-01 00:35:27 +09:00
ToruNiina
9694afbe32 Merge branch 'improve-error-message' 2020-02-29 23:43:23 +09:00
ToruNiina
d11e42ca7e fix: explicitly say the table is top-level
The top-level table has its region at the first character of the file.
That means that, in the case when a key is not found in the top-level
table, the error message points to the first character. If the file has
its first table at the first line, the error message would be like this.
```console
[error] key "a" not found
 --> example.toml
   |
 1 | [table]
   | ^------ in this table
```
It actually points to the top-level table at the first character,
not `[table]`. But it is too confusing. To avoid the confusion, the
error message should explicitly say "key not found in the top-level
table".
2020-02-29 22:56:29 +09:00
ToruNiina
128b66bda9 refactor: add missing whitespace 2020-02-29 22:54:50 +09:00
ToruNiina
d1af42f151 refactor: add throw_key_not_found_error
and replace related throw statements with it
2020-02-29 22:23:15 +09:00
ToruNiina
8acf105b56 doc: update contributor list and test commands 2020-02-27 19:30:34 +09:00
Toru Niina
b86b5364ba Merge pull request #103 from jwillikers/fix_tests
Use FetchContent to retrieve TOML test data
2020-02-27 19:13:02 +09:00
Jordan Williams
bfe57340f4 no longer explicitly clone the TOML repository in CI builds 2020-02-24 08:03:29 -06:00
Jordan Williams
02a6f029ad use ExternalProject to download the toml tests
In order to evaluate the relative paths correctly, add_test should use the current binary directory as the working directory.
2020-02-24 07:59:59 -06:00
Jordan Williams
9017900ff3 set version directly from CMake project command
This silences the warning for CMake policy CMP0048: https://cmake.org/cmake/help/v3.0/policy/CMP0048.html
2020-02-24 07:35:59 -06:00
Jordan Williams
3c5ebd73d7 require CMake version 3.1 2020-02-24 07:31:48 -06:00
Jordan Williams
2223eb4f62 Revert "no longer explicitly clone the TOML repository in CI builds"
This reverts commit fe644ea4b7.
2020-02-24 07:31:15 -06:00
Jordan Williams
a655a71cef Revert "use FetchContent to retrieve TOML test data"
This reverts commit 4c34986db0.
2020-02-24 07:31:06 -06:00
ToruNiina
c34001725c Merge branch 'workaround-gcc-48x' 2020-02-20 11:59:49 +09:00
ToruNiina
5e3ffb70dd fix: check clang macro when checking gcc is used 2020-02-19 17:00:22 +09:00
ToruNiina
2265ca41c6 ci: test with gcc 4.8 and 4.9 on CI 2020-02-19 15:47:34 +09:00
ToruNiina
82fec38e37 refactor: simplify internally-used function 2020-02-19 15:46:25 +09:00
ToruNiina
189b910384 fix: solve #97 in the naivest way, macros 2020-02-19 15:44:38 +09:00
Jordan Williams
fe644ea4b7 no longer explicitly clone the TOML repository in CI builds 2020-02-18 20:21:02 -06:00
Jordan Williams
4c34986db0 use FetchContent to retrieve TOML test data 2020-02-18 19:37:28 -06:00
ToruNiina
ac1130f9f4 tag: update patch version v3.3.1 2020-02-16 22:02:40 +09:00
ToruNiina
d290c3b7e5 doc: add contributor to README 2020-02-14 19:25:41 +09:00
Toru Niina
e4140ac1fd Merge pull request #102 from jwillikers/cmake_cache_variables
Set CMake Standard Cache Variables. Fixes #101
2020-02-13 13:22:25 +09:00
Jordan Williams
ef33c10ba8 use cache variables for the CMake standard and extensions settings 2020-02-12 07:44:47 -06:00
Toru Niina
ced710bb4c Merge pull request #100 from jwillikers/clang_warnings
Silence Clang Warnings. Fixes #98 & #99
2020-02-12 13:37:58 +09:00
Jordan Williams
6b5944e839 fix -Wundef warnings 2020-02-11 06:30:18 -06:00
Jordan Williams
76cae8c057 enable -Wundef flag for tests 2020-02-11 06:25:10 -06:00
Jordan Williams
3930a44ccd enable range-loop-analysis flag for tests 2020-02-11 06:17:54 -06:00
Jordan Williams
3b6417de00 fix clang range-loop-analysis warnings 2020-02-11 06:13:55 -06:00
ToruNiina
573a6f1d81 test: use JSON format_key to format a key in JSON
The rule to format a basic string and a key is different. `"""` cannot
be used with JSON keys. Also, toml key is basically not wrapped by `"`.
So we need a function to format a key in JSON.
2020-02-06 01:28:37 +09:00
ToruNiina
f6a41d986c feat: handle quotes in strings in the better way
- if a basic string contains any double quote, make it multiline.
  - because 1 or 2 consecutive "s do not require escape sequence in it.
- if a basic string will be sufficiently long, make it multiline.
- if 3 consecutive "s appeared, insert backslash to break it down.
2020-02-05 22:42:10 +09:00
ToruNiina
16fc172b21 feat: check string length before adding newline
In literal strings, only the first newline will be trimmed.
```toml
str = '''
The first newline will be trimmed.'''
```
The previous code always adds this first-newline, but after this commit
it checks the length of the string and adds newline if the string is
sufficiently long.
2020-02-05 22:39:08 +09:00
ToruNiina
7d03eb489a test: add test cases with quotes in ml-string
some of these example strings are copied from toml-lang/toml:README.md
and some are modified.
2020-02-04 22:37:11 +09:00
ToruNiina
0582e1535b fix: handle edge-cases with quotes in ml-string
See comments in the code for detail.
2020-02-04 22:36:39 +09:00
ToruNiina
d495df93a6 refactor: remove trailing whitespaces 2020-02-04 22:21:37 +09:00
ToruNiina
5ca3a3c262 refactor: change ifdef UNRELEASED_FEATURE region
to eliminate dead code after returning from a function
2020-02-04 21:05:03 +09:00
ToruNiina
aa8d574dfe chore: update minor version 2020-01-24 22:08:12 +09:00
ToruNiina
49fdb61731 refactor: add explicit to ctors of internal types 2020-01-24 15:58:24 +09:00
ToruNiina
b2bb21a473 doc: update year of copyright notice 2020-01-23 22:18:04 +09:00
ToruNiina
0c58549fc6 Merge branch 'master' of github.com:ToruNiina/toml11 2020-01-22 12:20:34 +09:00
ToruNiina
b7b5e847d3 ci: test with C++14, not only 11/17 2020-01-22 12:19:53 +09:00
ToruNiina
22d630fec1 feat: replace detail::stuff by std if possible 2020-01-20 12:18:05 +09:00
ToruNiina
f7bf341452 fix: add missing noexcept specifier 2020-01-19 21:06:10 +09:00
ToruNiina
0934d90f90 refactor: move ctors that are only used internally 2020-01-19 18:30:27 +09:00
ToruNiina
f2c8d0e279 refactor: add missing explicit to toml::exception 2020-01-19 17:51:24 +09:00
ToruNiina
8c7d83d985 Merge branch 'add-value-member-methods' 2020-01-17 20:30:26 +09:00
ToruNiina
5ce44adbdc doc: add description about toml::value memfuns 2020-01-17 20:26:36 +09:00
ToruNiina
5c5b1320d0 test: add test for map/vector methods 2020-01-16 20:58:36 +09:00
ToruNiina
8b737dc21f feat: add member methods to toml::value 2020-01-16 20:58:10 +09:00
ToruNiina
ee654b6c3f chore: add -Werror when building test codes 2020-01-13 11:31:03 +09:00
ToruNiina
c59782d180 fix: remove useless conversions in the test codes 2020-01-13 11:29:17 +09:00
ToruNiina
9bef715ccd fix: use u32 as a result of binary operation 2020-01-13 11:26:53 +09:00
ToruNiina
d2b1cf5123 refactor: just use a constructor
to remove conversions
2020-01-13 11:26:29 +09:00
ToruNiina
9f92916d1d fix: suppress -Wuseless-conversion
`{integer} + 1` will automatically be an int, so static_cast<int>(a+1)
will be useless conversion.
2020-01-13 11:24:48 +09:00
ToruNiina
666e4cf9dc fix: suppress sign-conversion warnings 2020-01-13 00:46:21 +09:00
ToruNiina
cafee29c64 test: add some combinations of types in toml::find 2020-01-13 00:27:39 +09:00
ToruNiina
a7a2272b29 chore: turn more diagnostic flags on 2020-01-13 00:16:33 +09:00
ToruNiina
dc0bca2bb6 fix: update patch version ... 2020-01-12 23:18:03 +09:00
ToruNiina
490abe04fd refactor: remove redundant template argument 2020-01-12 23:07:17 +09:00
ToruNiina
81ed4c0e9d Merge branch 'master' of github.com:ToruNiina/toml11 2020-01-10 21:17:17 +09:00
ToruNiina
1b07baf184 doc: add toml::get specialization using ctor 2020-01-10 20:46:54 +09:00
ToruNiina
9073d52159 test: check get<foo> works with constructor 2020-01-10 20:39:56 +09:00
ToruNiina
55260654bf feat: get user-defined value by constructor
If a user-defined constructor has constructor(const toml::value&),
then it should be convertible in `toml::get` and `toml::find`.
2020-01-10 20:38:52 +09:00
ToruNiina
aa6271af75 doc: update README 2020-01-09 01:40:05 +09:00
ToruNiina
c54a03f189 Merge branch 'master' into find-idx 2020-01-09 00:27:51 +09:00
ToruNiina
c153c0e8c3 ci: test with sanitizers 2020-01-08 23:28:17 +09:00
ToruNiina
1f90af8e67 ci: refactor list of env vars 2020-01-08 23:17:38 +09:00
ToruNiina
a0c5192b74 chore: add option to use sanitizers 2020-01-08 23:07:05 +09:00
ToruNiina
7f020f3f44 refactor: remove error prefix
that will automatically be added in format_underline
2020-01-07 22:27:13 +09:00
ToruNiina
827b433389 Merge branch 'master' into find-idx 2020-01-07 22:13:15 +09:00
ToruNiina
1148d01c70 Merge branch 'master' into find-idx 2019-10-07 11:11:08 +09:00
ToruNiina
17d78553ff test: add test cases for find(v, idx)
- check whether find(v, idx) throws
- check find(v, ks...) works with both indices and strings
2019-10-03 15:48:04 +09:00
ToruNiina
4c12dad51f feat: add find<T>(value, idx) for arrays (#79) 2019-10-03 15:27:25 +09:00
81 changed files with 6297 additions and 2522 deletions

View File

@@ -2,19 +2,24 @@ version: 2.1
jobs:
test_suite:
environment:
- GOPATH: /home/circleci/go
docker:
- image: circleci/golang:1.9
- image: cimg/go:1.16
steps:
- checkout
- run:
command: |
g++ --version
cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check_toml_test.cpp -o check_toml_test
go get github.com/BurntSushi/toml-test
$GOPATH/bin/toml-test ./check_toml_test
g++ -std=c++11 -O2 -Wall -Wextra -Werror -I../ check_toml_test.cpp -o check_toml_test
export PATH=$(pwd):${PATH}
git clone --depth 1 --branch v1.3.0 https://github.com/BurntSushi/toml-test.git
cd toml-test/
go install -v ./cmd/toml-test
cd -
toml-test check_toml_test
# go clean -modcache
# go get github.com/BurntSushi/toml-test/cmd/toml-test
# $GOPATH/bin/toml-test ./check_toml_test
test_serialization:
docker:
- image: circleci/buildpack-deps:bionic
@@ -24,7 +29,7 @@ jobs:
command: |
g++ --version
cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check_serialization.cpp -o check_serialization
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check_serialization.cpp -o check_serialization
git clone https://github.com/BurntSushi/toml-test.git
cp check_serialization toml-test/tests/valid
cd toml-test/tests/valid
@@ -47,7 +52,7 @@ jobs:
command: |
g++ --version
cd tests/
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check.cpp -o check
g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -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

View File

@@ -0,0 +1,6 @@
FROM gcr.io/oss-fuzz-base/base-builder
RUN apt-get update && apt-get install -y make autoconf automake libtool
COPY . $SRC/toml11
COPY .clusterfuzzlite/build.sh $SRC/build.sh
WORKDIR $SRC/toml11

View File

@@ -0,0 +1,3 @@
# ClusterFuzzLite set up
This folder contains a fuzzing set for [ClusterFuzzLite](https://google.github.io/clusterfuzzlite).

View File

@@ -0,0 +1,6 @@
#!/bin/bash -eu
# Copy fuzzer executable to $OUT/
$CXX $CFLAGS $LIB_FUZZING_ENGINE \
$SRC/toml11/.clusterfuzzlite/parse_fuzzer.cpp \
-o $OUT/parse_fuzzer \
-I$SRC/toml11/

View File

@@ -0,0 +1,16 @@
#include <toml.hpp>
#include <sstream>
#include <string>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
std::string s(reinterpret_cast<const char *>(data), size);
std::istringstream iss(s);
try {
const auto ref = toml::parse(iss);
} catch (...) {
}
return 0;
}

View File

@@ -0,0 +1 @@
language: c++

13
.editorconfig Normal file
View File

@@ -0,0 +1,13 @@
root = true
[*]
indent_style = space
[CMakeLists.txt]
indent_size = 4
[*.{c,h}*]
indent_size = 4
[*.{md,yml}]
indent_size = 2

30
.github/workflows/cflite_pr.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: ClusterFuzzLite PR fuzzing
on:
workflow_dispatch:
pull_request:
branches: [ master ]
permissions: read-all
jobs:
PR:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sanitizer: [address]
steps:
- name: Build Fuzzers (${{ matrix.sanitizer }})
id: build
uses: google/clusterfuzzlite/actions/build_fuzzers@v1
with:
sanitizer: ${{ matrix.sanitizer }}
language: c++
bad-build-check: false
- name: Run Fuzzers (${{ matrix.sanitizer }})
id: run
uses: google/clusterfuzzlite/actions/run_fuzzers@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
fuzz-seconds: 240
mode: 'code-change'
report-unreproducible-crashes: false
sanitizer: ${{ matrix.sanitizer }}

256
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,256 @@
name: build
on: [push, pull_request]
jobs:
build-linux-gcc:
runs-on: Ubuntu-22.04
strategy:
matrix:
compiler: ['g++-12', 'g++-11', 'g++-10', 'g++-9']
standard: ['11', '14', '17', '20']
unreleased: ['ON', 'OFF']
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Install
run: |
sudo apt-add-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install libboost-test-dev
sudo apt-get install language-pack-fr # test serializer w/ locale
sudo apt-get install ${{ matrix.compiler }}
- name: Configure
run: |
mkdir build && cd build
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }}
- name: Build
run: |
cd build && cmake --build .
- name: Test
run: |
cd build && ctest --output-on-failure
build-linux-clang:
runs-on: Ubuntu-22.04
strategy:
matrix:
compiler: ['15', '14', '13', '12', '11']
standard: ['11', '14', '17', '20']
unreleased: ['ON', 'OFF']
exclude:
- {compiler: '14', standard: '20'} # to avoid using gcc-13 libstdc++
- {compiler: '13', standard: '20'} # with older clang
- {compiler: '12', standard: '20'}
- {compiler: '11', standard: '20'}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Install
run: |
sudo apt-add-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install libboost-test-dev
sudo apt-get install language-pack-fr # test serializer w/ locale
sudo apt-get install clang-${{ matrix.compiler }}
- name: Configure
run: |
mkdir build && cd build
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_C_COMPILER=clang-${{ matrix.compiler }} -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }}
- name: Build
run: |
cd build && cmake --build .
- name: Test
run: |
cd build && ctest --output-on-failure
build-linux-old-gcc:
runs-on: Ubuntu-20.04
strategy:
matrix:
compiler: ['g++-8', 'g++-7']
standard: ['11', '14', '17', '20']
unreleased: ['ON', 'OFF']
exclude:
- {compiler: 'g++-7', standard: '20'}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Install
run: |
sudo apt-add-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install libboost-test-dev
sudo apt-get install language-pack-fr # test serializer w/ locale
sudo apt-get install ${{ matrix.compiler }}
- name: Configure
run: |
mkdir build && cd build
if [[ "${{ matrix.compiler }}" == "g++-8" && ( "${{ matrix.standard }}" == "17" || "${{ matrix.standard }}" == "20" ) ]] ; then
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_REQUIRE_FILESYSTEM_LIBRARY=ON -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }}
else
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }}
fi
- name: Build
run: |
cd build && cmake --build .
- name: Test
run: |
cd build && ctest --output-on-failure
build-linux-old-clang:
runs-on: Ubuntu-20.04
strategy:
matrix:
compiler: ['10', '9', '8', '7', '6.0']
standard: ['11', '14', '17', '20']
unreleased: ['ON', 'OFF']
exclude:
- {compiler: '6.0', standard: '20'}
- {compiler: '7', standard: '20'}
- {compiler: '8', standard: '20'}
- {compiler: '9', standard: '20'}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Install
run: |
sudo apt-add-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install libboost-test-dev
sudo apt-get install language-pack-fr # test serializer w/ locale
sudo apt-get install clang-${{ matrix.compiler }}
- name: Configure
run: |
mkdir build && cd build
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_C_COMPILER=clang-${{ matrix.compiler }} -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }}
- name: Build
run: |
cd build && cmake --build .
- name: Test
run: |
cd build && ctest --output-on-failure
# build-osx-13-arm64:
# runs-on: macos-13-arm64
# strategy:
# matrix:
# standard: ['11', '14', '17', '20']
# unreleased: ['ON', 'OFF']
# steps:
# - name: Checkout
# uses: actions/checkout@v4
# with:
# submodules: true
# - name: Install
# run: |
# brew install boost
# - name: Configure
# run: |
# mkdir build && cd build
# cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }}
# - name: Build
# run: |
# cd build && cmake --build .
# - name: Test
# run: |
# cd build && ctest --output-on-failure
build-osx-13:
runs-on: macos-13
strategy:
matrix:
standard: ['11', '14', '17', '20']
unreleased: ['ON', 'OFF']
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Install
run: |
brew install boost
env:
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
- name: Configure
run: |
mkdir build && cd build
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }}
- name: Build
run: |
cd build && cmake --build .
- name: Test
run: |
cd build && ctest --output-on-failure
build-osx-12:
runs-on: macos-12
strategy:
matrix:
standard: ['11', '14', '17', '20']
unreleased: ['ON', 'OFF']
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Install
run: |
brew install boost
- name: Configure
run: |
mkdir build && cd build
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }}
- name: Build
run: |
cd build && cmake --build .
- name: Test
run: |
cd build && ctest --output-on-failure
build-windows-msvc:
runs-on: windows-2022
strategy:
matrix:
standard: ['11', '14', '17', '20']
config: ['Release', 'Debug']
unreleased: ['ON', 'OFF']
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Install
run: |
(New-Object System.Net.WebClient).DownloadFile("https://github.com/actions/boost-versions/releases/download/1.72.0-20200608.4/boost-1.72.0-win32-msvc14.2-x86_64.tar.gz", "$env:TEMP\\boost.tar.gz")
7z.exe x "$env:TEMP\\boost.tar.gz" -o"$env:TEMP\\boostArchive" -y | Out-Null
7z.exe x "$env:TEMP\\boostArchive" -o"$env:TEMP\\boost" -y | Out-Null
Push-Location -Path "$env:TEMP\\boost"
Invoke-Expression .\\setup.ps1
- uses: ilammy/msvc-dev-cmd@v1
- name: Configure
shell: cmd
run: |
file --mime-encoding tests/test_literals.cpp
mkdir build
cd build
cmake ../ -G "NMake Makefiles" -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DBoost_NO_BOOST_CMAKE=ON -DBOOST_ROOT="C:\\hostedtoolcache\\windows\\Boost\\1.72.0\\x86_64" -DTOML11_USE_UNRELEASED_TOML_FEATURES=${{ matrix.unreleased }}
- name: Build
working-directory: ./build
run: |
cmake --build . --config "${{ matrix.config }}"
- name: Test
working-directory: ./build
run: |
file --mime-encoding tests/toml/tests/example.toml
file --mime-encoding tests/toml/tests/fruit.toml
file --mime-encoding tests/toml/tests/hard_example.toml
file --mime-encoding tests/toml/tests/hard_example_unicode.toml
ctest --build-config "${{ matrix.config }}" --output-on-failure

View File

@@ -1,250 +0,0 @@
dist: trusty
matrix:
include:
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-5" CXX_STANDARD=11 TOML_HEAD=OFF
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-5
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-6" CXX_STANDARD=11 TOML_HEAD=OFF
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-6
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-7" CXX_STANDARD=11 TOML_HEAD=OFF
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-7
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=11 TOML_HEAD=OFF
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=11 TOML_HEAD=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=17 TOML_HEAD=OFF
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: gcc
env: COMPILER="g++-8" CXX_STANDARD=17 TOML_HEAD=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-3.9" CXX_STANDARD=11 TOML_HEAD=OFF
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-3.9
packages:
- g++-8
- clang-3.9
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-4.0" CXX_STANDARD=11 TOML_HEAD=OFF
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-4.0
packages:
- g++-8
- clang-4.0
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-5.0" CXX_STANDARD=11 TOML_HEAD=OFF
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-5.0
packages:
- g++-8
- clang-5.0
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-6.0" CXX_STANDARD=11 TOML_HEAD=OFF
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-6.0
packages:
- g++-8
- clang-6.0
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-7" CXX_STANDARD=11 TOML_HEAD=OFF
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-7
packages:
- g++-8
- clang-7
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=11 TOML_HEAD=OFF
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- g++-8
- clang-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=11 TOML_HEAD=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- g++-8
- clang-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=17 TOML_HEAD=OFF
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- boost1.70
- os: linux
language: cpp
compiler: clang
env: COMPILER="clang++-8" CXX_STANDARD=17 TOML_HEAD=ON
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
- boost1.70
- os: osx
language: cpp
compiler: clang
env: CXX_STANDARD=11
cache:
directories:
- $HOME/Library/Caches/Homebrew
addons:
homebrew:
update: true
packages:
- cmake
- boost
script:
- |
if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
mkdir -p cmake
travis_retry wget "https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.tar.gz"
tar xf cmake-3.14.5-Linux-x86_64.tar.gz -C cmake --strip-components=1
export PATH=${TRAVIS_BUILD_DIR}/cmake/bin:${PATH}
fi
- cmake --version
- mkdir build
- cd build
- git clone https://github.com/toml-lang/toml.git
- cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD -DTOML11_USE_UNRELEASED_TOML_FEATURES=${TOML_HEAD} ..
- make
- ctest --output-on-failure
# https://stackoverflow.com/a/53331571
before_cache:
- |
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
brew cleanup
fi

View File

@@ -1,46 +1,26 @@
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 3.5)
enable_testing()
project(toml11)
project(toml11 VERSION 3.8.1)
set(toml11_VERSION_MAYOR 3)
set(toml11_VERSION_MINOR 2)
set(toml11_VERSION_PATCH 0)
set(toml11_VERSION
"${toml11_VERSION_MAYOR}.${toml11_VERSION_MINOR}.${toml11_VERSION_PATCH}"
)
option(toml11_BUILD_TEST "Build toml tests" OFF)
option(toml11_INSTALL "Install CMake targets during install step." ON)
option(toml11_TEST_WITH_ASAN "use LLVM address sanitizer" OFF)
option(toml11_TEST_WITH_UBSAN "use LLVM undefined behavior sanitizer" OFF)
option(toml11_BUILD_TEST "Build toml tests" ON)
option(TOML11_USE_UNRELEASED_TOML_FEATURES
"use features in toml-lang/toml master while testing" OFF)
include(CheckCXXCompilerFlag)
if("${CMAKE_VERSION}" VERSION_GREATER 3.1)
set(CMAKE_CXX_EXTENSIONS OFF)
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
endif()
set(CXX_STANDARD_REQUIRED ON)
else()
# Manually check for C++11 compiler flag.
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
CHECK_CXX_COMPILER_FLAG("-std=gnu++11" COMPILER_SUPPORTS_GNU11)
CHECK_CXX_COMPILER_FLAG("-std=gnu++0x" COMPILER_SUPPORTS_GNU0X)
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXXOX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
elseif(COMPILER_SUPPORTS_GNU11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
elseif(COMPILER_SUPPORTS_GNU0X)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
if(MSVC)
# add_definitions("/Zc:__cplusplus") # define __cplusplus value correctly
add_definitions("/utf-8") # enable to use u8"" literal
if(MSVC_VERSION LESS 1910)
message(STATUS "MSVC < 1910. DEFINE_CONVERSION_NON_INTRUSIVE is disabled")
add_definitions(-DTOML11_WITHOUT_DEFINE_NON_INTRUSIVE)
elseif(MSVC_VERSION LESS 1920)
add_definitions("/experimental:preprocessor") # MSVC 2017
else()
if(MSVC)
if(MSVC_VERSION LESS 1900)
message(SEND_ERROR "MSVC < 14.0 is not supported. Please update your compiler or use mingw")
endif()
else()
message(SEND_ERROR "The ${CMAKE_CXX_COMPILER} compiler lacks C++11 support. Use another compiler.")
endif()
add_definitions("/Zc:preprocessor") # MSVC 2019
endif()
endif()
@@ -67,38 +47,40 @@ write_basic_package_version_file(
COMPATIBILITY SameMajorVersion
)
configure_package_config_file(
cmake/toml11Config.cmake.in
${toml11_config}
INSTALL_DESTINATION ${toml11_install_cmake_dir}
PATH_VARS toml11_install_cmake_dir
)
if (toml11_INSTALL)
configure_package_config_file(
cmake/toml11Config.cmake.in
${toml11_config}
INSTALL_DESTINATION ${toml11_install_cmake_dir}
PATH_VARS toml11_install_cmake_dir
)
# Install config files
install(FILES ${toml11_config} ${toml11_config_version}
DESTINATION ${toml11_install_cmake_dir}
)
# Install config files
install(FILES ${toml11_config} ${toml11_config_version}
DESTINATION ${toml11_install_cmake_dir}
)
# Install header files
install(
FILES toml.hpp
DESTINATION "${toml11_install_include_dir}"
)
install(
DIRECTORY "toml"
DESTINATION "${toml11_install_include_dir}"
FILES_MATCHING PATTERN "*.hpp"
)
# Install header files
install(
FILES toml.hpp
DESTINATION "${toml11_install_include_dir}"
)
install(
DIRECTORY "toml"
DESTINATION "${toml11_install_include_dir}"
FILES_MATCHING PATTERN "*.hpp"
)
# Export targets and install them
install(TARGETS toml11
EXPORT toml11Targets
)
install(EXPORT toml11Targets
FILE toml11Targets.cmake
DESTINATION ${toml11_install_cmake_dir}
NAMESPACE toml11::
)
# Export targets and install them
install(TARGETS toml11
EXPORT toml11Targets
)
install(EXPORT toml11Targets
FILE toml11Targets.cmake
DESTINATION ${toml11_install_cmake_dir}
NAMESPACE toml11::
)
endif()
if (toml11_BUILD_TEST)
add_subdirectory(tests)

505
README.md
View File

@@ -1,23 +1,23 @@
toml11
======
[![Build Status on TravisCI](https://travis-ci.org/ToruNiina/toml11.svg?branch=master)](https://travis-ci.org/ToruNiina/toml11)
[![Build Status on GitHub Actions](https://github.com/ToruNiina/toml11/workflows/build/badge.svg)](https://github.com/ToruNiina/toml11/actions)
[![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)
toml11 is a C++11 header-only toml parser/encoder depending only on C++ standard library.
toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C++ standard library.
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.
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).
- It is compatible to the latest version of [TOML v1.0.0](https://toml.io/en/v1.0.0).
- It is one of the most TOML standard compliant libraries, tested with [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test).
- It shows highly informative error messages. You can see the error messages about invalid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
- It has configurable container. You can use any random-access containers and key-value maps as backend containers.
- It optionally preserves comments without any overhead.
- It has configurable serializer that supports comments, inline tables, literal strings and multiline strings.
- It supports user-defined type conversion from/into toml values.
- It correctly handles UTF-8 sequences, with or without BOM, both on posix and Windows.
## Example
@@ -27,15 +27,29 @@ You can see the error messages about invalid files and serialization results of
int main()
{
const auto data = toml::parse("example.toml");
// ```toml
// title = "an example toml file"
std::string title = toml::find<std::string>(data, "title");
std::cout << "the title is " << title << std::endl;
// nums = [3, 1, 4, 1, 5]
// ```
auto data = toml::parse("example.toml");
// nums = [1, 2, 3, 4, 5]
std::vector<int> nums = toml::find<std::vector<int>>(data, "nums");
std::cout << "the length of `nums` is" << nums.size() << std::endl;
// find a value with the specified type from a table
std::string title = toml::find<std::string>(data, "title");
// convert the whole array into any container automatically
std::vector<int> nums = toml::find<std::vector<int>>(data, "nums");
// access with STL-like manner
if(!data.contains("foo"))
{
data["foo"] = "bar";
}
// pass a fallback
std::string name = toml::find_or<std::string>(data, "name", "not found");
// width-dependent formatting
std::cout << std::setw(80) << data << std::endl;
return 0;
}
@@ -84,7 +98,7 @@ int main()
Just include the file after adding it to the include path.
```cpp
#include <toml11/toml.hpp> // that's all! now you can use it.
#include <toml.hpp> // that's all! now you can use it.
#include <iostream>
int main()
@@ -99,6 +113,39 @@ int main()
The convenient way is to add this repository as a git-submodule or to install
it in your system by CMake.
Note for MSVC: We recommend to set `/Zc:__cplusplus` to detect C++ version correctly.
### Example installation
For local installation build & use the provided install target
```bash
git clone https://github.com/ToruNiina/toml11.git
mkdir -p toml11/build
cd toml11/build
cmake .. -DCMAKE_CXX_STANDARD=11
sudo make install
```
In case you want to create a `.deb` you can use `checkinstall`.
```bash
sudo checkinstall
[[ .. skipping for clarity ]]
**********************************************************************
Done. The new package has been installed and saved to
/home/user/toml11/build/build_20230728-1_amd64.deb
You can remove it from your system anytime using:
dpkg -r build
**********************************************************************
```
You should get a package that you can install with `dpkg -i <myfile>.deb` and remove with `dpkg -r <myfile>.deb`
## Decoding a toml file
To parse a toml file, the only thing you have to do is
@@ -109,6 +156,9 @@ const std::string fname("sample.toml");
const toml::value data = toml::parse(fname);
```
As required by the TOML specification, the top-level value is always a table.
You can find a value inside it, cast it into a table explicitly, and insert it as a value into other `toml::value`.
If it encounters an error while opening a file, it will throw `std::runtime_error`.
You can also pass a `std::istream` to the `toml::parse` function.
@@ -166,7 +216,7 @@ what(): [error] bad time: should be HH:MM:SS.subsec
--> ./datetime-malformed-no-secs.toml
1 | no-secs = 1987-07-05T17:45Z
| ^------- HH:MM:SS.subsec
|
|
Hint: pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999
Hint: fail: 1979-05-27T7:32:00, 1979-05-27 17:32
```
@@ -231,7 +281,7 @@ See also [underlying types](#underlying-types).
**NOTE**: For some technical reason, automatic conversion between `integer` and
`floating` is not supported. If you want to get a floating value even if a value
has integer value, you need to convert it manually after obtaining a value,
like the followings.
like the following.
```cpp
const auto vx = toml::find(data, "x");
@@ -256,11 +306,11 @@ shape = "round"
``` cpp
const auto data = toml::parse("fruit.toml");
const auto& fruit = toml::find(data, "fruit");
const auto name = toml::find<std::string>(fruit, "apple");
const auto name = toml::find<std::string>(fruit, "name");
const auto& physical = toml::find(fruit, "physical");
const auto color = toml::find<std::string>(fruit, "color");
const auto shape = toml::find<std::string>(fruit, "shape");
const auto color = toml::find<std::string>(physical, "color");
const auto shape = toml::find<std::string>(physical, "shape");
```
Here, variable `fruit` is a `toml::value` and can be used as the first argument
@@ -274,10 +324,44 @@ const auto color = toml::find<std::string>(data, "fruit", "physical", "color");
const auto shape = toml::find<std::string>(data, "fruit", "physical", "shape");
```
### Finding a value in an array
You can find n-th value in an array by `toml::find`.
```toml
values = ["foo", "bar", "baz"]
```
``` cpp
const auto data = toml::parse("sample.toml");
const auto values = toml::find(data, "values");
const auto bar = toml::find<std::string>(values, 1);
```
`toml::find` can also search array recursively.
```cpp
const auto data = toml::parse("fruit.toml");
const auto bar = toml::find<std::string>(data, "values", 1);
```
Before calling `toml::find`, you can check if a value corresponding to a key
exists. You can use both `bool toml::value::contains(const key&) const` and
`std::size_t toml::value::count(const key&) const`. Those behaves like the
`std::map::contains` and `std::map::count`.
```cpp
const auto data = toml::parse("fruit.toml");
if(data.contains("fruit") && data.at("fruit").count("physical") != 0)
{
// ...
}
```
### In case of error
If the value does not exist, `toml::find` throws an error with the location of
the table.
If the value does not exist, `toml::find` throws `std::out_of_range` with the
location of the table.
```console
terminate called after throwing an instance of 'std::out_of_range'
@@ -287,11 +371,6 @@ terminate called after throwing an instance of 'std::out_of_range'
| ~~~~~ in this table
```
**Note**: It is recommended to find a table as `toml::value` because it has much information
compared to `toml::table`, which is an alias of
`std::unordered_map<std::string, toml::value>`. Since `toml::table` does not have
any information about toml file, such as where the table was defined in the file.
----
If the specified type differs from the actual value contained, it throws
@@ -423,6 +502,29 @@ if(answer.is_integer() && answer.as_integer(std::nothrow) == 42)
If `std::nothrow` is passed, the functions are marked as noexcept.
By casting a `toml::value` into an array or a table, you can iterate over the
elements.
```cpp
const auto data = toml::parse("example.toml");
std::cout << "keys in the top-level table are the following: \n";
for(const auto& kv : data.as_table())
{
std::cout << kv.first << '\n';
}
for(const auto& [k, v] : data.as_table()) // or in C++17
{
std::cout << k << '\n';
}
const auto& fruits = toml::find(data, "fruits");
for(const auto& v : fruits.as_array())
{
std::cout << toml::find<std::string>(v, "name") << '\n';
}
```
The full list of the functions is below.
```cpp
@@ -768,6 +870,19 @@ const auto data = toml::parse("example.toml");
const auto num = toml::find_or(data, "num", 42);
```
It works recursively if you pass several keys for subtables.
In that case, the last argument is considered to be the optional value.
All other arguments between `toml::value` and the optional value are considered as keys.
```cpp
// [fruit.physical]
// color = "red"
auto data = toml::parse("fruit.toml");
auto color = toml::find_or(data, "fruit", "physical", "color", "red");
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^
// arguments optional value
```
Also, `toml::get_or` returns a default value if `toml::get<T>` failed.
```cpp
@@ -825,7 +940,7 @@ toml::visit([](const auto& val) -> void {
```
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
receive all the possible TOML types. Also, the result types should be the same
each other.
## Constructing a toml::value
@@ -869,24 +984,50 @@ toml::value v(toml::local_time(std::chrono::hours(10)));
```
You can construct an array object not only from `initializer_list`, but also
from STL containers.
from STL containers. In that case, the element type must be convertible to
`toml::value`.
```cpp
std::vector<int> vec{1,2,3,4,5};
toml::value v = vec;
toml::value v(vec);
```
All the elements of `initializer_list` should be convertible into `toml::value`.
When you construct an array value, all the elements of `initializer_list`
must be convertible into `toml::value`.
If a `toml::value` has an array, you can `push_back` an element in it.
```cpp
toml::value v{1,2,3,4,5};
v.push_back(6);
```
`emplace_back` also works.
## Preserving comments
After toml11 v3, you can choose whether comments are preserved or not.
toml11 v3 or later allows you yo choose whether comments are preserved or not via template parameter
```cpp
const auto data1 = toml::parse<toml::discard_comments >("example.toml");
const auto data2 = toml::parse<toml::preserve_comments>("example.toml");
```
or macro definition.
```cpp
#define TOML11_PRESERVE_COMMENTS_BY_DEFAULT
#include <toml11/toml.hpp>
```
This feature is controlled by template parameter in `toml::basic_value<...>`.
`toml::value` is an alias of `toml::basic_value<...>`.
If template parameter is explicitly specified, the return value of `toml::parse`
will be `toml::basic_value<toml::preserve_comments>`.
If the macro is defined, the alias `toml::value` will be
`toml::basic_value<toml::preserve_comments>`.
Comments related to a value can be obtained by `toml::value::comments()`.
The return value has the same interface as `std::vector<std::string>`.
@@ -1063,7 +1204,7 @@ add a comma after the first element (like `[1,]`).
"[[table]]"_toml; // This is a table that has an array of tables inside.
"[[1]]"_toml; // This literal is ambiguous.
// Currently, it becomes a table that has array of table "1".
// Currently, it becomes an empty array of table named "1".
"1 = [{}]"_toml; // This is a table that has an array of table named 1.
"[[1,]]"_toml; // This is an array of arrays.
"[[1],]"_toml; // ditto.
@@ -1093,7 +1234,7 @@ const auto data = toml::parse("example.toml");
const foo f = toml::find<ext::foo>(data, "foo");
```
There are 2 ways to use `toml::get` with the types that you defined.
There are 3 ways to use `toml::get` with the types that you defined.
The first one is to implement `from_toml(const toml::value&)` member function.
@@ -1120,7 +1261,31 @@ struct foo
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.
The second is to implement `constructor(const toml::value&)`.
```cpp
namespace ext
{
struct foo
{
explicit foo(const toml::value& v)
: a(toml::find<int>(v, "a")), b(toml::find<double>(v, "b")),
c(toml::find<std::string>(v, "c"))
{}
int a;
double b;
std::string c;
};
} // ext
```
Note that implicit default constructor declaration will be suppressed
when a constructor is defined. If you want to use the struct (here, `foo`)
in a container (e.g. `std::vector<foo>`), you may need to define default
constructor explicitly.
The third is to implement specialization of `toml::from` for your type.
```cpp
namespace ext
@@ -1153,8 +1318,16 @@ struct from<ext::foo>
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.
In some cases, a class has a templatized constructor that takes a template, `T`.
It confuses `toml::get/find<T>` because it makes the class "constructible" from
`toml::value`. To avoid this problem, `toml::from` and `from_toml` always
precede constructor. It makes easier to implement conversion between
`toml::value` and types defined in other libraries because it skips constructor.
But, importantly, you cannot define `toml::from<T>` and `T.from_toml` at the same
time because it causes ambiguity in the overload resolution of `toml::get<T>` and `toml::find<T>`.
So the precedence is `toml::from<T>` == `T.from_toml()` > `T(toml::value)`.
If you want to convert any versions of `toml::basic_value`,
you need to templatize the conversion function as follows.
@@ -1205,9 +1378,9 @@ struct foo
double b;
std::string c;
toml::table into_toml() const // you need to mark it const.
toml::value into_toml() const // you need to mark it const.
{
return toml::table{{"a", this->a}, {"b", this->b}, {"c", this->c}};
return toml::value{{"a", this->a}, {"b", this->b}, {"c", this->c}};
}
};
} // ext
@@ -1234,9 +1407,9 @@ namespace toml
template<>
struct into<ext::foo>
{
static toml::table into_toml(const ext::foo& f)
static toml::value into_toml(const ext::foo& f)
{
return toml::table{{"a", f.a}, {"b", f.b}, {"c", f.c}};
return toml::value{{"a", f.a}, {"b", f.b}, {"c", f.c}};
}
};
} // toml
@@ -1248,6 +1421,55 @@ toml::value v(f);
Any type that can be converted to `toml::value`, e.g. `int`, `toml::table` and
`toml::array` are okay to return from `into_toml`.
You can also return a custom `toml::basic_value` from `toml::into`.
```cpp
namespace toml
{
template<>
struct into<ext::foo>
{
static toml::basic_value<toml::preserve_comments> into_toml(const ext::foo& f)
{
toml::basic_value<toml::preserve_comments> v{{"a", f.a}, {"b", f.b}, {"c", f.c}};
v.comments().push_back(" comment");
return v;
}
};
} // toml
```
But note that, if this `basic_value` would be assigned into other `toml::value`
that discards `comments`, the comments would be dropped.
### Macro to automatically define conversion functions
There is a helper macro that automatically generates conversion functions `from` and `into` for a simple struct.
```cpp
namespace foo
{
struct Foo
{
std::string s;
double d;
int i;
};
} // foo
TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i)
int main()
{
const auto file = toml::parse("example.toml");
auto f = toml::find<foo::Foo>(file, "foo");
}
```
And then you can use `toml::find<foo::Foo>(file, "foo");`
**Note** that, because of a slight difference in implementation of preprocessor between gcc/clang and MSVC, [you need to define `/Zc:preprocessor`](https://github.com/ToruNiina/toml11/issues/139#issuecomment-803683682) to use it in MSVC (Thank you @glebm !).
## Formatting user-defined error messages
When you encounter an error after you read the toml value, you may want to
@@ -1349,7 +1571,7 @@ const toml::source_location loc = v.location();
## Exceptions
All the exceptions thrown by toml11 inherits `toml::exception` that inherits
The following `exception` classes inherits `toml::exception` that inherits
`std::exception`.
```cpp
@@ -1376,6 +1598,16 @@ struct exception : public std::exception
It represents where the error occurs.
`syntax_error` will be thrown from `toml::parse` and `_toml` literal.
`type_error` will be thrown from `toml::get/find`, `toml::value::as_xxx()`, and
other functions that takes a content inside of `toml::value`.
Note that, currently, from `toml::value::at()` and `toml::find(value, key)`
may throw an `std::out_of_range` that does not inherits `toml::exception`.
Also, in some cases, most likely in the file open error, it will throw an
`std::runtime_error`.
## Colorize Error Messages
By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from
@@ -1421,7 +1653,7 @@ it uses [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code).
Without `TOML11_COLORIZE_ERROR_MESSAGE`, you can still colorize user-defined
error message by passing `true` to the `toml::format_error` function.
If you define `TOML11_COLORIZE_ERROR_MESSAGE`, the value is `true` by default.
If not, the defalut value would be `false`.
If not, the default value would be `false`.
```cpp
std::cerr << toml::format_error("[error] value should be positive",
@@ -1429,10 +1661,62 @@ std::cerr << toml::format_error("[error] value should be positive",
hints, /*colorize = */ true) << std::endl;
```
Note: It colorize `[error]` in red. That means that it detects `[error]` prefix
Note: It colorizes `[error]` in red. That means that it detects `[error]` prefix
at the front of the error message. If there is no `[error]` prefix,
`format_error` adds it to the error message.
Compared to the `TOML11_COLORIZE_ERROR_MESSAGE` macro that enables colorization
statically, toml11 provides `toml::color::enable` & `toml::color::disable`
functions to dynamically change the color mode. This feature overwrites
`TOML11_COLORIZE_ERROR_MESSAGE` and the `colorize` argument of
`toml::format_error` when you call `enable`.
Note: If either `TOML11_COLORIZE_ERROR_MESSAGE` is defined or the `colorize`
argument is used, it takes precedence, meaning that `disable` won't work.
Accordingly, we highly recommend using only one of them.
```cpp
toml::color::enable(); // enable colorization
toml::color::disable(); // disable colorization
```
If you use user-defined error message, you can manage the setting as follows:
```cpp
toml::color::enable();
std::cerr << toml::format_error("[error] value should be positive",
data.at("num"), "positive number required",
hints) << std::endl; // colorized
toml::color::disable();
std::cerr << toml::format_error("[error] value should be positive",
data.at("num"), "positive number required",
hints) << std::endl; // NOT colorized
```
Or you may use toml11 in your application like:
```cpp
std::vector<std::string> args(argv + 1, argv + argc);
auto result = std::find(args.begin(), args.end(), "--color");
if (result != args.end()) {
toml::color::enable();
} else {
toml::color::disable();
}
// use toml11 ...
```
## Opting out of the default `[error]` prefix
toml11 prints error messages with the `[error]` prefix by default.
Defining `TOML11_NO_ERROR_PREFIX` will let toml11 omit the prefix regardless of colorized or not in case you would use a custom prefix, such as `Error:`.
```cpp
#define TOML11_NO_ERROR_PREFIX
```
## Serializing TOML data
toml11 enables you to serialize data into toml format.
@@ -1522,7 +1806,7 @@ const std::string fmt = toml::format(v);
```
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
third arguments to set them. By default, the width is 80 and the precision is
`std::numeric_limits<double>::max_digit10`.
```cpp
@@ -1558,7 +1842,7 @@ flag that represents a kind of a string, `string_t::basic` and `string_t::litera
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.
The most important difference between `std::string` and `toml::string` is that
`toml::string` will be formatted as a TOML string when outputed with `ostream`.
`toml::string` will be formatted as a TOML string when outputted with `ostream`.
This feature is introduced to make it easy to write a custom serializer.
`Datetime` variants are `struct` that are defined in this library.
@@ -1567,26 +1851,18 @@ not capable of representing a Local Time independent from a specific day.
## Unreleased TOML features
There are some unreleased features in toml-lang/toml:master.
Currently, the following features are available after defining
`TOML11_USE_UNRELEASED_TOML_FEATURES` macro flag.
After TOML v1.0.0 has been released, some features are added to the main branch
of the TOML spec repository. (see: [CHANGELOG.md in toml-lang/toml repository](https://github.com/toml-lang/toml/blob/main/CHANGELOG.md)).
To use those features, `#define` `TOML11_USE_UNRELEASED_TOML_FEATURES` before
including `toml.hpp` or pass `-DTOML11_USE_UNRELEASED_TOML_FEATURES` to your
compiler.
The following list shows available "unreleased" features that can be activated
by defining a macro named `TOML11_USE_UNRELEASED_FEATURES`.
- Leading zeroes in exponent parts of floats are permitted.
- e.g. `1.0e+01`, `5e+05`
- [toml-lang/toml/PR/656](https://github.com/toml-lang/toml/pull/656)
- Allow raw tab characters in basic strings and multi-line basic strings.
- [toml-lang/toml/PR/627](https://github.com/toml-lang/toml/pull/627)
- Allow heterogeneous arrays
- [toml-lang/toml/PR/676](https://github.com/toml-lang/toml/pull/676)
- Add new `\e` shorthand for the escape character.
### Note about heterogeneous arrays
## Note about heterogeneous arrays
Although `toml::parse` allows heterogeneous arrays, constructor of `toml::value`
does not.
does not. Here the reason is explained.
```cpp
// this won't be compiled
@@ -1595,8 +1871,10 @@ toml::value v{
}
```
There is a workaround for this issue. By explicitly converting values into
There is a workaround for this. By explicitly converting values into
`toml::value`, you can initialize `toml::value` with a heterogeneous array.
Also, you can first initialize a `toml::value` with an array and then
`push_back` into it.
```cpp
// OK!
@@ -1604,6 +1882,17 @@ toml::value v{
toml::value("foo"), toml::value(3.14), toml::value(42),
toml::value{1,2,3,4,5}, toml::value{{"key", "value"}}
}
// OK!
toml::value v(toml::array{});
v.push_back("foo");
v.push_back(3.14);
// OK!
toml::array a;
a.push_back("foo");
a.push_back(3.14);
toml::value v(std::move(a));
```
The reason why the first example is not allowed is the following.
@@ -1632,15 +1921,14 @@ This means that the above C++ code makes constructor's overload resolution
ambiguous. So a constructor that allows both "table as an initializer-list" and
"heterogeneous array as an initializer-list" cannot be implemented.
Thus, although it is painful, you need to explicitly cast values into
`toml::value` when you initialize heterogeneous array in C++ code.
Thus, although it is painful, we need to explicitly cast values into
`toml::value` when you initialize heterogeneous array in a C++ code.
```cpp
// You need to do this when you want to initialize hetero array.
toml::value v{
toml::value("foo"), toml::value(3.14), toml::value(42),
toml::value{1,2,3,4,5}, toml::value{{"key", "value"}}
}
};
```
## Breaking Changes from v2
@@ -1679,14 +1967,13 @@ Such a big change will not happen in the coming years.
## 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.
After cloning this repository, run the following command (thank you @jwillikers
for automating test set fetching!).
```sh
$ mkdir build
$ cd build
$ git clone https://github.com/toml-lang/toml.git
$ cmake ..
$ cmake .. -Dtoml11_BUILD_TEST=ON
$ make
$ make test
```
@@ -1703,12 +1990,13 @@ I appreciate the help of the contributors who introduced the great feature to th
- 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
- Improved error messages for invalid keys to show the location where the parser fails
- Petr Beneš (@wbenny)
- Fixed warnings on MSVC
- Ivan Shynkarenka (@chronoxor)
- Fixed Visual Studio 2019 warnings
- @khoitd1997
- Fix compilation error in `<filesystem>` with MinGW
- Khoi Dinh Trinh (@khoitd1997)
- Fixed warnings while type conversion
- @KerstinKeller
- Added installation script to CMake
@@ -1719,11 +2007,76 @@ I appreciate the help of the contributors who introduced the great feature to th
- Suppress warnings in Debug mode
- OGAWA Kenichi (@kenichiice)
- Suppress warnings on intel compiler
- Fix include path in README
- Jordan Williams (@jwillikers)
- Fixed clang range-loop-analysis warnings
- Fixed feature test macro to suppress -Wundef
- Use cache variables in CMakeLists.txt
- Automate test set fetching, update and refactor CMakeLists.txt
- Scott McCaskill
- Parse 9 digits (nanoseconds) of fractional seconds in a `local_time`
- Shu Wang (@halfelf)
- fix "Finding a value in an array" example in README
- @maass-tv and @SeverinLeonhardt
- Fix MSVC warning C4866
- Mohammed Alyousef (@MoAlyousef)
- Made testing optional in CMake
- Alex Merry (@amerry)
- Add missing include files
- sneakypete81 (@sneakypete81)
- Fix typo in error message
- Oliver Kahrmann (@founderio)
- Fix missing filename in error message if parsed file is empty
- Karl Nilsson (@karl-nilsson)
- Fix many spelling errors
- ohdarling88 (@ohdarling)
- Fix a bug in a constructor of serializer
- estshorter (@estshorter)
- Fix MSVC warning C26478
- Philip Top (@phlptp)
- Improve checking standard library feature availability check
- Louis Marascio (@marascio)
- Fix free-nonheap-object warning
- Axel Huebl (@ax3l)
- Make installation optional if the library is embedded
- Ken Matsui (@ken-matsui)
- Support user-defined error message prefix
- Support dynamic color mode
- Giel van Schijndel (@muggenhor)
- Remove needless copy in `parse` function
- Lukáš Hrázký (@lukash)
- Add a `parse(FILE *)` interface and improve file-related error messages
- spiderman idog (@spiderman-idog)
- Fix typo in README
- Jajauma's GitHub (@Jajauma)
- Avoid possible lexer truncation warnings
- Moritz Klammler (@ctcmkl)
- Many patches in (#200) including:
- Improve CMake scripts, build process, and test file handling
- Detect error when `discard_comments` is accessed
- And more.
- Chris White (@cxw42)
- Fix address-sanitizer error when parsing literal strings having invalid UTF-8 characters
- Fix function name in error messages
- offa (@offa)
- Update checkout action to v3
- Update Required CMake version
- Cleanup old CI settings
- Sergey Vidyuk (@VestniK)
- Fix for case when vector iterator is raw pointer
- Kfir Gollan (@kfirgollan)
- Add installation example with checkinstall and cmake
- Martin Tournoij (@arp242)
- Escape control characters in keys
- @DavidKorczynski
- Add fuzzing test based on ClusterFuzzLite
- Esonhugh Skyworship (@Esonhugh)
- Fix function signature of `strerror_r` on macos
## Licensing terms
This product is licensed under the terms of the [MIT License](LICENSE).
- Copyright (c) 2017-2019 Toru Niina
- Copyright (c) 2017-2024 Toru Niina
All rights reserved.

View File

@@ -17,10 +17,9 @@ build_script:
- cd C:\toml11
- mkdir build
- cd build
- git clone https://github.com/toml-lang/toml.git
- file --mime-encoding toml/tests/hard_example_unicode.toml
- cmake -G"%generator%" -DBOOST_ROOT=C:/Libraries/boost_1_69_0 ..
- cmake -G"%generator%" -DCMAKE_CXX_STANDARD=11 -DBOOST_ROOT=C:/Libraries/boost_1_69_0 -Dtoml11_BUILD_TEST=ON ..
- cmake --build . --config "%configuration%"
- file --mime-encoding tests/toml/tests/hard_example_unicode.toml
test_script:
- ctest --build-config "%configuration%" --timeout 300 --output-on-failure

View File

@@ -1,2 +1,2 @@
@PACKAGE_INIT@
include("@PACKAGE_toml11_install_cmake_dir@/toml11Targets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/toml11Targets.cmake")

View File

@@ -1,3 +1,21 @@
include(ExternalProject)
set(TOML11_LANGSPEC_GIT_REPOSITORY "https://github.com/toml-lang/toml" CACHE STRING
"URL of the TOML language specification repository")
set(TOML11_LANGSPEC_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/toml" CACHE FILEPATH
"directory for the TOML language specification tree")
if(NOT EXISTS "${TOML11_LANGSPEC_SOURCE_DIR}/toml.abnf")
ExternalProject_Add(toml
SOURCE_DIR "${TOML11_LANGSPEC_SOURCE_DIR}"
GIT_REPOSITORY "${TOML11_LANGSPEC_GIT_REPOSITORY}"
GIT_TAG "v0.5.0"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND "")
endif()
set(TEST_NAMES
test_datetime
test_string
@@ -27,6 +45,7 @@ set(TEST_NAMES
test_get_or
test_find
test_find_or
test_find_or_recursive
test_expect
test_parse_file
test_serialize_file
@@ -36,9 +55,105 @@ set(TEST_NAMES
test_extended_conversions
)
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL)
CHECK_CXX_COMPILER_FLAG("-Wextra" COMPILER_SUPPORTS_WEXTRA)
CHECK_CXX_COMPILER_FLAG("-Wpedantic" COMPILER_SUPPORTS_WPEDANTIC)
CHECK_CXX_COMPILER_FLAG("-Werror" COMPILER_SUPPORTS_WERROR)
CHECK_CXX_COMPILER_FLAG("-Wsign-conversion" COMPILER_SUPPORTS_WSIGN_CONVERSION)
CHECK_CXX_COMPILER_FLAG("-Wconversion" COMPILER_SUPPORTS_WCONVERSION)
CHECK_CXX_COMPILER_FLAG("-Wduplicated-cond" COMPILER_SUPPORTS_WDUPLICATED_COND)
CHECK_CXX_COMPILER_FLAG("-Wduplicated-branches" COMPILER_SUPPORTS_WDUPLICATED_BRANCHES)
CHECK_CXX_COMPILER_FLAG("-Wlogical-op" COMPILER_SUPPORTS_WLOGICAL_OP)
CHECK_CXX_COMPILER_FLAG("-Wuseless-cast" COMPILER_SUPPORTS_WUSELESS_CAST)
CHECK_CXX_COMPILER_FLAG("-Wdouble-promotion" COMPILER_SUPPORTS_WDOUBLE_PROMOTION)
CHECK_CXX_COMPILER_FLAG("-Wrange-loop-analysis" COMPILER_SUPPORTS_WRANGE_LOOP_ANALYSIS)
CHECK_CXX_COMPILER_FLAG("-Wundef" COMPILER_SUPPORTS_WUNDEF)
CHECK_CXX_COMPILER_FLAG("-Wshadow" COMPILER_SUPPORTS_WSHADOW)
include(CheckCXXSourceCompiles)
# check which standard library implementation is used. If libstdc++ is used,
# it will fail to compile. It compiles if libc++ is used.
check_cxx_source_compiles("
#include <cstddef>
#ifdef __GLIBCXX__
static_assert(false);
#endif
int main() {
return 0;
}" TOML11_WITH_LIBCXX_LIBRARY)
# LLVM 8 requires -lc++fs if compiled with libc++ to use <filesystem>.
# LLVM 9+ does not require any special library.
# GCC 8 requires -lstdc++fs. GCC 9+ does not require it.
#
# Yes, we can check the version of the compiler used in the current build
# directly in CMake. But, in most cases, clang build uses libstdc++ as the
# standard library implementation and it makes the condition complicated.
# In many environment, the default installed C++ compiler is GCC and libstdc++
# is installed along with it. In most build on such an environment, even if we
# chose clang as the C++ compiler, still libstdc++ is used. Checking default
# gcc version makes the condition complicated.
# The purpose of this file is to compile tests. We know the environment on which
# the tests run. We can set this option and, I think, it is easier and better.
option(TOML11_REQUIRE_FILESYSTEM_LIBRARY "need to link -lstdc++fs or -lc++fs" OFF)
find_package(Boost COMPONENTS unit_test_framework REQUIRED)
set(PREVIOUSLY_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES}")
set(PREVIOUSLY_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}")
set(PREVIOUSLY_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
list(APPEND CMAKE_REQUIRED_INCLUDES ${Boost_INCLUDE_DIRS})
list(APPEND CMAKE_REQUIRED_LIBRARIES ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
if(APPLE)
list(APPEND CMAKE_REQUIRED_FLAGS "-std=c++11")
endif()
check_cxx_source_compiles("
#define BOOST_TEST_MODULE \"dummy\"
#undef BOOST_TEST_DYN_LINK
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
BOOST_AUTO_TEST_CASE(proforma) { BOOST_TEST(true); }
" TOML11_WITH_BOOST_TEST_HEADER)
check_cxx_source_compiles("
#define BOOST_TEST_MODULE \"dummy\"
#undef BOOST_TEST_DYN_LINK
#undef BOOST_TEST_NO_LIB
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_CASE(proforma) { BOOST_TEST(true); }
" TOML11_WITH_BOOST_TEST_STATIC)
check_cxx_source_compiles("
#define BOOST_TEST_MODULE \"dummy\"
#define BOOST_TEST_DYN_LINK
#undef BOOST_TEST_NO_LIB
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_CASE(proforma) { BOOST_TEST(true); }
" TOML11_WITH_BOOST_TEST_DYNAMIC)
set(CMAKE_REQUIRED_INCLUDES "${PREVIOUSLY_REQUIRED_INCLUDES}")
set(CMAKE_REQUIRED_LIBRARIES "${PREVIOUSLY_REQUIRED_LIBRARIES}")
set(CMAKE_REQUIRED_FLAGS "${PREVIOUSLY_REQUIRED_FLAGS}")
unset(PREVIOUSLY_REQUIRED_INCLUDES)
unset(PREVIOUSLY_REQUIRED_LIBRARIES)
unset(PREVIOUSLY_REQUIRED_FLAGS)
if(TOML11_WITH_BOOST_TEST_DYNAMIC)
add_definitions(-DUNITTEST_FRAMEWORK_LIBRARY_EXIST -DBOOST_TEST_DYN_LINK)
elseif(TOML11_WITH_BOOST_TEST_STATIC)
add_definitions(-DUNITTEST_FRAMEWORK_LIBRARY_EXIST)
elseif(TOML11_WITH_BOOST_TEST_HEADER)
add_definitions(-DBOOST_TEST_NO_LIB)
else()
message(FATAL_ERROR "Neither the Boost.Test static or shared library nor the header-only version seem to be usable.")
endif()
if(COMPILER_SUPPORTS_WALL)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
@@ -49,9 +164,39 @@ endif()
if(COMPILER_SUPPORTS_WPEDANTIC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wpedantic")
endif()
option(TOML11_USE_UNRELEASED_TOML_FEATURES
"use features in toml-lang/toml master while testing" OFF)
if(COMPILER_SUPPORTS_WERROR)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
endif()
if(COMPILER_SUPPORTS_WSHADOW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshadow")
endif()
if(COMPILER_SUPPORTS_WSIGN_CONVERSION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-conversion")
endif()
if(COMPILER_SUPPORTS_WCONVERSION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wconversion")
endif()
if(COMPILER_SUPPORTS_WDUPLICATED_COND)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wduplicated-cond")
endif()
if(COMPILER_SUPPORTS_WDUPLICATED_BRANCHES)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wduplicated-branches")
endif()
if(COMPILER_SUPPORTS_WLOGICAL_OP)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wlogical-op")
endif()
if(COMPILER_SUPPORTS_WUSELESS_CAST)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wuseless-cast")
endif()
if(COMPILER_SUPPORTS_WDOUBLE_PROMOTION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdouble-promotion")
endif()
if(COMPILER_SUPPORTS_WRANGE_LOOP_ANALYSIS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wrange-loop-analysis")
endif()
if(COMPILER_SUPPORTS_WUNDEF)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wundef")
endif()
if(TOML11_USE_UNRELEASED_TOML_FEATURES)
message(STATUS "adding TOML11_USE_UNRELEASED_TOML_FEATURES flag")
@@ -94,29 +239,61 @@ if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4820")
# pragma warning(pop): likely mismatch, popping warning state pushed in different file
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd5031")
# pragma warning(pop): spectre warnings in tests
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd5045")
# pragma warning(pop): spectre warnings in tests
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4265")
endif()
find_package(Boost COMPONENTS unit_test_framework REQUIRED)
add_definitions(-DBOOST_TEST_DYN_LINK)
add_definitions(-DUNITTEST_FRAMEWORK_LIBRARY_EXIST)
set(TEST_ENVIRON "TOMLDIR=${TOML11_LANGSPEC_SOURCE_DIR}")
if(WIN32)
# Set the PATH to be able to find Boost DLL
STRING(REPLACE ";" "\\;" PATH_STRING "$ENV{PATH}")
list(APPEND TEST_ENVIRON "PATH=${PATH_STRING}\;${Boost_LIBRARY_DIRS}")
endif()
foreach(TEST_NAME ${TEST_NAMES})
add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
target_link_libraries(${TEST_NAME} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} toml11::toml11)
target_include_directories(${TEST_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
target_include_directories(${TEST_NAME} SYSTEM PRIVATE ${Boost_INCLUDE_DIRS})
target_compile_definitions(${TEST_NAME} PRIVATE "BOOST_TEST_MODULE=\"${TEST_NAME}\"")
# Set the PATH to be able to find Boost DLL
if(WIN32)
STRING(REPLACE ";" "\\;" PATH_STRING "$ENV{PATH}")
set_tests_properties(${TEST_NAME}
PROPERTIES ENVIRONMENT "PATH=${PATH_STRING}\;${Boost_LIBRARY_DIRS}"
)
# to compile tests with <filesystem>...
if(TOML11_REQUIRE_FILESYSTEM_LIBRARY)
if(TOML11_WITH_LIBCXX_LIBRARY)
target_link_libraries(${TEST_NAME} "c++fs")
else()
target_link_libraries(${TEST_NAME} "stdc++fs")
endif()
endif()
target_include_directories(${TEST_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if(toml11_TEST_WITH_ASAN)
set_target_properties(${TEST_NAME} PROPERTIES
COMPILE_FLAGS "-fsanitize=address -fno-omit-frame-pointer"
LINK_FLAGS "-fsanitize=address -fno-omit-frame-pointer")
elseif(toml11_TEST_WITH_UBSAN)
set_target_properties(${TEST_NAME} PROPERTIES
COMPILE_FLAGS "-fsanitize=undefined"
LINK_FLAGS "-fsanitize=undefined")
endif()
endif()
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT "${TEST_ENVIRON}")
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)
target_link_libraries(test_multiple_translation_unit toml11::toml11)
if(WIN32)
add_executable(test_windows test_windows.cpp)
target_link_libraries(test_windows toml11::toml11)
endif()

View File

@@ -1,4 +1,5 @@
#include "toml.hpp"
#include <toml.hpp>
#include <iostream>
#include <iomanip>

View File

@@ -1,6 +1,7 @@
#include "toml.hpp"
#include <iostream>
#include <toml.hpp>
#include <iomanip>
#include <iostream>
int main(int argc, char **argv)
{
@@ -22,15 +23,40 @@ int main(int argc, char **argv)
if(data != serialized)
{
std::cerr << "============================================================\n";
std::cerr << "result (w/o comment) different: " << filename << std::endl;
std::cerr << "------------------------------------------------------------\n";
std::cerr << "# serialized\n";
std::cerr << serialized;
std::cerr << "------------------------------------------------------------\n";
std::cerr << "# data\n";
std::cerr << data;
return 1;
// this is really a ditry hack, but is the easiest way...
// TODO: cleanup by adding comparison function to check if a value is NaN or not
if(filename.substr(filename.size() - 22, 22) == "float-inf-and-nan.toml" &&
std::isnan (toml::find<double>(serialized, "nan")) &&
!std::signbit (toml::find<double>(serialized, "nan")) &&
std::isnan (toml::find<double>(serialized, "nan_plus")) &&
!std::signbit (toml::find<double>(serialized, "nan_plus")) &&
std::isnan (toml::find<double>(serialized, "nan_neg")) &&
std::signbit (toml::find<double>(serialized, "nan_neg")) &&
!std::isnan (toml::find<double>(serialized, "infinity")) &&
!std::isfinite(toml::find<double>(serialized, "infinity")) &&
!std::signbit (toml::find<double>(serialized, "infinity")) &&
!std::isnan (toml::find<double>(serialized, "infinity_plus")) &&
!std::isfinite(toml::find<double>(serialized, "infinity_plus")) &&
!std::signbit (toml::find<double>(serialized, "infinity_plus")) &&
!std::isnan (toml::find<double>(serialized, "infinity_neg")) &&
!std::isfinite(toml::find<double>(serialized, "infinity_neg")) &&
std::signbit (toml::find<double>(serialized, "infinity_neg")))
{
// then it is correctly serialized.
// Note that, the result of (nan == nan) is false. so `data == serialized` is false.
}
else
{
std::cerr << "============================================================\n";
std::cerr << "result (w/o comment) different: " << filename << std::endl;
std::cerr << "------------------------------------------------------------\n";
std::cerr << "# serialized\n";
std::cerr << serialized;
std::cerr << "------------------------------------------------------------\n";
std::cerr << "# data\n";
std::cerr << data;
return 1;
}
}
}
{
@@ -42,15 +68,46 @@ int main(int argc, char **argv)
const auto serialized = toml::parse<toml::preserve_comments>("tmp.toml");
if(data != serialized)
{
std::cerr << "============================================================\n";
std::cerr << "result (w/ comment) different: " << filename << std::endl;
std::cerr << "------------------------------------------------------------\n";
std::cerr << "# serialized\n";
std::cerr << serialized;
std::cerr << "------------------------------------------------------------\n";
std::cerr << "# data\n";
std::cerr << data;
return 1;
// this is really a ditry hack, but is the easiest way...
// TODO: cleanup by adding comparison function to check if a value is NaN or not
if(filename.substr(filename.size() - 22, 22) == "float-inf-and-nan.toml" &&
std::isnan (toml::find<double>(serialized, "nan")) &&
!std::signbit (toml::find<double>(serialized, "nan")) &&
std::isnan (toml::find<double>(serialized, "nan_plus")) &&
!std::signbit (toml::find<double>(serialized, "nan_plus")) &&
std::isnan (toml::find<double>(serialized, "nan_neg")) &&
std::signbit (toml::find<double>(serialized, "nan_neg")) &&
!std::isnan (toml::find<double>(serialized, "infinity")) &&
!std::isfinite(toml::find<double>(serialized, "infinity")) &&
!std::signbit (toml::find<double>(serialized, "infinity")) &&
!std::isnan (toml::find<double>(serialized, "infinity_plus")) &&
!std::isfinite(toml::find<double>(serialized, "infinity_plus")) &&
!std::signbit (toml::find<double>(serialized, "infinity_plus")) &&
!std::isnan (toml::find<double>(serialized, "infinity_neg")) &&
!std::isfinite(toml::find<double>(serialized, "infinity_neg")) &&
std::signbit (toml::find<double>(serialized, "infinity_neg")) &&
toml::find(data, "nan").comments() == toml::find(serialized, "nan").comments() &&
toml::find(data, "nan_plus").comments() == toml::find(serialized, "nan_plus").comments() &&
toml::find(data, "nan_neg").comments() == toml::find(serialized, "nan_neg").comments() &&
toml::find(data, "infinity").comments() == toml::find(serialized, "infinity").comments() &&
toml::find(data, "infinity_plus").comments() == toml::find(serialized, "infinity_plus").comments() &&
toml::find(data, "infinity_neg").comments() == toml::find(serialized, "infinity_neg").comments() )
{
// then it is correctly serialized.
// Note that, the result of (nan == nan) is false. so `data == serialized` is false.
}
else
{
std::cerr << "============================================================\n";
std::cerr << "result (w/ comment) different: " << filename << std::endl;
std::cerr << "------------------------------------------------------------\n";
std::cerr << "# serialized\n";
std::cerr << serialized;
std::cerr << "------------------------------------------------------------\n";
std::cerr << "# data\n";
std::cerr << data;
return 1;
}
}
}
return 0;

View File

@@ -1,6 +1,7 @@
#include "toml.hpp"
#include <iostream>
#include <toml.hpp>
#include <iomanip>
#include <iostream>
struct json_serializer
{
@@ -16,6 +17,14 @@ struct json_serializer
}
void operator()(toml::floating v)
{
if(std::isnan(v) && std::signbit(v))
{
// toml-test does not allow negative NaN represented in "-nan" because
// there are languages that does not distinguish nan and -nan.
// But toml11 keeps sign from input. To resolve this difference,
// we convert -nan to nan here.
v = std::numeric_limits<toml::floating>::quiet_NaN();
}
std::cout << "{\"type\":\"float\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
@@ -24,23 +33,24 @@ struct json_serializer
// 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) << "\"}";
toml::serializer<toml::value> ser(std::numeric_limits<std::size_t>::max());
std::cout << "{\"type\":\"string\",\"value\":"
<< ser(v.str) << "}";
return ;
}
void operator()(const toml::local_time& v)
{
std::cout << "{\"type\":\"local_time\",\"value\":\"" << toml::value(v) << "\"}";
std::cout << "{\"type\":\"time-local\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(const toml::local_date& v)
{
std::cout << "{\"type\":\"local_date\",\"value\":\"" << toml::value(v) << "\"}";
std::cout << "{\"type\":\"date-local\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(const toml::local_datetime& v)
{
std::cout << "{\"type\":\"local_datetime\",\"value\":\"" << toml::value(v) << "\"}";
std::cout << "{\"type\":\"datetime-local\",\"value\":\"" << toml::value(v) << "\"}";
return ;
}
void operator()(const toml::offset_datetime& v)
@@ -64,7 +74,8 @@ struct json_serializer
}
else
{
std::cout << "{\"type\":\"array\",\"value\":[";
// std::cout << "{\"type\":\"array\",\"value\":[";
std::cout << "[";
bool is_first = true;
for(const auto& elem : v)
{
@@ -72,7 +83,7 @@ struct json_serializer
is_first = false;
toml::visit(*this, elem);
}
std::cout << "]}";
std::cout << "]";
}
return ;
}
@@ -84,34 +95,20 @@ struct json_serializer
{
if(!is_first) {std::cout << ", ";}
is_first = false;
std::cout << toml::format(toml::value(elem.first),
std::numeric_limits<std::size_t>::max());
std::cout << ':';
const auto k = toml::format_key(elem.first);
if(k.at(0) == '"')
{
std::cout << k << ":";
}
else // bare key
{
std::cout << '\"' << k << "\":";
}
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()

View File

@@ -1,17 +1,11 @@
#define BOOST_TEST_MODULE "test_comments"
#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 "unit_test.hpp"
BOOST_AUTO_TEST_CASE(test_comment_before)
{
{
const std::string file = u8R"(
const std::string file = R"(
# comment for a.
a = 42
# comment for b.
@@ -20,16 +14,16 @@ BOOST_AUTO_TEST_CASE(test_comment_before)
std::istringstream iss(file);
const auto v = toml::parse<toml::preserve_comments>(iss);
const auto& a = toml::find(v, "a");
const auto& b = toml::find(v, "b");
const auto a = toml::find(v, "a");
const auto b = toml::find(v, "b");
BOOST_TEST(a.comments().size() == 1u);
BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(b.comments().size() == 1u);
BOOST_TEST(b.comments().front() == u8" comment for b.");
BOOST_TEST(b.comments().front() == " comment for b.");
}
{
const std::string file = u8R"(
const std::string file = R"(
# comment for a.
# another comment for a.
a = 42
@@ -41,22 +35,22 @@ BOOST_AUTO_TEST_CASE(test_comment_before)
std::istringstream iss(file);
const auto v = toml::parse<toml::preserve_comments>(iss);
const auto& a = toml::find(v, "a");
const auto& b = toml::find(v, "b");
const auto a = toml::find(v, "a");
const auto b = toml::find(v, "b");
BOOST_TEST(a.comments().size() == 2u);
BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(a.comments().back() == u8" another comment for a.");
BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(a.comments().back() == " another comment for a.");
BOOST_TEST(b.comments().size() == 2u);
BOOST_TEST(b.comments().front() == u8" comment for b.");
BOOST_TEST(b.comments().back() == u8" also comment for b.");
BOOST_TEST(b.comments().front() == " comment for b.");
BOOST_TEST(b.comments().back() == " also comment for b.");
}
}
BOOST_AUTO_TEST_CASE(test_comment_inline)
{
{
const std::string file = u8R"(
const std::string file = R"(
a = 42 # comment for a.
b = "baz" # comment for b.
)";
@@ -64,16 +58,16 @@ BOOST_AUTO_TEST_CASE(test_comment_inline)
std::istringstream iss(file);
const auto v = toml::parse<toml::preserve_comments>(iss);
const auto& a = toml::find(v, "a");
const auto& b = toml::find(v, "b");
const auto a = toml::find(v, "a");
const auto b = toml::find(v, "b");
BOOST_TEST(a.comments().size() == 1u);
BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(b.comments().size() == 1u);
BOOST_TEST(b.comments().front() == u8" comment for b.");
BOOST_TEST(b.comments().front() == " comment for b.");
}
{
const std::string file = u8R"(
const std::string file = R"(
a = [
42,
] # comment for a.
@@ -85,23 +79,23 @@ BOOST_AUTO_TEST_CASE(test_comment_inline)
std::istringstream iss(file);
const auto v = toml::parse<toml::preserve_comments>(iss);
const auto& a = toml::find(v, "a");
const auto& b = toml::find(v, "b");
const auto& b0 = b.as_array().at(0);
const auto a = toml::find(v, "a");
const auto b = toml::find(v, "b");
const auto b0 = b.as_array().at(0);
BOOST_TEST(a.comments().size() == 1u);
BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(b.comments().size() == 1u);
BOOST_TEST(b.comments().front() == u8" this is a comment for b.");
BOOST_TEST(b.comments().front() == " this is a comment for b.");
BOOST_TEST(b0.comments().size() == 1u);
BOOST_TEST(b0.comments().front() == u8" this is not a comment for b, but \"bar\"");
BOOST_TEST(b0.comments().front() == " this is not a comment for b, but \"bar\"");
}
}
BOOST_AUTO_TEST_CASE(test_comment_both)
{
{
const std::string file = u8R"(
const std::string file = R"(
# comment for a.
a = 42 # inline comment for a.
# comment for b.
@@ -116,31 +110,68 @@ BOOST_AUTO_TEST_CASE(test_comment_both)
std::istringstream iss(file);
const auto v = toml::parse<toml::preserve_comments>(iss);
const auto& a = toml::find(v, "a");
const auto& b = toml::find(v, "b");
const auto& c = toml::find(v, "c");
const auto& c0 = c.as_array().at(0);
const auto a = toml::find(v, "a");
const auto b = toml::find(v, "b");
const auto c = toml::find(v, "c");
const auto c0 = c.as_array().at(0);
BOOST_TEST(a.comments().size() == 2u);
BOOST_TEST(a.comments().front() == u8" comment for a.");
BOOST_TEST(a.comments().back() == u8" inline comment for a.");
BOOST_TEST(a.comments().front() == " comment for a.");
BOOST_TEST(a.comments().back() == " inline comment for a.");
BOOST_TEST(b.comments().size() == 2u);
BOOST_TEST(b.comments().front() == u8" comment for b.");
BOOST_TEST(b.comments().back() == u8" inline comment for b.");
BOOST_TEST(b.comments().front() == " comment for b.");
BOOST_TEST(b.comments().back() == " inline comment for b.");
BOOST_TEST(c.comments().size() == 2u);
BOOST_TEST(c.comments().front() == u8" comment for c.");
BOOST_TEST(c.comments().back() == u8" another comment for c.");
BOOST_TEST(c.comments().front() == " comment for c.");
BOOST_TEST(c.comments().back() == " another comment for c.");
BOOST_TEST(c0.comments().size() == 2u);
BOOST_TEST(c0.comments().front() == u8" comment for the first element.");
BOOST_TEST(c0.comments().back() == u8" this also.");
BOOST_TEST(c0.comments().front() == " comment for the first element.");
BOOST_TEST(c0.comments().back() == " this also.");
}
}
BOOST_AUTO_TEST_CASE(test_comments_on_implicit_values)
{
{
const std::string file = R"(
# comment for the first element of array-of-tables.
[[array-of-tables]]
foo = "bar"
)";
std::istringstream iss(file);
const auto v = toml::parse<toml::preserve_comments>(iss);
const auto aot = toml::find(v, "array-of-tables");
const auto elm = aot.at(0);
BOOST_TEST(aot.comments().empty());
BOOST_TEST(elm.comments().size() == 1);
BOOST_TEST(elm.comments().front() == " comment for the first element of array-of-tables.");
}
{
const std::string file = R"(
# comment for the array itself
array-of-tables = [
# comment for the first element of array-of-tables.
{foo = "bar"}
]
)";
std::istringstream iss(file);
const auto v = toml::parse<toml::preserve_comments>(iss);
const auto aot = toml::find(v, "array-of-tables");
const auto elm = aot.at(0);
BOOST_TEST(aot.comments().size() == 1);
BOOST_TEST(aot.comments().front() == " comment for the array itself");
BOOST_TEST(elm.comments().size() == 1);
BOOST_TEST(elm.comments().front() == " comment for the first element of array-of-tables.");
}
}
BOOST_AUTO_TEST_CASE(test_discard_comment)
{
const std::string file = u8R"(
const std::string file = R"(
# comment for a.
a = 42 # inline comment for a.
# comment for b.
@@ -153,12 +184,12 @@ BOOST_AUTO_TEST_CASE(test_discard_comment)
)";
std::istringstream iss(file);
const auto v = toml::parse(iss);
const auto v = toml::parse<toml::discard_comments>(iss);
const auto& a = toml::find(v, "a");
const auto& b = toml::find(v, "b");
const auto& c = toml::find(v, "c");
const auto& c0 = c.as_array().at(0);
const auto a = toml::find(v, "a");
const auto b = toml::find(v, "b");
const auto c = toml::find(v, "c");
const auto c0 = c.as_array().at(0);
BOOST_TEST(a.comments().empty());
BOOST_TEST(b.comments().empty());
@@ -235,7 +266,7 @@ BOOST_AUTO_TEST_CASE(test_construct_value_with_comments)
BOOST_TEST(v.is_string());
BOOST_TEST(v.as_string() == "str");
}
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
{
using namespace std::literals::string_view_literals;
const value_type v("str"sv, {"comment1", "comment2"});

View File

@@ -1,12 +1,7 @@
#define BOOST_TEST_MODULE "test_datetime"
#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/datetime.hpp>
#include "unit_test.hpp"
BOOST_AUTO_TEST_CASE(test_local_date)
{
const toml::local_date date(2018, toml::month_t::Jan, 1);

View File

@@ -1,13 +1,9 @@
#define BOOST_TEST_MODULE "test_error_detection"
#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 "unit_test.hpp"
#include <fstream>
#include <iostream>
BOOST_AUTO_TEST_CASE(test_detect_empty_key)
{
@@ -80,13 +76,13 @@ BOOST_AUTO_TEST_CASE(test_detect_conflicting_value)
BOOST_AUTO_TEST_CASE(test_detect_inhomogeneous_array)
{
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
BOOST_TEST_MESSAGE("heterogeneous array will be allowed in the next release");
#else
#ifdef TOML11_DISALLOW_HETEROGENEOUS_ARRAYS
std::istringstream stream(std::string(
"a = [1, 1.0]\n"
));
BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error);
#else
BOOST_TEST_MESSAGE("After v1.0.0-rc.1, heterogeneous arrays are allowed");
#endif
}

View File

@@ -1,16 +1,12 @@
#define BOOST_TEST_MODULE "test_expect"
#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 "unit_test.hpp"
#include <array>
#include <deque>
#include <list>
#include <map>
#include <unordered_map>
#include <list>
#include <deque>
#include <array>
BOOST_AUTO_TEST_CASE(test_expect)
{

View File

@@ -1,11 +1,7 @@
#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>
#include "unit_test.hpp"
#include <deque>
#include <map>
@@ -44,6 +40,16 @@ struct qux
int a;
std::string b;
};
struct foobar
{
// via constructor
explicit foobar(const toml::value& v)
: a(toml::find<int>(v, "a")), b(toml::find<std::string>(v, "b"))
{}
int a;
std::string b;
};
} // extlib
namespace toml
@@ -60,9 +66,9 @@ struct from<extlib::foo>
template<>
struct into<extlib::foo>
{
static toml::table into_toml(const extlib::foo& f)
static toml::value into_toml(const extlib::foo& f)
{
return toml::table{{"a", f.a}, {"b", f.b}};
return toml::value{{"a", f.a}, {"b", f.b}};
}
};
@@ -122,6 +128,17 @@ struct qux
int a;
std::string b;
};
struct foobar
{
template<typename C, template<typename ...> class M, template<typename ...> class A>
explicit foobar(const toml::basic_value<C, M, A>& v)
: a(toml::find<int>(v, "a")), b(toml::find<std::string>(v, "b"))
{}
int a;
std::string b;
};
} // extlib2
namespace toml
@@ -284,6 +301,27 @@ BOOST_AUTO_TEST_CASE(test_conversion_one_way)
}
}
BOOST_AUTO_TEST_CASE(test_conversion_via_constructor)
{
{
const toml::value v{{"a", 42}, {"b", "foobar"}};
const auto foobar = toml::get<extlib::foobar>(v);
BOOST_TEST(foobar.a == 42);
BOOST_TEST(foobar.b == "foobar");
}
{
const toml::basic_value<toml::discard_comments, std::map> v{
{"a", 42}, {"b", "foobar"}
};
const auto foobar = toml::get<extlib2::foobar>(v);
BOOST_TEST(foobar.a == 42);
BOOST_TEST(foobar.b == "foobar");
}
}
BOOST_AUTO_TEST_CASE(test_recursive_conversion)
{
{
@@ -384,5 +422,206 @@ BOOST_AUTO_TEST_CASE(test_recursive_conversion)
BOOST_TEST(bars.at(2).b == "quux");
BOOST_TEST(bars.at(3).b == "foobar");
}
// via constructor
{
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 foobars = toml::get<std::vector<extlib::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at(0).a == 42);
BOOST_TEST(foobars.at(1).a == 43);
BOOST_TEST(foobars.at(2).a == 44);
BOOST_TEST(foobars.at(3).a == 45);
BOOST_TEST(foobars.at(0).b == "baz");
BOOST_TEST(foobars.at(1).b == "qux");
BOOST_TEST(foobars.at(2).b == "quux");
BOOST_TEST(foobars.at(3).b == "foobar");
}
{
const auto foobars = toml::get<std::vector<extlib2::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at(0).a == 42);
BOOST_TEST(foobars.at(1).a == 43);
BOOST_TEST(foobars.at(2).a == 44);
BOOST_TEST(foobars.at(3).a == 45);
BOOST_TEST(foobars.at(0).b == "baz");
BOOST_TEST(foobars.at(1).b == "qux");
BOOST_TEST(foobars.at(2).b == "quux");
BOOST_TEST(foobars.at(3).b == "foobar");
}
}
{
const toml::basic_value<toml::discard_comments, std::map, std::deque>
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 foobars = toml::get<std::vector<extlib2::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at(0).a == 42);
BOOST_TEST(foobars.at(1).a == 43);
BOOST_TEST(foobars.at(2).a == 44);
BOOST_TEST(foobars.at(3).a == 45);
BOOST_TEST(foobars.at(0).b == "baz");
BOOST_TEST(foobars.at(1).b == "qux");
BOOST_TEST(foobars.at(2).b == "quux");
BOOST_TEST(foobars.at(3).b == "foobar");
}
// via constructor
{
const toml::value v{
{"0", toml::table{{"a", 42}, {"b", "baz"}}},
{"1", toml::table{{"a", 43}, {"b", "qux"}}},
{"2", toml::table{{"a", 44}, {"b", "quux"}}},
{"3", toml::table{{"a", 45}, {"b", "foobar"}}}
};
{
const auto foobars = toml::get<std::map<std::string, extlib::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at("0").a == 42);
BOOST_TEST(foobars.at("1").a == 43);
BOOST_TEST(foobars.at("2").a == 44);
BOOST_TEST(foobars.at("3").a == 45);
BOOST_TEST(foobars.at("0").b == "baz");
BOOST_TEST(foobars.at("1").b == "qux");
BOOST_TEST(foobars.at("2").b == "quux");
BOOST_TEST(foobars.at("3").b == "foobar");
}
{
const auto foobars = toml::get<std::map<std::string, extlib2::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at("0").a == 42);
BOOST_TEST(foobars.at("1").a == 43);
BOOST_TEST(foobars.at("2").a == 44);
BOOST_TEST(foobars.at("3").a == 45);
BOOST_TEST(foobars.at("0").b == "baz");
BOOST_TEST(foobars.at("1").b == "qux");
BOOST_TEST(foobars.at("2").b == "quux");
BOOST_TEST(foobars.at("3").b == "foobar");
}
}
{
const toml::basic_value<toml::discard_comments, std::map, std::deque>
v{
{"0", toml::table{{"a", 42}, {"b", "baz"}}},
{"1", toml::table{{"a", 43}, {"b", "qux"}}},
{"2", toml::table{{"a", 44}, {"b", "quux"}}},
{"3", toml::table{{"a", 45}, {"b", "foobar"}}}
};
const auto foobars = toml::get<std::map<std::string, extlib::foobar>>(v);
BOOST_TEST(foobars.size() == 4ul);
BOOST_TEST(foobars.at("0").a == 42);
BOOST_TEST(foobars.at("1").a == 43);
BOOST_TEST(foobars.at("2").a == 44);
BOOST_TEST(foobars.at("3").a == 45);
BOOST_TEST(foobars.at("0").b == "baz");
BOOST_TEST(foobars.at("1").b == "qux");
BOOST_TEST(foobars.at("2").b == "quux");
BOOST_TEST(foobars.at("3").b == "foobar");
}
}
// ===========================================================================
#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
namespace extlib3
{
struct foo
{
int a;
std::string b;
};
struct bar
{
int a;
std::string b;
foo f;
};
} // extlib3
TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(extlib3::foo, a, b)
TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(extlib3::bar, a, b, f)
BOOST_AUTO_TEST_CASE(test_conversion_via_macro)
{
{
const toml::value v{{"a", 42}, {"b", "baz"}};
const auto foo = toml::get<extlib3::foo>(v);
BOOST_TEST(foo.a == 42);
BOOST_TEST(foo.b == "baz");
const toml::value v2(foo);
BOOST_TEST(v2 == v);
}
{
const toml::basic_value<toml::discard_comments, std::map, std::deque> v{
{"a", 42}, {"b", "baz"}
};
const auto foo = toml::get<extlib3::foo>(v);
BOOST_TEST(foo.a == 42);
BOOST_TEST(foo.b == "baz");
const toml::basic_value<toml::discard_comments, std::map, std::deque> v2(foo);
BOOST_TEST(v2 == v);
}
// -----------------------------------------------------------------------
{
const toml::value v{
{"a", 42},
{"b", "bar.b"},
{"f", toml::table{{"a", 42}, {"b", "foo.b"}}}
};
const auto bar = toml::get<extlib3::bar>(v);
BOOST_TEST(bar.a == 42);
BOOST_TEST(bar.b == "bar.b");
BOOST_TEST(bar.f.a == 42);
BOOST_TEST(bar.f.b == "foo.b");
const toml::value v2(bar);
BOOST_TEST(v2 == v);
}
{
const toml::basic_value<toml::discard_comments, std::map, std::deque> v{
{"a", 42},
{"b", "bar.b"},
{"f", toml::table{{"a", 42}, {"b", "foo.b"}}}
};
const auto bar = toml::get<extlib3::bar>(v);
BOOST_TEST(bar.a == 42);
BOOST_TEST(bar.b == "bar.b");
BOOST_TEST(bar.f.a == 42);
BOOST_TEST(bar.f.b == "foo.b");
const toml::basic_value<toml::discard_comments, std::map, std::deque> v2(bar);
BOOST_TEST(v2 == v);
}
}
#endif // TOML11_WITHOUT_DEFINE_NON_INTRUSIVE

View File

@@ -1,25 +1,20 @@
#define BOOST_TEST_MODULE "test_find"
#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 <unordered_map>
#include <list>
#include <deque>
#include "unit_test.hpp"
#include <array>
#if __cplusplus >= 201703L
#include <deque>
#include <list>
#include <map>
#include <tuple>
#include <unordered_map>
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#include <string_view>
#endif
#include <tuple>
using test_value_types = std::tuple<
toml::value,
toml::basic_value<toml::discard_comments>,
toml::basic_value<toml::preserve_comments>,
toml::basic_value<toml::discard_comments, std::map, std::deque>,
toml::basic_value<toml::preserve_comments, std::map, std::deque>
@@ -101,6 +96,78 @@ BOOST_AUTO_TEST_CASE(test_find_throws)
}
}
BOOST_AUTO_TEST_CASE(test_find_array_throws)
{
// -----------------------------------------------------------------------
// const-reference version
{
// value is not an array
const toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, 0), toml::type_error);
}
{
// the value corresponding to the key is not the expected type
const toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, 0), toml::type_error);
}
{
// the value corresponding to the key is not found
const toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::integer>(v, 6), std::out_of_range);
}
{
// the positive control.
const toml::value v{1, 2, 3, 4, 5};
BOOST_TEST(3 == toml::find<int>(v, 2));
}
// -----------------------------------------------------------------------
// non-const reference version
{
// value is not an array
toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, 0), toml::type_error);
}
{
// the value corresponding to the key is not the expected type
toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::boolean>(v, 0), toml::type_error);
}
{
// the value corresponding to the key is not found
toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::integer>(v, 6), std::out_of_range);
}
{
// the positive control.
toml::value v{1, 2, 3, 4, 5};
BOOST_TEST(3 == toml::find<int>(v, 2));
}
// -----------------------------------------------------------------------
// move version
{
// value is not an array
toml::value v(true);
BOOST_CHECK_THROW(toml::find<toml::boolean>(std::move(v), 0), toml::type_error);
}
{
// the value corresponding to the key is not the expected type
toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::boolean>(std::move(v), 0), toml::type_error);
}
{
// the value corresponding to the key is not found
toml::value v{1, 2, 3, 4, 5};
BOOST_CHECK_THROW(toml::find<toml::integer>(std::move(v), 6), std::out_of_range);
}
{
// the positive control.
toml::value v{1, 2, 3, 4, 5};
BOOST_TEST(3 == toml::find<int>(std::move(v), 2));
}
}
BOOST_AUTO_TEST_CASE(test_find_recursive)
{
// recursively search tables
@@ -126,8 +193,76 @@ BOOST_AUTO_TEST_CASE(test_find_recursive)
num2 = 42;
BOOST_TEST(42 == toml::find<int>(v, a, b, c, d));
auto num3 = toml::find<toml::integer>(std::move(v), a, b, c, d);
auto num3 = toml::find<toml::integer>(v, a, "b", c, "d");
BOOST_TEST(42 == num3);
auto num4 = toml::find<toml::integer>(std::move(v), a, b, c, d);
BOOST_TEST(42 == num4);
}
// recursively search arrays
{
toml::value v{
toml::array{"array", "of", "string"},
toml::array{toml::array{1, 2, 3}, toml::array{3.14, 2.71}}
};
BOOST_TEST("array" == toml::find<std::string>(v, 0, 0));
BOOST_TEST("of" == toml::find<std::string>(v, 0, 1));
BOOST_TEST("string" == toml::find<std::string>(v, 0, 2));
BOOST_TEST(1 == toml::find<int>(v, 1, 0, 0));
BOOST_TEST(2 == toml::find<int>(v, 1, 0, 1));
BOOST_TEST(3 == toml::find<int>(v, 1, 0, 2));
BOOST_TEST(3.14 == toml::find<double>(v, 1, 1, 0));
BOOST_TEST(2.71 == toml::find<double>(v, 1, 1, 1));
// reference that can be used to modify the content
auto& num = toml::find<toml::integer>(v, 1, 0, 2);
num = 42;
BOOST_TEST( 1 == toml::find<int>(v, 1, 0, 0));
BOOST_TEST( 2 == toml::find<int>(v, 1, 0, 1));
BOOST_TEST(42 == toml::find<int>(v, 1, 0, 2));
// move value
auto num2 = toml::find<toml::integer>(std::move(v), 1, 0, 2);
BOOST_TEST(42 == num2);
}
// recursively search mixtures
{
toml::value v = toml::table{{"array", toml::array{
toml::array{1, 2, 3},
toml::array{
toml::table{{"foo", "bar"}, {"baz", "qux"}},
toml::table{{"pi", 3.14}, {"e", 2.71}}
}}
}};
BOOST_TEST(1 == toml::find<int>(v, "array", 0, 0));
BOOST_TEST(2 == toml::find<int>(v, "array", 0, 1));
BOOST_TEST(3 == toml::find<int>(v, "array", 0, 2));
BOOST_TEST("bar" == toml::find<std::string>(v, "array", 1, 0, "foo"));
BOOST_TEST("qux" == toml::find<std::string>(v, "array", 1, 0, "baz"));
BOOST_TEST(3.14 == toml::find<double>(v, "array", 1, 1, "pi"));
BOOST_TEST(2.71 == toml::find<double>(v, "array", 1, 1, "e"));
const std::string ar("array");
const auto ar_c = "array";
const std::string pi("pi");
const auto pi_c = "pi";
BOOST_TEST(3.14 == toml::find<double>(v, ar, 1, 1, "pi"));
BOOST_TEST(3.14 == toml::find<double>(v, ar, 1, 1, pi));
BOOST_TEST(3.14 == toml::find<double>(v, ar, 1, 1, pi_c));
BOOST_TEST(3.14 == toml::find<double>(v, ar_c, 1, 1, "pi"));
BOOST_TEST(3.14 == toml::find<double>(v, ar_c, 1, 1, pi));
BOOST_TEST(3.14 == toml::find<double>(v, ar_c, 1, 1, pi_c));
BOOST_TEST(3.14 == toml::find<double>(v, "array", 1, 1, pi));
BOOST_TEST(3.14 == toml::find<double>(v, "array", 1, 1, pi_c));
}
}
@@ -309,10 +444,11 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_floating_type, value_type, test_value_ty
{
{
value_type v{{"key", 3.14}};
BOOST_TEST(static_cast<float >(3.14) == toml::find<float >(v, "key"));
BOOST_TEST(static_cast<double >(3.14) == toml::find<double >(v, "key"));
BOOST_TEST(static_cast<long double>(3.14) == toml::find<long double>(v, "key"));
BOOST_TEST(static_cast<float >(3.14) == toml::find<float >(std::move(v), "key"));
const double ref(3.14);
BOOST_TEST(static_cast<float >(ref) == toml::find<float >(v, "key"));
BOOST_TEST( ref == toml::find<double >(v, "key"));
BOOST_TEST(static_cast<long double>(ref) == toml::find<long double>(v, "key"));
BOOST_TEST(static_cast<float >(ref) == toml::find<float >(std::move(v), "key"));
}
}
@@ -336,7 +472,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_string_type, value_type, test_value_type
BOOST_TEST("foo" == moved);
}
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
{
value_type v{{"key", toml::string("foo", toml::string_t::basic)}};
BOOST_TEST("foo" == toml::find<std::string_view>(v, "key"));
@@ -373,14 +509,14 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_toml_array, value_type, test_value_types
BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3));
std::array<int, 4> ary = toml::find<std::array<int, 4>>(v, "key");
BOOST_TEST(static_cast<int>(42) == ary.at(0));
BOOST_TEST(static_cast<int>(54) == ary.at(1));
BOOST_TEST(static_cast<int>(69) == ary.at(2));
BOOST_TEST(static_cast<int>(72) == ary.at(3));
BOOST_TEST(42 == ary.at(0));
BOOST_TEST(54 == ary.at(1));
BOOST_TEST(69 == ary.at(2));
BOOST_TEST(72 == ary.at(3));
std::tuple<int, short, unsigned, long> tpl =
toml::find<std::tuple<int, short, unsigned, long>>(v, "key");
BOOST_TEST(static_cast<int >(42) == std::get<0>(tpl));
BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
@@ -420,14 +556,14 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_move_toml_array, value_type, test_value_
BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3));
std::array<int, 4> ary = toml::find<std::array<int, 4>>(std::move(v4), "key");
BOOST_TEST(static_cast<int>(42) == ary.at(0));
BOOST_TEST(static_cast<int>(54) == ary.at(1));
BOOST_TEST(static_cast<int>(69) == ary.at(2));
BOOST_TEST(static_cast<int>(72) == ary.at(3));
BOOST_TEST(42 == ary.at(0));
BOOST_TEST(54 == ary.at(1));
BOOST_TEST(69 == ary.at(2));
BOOST_TEST(72 == ary.at(3));
std::tuple<int, short, unsigned, long> tpl =
toml::find<std::tuple<int, short, unsigned, long>>(std::move(v5), "key");
BOOST_TEST(static_cast<int >(42) == std::get<0>(tpl));
BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
@@ -689,4 +825,3 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_offset_datetime, value_type, test_va
BOOST_TEST(tm.tm_sec == 0);
}
}

View File

@@ -1,23 +1,20 @@
#define BOOST_TEST_MODULE "test_find_or"
#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 <unordered_map>
#include <list>
#include <deque>
#include "unit_test.hpp"
#include <array>
#include <deque>
#include <list>
#include <map>
#include <tuple>
#if __cplusplus >= 201703L
#include <unordered_map>
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#include <string_view>
#endif
using test_value_types = std::tuple<
toml::value,
toml::basic_value<toml::discard_comments>,
toml::basic_value<toml::preserve_comments>,
toml::basic_value<toml::discard_comments, std::map, std::deque>,
toml::basic_value<toml::preserve_comments, std::map, std::deque>
@@ -375,7 +372,8 @@ BOOST_AUTO_TEST_CASE(test_find_or_floating)
toml::value v1{{"key", 42}};
toml::value v2{{"key", 3.14}};
BOOST_TEST(2.71f == toml::find_or(v1, "key", 2.71f));
BOOST_TEST(static_cast<float>(double(3.14)) == toml::find_or(v2, "key", 2.71f));
const double ref(3.14);
BOOST_TEST(static_cast<float>(ref) == toml::find_or(v2, "key", 2.71f));
}
{
toml::value v1{{"key", 42}};
@@ -383,7 +381,8 @@ BOOST_AUTO_TEST_CASE(test_find_or_floating)
const auto moved1 = toml::find_or(std::move(v1), "key", 2.71f);
const auto moved2 = toml::find_or(std::move(v2), "key", 2.71f);
BOOST_TEST(2.71f == moved1);
BOOST_TEST(static_cast<float>(double(3.14)) == moved2);
const double ref(3.14);
BOOST_TEST(static_cast<float>(ref) == moved2);
}
}

View File

@@ -0,0 +1,393 @@
#include <toml.hpp>
#include "unit_test.hpp"
#include <array>
#include <deque>
#include <list>
#include <map>
#include <tuple>
#include <unordered_map>
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#include <string_view>
#endif
using test_value_types = std::tuple<
toml::basic_value<toml::discard_comments>,
toml::basic_value<toml::preserve_comments>,
toml::basic_value<toml::discard_comments, std::map, std::deque>,
toml::basic_value<toml::preserve_comments, std::map, std::deque>
>;
namespace test
{
template<typename charT, typename traits, typename T, typename Alloc>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const std::vector<T, Alloc>& v)
{
os << "[ ";
for(const auto& i : v) {os << i << ' ';}
os << ']';
return os;
}
template<typename charT, typename traits, typename T, typename Alloc>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const std::deque<T, Alloc>& v)
{
os << "[ ";
for(const auto& i : v) {os << i << ' ';}
os << ']';
return os;
}
template<typename charT, typename traits, typename T, typename Alloc>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const std::list<T, Alloc>& v)
{
os << "[ ";
for(const auto& i : v) {os << i << ' ';}
os << ']';
return os;
}
template<typename charT, typename traits,
typename Key, typename Value, typename Comp, typename Alloc>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os,
const std::map<Key, Value, Comp, Alloc>& v)
{
os << "[ ";
for(const auto& i : v) {os << '{' << i.first << ", " << i.second << "} ";}
os << ']';
return os;
}
template<typename charT, typename traits,
typename Key, typename Value, typename Hash, typename Eq, typename Alloc>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os,
const std::unordered_map<Key, Value, Hash, Eq, Alloc>& v)
{
os << "[ ";
for(const auto& i : v) {os << '{' << i.first << ", " << i.second << "} ";}
os << ']';
return os;
}
} // test
#define TOML11_TEST_FIND_OR_EXACT(toml_type, init_expr, opt_expr) \
{ \
using namespace test; \
const toml::toml_type init init_expr ; \
const toml::toml_type opt opt_expr ; \
const value_type v{{"key1", value_type{{"key2", init}} }};\
BOOST_TEST(init != opt); \
BOOST_TEST(init == toml::find_or(v, "key1", "key2", opt));\
} \
/**/
BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_exact, value_type, test_value_types)
{
TOML11_TEST_FIND_OR_EXACT(boolean, ( true), (false))
TOML11_TEST_FIND_OR_EXACT(integer, ( 42), ( 54))
TOML11_TEST_FIND_OR_EXACT(floating, ( 3.14), ( 2.71))
TOML11_TEST_FIND_OR_EXACT(string, ("foo"), ("bar"))
TOML11_TEST_FIND_OR_EXACT(local_time, (12, 30, 45), (6, 0, 30))
TOML11_TEST_FIND_OR_EXACT(local_date, (2019, toml::month_t::Apr, 1),
(1999, toml::month_t::Jan, 2))
TOML11_TEST_FIND_OR_EXACT(local_datetime,
(toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)),
(toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30))
)
TOML11_TEST_FIND_OR_EXACT(offset_datetime,
(toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)),
(toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0))
)
{
const typename value_type::array_type init{1,2,3,4,5};
const typename value_type::array_type opt {6,7,8,9,10};
const value_type v{{"key", init}};
BOOST_TEST(init != opt);
BOOST_TEST(init == toml::find_or(v, "key", opt));
}
{
const typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}};
const typename value_type::table_type opt {{"key1", 54}, {"key2", "bar"}};
const value_type v{{"key", init}};
BOOST_TEST(init != opt);
BOOST_TEST(init == toml::find_or(v, "key", opt));
}
}
#undef TOML11_TEST_FIND_OR_EXACT
#define TOML11_TEST_FIND_OR_MOVE(toml_type, init_expr, opt_expr) \
{ \
using namespace test; \
const toml::toml_type init init_expr ; \
toml::toml_type opt opt_expr ; \
value_type v{{"key1", value_type{{"key2", init}} }}; \
BOOST_TEST(init != opt); \
const auto moved = toml::find_or(std::move(v), "key1", "key2", std::move(opt));\
BOOST_TEST(init == moved); \
} \
/**/
BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_move, value_type, test_value_types)
{
TOML11_TEST_FIND_OR_MOVE(boolean, ( true), (false))
TOML11_TEST_FIND_OR_MOVE(integer, ( 42), ( 54))
TOML11_TEST_FIND_OR_MOVE(floating, ( 3.14), ( 2.71))
TOML11_TEST_FIND_OR_MOVE(string, ("foo"), ("bar"))
TOML11_TEST_FIND_OR_MOVE(local_time, (12, 30, 45), (6, 0, 30))
TOML11_TEST_FIND_OR_MOVE(local_date, (2019, toml::month_t::Apr, 1),
(1999, toml::month_t::Jan, 2))
TOML11_TEST_FIND_OR_MOVE(local_datetime,
(toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)),
(toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30))
)
TOML11_TEST_FIND_OR_MOVE(offset_datetime,
(toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)),
(toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0))
)
{
typename value_type::array_type init{1,2,3,4,5};
typename value_type::array_type opt {6,7,8,9,10};
value_type v{{"key", init}};
BOOST_TEST(init != opt);
const auto moved = toml::find_or(std::move(v), "key", std::move(opt));
BOOST_TEST(init == moved);
}
{
typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}};
typename value_type::table_type opt {{"key1", 54}, {"key2", "bar"}};
value_type v{{"key", init}};
BOOST_TEST(init != opt);
const auto moved = toml::find_or(std::move(v), "key", std::move(opt));
BOOST_TEST(init == moved);
}
}
#undef TOML11_TEST_FIND_OR_MOVE
#define TOML11_TEST_FIND_OR_MODIFY(toml_type, init_expr, opt_expr)\
{ \
using namespace test; \
const toml::toml_type init init_expr ; \
toml::toml_type opt1 opt_expr ; \
toml::toml_type opt2 opt_expr ; \
value_type v{{"key1", value_type{{"key2", init}} }}; \
BOOST_TEST(init != opt1); \
toml::find_or(v, "key1", "key2", opt2) = opt1; \
BOOST_TEST(opt1 == toml::find<toml::toml_type>(v, "key1", "key2"));\
} \
/**/
BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_modify, value_type, test_value_types)
{
TOML11_TEST_FIND_OR_MODIFY(boolean, ( true), (false))
TOML11_TEST_FIND_OR_MODIFY(integer, ( 42), ( 54))
TOML11_TEST_FIND_OR_MODIFY(floating, ( 3.14), ( 2.71))
TOML11_TEST_FIND_OR_MODIFY(string, ("foo"), ("bar"))
TOML11_TEST_FIND_OR_MODIFY(local_time, (12, 30, 45), (6, 0, 30))
TOML11_TEST_FIND_OR_MODIFY(local_date, (2019, toml::month_t::Apr, 1),
(1999, toml::month_t::Jan, 2))
TOML11_TEST_FIND_OR_MODIFY(local_datetime,
(toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45)),
(toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30))
)
TOML11_TEST_FIND_OR_MODIFY(offset_datetime,
(toml::local_date(2019, toml::month_t::Apr, 1), toml::local_time(12, 30, 45), toml::time_offset( 9, 0)),
(toml::local_date(1999, toml::month_t::Jan, 2), toml::local_time( 6, 0, 30), toml::time_offset(-3, 0))
)
{
typename value_type::array_type init{1,2,3,4,5};
typename value_type::array_type opt1{6,7,8,9,10};
typename value_type::array_type opt2{6,7,8,9,10};
BOOST_TEST(init != opt1);
value_type v{{"key", init}};
toml::find_or(v, "key", opt2) = opt1;
BOOST_TEST(opt1 == toml::find<typename value_type::array_type>(v, "key"));
}
{
typename value_type::table_type init{{"key1", 42}, {"key2", "foo"}};
typename value_type::table_type opt1{{"key1", 54}, {"key2", "bar"}};
typename value_type::table_type opt2{{"key1", 54}, {"key2", "bar"}};
value_type v{{"key", init}};
BOOST_TEST(init != opt1);
toml::find_or(v, "key", opt2) = opt1;
BOOST_TEST(opt1 == toml::find<typename value_type::table_type>(v, "key"));
}
}
#undef TOML11_TEST_FIND_OR_MODIFY
#define TOML11_TEST_FIND_OR_FALLBACK(init_type, opt_type) \
{ \
using namespace test; \
value_type v1{{"key1", value_type{{"key3", "foo"}}}}; \
BOOST_TEST(opt_type == toml::find_or(v1, "key1", "key2", opt_type));\
value_type v2{{"key1", "foo"}}; \
BOOST_TEST(opt_type == toml::find_or(v2, "key1", "key3", opt_type));\
} \
/**/
BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_fallback, value_type, test_value_types)
{
const toml::boolean boolean (true);
const toml::integer integer (42);
const toml::floating floating (3.14);
const toml::string string ("foo");
const toml::local_time local_time (12, 30, 45);
const toml::local_date local_date (2019, toml::month_t::Apr, 1);
const toml::local_datetime local_datetime (
toml::local_date(2019, toml::month_t::Apr, 1),
toml::local_time(12, 30, 45));
const toml::offset_datetime offset_datetime(
toml::local_date(2019, toml::month_t::Apr, 1),
toml::local_time(12, 30, 45), toml::time_offset( 9, 0));
using array_type = typename value_type::array_type;
using table_type = typename value_type::table_type;
const array_type array{1, 2, 3, 4, 5};
const table_type table{{"key1", 42}, {"key2", "foo"}};
TOML11_TEST_FIND_OR_FALLBACK(boolean, integer );
TOML11_TEST_FIND_OR_FALLBACK(boolean, floating );
TOML11_TEST_FIND_OR_FALLBACK(boolean, string );
TOML11_TEST_FIND_OR_FALLBACK(boolean, local_time );
TOML11_TEST_FIND_OR_FALLBACK(boolean, local_date );
TOML11_TEST_FIND_OR_FALLBACK(boolean, local_datetime );
TOML11_TEST_FIND_OR_FALLBACK(boolean, offset_datetime);
TOML11_TEST_FIND_OR_FALLBACK(boolean, array );
TOML11_TEST_FIND_OR_FALLBACK(boolean, table );
TOML11_TEST_FIND_OR_FALLBACK(integer, boolean );
TOML11_TEST_FIND_OR_FALLBACK(integer, floating );
TOML11_TEST_FIND_OR_FALLBACK(integer, string );
TOML11_TEST_FIND_OR_FALLBACK(integer, local_time );
TOML11_TEST_FIND_OR_FALLBACK(integer, local_date );
TOML11_TEST_FIND_OR_FALLBACK(integer, local_datetime );
TOML11_TEST_FIND_OR_FALLBACK(integer, offset_datetime);
TOML11_TEST_FIND_OR_FALLBACK(integer, array );
TOML11_TEST_FIND_OR_FALLBACK(integer, table );
TOML11_TEST_FIND_OR_FALLBACK(floating, boolean );
TOML11_TEST_FIND_OR_FALLBACK(floating, integer );
TOML11_TEST_FIND_OR_FALLBACK(floating, string );
TOML11_TEST_FIND_OR_FALLBACK(floating, local_time );
TOML11_TEST_FIND_OR_FALLBACK(floating, local_date );
TOML11_TEST_FIND_OR_FALLBACK(floating, local_datetime );
TOML11_TEST_FIND_OR_FALLBACK(floating, offset_datetime);
TOML11_TEST_FIND_OR_FALLBACK(floating, array );
TOML11_TEST_FIND_OR_FALLBACK(floating, table );
TOML11_TEST_FIND_OR_FALLBACK(string, boolean );
TOML11_TEST_FIND_OR_FALLBACK(string, integer );
TOML11_TEST_FIND_OR_FALLBACK(string, floating );
TOML11_TEST_FIND_OR_FALLBACK(string, local_time );
TOML11_TEST_FIND_OR_FALLBACK(string, local_date );
TOML11_TEST_FIND_OR_FALLBACK(string, local_datetime );
TOML11_TEST_FIND_OR_FALLBACK(string, offset_datetime);
TOML11_TEST_FIND_OR_FALLBACK(string, array );
TOML11_TEST_FIND_OR_FALLBACK(string, table );
TOML11_TEST_FIND_OR_FALLBACK(local_time, boolean );
TOML11_TEST_FIND_OR_FALLBACK(local_time, integer );
TOML11_TEST_FIND_OR_FALLBACK(local_time, floating );
TOML11_TEST_FIND_OR_FALLBACK(local_time, string );
TOML11_TEST_FIND_OR_FALLBACK(local_time, local_date );
TOML11_TEST_FIND_OR_FALLBACK(local_time, local_datetime );
TOML11_TEST_FIND_OR_FALLBACK(local_time, offset_datetime);
TOML11_TEST_FIND_OR_FALLBACK(local_time, array );
TOML11_TEST_FIND_OR_FALLBACK(local_time, table );
TOML11_TEST_FIND_OR_FALLBACK(local_date, boolean );
TOML11_TEST_FIND_OR_FALLBACK(local_date, integer );
TOML11_TEST_FIND_OR_FALLBACK(local_date, floating );
TOML11_TEST_FIND_OR_FALLBACK(local_date, string );
TOML11_TEST_FIND_OR_FALLBACK(local_date, local_time );
TOML11_TEST_FIND_OR_FALLBACK(local_date, local_datetime );
TOML11_TEST_FIND_OR_FALLBACK(local_date, offset_datetime);
TOML11_TEST_FIND_OR_FALLBACK(local_date, array );
TOML11_TEST_FIND_OR_FALLBACK(local_date, table );
TOML11_TEST_FIND_OR_FALLBACK(local_datetime, boolean );
TOML11_TEST_FIND_OR_FALLBACK(local_datetime, integer );
TOML11_TEST_FIND_OR_FALLBACK(local_datetime, floating );
TOML11_TEST_FIND_OR_FALLBACK(local_datetime, string );
TOML11_TEST_FIND_OR_FALLBACK(local_datetime, local_time );
TOML11_TEST_FIND_OR_FALLBACK(local_datetime, local_date );
TOML11_TEST_FIND_OR_FALLBACK(local_datetime, offset_datetime);
TOML11_TEST_FIND_OR_FALLBACK(local_datetime, array );
TOML11_TEST_FIND_OR_FALLBACK(local_datetime, table );
TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, boolean );
TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, integer );
TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, floating );
TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, string );
TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, local_time );
TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, local_date );
TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, local_datetime );
TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, array );
TOML11_TEST_FIND_OR_FALLBACK(offset_datetime, table );
TOML11_TEST_FIND_OR_FALLBACK(array, boolean );
TOML11_TEST_FIND_OR_FALLBACK(array, integer );
TOML11_TEST_FIND_OR_FALLBACK(array, floating );
TOML11_TEST_FIND_OR_FALLBACK(array, string );
TOML11_TEST_FIND_OR_FALLBACK(array, local_time );
TOML11_TEST_FIND_OR_FALLBACK(array, local_date );
TOML11_TEST_FIND_OR_FALLBACK(array, local_datetime );
TOML11_TEST_FIND_OR_FALLBACK(array, offset_datetime);
TOML11_TEST_FIND_OR_FALLBACK(array, table );
TOML11_TEST_FIND_OR_FALLBACK(table, boolean );
TOML11_TEST_FIND_OR_FALLBACK(table, integer );
TOML11_TEST_FIND_OR_FALLBACK(table, floating );
TOML11_TEST_FIND_OR_FALLBACK(table, string );
TOML11_TEST_FIND_OR_FALLBACK(table, local_time );
TOML11_TEST_FIND_OR_FALLBACK(table, local_date );
TOML11_TEST_FIND_OR_FALLBACK(table, local_datetime );
TOML11_TEST_FIND_OR_FALLBACK(table, offset_datetime);
TOML11_TEST_FIND_OR_FALLBACK(table, array );
}
#undef TOML11_TEST_FIND_OR_FALLBACK
struct move_only_type
{
explicit move_only_type(const std::string& n): name_(n) {}
void from_toml(const toml::value& v)
{
this->name_ = toml::find<std::string>(v, "name");
return;
}
move_only_type(): name_("default"){}
~move_only_type() = default;
move_only_type(move_only_type&&) = default;
move_only_type& operator=(move_only_type&&) = default;
move_only_type(const move_only_type&) = delete;
move_only_type& operator=(const move_only_type&) = delete;
bool operator==(const move_only_type& other) const noexcept {return this->name_ == other.name_;}
bool operator!=(const move_only_type& other) const noexcept {return this->name_ != other.name_;}
bool operator< (const move_only_type& other) const noexcept {return this->name_ < other.name_;}
bool operator<=(const move_only_type& other) const noexcept {return this->name_ <= other.name_;}
bool operator> (const move_only_type& other) const noexcept {return this->name_ > other.name_;}
bool operator>=(const move_only_type& other) const noexcept {return this->name_ >= other.name_;}
std::string name_;
};
std::ostream& operator<<(std::ostream& os, const move_only_type& mot)
{
os << mot.name_;
return os;
}
BOOST_AUTO_TEST_CASE_TEMPLATE(test_find_or_move_only, value_type, test_value_types)
{
const move_only_type ref("reference");
move_only_type opt("optional");
{
const value_type v{{"key1", value_type{{"key2", value_type{{"name", "reference"}} }} }};
BOOST_TEST(ref == toml::find_or(v, "key1", "key2", std::move(opt)));
}
}

View File

@@ -1,11 +1,7 @@
#define BOOST_TEST_MODULE "test_format_error"
#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 "unit_test.hpp"
#include <iostream>
// to check it successfully compiles. it does not check the formatted string.

View File

@@ -1,23 +1,20 @@
#define BOOST_TEST_MODULE "test_get"
#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 <unordered_map>
#include <list>
#include <deque>
#include "unit_test.hpp"
#include <array>
#include <deque>
#include <list>
#include <map>
#include <tuple>
#if __cplusplus >= 201703L
#include <unordered_map>
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#include <string_view>
#endif
using test_value_types = std::tuple<
toml::value,
toml::basic_value<toml::discard_comments>,
toml::basic_value<toml::preserve_comments>,
toml::basic_value<toml::discard_comments, std::map, std::deque>,
toml::basic_value<toml::preserve_comments, std::map, std::deque>
@@ -195,11 +192,11 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_floating_type, value_type, test_value_typ
{
{
value_type v(3.14);
BOOST_TEST(static_cast<float >(3.14) == toml::get<float >(v));
BOOST_TEST(static_cast<double >(3.14) == toml::get<double >(v));
BOOST_TEST(static_cast<long double>(3.14) == toml::get<long double>(v));
BOOST_TEST(3.14f == toml::get<float>(std::move(v)));
const double ref(3.14);
BOOST_TEST(static_cast<float >(ref) == toml::get<float >(v));
BOOST_TEST( ref == toml::get<double >(v));
BOOST_TEST(static_cast<long double>(ref) == toml::get<long double>(v));
BOOST_TEST(static_cast<float >(ref) == toml::get<float>(std::move(v)));
}
}
@@ -224,7 +221,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_string_type, value_type, test_value_types
BOOST_TEST("foobar" == x);
}
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
{
value_type v("foo", toml::string_t::basic);
BOOST_TEST("foo" == toml::get<std::string_view>(v));
@@ -262,14 +259,14 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_array, value_type, test_value_types)
BOOST_TEST(static_cast<std::int64_t>(72) == deq.at(3));
std::array<int, 4> ary = toml::get<std::array<int, 4>>(v);
BOOST_TEST(static_cast<int>(42) == ary.at(0));
BOOST_TEST(static_cast<int>(54) == ary.at(1));
BOOST_TEST(static_cast<int>(69) == ary.at(2));
BOOST_TEST(static_cast<int>(72) == ary.at(3));
BOOST_TEST(42 == ary.at(0));
BOOST_TEST(54 == ary.at(1));
BOOST_TEST(69 == ary.at(2));
BOOST_TEST(72 == ary.at(3));
std::tuple<int, short, unsigned, long> tpl =
toml::get<std::tuple<int, short, unsigned, long>>(v);
BOOST_TEST(static_cast<int >(42) == std::get<0>(tpl));
BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
@@ -308,16 +305,16 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_array, value_type, test_value_types)
{
value_type v{42, 54, 69, 72};
std::array<int, 4> ary = toml::get<std::array<int, 4>>(std::move(v));
BOOST_TEST(static_cast<int>(42) == ary.at(0));
BOOST_TEST(static_cast<int>(54) == ary.at(1));
BOOST_TEST(static_cast<int>(69) == ary.at(2));
BOOST_TEST(static_cast<int>(72) == ary.at(3));
BOOST_TEST(42 == ary.at(0));
BOOST_TEST(54 == ary.at(1));
BOOST_TEST(69 == ary.at(2));
BOOST_TEST(72 == ary.at(3));
}
{
value_type v{42, 54, 69, 72};
std::tuple<int, short, unsigned, long> tpl =
toml::get<std::tuple<int, short, unsigned, long>>(std::move(v));
BOOST_TEST(static_cast<int >(42) == std::get<0>(tpl));
BOOST_TEST( 42 == std::get<0>(tpl));
BOOST_TEST(static_cast<short >(54) == std::get<1>(tpl));
BOOST_TEST(static_cast<unsigned>(69) == std::get<2>(tpl));
BOOST_TEST(static_cast<long >(72) == std::get<3>(tpl));
@@ -506,4 +503,3 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_get_toml_offset_datetime, value_type, test_va
BOOST_TEST(tm.tm_sec == 0);
}
}

View File

@@ -1,23 +1,20 @@
#define BOOST_TEST_MODULE "test_get_or"
#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 <unordered_map>
#include <list>
#include <deque>
#include "unit_test.hpp"
#include <array>
#include <deque>
#include <list>
#include <map>
#include <tuple>
#if __cplusplus >= 201703L
#include <unordered_map>
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#include <string_view>
#endif
using test_value_types = std::tuple<
toml::value,
toml::basic_value<toml::discard_comments>,
toml::basic_value<toml::preserve_comments>,
toml::basic_value<toml::discard_comments, std::map, std::deque>,
toml::basic_value<toml::preserve_comments, std::map, std::deque>

View File

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

View File

@@ -1,6 +1,6 @@
#define BOOST_TEST_MODULE "test_lex_boolean"
#include <boost/test/unit_test.hpp>
#include <toml/lexer.hpp>
#include "unit_test.hpp"
#include "test_lex_aux.hpp"
using namespace toml;

View File

@@ -1,6 +1,6 @@
#define BOOST_TEST_MODULE "test_lex_datetime"
#include <boost/test/unit_test.hpp>
#include <toml/lexer.hpp>
#include "unit_test.hpp"
#include "test_lex_aux.hpp"
using namespace toml;

View File

@@ -1,9 +1,10 @@
#define BOOST_TEST_MODULE "test_lex_floating"
#include <boost/test/unit_test.hpp>
#include <toml/lexer.hpp>
#include <limits>
#include "unit_test.hpp"
#include "test_lex_aux.hpp"
#include <limits>
using namespace toml;
using namespace detail;

View File

@@ -1,6 +1,6 @@
#define BOOST_TEST_MODULE "test_lex_integer"
#include <boost/test/unit_test.hpp>
#include <toml/lexer.hpp>
#include "unit_test.hpp"
#include "test_lex_aux.hpp"
using namespace toml;

View File

@@ -1,6 +1,6 @@
#define BOOST_TEST_MODULE "lex_key_comment_test"
#include <boost/test/unit_test.hpp>
#include <toml/lexer.hpp>
#include "unit_test.hpp"
#include "test_lex_aux.hpp"
using namespace toml;
@@ -18,12 +18,11 @@ BOOST_AUTO_TEST_CASE(test_quoted_key)
{
TOML11_TEST_LEX_ACCEPT(lex_key, "\"127.0.0.1\"", "\"127.0.0.1\"");
TOML11_TEST_LEX_ACCEPT(lex_key, "\"character encoding\"", "\"character encoding\"");
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
// UTF-8 codepoint of characters that looks like "key" written upside down
TOML11_TEST_LEX_ACCEPT(lex_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"",
"\"\xCA\x8E\xC7\x9D\xCA\x9E\"");
#else
TOML11_TEST_LEX_ACCEPT(lex_key, u8"\"ʎǝʞ\"", u8"\"ʎǝʞ\"");
#endif
TOML11_TEST_LEX_ACCEPT(lex_key, "'key2'", "'key2'");
TOML11_TEST_LEX_ACCEPT(lex_key, "'quoted \"value\"'", "'quoted \"value\"'");
}

View File

@@ -1,6 +1,6 @@
#define BOOST_TEST_MODULE "test_lex_string"
#include <boost/test/unit_test.hpp>
#include <toml/lexer.hpp>
#include "unit_test.hpp"
#include "test_lex_aux.hpp"
using namespace toml;
@@ -31,15 +31,9 @@ BOOST_AUTO_TEST_CASE(test_basic_string)
"\"192.168.1.1\"",
"\"192.168.1.1\"");
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\xE4\xB8\xAD\xE5\x9B\xBD\"",
"\"\xE4\xB8\xAD\xE5\x9B\xBD\"");
#else
TOML11_TEST_LEX_ACCEPT(lex_string,
u8"\"中国\"",
u8"\"中国\"");
#endif
"\"\xE4\xB8\xAD\xE5\x9B\xBD\"", // UTF-8 string (means "China" in
"\"\xE4\xB8\xAD\xE5\x9B\xBD\""); // Chinese characters)
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"You'll hate me after this - #\"",
@@ -57,6 +51,22 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string)
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"",
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"",
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"",
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"");
}
BOOST_AUTO_TEST_CASE(test_literal_string)
@@ -83,4 +93,16 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string)
TOML11_TEST_LEX_ACCEPT(lex_string,
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''",
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"''''That's still pointless', she said.'''",
"''''That's still pointless', she said.'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"''''This,' she said, 'is just a pointless statement.''''",
"''''This,' she said, 'is just a pointless statement.''''");
}

View File

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

View File

@@ -1,11 +1,6 @@
#define BOOST_TEST_MODULE "parse_array<toml::value>_test"
#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/parser.hpp>
#include "unit_test.hpp"
#include "test_parse_aux.hpp"
using namespace toml;
@@ -13,30 +8,30 @@ using namespace detail;
BOOST_AUTO_TEST_CASE(test_oneline_array)
{
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[]", array());
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<toml::value>, "[]", array());
{
array a(5);
a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
a[3] = toml::value(1); a[4] = toml::value(5);
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[3,1,4,1,5]", a);
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<toml::value>, "[3,1,4,1,5]", a);
}
{
array a(3);
a[0] = toml::value("foo"); a[1] = toml::value("bar");
a[2] = toml::value("baz");
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\"foo\", \"bar\", \"baz\"]", a);
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<toml::value>, "[\"foo\", \"bar\", \"baz\"]", a);
}
{
array a(5);
a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
a[3] = toml::value(1); a[4] = toml::value(5);
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[3,1,4,1,5,]", a);
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<toml::value>, "[3,1,4,1,5,]", a);
}
{
array a(3);
a[0] = toml::value("foo"); a[1] = toml::value("bar");
a[2] = toml::value("baz");
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\"foo\", \"bar\", \"baz\",]", a);
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<toml::value>, "[\"foo\", \"bar\", \"baz\",]", a);
}
}
@@ -71,62 +66,153 @@ BOOST_AUTO_TEST_CASE(test_oneline_array_value)
BOOST_AUTO_TEST_CASE(test_multiline_array)
{
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\n#comment\n]", array());
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value< discard_comments>>, "[\n#comment\n]", typename basic_value< discard_comments>::array_type());
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<preserve_comments>>, "[\n#comment\n]", typename basic_value<preserve_comments>::array_type());
{
array a(5);
a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
a[3] = toml::value(1); a[4] = toml::value(5);
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[3,\n1,\n4,\n1,\n5]", a);
typename basic_value<discard_comments>::array_type a(5);
a[0] = basic_value<discard_comments>(3);
a[1] = basic_value<discard_comments>(1);
a[2] = basic_value<discard_comments>(4);
a[3] = basic_value<discard_comments>(1);
a[4] = basic_value<discard_comments>(5);
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<discard_comments>>, "[3,\n1,\n4,\n1,\n5]", a);
}
{
array a(3);
a[0] = toml::value("foo"); a[1] = toml::value("bar");
a[2] = toml::value("baz");
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\"foo\",\n\"bar\",\n\"baz\"]", a);
typename basic_value<preserve_comments>::array_type a(5);
a[0] = basic_value<preserve_comments>(3);
a[1] = basic_value<preserve_comments>(1);
a[2] = basic_value<preserve_comments>(4);
a[3] = basic_value<preserve_comments>(1);
a[4] = basic_value<preserve_comments>(5);
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<preserve_comments>>, "[3,\n1,\n4,\n1,\n5]", a);
}
{
array a(5);
a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
a[3] = toml::value(1); a[4] = toml::value(5);
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5]", a);
typename basic_value<discard_comments>::array_type a(5);
a[0] = basic_value<discard_comments>(3);
a[1] = basic_value<discard_comments>(1);
a[2] = basic_value<discard_comments>(4);
a[3] = basic_value<discard_comments>(1);
a[4] = basic_value<discard_comments>(5);
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<discard_comments>>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a);
}
{
array a(3);
a[0] = toml::value("foo"); a[1] = toml::value("b#r");
a[2] = toml::value("b#z");
TOML11_TEST_PARSE_EQUAL(parse_array<toml::value>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a);
typename basic_value<preserve_comments>::array_type a(5);
a[0] = basic_value<preserve_comments>(3, {"comment"});
a[1] = basic_value<preserve_comments>(1, {"comment"});
a[2] = basic_value<preserve_comments>(4, {"comment"});
a[3] = basic_value<preserve_comments>(1, {"comment"});
a[4] = basic_value<preserve_comments>(5, {"comment"});
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<preserve_comments>>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a);
}
{
typename basic_value<discard_comments>::array_type a(3);
a[0] = basic_value<discard_comments>("foo");
a[1] = basic_value<discard_comments>("bar");
a[2] = basic_value<discard_comments>("baz");
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<discard_comments>>, "[\"foo\",\n\"bar\",\n\"baz\"]", a);
}
{
typename basic_value<preserve_comments>::array_type a(3);
a[0] = basic_value<preserve_comments>("foo");
a[1] = basic_value<preserve_comments>("bar");
a[2] = basic_value<preserve_comments>("baz");
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<preserve_comments>>, "[\"foo\",\n\"bar\",\n\"baz\"]", a);
}
{
typename basic_value<discard_comments>::array_type a(3);
a[0] = basic_value<discard_comments>("foo");
a[1] = basic_value<discard_comments>("b#r");
a[2] = basic_value<discard_comments>("b#z");
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<discard_comments>>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a);
}
{
typename basic_value<preserve_comments>::array_type a(3);
a[0] = basic_value<preserve_comments>("foo", {"comment"});
a[1] = basic_value<preserve_comments>("b#r", {"comment"});
a[2] = basic_value<preserve_comments>("b#z", {"comment"});
TOML11_TEST_PARSE_EQUAL_VAT(parse_array<basic_value<preserve_comments>>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a);
}
}
BOOST_AUTO_TEST_CASE(test_multiline_array_value)
{
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\n#comment\n]", toml::value(array()));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value< discard_comments>>, "[\n#comment\n]", basic_value< discard_comments>(typename basic_value< discard_comments>::array_type()));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<preserve_comments>>, "[\n#comment\n]", basic_value<preserve_comments>(typename basic_value<preserve_comments>::array_type()));
{
array a(5);
a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
a[3] = toml::value(1); a[4] = toml::value(5);
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[3,\n1,\n4,\n1,\n5]", toml::value(a));
typename basic_value<discard_comments>::array_type a(5);
a[0] = basic_value<discard_comments>(3);
a[1] = basic_value<discard_comments>(1);
a[2] = basic_value<discard_comments>(4);
a[3] = basic_value<discard_comments>(1);
a[4] = basic_value<discard_comments>(5);
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<discard_comments>>, "[3,\n1,\n4,\n1,\n5]", basic_value<discard_comments>(a));
}
{
array a(3);
a[0] = toml::value("foo"); a[1] = toml::value("bar");
a[2] = toml::value("baz");
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",\n\"bar\",\n\"baz\"]", toml::value(a));
typename basic_value<preserve_comments>::array_type a(5);
a[0] = basic_value<preserve_comments>(3);
a[1] = basic_value<preserve_comments>(1);
a[2] = basic_value<preserve_comments>(4);
a[3] = basic_value<preserve_comments>(1);
a[4] = basic_value<preserve_comments>(5);
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<preserve_comments>>, "[3,\n1,\n4,\n1,\n5]", basic_value<preserve_comments>(a));
}
{
array a(5);
a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4);
a[3] = toml::value(1); a[4] = toml::value(5);
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5]", toml::value(a));
typename basic_value<discard_comments>::array_type a(5);
a[0] = basic_value<discard_comments>(3);
a[1] = basic_value<discard_comments>(1);
a[2] = basic_value<discard_comments>(4);
a[3] = basic_value<discard_comments>(1);
a[4] = basic_value<discard_comments>(5);
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<discard_comments>>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", basic_value<discard_comments>(a));
}
{
array a(3);
a[0] = toml::value("foo"); a[1] = toml::value("b#r");
a[2] = toml::value("b#z");
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", toml::value(a));
typename basic_value<preserve_comments>::array_type a(5);
a[0] = basic_value<preserve_comments>(3, {"comment"});
a[1] = basic_value<preserve_comments>(1, {"comment"});
a[2] = basic_value<preserve_comments>(4, {"comment"});
a[3] = basic_value<preserve_comments>(1, {"comment"});
a[4] = basic_value<preserve_comments>(5, {"comment"});
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<preserve_comments>>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", basic_value<preserve_comments>(a));
}
{
typename basic_value<discard_comments>::array_type a(3);
a[0] = basic_value<discard_comments>("foo");
a[1] = basic_value<discard_comments>("bar");
a[2] = basic_value<discard_comments>("baz");
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<discard_comments>>, "[\"foo\",\n\"bar\",\n\"baz\"]", basic_value<discard_comments>(a));
}
{
typename basic_value<preserve_comments>::array_type a(3);
a[0] = basic_value<preserve_comments>("foo");
a[1] = basic_value<preserve_comments>("bar");
a[2] = basic_value<preserve_comments>("baz");
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<preserve_comments>>, "[\"foo\",\n\"bar\",\n\"baz\"]", basic_value<preserve_comments>(a));
}
{
typename basic_value<discard_comments>::array_type a(3);
a[0] = basic_value<discard_comments>("foo");
a[1] = basic_value<discard_comments>("b#r");
a[2] = basic_value<discard_comments>("b#z");
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<discard_comments>>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", basic_value<discard_comments>(a));
}
{
typename basic_value<preserve_comments>::array_type a(3);
a[0] = basic_value<preserve_comments>("foo", {"comment"});
a[1] = basic_value<preserve_comments>("b#r", {"comment"});
a[2] = basic_value<preserve_comments>("b#z", {"comment"});
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<preserve_comments>>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", basic_value<preserve_comments>(a));
}
}
BOOST_AUTO_TEST_CASE(test_heterogeneous_array)
@@ -172,3 +258,31 @@ BOOST_AUTO_TEST_CASE(test_heterogeneous_array)
}
#endif
}
BOOST_AUTO_TEST_CASE(test_comments_after_comma)
{
{
typename basic_value<discard_comments>::array_type a(3);
a[0] = basic_value<discard_comments>("foo");
a[1] = basic_value<discard_comments>("bar");
a[2] = basic_value<discard_comments>("baz");
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<discard_comments>>,
"[ \"foo\" # comment\n"
", \"bar\" # comment\n"
", \"baz\" # comment\n"
"]", basic_value<discard_comments>(a));
}
{
typename basic_value<preserve_comments>::array_type a(3);
a[0] = basic_value<preserve_comments>("foo", {" comment"});
a[1] = basic_value<preserve_comments>("bar", {" comment"});
a[2] = basic_value<preserve_comments>("baz", {" comment"});
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<basic_value<preserve_comments>>,
"[ \"foo\" # comment\n"
", \"bar\" # comment\n"
", \"baz\" # comment\n"
"]", basic_value<preserve_comments>(a));
}
}

View File

@@ -7,7 +7,7 @@
#define TOML11_TEST_PARSE_EQUAL(psr, tkn, expct) \
do { \
const std::string token(tkn); \
toml::detail::location<std::string> loc("test", token); \
toml::detail::location loc("test", token); \
const auto result = psr(loc); \
BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \
@@ -20,11 +20,27 @@ do { \
} while(false); \
/**/
#define TOML11_TEST_PARSE_EQUAL_VAT(psr, tkn, expct) \
do { \
const std::string token(tkn); \
toml::detail::location loc("test", token); \
const auto result = psr(loc, 0); \
BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \
BOOST_TEST(result.unwrap().first == expct); \
} else { \
std::cerr << "parser " << #psr << " failed with input `"; \
std::cerr << token << "`.\n"; \
std::cerr << "reason: " << result.unwrap_err() << '\n'; \
} \
} while(false); \
/**/
#define TOML11_TEST_PARSE_EQUAL_VALUE(psr, tkn, expct) \
do { \
const std::string token(tkn); \
toml::detail::location<std::string> loc("test", token); \
const auto result = psr(loc); \
toml::detail::location loc("test", token); \
const auto result = psr(loc, 0); \
BOOST_TEST(result.is_ok()); \
if(result.is_ok()){ \
BOOST_TEST(result.unwrap() == expct); \
@@ -35,3 +51,4 @@ do { \
} \
} while(false); \
/**/

View File

@@ -1,11 +1,6 @@
#define BOOST_TEST_MODULE "test_parse_boolean"
#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/parser.hpp>
#include "unit_test.hpp"
#include "test_parse_aux.hpp"
using namespace toml;

View File

@@ -1,11 +1,6 @@
#define BOOST_TEST_MODULE "parse_datetime_test"
#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/parser.hpp>
#include "unit_test.hpp"
#include "test_parse_aux.hpp"
using namespace toml;
@@ -17,6 +12,11 @@ BOOST_AUTO_TEST_CASE(test_time)
TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00.99", toml::local_time(7, 32, 0, 990, 0));
TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00.999", toml::local_time(7, 32, 0, 999, 0));
TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00.999999", toml::local_time(7, 32, 0, 999, 999));
TOML11_TEST_PARSE_EQUAL(parse_local_time, "00:00:00.000000", toml::local_time( 0, 0, 0, 0, 0));
TOML11_TEST_PARSE_EQUAL(parse_local_time, "23:59:59.999999", toml::local_time(23, 59, 59, 999, 999));
TOML11_TEST_PARSE_EQUAL(parse_local_time, "23:59:60.999999", toml::local_time(23, 59, 60, 999, 999)); // leap second
}
BOOST_AUTO_TEST_CASE(test_time_value)
@@ -25,17 +25,125 @@ BOOST_AUTO_TEST_CASE(test_time_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "07:32:00.99", toml::value(toml::local_time(7, 32, 0, 990, 0)));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "07:32:00.999", toml::value(toml::local_time(7, 32, 0, 999, 0)));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "07:32:00.999999", toml::value(toml::local_time(7, 32, 0, 999, 999)));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "00:00:00.000000", toml::value(toml::local_time( 0, 0, 0, 0, 0)));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "23:59:59.999999", toml::value(toml::local_time(23, 59, 59, 999, 999)));
std::istringstream stream1(std::string("invalid-datetime = 24:00:00"));
std::istringstream stream2(std::string("invalid-datetime = 00:60:00"));
std::istringstream stream3(std::string("invalid-datetime = 00:00:61"));
BOOST_CHECK_THROW(toml::parse(stream1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream2), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream3), toml::syntax_error);
}
BOOST_AUTO_TEST_CASE(test_date)
{
TOML11_TEST_PARSE_EQUAL(parse_local_date, "1979-05-27",
toml::local_date(1979, toml::month_t::May, 27));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "1979-05-27", toml::local_date(1979, toml::month_t::May, 27));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-01-01", toml::local_date(2000, toml::month_t::Jan, 1));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-01-31", toml::local_date(2000, toml::month_t::Jan, 31));
std::istringstream stream1_1(std::string("invalid-datetime = 2000-01-00"));
std::istringstream stream1_2(std::string("invalid-datetime = 2000-01-32"));
BOOST_CHECK_THROW(toml::parse(stream1_1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream1_2), toml::syntax_error);
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-02-01", toml::local_date(2000, toml::month_t::Feb, 1));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-02-29", toml::local_date(2000, toml::month_t::Feb, 29));
std::istringstream stream2_1(std::string("invalid-datetime = 2000-02-00"));
std::istringstream stream2_2(std::string("invalid-datetime = 2000-02-30"));
BOOST_CHECK_THROW(toml::parse(stream2_1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream2_2), toml::syntax_error);
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2001-02-28", toml::local_date(2001, toml::month_t::Feb, 28));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2004-02-29", toml::local_date(2004, toml::month_t::Feb, 29));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2100-02-28", toml::local_date(2100, toml::month_t::Feb, 28));
std::istringstream stream2_3(std::string("invalid-datetime = 2001-02-29"));
std::istringstream stream2_4(std::string("invalid-datetime = 2004-02-30"));
std::istringstream stream2_5(std::string("invalid-datetime = 2100-02-29"));
BOOST_CHECK_THROW(toml::parse(stream2_3), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream2_4), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream2_5), toml::syntax_error);
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-03-01", toml::local_date(2000, toml::month_t::Mar, 1));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-03-31", toml::local_date(2000, toml::month_t::Mar, 31));
std::istringstream stream3_1(std::string("invalid-datetime = 2000-03-00"));
std::istringstream stream3_2(std::string("invalid-datetime = 2000-03-32"));
BOOST_CHECK_THROW(toml::parse(stream3_1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream3_2), toml::syntax_error);
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-04-01", toml::local_date(2000, toml::month_t::Apr, 1));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-04-30", toml::local_date(2000, toml::month_t::Apr, 30));
std::istringstream stream4_1(std::string("invalid-datetime = 2000-04-00"));
std::istringstream stream4_2(std::string("invalid-datetime = 2000-04-31"));
BOOST_CHECK_THROW(toml::parse(stream4_1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream4_2), toml::syntax_error);
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-05-01", toml::local_date(2000, toml::month_t::May, 1));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-05-31", toml::local_date(2000, toml::month_t::May, 31));
std::istringstream stream5_1(std::string("invalid-datetime = 2000-05-00"));
std::istringstream stream5_2(std::string("invalid-datetime = 2000-05-32"));
BOOST_CHECK_THROW(toml::parse(stream5_1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream5_2), toml::syntax_error);
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-06-01", toml::local_date(2000, toml::month_t::Jun, 1));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-06-30", toml::local_date(2000, toml::month_t::Jun, 30));
std::istringstream stream6_1(std::string("invalid-datetime = 2000-06-00"));
std::istringstream stream6_2(std::string("invalid-datetime = 2000-06-31"));
BOOST_CHECK_THROW(toml::parse(stream6_1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream6_2), toml::syntax_error);
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-07-01", toml::local_date(2000, toml::month_t::Jul, 1));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-07-31", toml::local_date(2000, toml::month_t::Jul, 31));
std::istringstream stream7_1(std::string("invalid-datetime = 2000-07-00"));
std::istringstream stream7_2(std::string("invalid-datetime = 2000-07-32"));
BOOST_CHECK_THROW(toml::parse(stream7_1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream7_2), toml::syntax_error);
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-08-01", toml::local_date(2000, toml::month_t::Aug, 1));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-08-31", toml::local_date(2000, toml::month_t::Aug, 31));
std::istringstream stream8_1(std::string("invalid-datetime = 2000-08-00"));
std::istringstream stream8_2(std::string("invalid-datetime = 2000-08-32"));
BOOST_CHECK_THROW(toml::parse(stream8_1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream8_2), toml::syntax_error);
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-09-01", toml::local_date(2000, toml::month_t::Sep, 1));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-09-30", toml::local_date(2000, toml::month_t::Sep, 30));
std::istringstream stream9_1(std::string("invalid-datetime = 2000-09-00"));
std::istringstream stream9_2(std::string("invalid-datetime = 2000-09-31"));
BOOST_CHECK_THROW(toml::parse(stream9_1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream9_2), toml::syntax_error);
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-10-01", toml::local_date(2000, toml::month_t::Oct, 1));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-10-31", toml::local_date(2000, toml::month_t::Oct, 31));
std::istringstream stream10_1(std::string("invalid-datetime = 2000-10-00"));
std::istringstream stream10_2(std::string("invalid-datetime = 2000-10-32"));
BOOST_CHECK_THROW(toml::parse(stream10_1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream10_2), toml::syntax_error);
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-11-01", toml::local_date(2000, toml::month_t::Nov, 1));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-11-30", toml::local_date(2000, toml::month_t::Nov, 30));
std::istringstream stream11_1(std::string("invalid-datetime = 2000-11-00"));
std::istringstream stream11_2(std::string("invalid-datetime = 2000-11-31"));
BOOST_CHECK_THROW(toml::parse(stream11_1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream11_2), toml::syntax_error);
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-12-01", toml::local_date(2000, toml::month_t::Dec, 1));
TOML11_TEST_PARSE_EQUAL(parse_local_date, "2000-12-31", toml::local_date(2000, toml::month_t::Dec, 31));
std::istringstream stream12_1(std::string("invalid-datetime = 2000-12-00"));
std::istringstream stream12_2(std::string("invalid-datetime = 2000-12-32"));
BOOST_CHECK_THROW(toml::parse(stream12_1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream12_2), toml::syntax_error);
std::istringstream stream13_1(std::string("invalid-datetime = 2000-13-01"));
BOOST_CHECK_THROW(toml::parse(stream13_1), toml::syntax_error);
std::istringstream stream0_1(std::string("invalid-datetime = 2000-00-01"));
BOOST_CHECK_THROW(toml::parse(stream0_1), toml::syntax_error);
}
BOOST_AUTO_TEST_CASE(test_date_value)
{
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "1979-05-27",
value(toml::local_date(1979, toml::month_t::May, 27)));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "1979-05-27", value(toml::local_date(1979, toml::month_t::May, 27)));
}
BOOST_AUTO_TEST_CASE(test_datetime)
@@ -107,6 +215,11 @@ BOOST_AUTO_TEST_CASE(test_offset_datetime)
TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00.999999+09:00",
toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27),
toml::local_time(7, 32, 0, 999, 999), toml::time_offset(9, 0)));
std::istringstream stream1(std::string("invalid-datetime = 2000-01-01T00:00:00+24:00"));
std::istringstream stream2(std::string("invalid-datetime = 2000-01-01T00:00:00+00:60"));
BOOST_CHECK_THROW(toml::parse(stream1), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(stream2), toml::syntax_error);
}
BOOST_AUTO_TEST_CASE(test_offset_datetime_value)

View File

@@ -1,22 +1,18 @@
#define BOOST_TEST_MODULE "test_parse_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>
#include <map>
#include "unit_test.hpp"
#include <deque>
#include <fstream>
#include <iostream>
#include <map>
BOOST_AUTO_TEST_CASE(test_example)
{
const auto data = toml::parse("toml/tests/example.toml");
const auto data = toml::parse(testinput("example.toml"));
BOOST_TEST(toml::find<std::string>(data, "title") == "TOML Example");
const auto& owner = toml::find(data, "owner");
const auto owner = toml::find(data, "owner");
{
BOOST_TEST(toml::find<std::string>(owner, "name") == "Tom Preston-Werner");
BOOST_TEST(toml::find<std::string>(owner, "organization") == "GitHub");
@@ -27,7 +23,7 @@ BOOST_AUTO_TEST_CASE(test_example)
toml::local_time(7, 32, 0), toml::time_offset(0, 0)));
}
const auto& database = toml::find(data, "database");
const auto database = toml::find(data, "database");
{
BOOST_TEST(toml::find<std::string>(database, "server") == "192.168.1.1");
const std::vector<int> expected_ports{8001, 8001, 8002};
@@ -36,7 +32,7 @@ BOOST_AUTO_TEST_CASE(test_example)
BOOST_TEST(toml::find<bool>(database, "enabled") == true);
}
const auto& servers = toml::find(data, "servers");
const auto servers = toml::find(data, "servers");
{
toml::table alpha = toml::find<toml::table>(servers, "alpha");
BOOST_TEST(toml::get<std::string>(alpha.at("ip")) == "10.0.0.1");
@@ -48,7 +44,7 @@ BOOST_AUTO_TEST_CASE(test_example)
BOOST_TEST(toml::get<std::string>(beta.at("country")) == "\xE4\xB8\xAD\xE5\x9B\xBD");
}
const auto& clients = toml::find(data, "clients");
const auto clients = toml::find(data, "clients");
{
toml::array clients_data = toml::find<toml::array>(clients, "data");
@@ -76,11 +72,11 @@ BOOST_AUTO_TEST_CASE(test_example)
BOOST_AUTO_TEST_CASE(test_example_stream)
{
std::ifstream ifs("toml/tests/example.toml");
std::ifstream ifs(testinput("example.toml"), std::ios::binary);
const auto data = toml::parse(ifs);
BOOST_TEST(toml::find<std::string>(data, "title") == "TOML Example");
const auto& owner = toml::find(data, "owner");
const auto owner = toml::find(data, "owner");
{
BOOST_TEST(toml::find<std::string>(owner, "name") == "Tom Preston-Werner");
BOOST_TEST(toml::find<std::string>(owner, "organization") == "GitHub");
@@ -91,7 +87,7 @@ BOOST_AUTO_TEST_CASE(test_example_stream)
toml::local_time(7, 32, 0), toml::time_offset(0, 0)));
}
const auto& database = toml::find(data, "database");
const auto database = toml::find(data, "database");
{
BOOST_TEST(toml::find<std::string>(database, "server") == "192.168.1.1");
const std::vector<int> expected_ports{8001, 8001, 8002};
@@ -100,7 +96,7 @@ BOOST_AUTO_TEST_CASE(test_example_stream)
BOOST_TEST(toml::find<bool>(database, "enabled") == true);
}
const auto& servers = toml::find(data, "servers");
const auto servers = toml::find(data, "servers");
{
toml::table alpha = toml::find<toml::table>(servers, "alpha");
BOOST_TEST(toml::get<std::string>(alpha.at("ip")) == "10.0.0.1");
@@ -112,7 +108,76 @@ BOOST_AUTO_TEST_CASE(test_example_stream)
BOOST_TEST(toml::get<std::string>(beta.at("country")) == "\xE4\xB8\xAD\xE5\x9B\xBD");
}
const auto& clients = toml::find(data, "clients");
const auto clients = toml::find(data, "clients");
{
toml::array clients_data = toml::find<toml::array>(clients, "data");
std::vector<std::string> expected_name{"gamma", "delta"};
BOOST_CHECK(toml::get<std::vector<std::string>>(clients_data.at(0)) == expected_name);
std::vector<int> expected_number{1, 2};
BOOST_CHECK(toml::get<std::vector<int>>(clients_data.at(1)) == expected_number);
std::vector<std::string> expected_hosts{"alpha", "omega"};
BOOST_CHECK(toml::find<std::vector<std::string>>(clients, "hosts") == expected_hosts);
}
std::vector<toml::table> products =
toml::find<std::vector<toml::table>>(data, "products");
{
BOOST_TEST(toml::get<std::string>(products.at(0).at("name")) ==
"Hammer");
BOOST_TEST(toml::get<std::int64_t>(products.at(0).at("sku")) ==
738594937);
BOOST_TEST(toml::get<std::string>(products.at(1).at("name")) ==
"Nail");
BOOST_TEST(toml::get<std::int64_t>(products.at(1).at("sku")) ==
284758393);
BOOST_TEST(toml::get<std::string>(products.at(1).at("color")) ==
"gray");
}
}
BOOST_AUTO_TEST_CASE(test_example_file_pointer)
{
FILE * file = fopen(testinput("example.toml").c_str(), "rb");
const auto data = toml::parse(file, "toml/tests/example.toml");
fclose(file);
BOOST_TEST(toml::find<std::string>(data, "title") == "TOML Example");
const auto owner = toml::find(data, "owner");
{
BOOST_TEST(toml::find<std::string>(owner, "name") == "Tom Preston-Werner");
BOOST_TEST(toml::find<std::string>(owner, "organization") == "GitHub");
BOOST_TEST(toml::find<std::string>(owner, "bio") ==
"GitHub Cofounder & CEO\nLikes tater tots and beer.");
BOOST_TEST(toml::find<toml::offset_datetime>(owner, "dob") ==
toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27),
toml::local_time(7, 32, 0), toml::time_offset(0, 0)));
}
const auto database = toml::find(data, "database");
{
BOOST_TEST(toml::find<std::string>(database, "server") == "192.168.1.1");
const std::vector<int> expected_ports{8001, 8001, 8002};
BOOST_CHECK(toml::find<std::vector<int>>(database, "ports") == expected_ports);
BOOST_TEST(toml::find<int >(database, "connection_max") == 5000);
BOOST_TEST(toml::find<bool>(database, "enabled") == true);
}
const auto servers = toml::find(data, "servers");
{
toml::table alpha = toml::find<toml::table>(servers, "alpha");
BOOST_TEST(toml::get<std::string>(alpha.at("ip")) == "10.0.0.1");
BOOST_TEST(toml::get<std::string>(alpha.at("dc")) == "eqdc10");
toml::table beta = toml::find<toml::table>(servers, "beta");
BOOST_TEST(toml::get<std::string>(beta.at("ip")) == "10.0.0.2");
BOOST_TEST(toml::get<std::string>(beta.at("dc")) == "eqdc10");
BOOST_TEST(toml::get<std::string>(beta.at("country")) == "\xE4\xB8\xAD\xE5\x9B\xBD");
}
const auto clients = toml::find(data, "clients");
{
toml::array clients_data = toml::find<toml::array>(clients, "data");
std::vector<std::string> expected_name{"gamma", "delta"};
@@ -144,7 +209,7 @@ BOOST_AUTO_TEST_CASE(test_example_stream)
BOOST_AUTO_TEST_CASE(test_fruit)
{
const auto data = toml::parse("toml/tests/fruit.toml");
const auto data = toml::parse(testinput("fruit.toml"));
const auto blah = toml::find<toml::array>(toml::find(data, "fruit"), "blah");
BOOST_TEST(toml::find<std::string>(blah.at(0), "name") == "apple");
BOOST_TEST(toml::find<std::string>(blah.at(1), "name") == "banana");
@@ -162,7 +227,7 @@ BOOST_AUTO_TEST_CASE(test_fruit)
BOOST_AUTO_TEST_CASE(test_hard_example)
{
const auto data = toml::parse("toml/tests/hard_example.toml");
const auto data = toml::parse(testinput("hard_example.toml"));
const auto the = toml::find(data, "the");
BOOST_TEST(toml::find<std::string>(the, "test_string") ==
"You'll hate me after this - #");
@@ -189,7 +254,7 @@ BOOST_AUTO_TEST_CASE(test_hard_example)
}
BOOST_AUTO_TEST_CASE(test_hard_example_comment)
{
const auto data = toml::parse<toml::preserve_comments>("toml/tests/hard_example.toml");
const auto data = toml::parse<toml::preserve_comments>(testinput("hard_example.toml"));
const auto the = toml::find(data, "the");
BOOST_TEST(toml::find<std::string>(the, "test_string") ==
"You'll hate me after this - #");
@@ -218,10 +283,10 @@ BOOST_AUTO_TEST_CASE(test_hard_example_comment)
BOOST_AUTO_TEST_CASE(test_example_preserve_comment)
{
const auto data = toml::parse<toml::preserve_comments>("toml/tests/example.toml");
const auto data = toml::parse<toml::preserve_comments>(testinput("example.toml"));
BOOST_TEST(toml::find<std::string>(data, "title") == "TOML Example");
const auto& owner = toml::find(data, "owner");
const auto owner = toml::find(data, "owner");
{
BOOST_TEST(toml::find<std::string>(owner, "name") == "Tom Preston-Werner");
BOOST_TEST(toml::find<std::string>(owner, "organization") == "GitHub");
@@ -234,7 +299,7 @@ BOOST_AUTO_TEST_CASE(test_example_preserve_comment)
" First class dates? Why not?");
}
const auto& database = toml::find(data, "database");
const auto database = toml::find(data, "database");
{
BOOST_TEST(toml::find<std::string>(database, "server") == "192.168.1.1");
const std::vector<int> expected_ports{8001, 8001, 8002};
@@ -243,15 +308,15 @@ BOOST_AUTO_TEST_CASE(test_example_preserve_comment)
BOOST_TEST(toml::find<bool>(database, "enabled") == true);
}
const auto& servers = toml::find(data, "servers");
const auto servers = toml::find(data, "servers");
{
const auto& alpha = toml::find(servers, "alpha");
const auto alpha = toml::find(servers, "alpha");
BOOST_TEST(alpha.comments().at(0) ==
" You can indent as you please. Tabs or spaces. TOML don't care.");
BOOST_TEST(toml::find<std::string>(alpha, "ip") == "10.0.0.1");
BOOST_TEST(toml::find<std::string>(alpha, "dc") == "eqdc10");
const auto& beta = toml::find(servers, "beta");
const auto beta = toml::find(servers, "beta");
BOOST_TEST(toml::find<std::string>(beta, "ip") == "10.0.0.2");
BOOST_TEST(toml::find<std::string>(beta, "dc") == "eqdc10");
BOOST_TEST(toml::find<std::string>(beta, "country") ==
@@ -260,7 +325,7 @@ BOOST_AUTO_TEST_CASE(test_example_preserve_comment)
" This should be parsed as UTF-8");
}
const auto& clients = toml::find(data, "clients");
const auto clients = toml::find(data, "clients");
{
BOOST_TEST(toml::find(clients, "data").comments().at(0) ==
" just an update to make sure parsers support it");
@@ -300,8 +365,8 @@ BOOST_AUTO_TEST_CASE(test_example_preserve_comment)
BOOST_AUTO_TEST_CASE(test_example_preserve_stdmap_stddeque)
{
const auto data = toml::parse<toml::preserve_comments, std::map, std::deque
>("toml/tests/example.toml");
const auto data = toml::parse<toml::preserve_comments, std::map, std::deque>(
testinput("example.toml"));
static_assert(std::is_same<typename decltype(data)::table_type,
std::map<toml::key, typename std::remove_cv<decltype(data)>::type>
@@ -311,7 +376,7 @@ BOOST_AUTO_TEST_CASE(test_example_preserve_stdmap_stddeque)
>::value, "");
BOOST_TEST(toml::find<std::string>(data, "title") == "TOML Example");
const auto& owner = toml::find(data, "owner");
const auto owner = toml::find(data, "owner");
{
BOOST_TEST(toml::find<std::string>(owner, "name") == "Tom Preston-Werner");
BOOST_TEST(toml::find<std::string>(owner, "organization") == "GitHub");
@@ -324,7 +389,7 @@ BOOST_AUTO_TEST_CASE(test_example_preserve_stdmap_stddeque)
" First class dates? Why not?");
}
const auto& database = toml::find(data, "database");
const auto database = toml::find(data, "database");
{
BOOST_TEST(toml::find<std::string>(database, "server") == "192.168.1.1");
const std::vector<int> expected_ports{8001, 8001, 8002};
@@ -333,15 +398,15 @@ BOOST_AUTO_TEST_CASE(test_example_preserve_stdmap_stddeque)
BOOST_TEST(toml::find<bool>(database, "enabled") == true);
}
const auto& servers = toml::find(data, "servers");
const auto servers = toml::find(data, "servers");
{
const auto& alpha = toml::find(servers, "alpha");
const auto alpha = toml::find(servers, "alpha");
BOOST_TEST(alpha.comments().at(0) ==
" You can indent as you please. Tabs or spaces. TOML don't care.");
BOOST_TEST(toml::find<std::string>(alpha, "ip") == "10.0.0.1");
BOOST_TEST(toml::find<std::string>(alpha, "dc") == "eqdc10");
const auto& beta = toml::find(servers, "beta");
const auto beta = toml::find(servers, "beta");
BOOST_TEST(toml::find<std::string>(beta, "ip") == "10.0.0.2");
BOOST_TEST(toml::find<std::string>(beta, "dc") == "eqdc10");
BOOST_TEST(toml::find<std::string>(beta, "country") ==
@@ -350,7 +415,7 @@ BOOST_AUTO_TEST_CASE(test_example_preserve_stdmap_stddeque)
" This should be parsed as UTF-8");
}
const auto& clients = toml::find(data, "clients");
const auto clients = toml::find(data, "clients");
{
BOOST_TEST(toml::find(clients, "data").comments().at(0) ==
" just an update to make sure parsers support it");
@@ -779,6 +844,22 @@ BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines)
BOOST_TEST(toml::find<std::string>(toml::find(data, "table"), "key") == "value");
}
// without newline
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\"\n"
"a = 0"
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_with_newline.toml");
BOOST_TEST(toml::find<std::string>(data, "key") == "value");
BOOST_TEST(toml::find<std::string>(toml::find(data, "table"), "key") == "value");
}
// CRLF
@@ -889,3 +970,40 @@ BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines)
}
}
BOOST_AUTO_TEST_CASE(test_file_ends_without_lf)
{
{
const std::string table(
"key = \"value\"\n"
"[table]\n"
"key = \"value\""
);
std::istringstream iss(table);
const auto data = toml::parse(iss,
"test_files_end_without_lf.toml");
BOOST_TEST(toml::find<std::string>(data, "key") == "value");
BOOST_TEST(toml::find<std::string>(toml::find(data, "table"), "key") == "value");
}
}
BOOST_AUTO_TEST_CASE(test_parse_function_compiles)
{
using result_type = decltype(toml::parse("string literal"));
(void) [](const char* that) -> result_type { return toml::parse(that); };
(void) [](char* that) -> result_type { return toml::parse(that); };
(void) [](const std::string& that) -> result_type { return toml::parse(that); };
(void) [](std::string& that) -> result_type { return toml::parse(that); };
(void) [](std::string&& that) -> result_type { return toml::parse(that); };
#ifdef TOML11_HAS_STD_FILESYSTEM
(void) [](const std::filesystem::path& that) -> result_type { return toml::parse(that); };
(void) [](std::filesystem::path& that) -> result_type { return toml::parse(that); };
(void) [](std::filesystem::path&& that) -> result_type { return toml::parse(that); };
#endif
(void) [](std::FILE* that) -> result_type { return toml::parse(that, "mandatory.toml"); };
}
BOOST_AUTO_TEST_CASE(test_parse_nonexistent_file)
{
BOOST_CHECK_THROW(toml::parse("nonexistent.toml"), std::ios_base::failure);
}

View File

@@ -1,14 +1,10 @@
#define BOOST_TEST_MODULE "parse_floating_test"
#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/parser.hpp>
#include <cmath>
#include "unit_test.hpp"
#include "test_parse_aux.hpp"
#include <cmath>
using namespace toml;
using namespace detail;
@@ -128,7 +124,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
{
{
const std::string token("inf");
toml::detail::location<std::string> loc("test", token);
toml::detail::location loc("test", token);
const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isinf(r.unwrap().first));
@@ -136,7 +132,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
}
{
const std::string token("+inf");
toml::detail::location<std::string> loc("test", token);
toml::detail::location loc("test", token);
const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isinf(r.unwrap().first));
@@ -144,7 +140,7 @@ BOOST_AUTO_TEST_CASE(test_inf)
}
{
const std::string token("-inf");
toml::detail::location<std::string> loc("test", token);
toml::detail::location loc("test", token);
const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isinf(r.unwrap().first));
@@ -156,23 +152,30 @@ BOOST_AUTO_TEST_CASE(test_nan)
{
{
const std::string token("nan");
toml::detail::location<std::string> loc("test", token);
toml::detail::location loc("test", token);
const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isnan(r.unwrap().first));
}
{
const std::string token("+nan");
toml::detail::location<std::string> loc("test", token);
toml::detail::location loc("test", token);
const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isnan(r.unwrap().first));
}
{
const std::string token("-nan");
toml::detail::location<std::string> loc("test", token);
toml::detail::location loc("test", token);
const auto r = parse_floating(loc);
BOOST_CHECK(r.is_ok());
BOOST_CHECK(std::isnan(r.unwrap().first));
}
}
BOOST_AUTO_TEST_CASE(test_overflow)
{
std::istringstream float_overflow (std::string("float-overflow = 1.0e+1024"));
BOOST_CHECK_THROW(toml::parse(float_overflow ), toml::syntax_error);
// istringstream >> float does not set failbit in case of underflow.
}

View File

@@ -1,11 +1,6 @@
#define BOOST_TEST_MODULE "parse_inline_table_test"
#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/parser.hpp>
#include "unit_test.hpp"
#include "test_parse_aux.hpp"
using namespace toml;
@@ -13,19 +8,19 @@ using namespace detail;
BOOST_AUTO_TEST_CASE(test_inline_table)
{
TOML11_TEST_PARSE_EQUAL(parse_inline_table<toml::value>, "{}", table());
TOML11_TEST_PARSE_EQUAL_VAT(parse_inline_table<toml::value>, "{}", table());
{
table t;
t["foo"] = toml::value(42);
t["bar"] = toml::value("baz");
TOML11_TEST_PARSE_EQUAL(parse_inline_table<toml::value>, "{foo = 42, bar = \"baz\"}", t);
TOML11_TEST_PARSE_EQUAL_VAT(parse_inline_table<toml::value>, "{foo = 42, bar = \"baz\"}", t);
}
{
table t;
table t_sub;
t_sub["name"] = toml::value("pug");
t["type"] = toml::value(t_sub);
TOML11_TEST_PARSE_EQUAL(parse_inline_table<toml::value>, "{type.name = \"pug\"}", t);
TOML11_TEST_PARSE_EQUAL_VAT(parse_inline_table<toml::value>, "{type.name = \"pug\"}", t);
}
}

View File

@@ -1,11 +1,6 @@
#define BOOST_TEST_MODULE "parse_integer_test"
#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/parser.hpp>
#include "unit_test.hpp"
#include "test_parse_aux.hpp"
using namespace toml;
@@ -89,4 +84,33 @@ BOOST_AUTO_TEST_CASE(test_bin_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "0b010000", value(16));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "0b01_00_00", value(16));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "0b111111", value(63));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"0b1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000_1000",
// 1 0 0 0
// 0 C 8 4
value(0x0888888888888888));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"0b01111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111",
// 1 0 0 0
// 0 C 8 4
value(0x7FFFFFFFFFFFFFFF));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"0b00000000_01111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111",
// 1 0 0 0
// 0 C 8 4
value(0x7FFFFFFFFFFFFFFF));
}
BOOST_AUTO_TEST_CASE(test_integer_overflow)
{
std::istringstream dec_overflow(std::string("dec-overflow = 9223372036854775808"));
std::istringstream hex_overflow(std::string("hex-overflow = 0x1_00000000_00000000"));
std::istringstream oct_overflow(std::string("oct-overflow = 0o1_000_000_000_000_000_000_000"));
// 64 56 48 40 32 24 16 8
std::istringstream bin_overflow(std::string("bin-overflow = 0b10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"));
BOOST_CHECK_THROW(toml::parse(dec_overflow), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(hex_overflow), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(oct_overflow), toml::syntax_error);
BOOST_CHECK_THROW(toml::parse(bin_overflow), toml::syntax_error);
}

View File

@@ -1,11 +1,6 @@
#define BOOST_TEST_MODULE "parse_key_test"
#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/parser.hpp>
#include "unit_test.hpp"
#include "test_parse_aux.hpp"
using namespace toml;

View File

@@ -1,11 +1,6 @@
#define BOOST_TEST_MODULE "parse_string_test"
#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/parser.hpp>
#include "unit_test.hpp"
#include "test_parse_aux.hpp"
using namespace toml;
@@ -29,16 +24,16 @@ BOOST_AUTO_TEST_CASE(test_string)
BOOST_AUTO_TEST_CASE(test_string_value)
{
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"The quick brown fox jumps over the lazy dog\"",
toml::value("The quick brown fox jumps over the lazy dog", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\'The quick brown fox jumps over the lazy dog\'",
toml::value("The quick brown fox jumps over the lazy dog", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"",
toml::value("The quick brown fox jumps over the lazy dog", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''The quick brown fox \njumps over the lazy dog'''",
toml::value("The quick brown fox \njumps over the lazy dog", string_t::literal));
}
@@ -69,14 +64,17 @@ BOOST_AUTO_TEST_CASE(test_basic_string)
TOML11_TEST_PARSE_EQUAL(parse_string,
"\" And when \\\"'s are in the along with # \\\"\"",
string(" And when \"'s are in the along with # \"", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"Here are fifteen apostrophes: '''''''''''''''\"",
string("Here are fifteen apostrophes: '''''''''''''''", string_t::basic));
}
BOOST_AUTO_TEST_CASE(test_basic_string_value)
{
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"",
value("GitHub Cofounder & CEO\nLikes tater tots and beer.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"192.168.1.1\"",
value("192.168.1.1", string_t::basic));
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
@@ -84,16 +82,19 @@ BOOST_AUTO_TEST_CASE(test_basic_string_value)
"\"\xE4\xB8\xAD\xE5\x9B\xBD\"",
value("\xE4\xB8\xAD\xE5\x9B\xBD", string_t::basic));
#else
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"中国\"",
value("中国", string_t::basic));
#endif
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"You'll hate me after this - #\"",
value("You'll hate me after this - #", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\" And when \\\"'s are in the along with # \\\"\"",
value(" And when \"'s are in the along with # \"", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"Here are fifteen apostrophes: '''''''''''''''\"",
value("Here are fifteen apostrophes: '''''''''''''''", string_t::basic));
}
BOOST_AUTO_TEST_CASE(test_ml_basic_string)
@@ -104,16 +105,41 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string)
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"",
string("The quick brown fox jumps over the lazy dog.", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"",
string("Here are two quotation marks: \"\". Simple enough.", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
string("Here are three quotation marks: \"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
string("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"",
string("\"This,\" she said, \"is just a pointless statement.\"", string_t::basic));
}
BOOST_AUTO_TEST_CASE(test_ml_basic_string_value)
{
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"",
value("The quick brown fox jumps over the lazy dog.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"",
value("The quick brown fox jumps over the lazy dog.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"",
value("Here are two quotation marks: \"\". Simple enough.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
value("Here are three quotation marks: \"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
value("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"",
value("\"This,\" she said, \"is just a pointless statement.\"", string_t::basic));
}
BOOST_AUTO_TEST_CASE(test_literal_string)
@@ -134,16 +160,16 @@ BOOST_AUTO_TEST_CASE(test_literal_string)
BOOST_AUTO_TEST_CASE(test_literal_string_value)
{
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'C:\\Users\\nodejs\\templates'",
value("C:\\Users\\nodejs\\templates", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'\\\\ServerX\\admin$\\system32\\'",
value("\\\\ServerX\\admin$\\system32\\", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'Tom \"Dubs\" Preston-Werner'",
value("Tom \"Dubs\" Preston-Werner", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'<\\i\\c*\\s*>'",
value("<\\i\\c*\\s*>", string_t::literal));
}
@@ -156,18 +182,49 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string)
TOML11_TEST_PARSE_EQUAL(parse_string,
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''",
string("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal));
TOML11_TEST_PARSE_EQUAL(parse_string,
"''''That's still pointless', she said.'''",
string("'That's still pointless', she said.", string_t::literal));
TOML11_TEST_PARSE_EQUAL(parse_string,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
string("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::literal));
TOML11_TEST_PARSE_EQUAL(parse_string,
"''''This,' she said, 'is just a pointless statement.''''",
string("'This,' she said, 'is just a pointless statement.'", string_t::literal));
}
BOOST_AUTO_TEST_CASE(test_ml_literal_string_value)
{
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''I [dw]on't need \\d{2} apples'''",
value("I [dw]on't need \\d{2} apples", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''",
value("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"''''That's still pointless', she said.'''",
value("'That's still pointless', she said.", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
value("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"''''This,' she said, 'is just a pointless statement.''''",
value("'This,' she said, 'is just a pointless statement.'", string_t::literal));
}
BOOST_AUTO_TEST_CASE(test_simple_excape_sequences)
{
TOML11_TEST_PARSE_EQUAL(parse_string,
R"("\"\\\b\f\n\r\t")",
string("\"\\\b\f\n\r\t", string_t::basic));
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
TOML11_TEST_PARSE_EQUAL(parse_string,
R"("\e")",
string("\x1b", string_t::basic));
#endif
}
BOOST_AUTO_TEST_CASE(test_unicode_escape_sequence)
{
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)

View File

@@ -1,12 +1,7 @@
#define BOOST_TEST_MODULE "parse_table_test"
#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/parser.hpp>
#include <toml/get.hpp>
#include <toml/parser.hpp>
#include "unit_test.hpp"
#include "test_parse_aux.hpp"
using namespace toml;
@@ -19,7 +14,7 @@ BOOST_AUTO_TEST_CASE(test_normal_table)
"key2 = 42\n"
"key3 = 3.14\n"
);
location<std::string> loc("test", table);
location loc("test", table);
const auto result = toml::detail::parse_ml_table<toml::value>(loc);
BOOST_TEST(result.is_ok());
@@ -36,7 +31,7 @@ BOOST_AUTO_TEST_CASE(test_nested_table)
"a.b = \"value\"\n"
"a.c.d = 42\n"
);
location<std::string> loc("test", table);
location loc("test", table);
const auto result = toml::detail::parse_ml_table<toml::value>(loc);
BOOST_TEST(result.is_ok());

View File

@@ -1,11 +1,6 @@
#define BOOST_TEST_MODULE "parse_table_key_test"
#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/parser.hpp>
#include "unit_test.hpp"
#include "test_parse_aux.hpp"
using namespace toml;

View File

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

View File

@@ -1,8 +1,9 @@
#define BOOST_TEST_MODULE "test_result"
#include <boost/test/unit_test.hpp>
#include <iostream>
#include <toml/result.hpp>
#include "unit_test.hpp"
#include <iostream>
BOOST_AUTO_TEST_CASE(test_construct)
{
{
@@ -437,5 +438,3 @@ BOOST_AUTO_TEST_CASE(test_and_or_other)
BOOST_TEST("foo" == r1_gen().and_other(r2_gen()).unwrap_err());
}
}

View File

@@ -1,15 +1,14 @@
#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 "unit_test.hpp"
#include <deque>
#include <map>
#include <iostream>
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <clocale>
template<typename Comment,
template<typename ...> class Table,
@@ -46,7 +45,7 @@ bool has_comment_inside(const toml::basic_value<Comment, Table, Array>& v)
BOOST_AUTO_TEST_CASE(test_example)
{
const auto data = toml::parse("toml/tests/example.toml");
const auto data = toml::parse(testinput("example.toml"));
{
std::ofstream ofs("tmp1.toml");
ofs << std::setw(80) << data;
@@ -68,7 +67,7 @@ BOOST_AUTO_TEST_CASE(test_example)
BOOST_AUTO_TEST_CASE(test_example_map_dq)
{
const auto data = toml::parse<toml::discard_comments, std::map, std::deque>(
"toml/tests/example.toml");
testinput("example.toml"));
{
std::ofstream ofs("tmp1_map_dq.toml");
ofs << std::setw(80) << data;
@@ -90,7 +89,7 @@ BOOST_AUTO_TEST_CASE(test_example_map_dq)
BOOST_AUTO_TEST_CASE(test_example_with_comment)
{
const auto data = toml::parse<toml::preserve_comments>("toml/tests/example.toml");
const auto data = toml::parse<toml::preserve_comments>(testinput("example.toml"));
{
std::ofstream ofs("tmp1_com.toml");
ofs << std::setw(80) << data;
@@ -116,7 +115,7 @@ BOOST_AUTO_TEST_CASE(test_example_with_comment)
BOOST_AUTO_TEST_CASE(test_example_with_comment_nocomment)
{
{
const auto data = toml::parse<toml::preserve_comments>("toml/tests/example.toml");
const auto data = toml::parse<toml::preserve_comments>(testinput("example.toml"));
{
std::ofstream ofs("tmp1_com_nocomment.toml");
ofs << std::setw(80) << toml::nocomment << data;
@@ -126,8 +125,8 @@ BOOST_AUTO_TEST_CASE(test_example_with_comment_nocomment)
BOOST_TEST(!has_comment_inside(serialized));
}
{
const auto data_nocomment = toml::parse("toml/tests/example.toml");
auto serialized = toml::parse("tmp1_com_nocomment.toml");
const auto data_nocomment = toml::parse<toml::discard_comments>(testinput("example.toml"));
auto serialized = toml::parse<toml::discard_comments>("tmp1_com_nocomment.toml");
{
auto& owner = toml::find(serialized, "owner");
auto& bio = toml::find<std::string>(owner, "bio");
@@ -145,7 +144,7 @@ BOOST_AUTO_TEST_CASE(test_example_with_comment_nocomment)
BOOST_AUTO_TEST_CASE(test_example_with_comment_map_dq)
{
const auto data = toml::parse<toml::preserve_comments, std::map, std::deque>(
"toml/tests/example.toml");
testinput("example.toml"));
{
std::ofstream ofs("tmp1_com_map_dq.toml");
ofs << std::setw(80) << data;
@@ -172,7 +171,7 @@ BOOST_AUTO_TEST_CASE(test_example_with_comment_map_dq)
BOOST_AUTO_TEST_CASE(test_example_with_comment_map_dq_nocomment)
{
{
const auto data = toml::parse<toml::preserve_comments, std::map, std::deque>("toml/tests/example.toml");
const auto data = toml::parse<toml::preserve_comments, std::map, std::deque>(testinput("example.toml"));
{
std::ofstream ofs("tmp1_com_map_dq_nocomment.toml");
ofs << std::setw(80) << toml::nocomment << data;
@@ -181,8 +180,8 @@ BOOST_AUTO_TEST_CASE(test_example_with_comment_map_dq_nocomment)
BOOST_TEST(!has_comment_inside(serialized));
}
{
const auto data_nocomment = toml::parse("toml/tests/example.toml");
auto serialized = toml::parse("tmp1_com_map_dq_nocomment.toml");
const auto data_nocomment = toml::parse<toml::discard_comments>(testinput("example.toml"));
auto serialized = toml::parse<toml::discard_comments>("tmp1_com_map_dq_nocomment.toml");
{
auto& owner = toml::find(serialized, "owner");
auto& bio = toml::find<std::string>(owner, "bio");
@@ -198,7 +197,7 @@ BOOST_AUTO_TEST_CASE(test_example_with_comment_map_dq_nocomment)
BOOST_AUTO_TEST_CASE(test_fruit)
{
const auto data = toml::parse("toml/tests/fruit.toml");
const auto data = toml::parse(testinput("fruit.toml"));
{
std::ofstream ofs("tmp2.toml");
ofs << std::setw(80) << data;
@@ -210,7 +209,7 @@ BOOST_AUTO_TEST_CASE(test_fruit)
BOOST_AUTO_TEST_CASE(test_fruit_map_dq)
{
const auto data = toml::parse<toml::discard_comments, std::map, std::deque>(
"toml/tests/fruit.toml");
testinput("fruit.toml"));
{
std::ofstream ofs("tmp2.toml");
ofs << std::setw(80) << data;
@@ -222,7 +221,7 @@ BOOST_AUTO_TEST_CASE(test_fruit_map_dq)
BOOST_AUTO_TEST_CASE(test_fruit_with_comments)
{
const auto data = toml::parse<toml::preserve_comments>("toml/tests/fruit.toml");
const auto data = toml::parse<toml::preserve_comments>(testinput("fruit.toml"));
{
std::ofstream ofs("tmp2_com.toml");
ofs << std::setw(80) << data;
@@ -234,7 +233,7 @@ BOOST_AUTO_TEST_CASE(test_fruit_with_comments)
BOOST_AUTO_TEST_CASE(test_fruit_with_comments_map_dq)
{
const auto data = toml::parse<toml::preserve_comments, std::map, std::deque>(
"toml/tests/fruit.toml");
testinput("fruit.toml"));
{
std::ofstream ofs("tmp2_com.toml");
ofs << std::setw(80) << data;
@@ -245,7 +244,7 @@ BOOST_AUTO_TEST_CASE(test_fruit_with_comments_map_dq)
BOOST_AUTO_TEST_CASE(test_hard_example)
{
const auto data = toml::parse("toml/tests/hard_example.toml");
const auto data = toml::parse(testinput("hard_example.toml"));
{
std::ofstream ofs("tmp3.toml");
ofs << std::setw(80) << data;
@@ -257,7 +256,7 @@ BOOST_AUTO_TEST_CASE(test_hard_example)
BOOST_AUTO_TEST_CASE(test_hard_example_map_dq)
{
const auto data = toml::parse<toml::discard_comments, std::map, std::deque>(
"toml/tests/hard_example.toml");
testinput("hard_example.toml"));
{
std::ofstream ofs("tmp3.toml");
ofs << std::setw(80) << data;
@@ -270,7 +269,7 @@ BOOST_AUTO_TEST_CASE(test_hard_example_map_dq)
BOOST_AUTO_TEST_CASE(test_hard_example_with_comment)
{
const auto data = toml::parse<toml::preserve_comments, std::map, std::deque>(
"toml/tests/hard_example.toml");
testinput("hard_example.toml"));
{
std::ofstream ofs("tmp3_com.toml");
ofs << std::setw(80) << data;
@@ -303,3 +302,104 @@ BOOST_AUTO_TEST_CASE(test_format_key)
BOOST_TEST("\"special-chars-\\\\-\\\"-\\b-\\f-\\r-\\n-\\t\"" == toml::format_key(key));
}
}
// In toml11, an implicitly-defined value does not have any comments.
// So, in the following file,
// ```toml
// # comment
// [[array-of-tables]]
// foo = "bar"
// ```
// The array named "array-of-tables" does not have the comment, but the first
// element of the array has. That means that, the above file is equivalent to
// the following.
// ```toml
// array-of-tables = [
// # comment
// {foo = "bar"},
// ]
// ```
// If the array itself has a comment (value_has_comment_ == true), we should try
// to make it inline.
// ```toml
// # comment about array
// array-of-tables = [
// # comment about table element
// {foo = "bar"}
// ]
// ```
// If it is formatted as a multiline table, the two comments becomes
// indistinguishable.
// ```toml
// # comment about array
// # comment about table element
// [[array-of-tables]]
// foo = "bar"
// ```
// So we need to try to make it inline, and it force-inlines regardless
// of the line width limit.
// It may fail if the element of a table has comment. In that case,
// the array-of-tables will be formatted as a multiline table.
BOOST_AUTO_TEST_CASE(test_distinguish_comment)
{
const std::string str = R"(# comment about array itself
array_of_table = [
# comment about the first element (table)
{key = "value"},
])";
std::istringstream iss(str);
const auto data = toml::parse<toml::preserve_comments>(iss);
const auto serialized = toml::format(data, /*width = */ 0);
std::istringstream reparse(serialized);
const auto parsed = toml::parse<toml::preserve_comments>(reparse);
BOOST_TEST(parsed.at("array_of_table").comments().size() == 1u);
BOOST_TEST(parsed.at("array_of_table").comments().front() == " comment about array itself");
BOOST_TEST(parsed.at("array_of_table").at(0).comments().size() == 1u);
BOOST_TEST(parsed.at("array_of_table").at(0).comments().front() == " comment about the first element (table)");
}
BOOST_AUTO_TEST_CASE(test_serialize_under_locale)
{
// avoid null init (setlocale returns null when it failed)
std::string setloc(std::setlocale(LC_ALL, nullptr));
// fr_FR is a one of locales that uses `,` as a decimal separator.
if(const char* try_hyphen = std::setlocale(LC_ALL, "fr_FR.UTF-8"))
{
setloc = std::string(try_hyphen);
}
else if(const char* try_nohyphen = std::setlocale(LC_ALL, "fr_FR.utf8"))
{
setloc = std::string(try_nohyphen);
}
// In some envs, fr_FR locale has not been installed. Tests must work even in such a case.
// else
// {
// BOOST_TEST(false);
// }
BOOST_TEST_MESSAGE("current locale at the beginning of the test = " << setloc);
const std::string str = R"(
pi = 3.14159
large_int = 1234567890
)";
std::istringstream iss(str);
const auto ref = toml::parse(iss);
const auto serialized_str = toml::format(ref, /*width = */ 80);
BOOST_TEST_MESSAGE("serialized = " << serialized_str);
std::istringstream serialized_iss(serialized_str);
const auto serialized_ref = toml::parse(serialized_iss);
BOOST_TEST(serialized_ref.at("pi").as_floating() == ref.at("pi").as_floating());
BOOST_TEST(serialized_ref.at("large_int").as_integer() == ref.at("large_int").as_integer());
const std::string endloc(std::setlocale(LC_ALL, nullptr));
BOOST_TEST_MESSAGE("current locale at the end of the test = " << endloc);
// check if serializer change global locale
BOOST_TEST(setloc == endloc);
}

View File

@@ -1,7 +1,7 @@
#define BOOST_TEST_MODULE "test_string"
#include <boost/test/unit_test.hpp>
#include <toml.hpp>
#include "unit_test.hpp"
BOOST_AUTO_TEST_CASE(test_basic_string)
{
{
@@ -133,7 +133,7 @@ BOOST_AUTO_TEST_CASE(test_string_add_assign)
str += str2;
BOOST_TEST(str.str == "foobar");
}
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
// std::string_view
{
toml::string str("foo");
@@ -151,4 +151,3 @@ BOOST_AUTO_TEST_CASE(test_string_add_assign)
}

View File

@@ -1,21 +1,16 @@
#define BOOST_TEST_MODULE "test_traits"
#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/types.hpp>
#include <list>
#include <forward_list>
#include <deque>
#include "unit_test.hpp"
#include <array>
#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <string>
struct dummy_type{};

View File

@@ -1,35 +1,28 @@
#define BOOST_TEST_MODULE "test_acceptor"
#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/utility.hpp>
#include <vector>
#include <array>
BOOST_AUTO_TEST_CASE(test_resize)
#include "unit_test.hpp"
#include <array>
#include <vector>
BOOST_AUTO_TEST_CASE(test_try_reserve)
{
{
typedef std::vector<int> resizable_type;
typedef std::array<int,1> non_resizable_type;
BOOST_TEST(toml::detail::has_resize_method<resizable_type>::value);
BOOST_TEST(!toml::detail::has_resize_method<non_resizable_type>::value);
// since BOOST_TEST is a macro, it cannot handle commas correctly.
// When toml::detail::has_reserve_method<std::array<int, 1>>::value
// is passed to a macro, C preprocessor considers
// toml::detail::has_reserve_method<std::array<int as the first argument
// and 1>>::value as the second argument. We need an alias to avoid
// this problem.
using reservable_type = std::vector<int> ;
using nonreservable_type = std::array<int, 1>;
BOOST_TEST( toml::detail::has_reserve_method<reservable_type >::value);
BOOST_TEST(!toml::detail::has_reserve_method<nonreservable_type>::value);
}
{
std::vector<int> v;
toml::resize(v, 10);
BOOST_TEST(v.size() == 10u);
}
{
std::array<int, 15> a;
toml::resize(a, 10);
BOOST_TEST(a.size() == 15u);
}
{
std::array<int, 15> a;
BOOST_CHECK_THROW(toml::resize(a, 20), std::invalid_argument);
toml::try_reserve(v, 100);
BOOST_TEST(v.capacity() == 100u);
}
}

View File

@@ -1,15 +1,11 @@
#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>
#include "unit_test.hpp"
#include <map>
#include <list>
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#include <string_view>
#endif
@@ -423,7 +419,7 @@ BOOST_AUTO_TEST_CASE(test_value_string)
BOOST_TEST(v2.as_boolean() == true);
BOOST_TEST(v3.as_boolean() == true);
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
std::string_view sv = "foo";
toml::value v7(sv);
@@ -966,3 +962,54 @@ BOOST_AUTO_TEST_CASE(test_value_bracket)
BOOST_CHECK_THROW(v1["foo"], toml::type_error);
}
}
BOOST_AUTO_TEST_CASE(test_value_map_methods)
{
{
toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}};
BOOST_TEST(v1.count("foo") == 1u);
BOOST_TEST(v1.count("bar") == 1u);
BOOST_TEST(v1.count("baz") == 1u);
BOOST_TEST(v1.count("qux") == 0u);
BOOST_TEST( v1.contains("foo"));
BOOST_TEST( v1.contains("bar"));
BOOST_TEST( v1.contains("baz"));
BOOST_TEST(!v1.contains("qux"));
BOOST_TEST(v1.size() == 3);
v1["qux"] = 54;
BOOST_TEST(v1.count("qux") == 1u);
BOOST_TEST(v1.contains("qux"));
BOOST_TEST(v1.size() == 4);
}
{
toml::value v1(42);
BOOST_CHECK_THROW(v1.size() , toml::type_error);
BOOST_CHECK_THROW(v1.count("k") , toml::type_error);
BOOST_CHECK_THROW(v1.contains("k"), toml::type_error);
}
}
BOOST_AUTO_TEST_CASE(test_value_vector_methods)
{
{
toml::value v1{1, 2, 3, 4, 5};
BOOST_TEST(v1.size() == 5);
v1.push_back(6);
BOOST_TEST(v1.size() == 6);
v1.emplace_back(6);
BOOST_TEST(v1.size() == 7);
}
{
toml::value v1(42);
BOOST_CHECK_THROW(v1.size(), toml::type_error);
BOOST_CHECK_THROW(v1.push_back(1), toml::type_error);
BOOST_CHECK_THROW(v1.emplace_back(1), toml::type_error);
}
}

12
tests/test_windows.cpp Normal file
View File

@@ -0,0 +1,12 @@
#include <windows.h>
#include <toml.hpp>
#include <iostream>
int main()
{
using namespace toml::literals::toml_literals;
const auto data = R"(windows = "defines min and max as a macro")"_toml;
std::cout << toml::find<std::string>(data, "windows") << std::endl;
return 0;
}

23
tests/unit_test.hpp Normal file
View File

@@ -0,0 +1,23 @@
#ifndef BOOST_TEST_MODULE
# error "Please #define BOOST_TEST_MODULE before you #include <unit_test.hpp>"
#endif
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
# include <boost/test/unit_test.hpp>
#else
# include <boost/test/included/unit_test.hpp>
#endif
#include <cstdlib>
#include <string>
static inline auto testinput(const std::string& basename) -> std::string
{
const auto this_or_that = [](const char *const s, const char *const t) { return s ? s : t; };
std::string directory = this_or_that(std::getenv("TOMLDIR"), "toml");
if (!directory.empty() && directory.back() != '/')
{
directory.push_back('/');
}
return directory.append("tests/").append(basename);
}

View File

@@ -25,17 +25,14 @@
#ifndef TOML_FOR_MODERN_CPP
#define TOML_FOR_MODERN_CPP
#ifndef __cplusplus
# error "__cplusplus is not defined"
#endif
#if __cplusplus < 201103L && _MSC_VER < 1900
# error "toml11 requires C++11 or later."
#endif
#define TOML11_VERSION_MAJOR 3
#define TOML11_VERSION_MINOR 8
#define TOML11_VERSION_PATCH 1
#include "toml/parser.hpp"
#include "toml/literal.hpp"
#include "toml/serializer.hpp"
#include "toml/get.hpp"
#include "toml/macros.hpp"
#endif// TOML_FOR_MODERN_CPP

View File

@@ -1,7 +1,7 @@
#ifndef TOML11_COLOR_HPP
#define TOML11_COLOR_HPP
#include <ostream>
#include <cstdint>
#include <ostream>
#ifdef TOML11_COLORIZE_ERROR_MESSAGE
#define TOML11_ERROR_MESSAGE_COLORIZED true
@@ -17,11 +17,41 @@ namespace color_ansi
{
namespace detail
{
inline int colorize_index()
{
static const int index = std::ios_base::xalloc();
return index;
}
// Control color mode globally
class color_mode
{
public:
inline void enable()
{
should_color_ = true;
}
inline void disable()
{
should_color_ = false;
}
inline bool should_color() const
{
return should_color_;
}
static color_mode& status()
{
static color_mode status_;
return status_;
}
private:
bool should_color_ = false;
};
} // detail
inline std::ostream& colorize(std::ostream& os)
@@ -55,6 +85,21 @@ inline std::ostream& cyan (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;}
inline std::ostream& white (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;}
inline void enable()
{
return detail::color_mode::status().enable();
}
inline void disable()
{
return detail::color_mode::status().disable();
}
inline bool should_color()
{
return detail::color_mode::status().should_color();
}
} // color_ansi
// ANSI escape sequence is the only and default colorization method currently

View File

@@ -2,18 +2,20 @@
// Distributed under the MIT License.
#ifndef TOML11_COMBINATOR_HPP
#define TOML11_COMBINATOR_HPP
#include "traits.hpp"
#include "result.hpp"
#include "utility.hpp"
#include "region.hpp"
#include <type_traits>
#include <iterator>
#include <limits>
#include <array>
#include <iomanip>
#include <cstdio>
#include <cassert>
#include <cctype>
#include <cstdio>
#include <array>
#include <iomanip>
#include <iterator>
#include <limits>
#include <type_traits>
#include "region.hpp"
#include "result.hpp"
#include "traits.hpp"
#include "utility.hpp"
// they scans characters and returns region if it matches to the condition.
// when they fail, it does not change the location.
@@ -27,7 +29,7 @@ namespace detail
// to output character as an error message.
inline std::string show_char(const char c)
{
// It supress an error that occurs only in Debug mode of MSVC++ on Windows.
// It suppresses an error that occurs only in Debug mode of MSVC++ on Windows.
// I'm not completely sure but they check the value of char to be in the
// range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes
// has negative value (if char has sign). So here it re-interprets c as
@@ -56,13 +58,9 @@ struct character
{
static constexpr char target = C;
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter();
@@ -73,7 +71,7 @@ struct character
}
loc.advance(); // update location
return ok(region<Cont>(loc, first, loc.iter()));
return ok(region(loc, first, loc.iter()));
}
};
template<char C>
@@ -89,13 +87,9 @@ struct in_range
static constexpr char upper = Up;
static constexpr char lower = Low;
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return none();}
const auto first = loc.iter();
@@ -106,7 +100,7 @@ struct in_range
}
loc.advance();
return ok(region<Cont>(loc, first, loc.iter()));
return ok(region(loc, first, loc.iter()));
}
};
template<char L, char U> constexpr char in_range<L, U>::upper;
@@ -117,13 +111,9 @@ template<char L, char U> constexpr char in_range<L, U>::lower;
template<typename Combinator>
struct exclude
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
if(loc.iter() == loc.end()) {return none();}
auto first = loc.iter();
@@ -134,7 +124,7 @@ struct exclude
return none();
}
loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
return ok(region<Cont>(loc, first, loc.iter()));
return ok(region(loc, first, loc.iter()));
}
};
@@ -142,19 +132,15 @@ struct exclude
template<typename Combinator>
struct maybe
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
const auto rslt = Combinator::invoke(loc);
if(rslt.is_ok())
{
return rslt;
}
return ok(region<Cont>(loc));
return ok(region(loc));
}
};
@@ -164,15 +150,11 @@ struct sequence;
template<typename Head, typename ... Tail>
struct sequence<Head, Tail...>
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
const auto first = loc.iter();
const auto rslt = Head::invoke(loc);
auto rslt = Head::invoke(loc);
if(rslt.is_err())
{
loc.reset(first);
@@ -182,9 +164,9 @@ struct sequence<Head, Tail...>
}
// called from the above function only, recursively.
template<typename Cont, typename Iterator>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc, region<Cont> reg, Iterator first)
template<typename Iterator>
static result<region, none_t>
invoke(location& loc, region reg, Iterator first)
{
const auto rslt = Head::invoke(loc);
if(rslt.is_err())
@@ -201,9 +183,9 @@ template<typename Head>
struct sequence<Head>
{
// would be called from sequence<T ...>::invoke only.
template<typename Cont, typename Iterator>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc, region<Cont> reg, Iterator first)
template<typename Iterator>
static result<region, none_t>
invoke(location& loc, region reg, Iterator first)
{
const auto rslt = Head::invoke(loc);
if(rslt.is_err())
@@ -222,13 +204,9 @@ struct either;
template<typename Head, typename ... Tail>
struct either<Head, Tail...>
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
const auto rslt = Head::invoke(loc);
if(rslt.is_ok()) {return rslt;}
return either<Tail...>::invoke(loc);
@@ -237,12 +215,9 @@ struct either<Head, Tail...>
template<typename Head>
struct either<Head>
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
static_assert(std::is_same<char, typename Cont::value_type>::value,
"internal error: container::value_type should be `char`.");
return Head::invoke(loc);
}
};
@@ -257,11 +232,10 @@ struct unlimited{};
template<typename T, std::size_t N>
struct repeat<T, exactly<N>>
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
region<Cont> retval(loc);
region retval(loc);
const auto first = loc.iter();
for(std::size_t i=0; i<N; ++i)
{
@@ -280,11 +254,10 @@ struct repeat<T, exactly<N>>
template<typename T, std::size_t N>
struct repeat<T, at_least<N>>
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
region<Cont> retval(loc);
region retval(loc);
const auto first = loc.iter();
for(std::size_t i=0; i<N; ++i)
@@ -312,11 +285,10 @@ struct repeat<T, at_least<N>>
template<typename T>
struct repeat<T, unlimited>
{
template<typename Cont>
static result<region<Cont>, none_t>
invoke(location<Cont>& loc)
static result<region, none_t>
invoke(location& loc)
{
region<Cont> retval(loc);
region retval(loc);
while(true)
{
auto rslt = T::invoke(loc);

View File

@@ -2,12 +2,19 @@
// Distributed under the MIT License.
#ifndef TOML11_COMMENTS_HPP
#define TOML11_COMMENTS_HPP
#include <initializer_list>
#include <iterator>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <iterator>
#include <initializer_list>
#include <vector>
#include <string>
#ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments
#else
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments
#endif
// This file provides mainly two classes, `preserve_comments` and `discard_comments`.
// Those two are a container that have the same interface as `std::vector<std::string>`
@@ -81,6 +88,54 @@ struct preserve_comments
void assign(std::initializer_list<std::string> ini) {comments.assign(ini);}
void assign(size_type n, const std::string& val) {comments.assign(n, val);}
// Related to the issue #97.
//
// It is known that `std::vector::insert` and `std::vector::erase` in
// the standard library implementation included in GCC 4.8.5 takes
// `std::vector::iterator` instead of `std::vector::const_iterator`.
// Because of the const-correctness, we cannot convert a `const_iterator` to
// an `iterator`. It causes compilation error in GCC 4.8.5.
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__)
# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805
# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
# endif
#endif
#ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
iterator insert(iterator p, const std::string& x)
{
return comments.insert(p, x);
}
iterator insert(iterator p, std::string&& x)
{
return comments.insert(p, std::move(x));
}
void insert(iterator p, size_type n, const std::string& x)
{
return comments.insert(p, n, x);
}
template<typename InputIterator>
void insert(iterator p, InputIterator first, InputIterator last)
{
return comments.insert(p, first, last);
}
void insert(iterator p, std::initializer_list<std::string> ini)
{
return comments.insert(p, ini);
}
template<typename ... Ts>
iterator emplace(iterator p, Ts&& ... args)
{
return comments.emplace(p, std::forward<Ts>(args)...);
}
iterator erase(iterator pos) {return comments.erase(pos);}
iterator erase(iterator first, iterator last)
{
return comments.erase(first, last);
}
#else
iterator insert(const_iterator p, const std::string& x)
{
return comments.insert(p, x);
@@ -114,6 +169,7 @@ struct preserve_comments
{
return comments.erase(first, last);
}
#endif
void swap(preserve_comments& other) {comments.swap(other.comments);}
@@ -290,7 +346,7 @@ operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::differ
//
// Why this is chose as the default type is because the last version (2.x.y)
// does not contain any comments in a value. To minimize the impact on the
// efficiency, this is choosed as a default.
// efficiency, this is chosen as a default.
//
// To reduce the memory footprint, later we can try empty base optimization (EBO).
struct discard_comments
@@ -369,14 +425,14 @@ struct discard_comments
// empty, so accessing through operator[], front/back, data causes address
// error.
reference operator[](const size_type) noexcept {return *data();}
const_reference operator[](const size_type) const noexcept {return *data();}
reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");}
const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");}
reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");}
const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");}
reference front() noexcept {return *data();}
const_reference front() const noexcept {return *data();}
reference back() noexcept {return *data();}
const_reference back() const noexcept {return *data();}
reference front() noexcept {never_call("toml::discard_comment::front");}
const_reference front() const noexcept {never_call("toml::discard_comment::front");}
reference back() noexcept {never_call("toml::discard_comment::back");}
const_reference back() const noexcept {never_call("toml::discard_comment::back");}
pointer data() noexcept {return nullptr;}
const_pointer data() const noexcept {return nullptr;}
@@ -394,6 +450,18 @@ struct discard_comments
const_reverse_iterator rend() const noexcept {return const_iterator{};}
const_reverse_iterator crbegin() const noexcept {return const_iterator{};}
const_reverse_iterator crend() const noexcept {return const_iterator{};}
private:
[[noreturn]] static void never_call(const char *const this_function)
{
#ifdef __has_builtin
# if __has_builtin(__builtin_unreachable)
__builtin_unreachable();
# endif
#endif
throw std::logic_error{this_function};
}
};
inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;}

View File

@@ -2,15 +2,16 @@
// Distributed under the MIT License.
#ifndef TOML11_DATETIME_HPP
#define TOML11_DATETIME_HPP
#include <chrono>
#include <tuple>
#include <array>
#include <ostream>
#include <iomanip>
#include <cstdint>
#include <cstdlib>
#include <ctime>
#include <array>
#include <chrono>
#include <iomanip>
#include <ostream>
#include <tuple>
namespace toml
{
@@ -20,22 +21,7 @@ namespace toml
namespace detail
{
// TODO: find more sophisticated way to handle this
#if _POSIX_C_SOURCE >= 1 || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_r(src, &dst);
if (!result) { throw std::runtime_error("localtime_r failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_r(src, &dst);
if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst;
}
#elif _MSC_VER
#if defined(_MSC_VER)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
@@ -50,6 +36,21 @@ inline std::tm gmtime_s(const std::time_t* src)
if (result) { throw std::runtime_error("gmtime_s failed."); }
return dst;
}
#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_r(src, &dst);
if (!result) { throw std::runtime_error("localtime_r failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_r(src, &dst);
if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst;
}
#else // fallback. not threadsafe
inline std::tm localtime_s(const std::time_t* src)
{
@@ -84,9 +85,9 @@ enum class month_t : std::uint8_t
struct local_date
{
std::int16_t year; // A.D. (like, 2018)
std::uint8_t month; // [0, 11]
std::uint8_t day; // [1, 31]
std::int16_t year{}; // A.D. (like, 2018)
std::uint8_t month{}; // [0, 11]
std::uint8_t day{}; // [1, 31]
local_date(int y, month_t m, int d)
: year (static_cast<std::int16_t>(y)),
@@ -172,20 +173,20 @@ template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const local_date& date)
{
os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month + 1) << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day );
os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month) + 1 << '-';
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day ) ;
return os;
}
struct local_time
{
std::uint8_t hour; // [0, 23]
std::uint8_t minute; // [0, 59]
std::uint8_t second; // [0, 60]
std::uint16_t millisecond; // [0, 999]
std::uint16_t microsecond; // [0, 999]
std::uint16_t nanosecond; // [0, 999]
std::uint8_t hour{}; // [0, 23]
std::uint8_t minute{}; // [0, 59]
std::uint8_t second{}; // [0, 60]
std::uint16_t millisecond{}; // [0, 999]
std::uint16_t microsecond{}; // [0, 999]
std::uint16_t nanosecond{}; // [0, 999]
local_time(int h, int m, int s,
int ms = 0, int us = 0, int ns = 0)
@@ -296,8 +297,8 @@ operator<<(std::basic_ostream<charT, traits>& os, const local_time& time)
struct time_offset
{
std::int8_t hour; // [-12, 12]
std::int8_t minute; // [-59, 59]
std::int8_t hour{}; // [-12, 12]
std::int8_t minute{}; // [-59, 59]
time_offset(int h, int m)
: hour (static_cast<std::int8_t>(h)),
@@ -363,8 +364,8 @@ operator<<(std::basic_ostream<charT, traits>& os, const time_offset& offset)
struct local_datetime
{
local_date date;
local_time time;
local_date date{};
local_time time{};
local_datetime(local_date d, local_time t): date(d), time(t) {}
@@ -477,9 +478,9 @@ operator<<(std::basic_ostream<charT, traits>& os, const local_datetime& dt)
struct offset_datetime
{
local_date date;
local_time time;
time_offset offset;
local_date date{};
local_time time{};
time_offset offset{};
offset_datetime(local_date d, local_time t, time_offset o)
: date(d), time(t), offset(o)

View File

@@ -2,17 +2,36 @@
// Distributed under the MIT License.
#ifndef TOML11_EXCEPTION_HPP
#define TOML11_EXCEPTION_HPP
#include "source_location.hpp"
#include <stdexcept>
#include <array>
#include <string>
#include <stdexcept>
#include <cstring>
#include "source_location.hpp"
namespace toml
{
struct file_io_error : public std::runtime_error
{
public:
file_io_error(int errnum, const std::string& msg, const std::string& fname)
: std::runtime_error(msg + " \"" + fname + "\": errno = " + std::to_string(errnum)),
errno_(errnum)
{}
int get_errno() const noexcept {return errno_;}
private:
int errno_;
};
struct exception : public std::exception
{
public:
exception(const source_location& loc): loc_(loc) {}
explicit exception(const source_location& loc): loc_(loc) {}
virtual ~exception() noexcept override = default;
virtual const char* what() const noexcept override {return "";}
virtual source_location const& location() const noexcept {return loc_;}

View File

@@ -2,7 +2,6 @@
// Distributed under the MIT License.
#ifndef TOML11_FROM_HPP
#define TOML11_FROM_HPP
#include "traits.hpp"
namespace toml
{

View File

@@ -2,10 +2,11 @@
// Distributed under the MIT License.
#ifndef TOML11_GET_HPP
#define TOML11_GET_HPP
#include <algorithm>
#include "from.hpp"
#include "result.hpp"
#include "value.hpp"
#include <algorithm>
namespace toml
{
@@ -139,7 +140,7 @@ get(basic_value<C, M, V>&& v)
// ============================================================================
// std::string_view
#if __cplusplus >= 201703L
#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
inline detail::enable_if_t<std::is_same<T, std::string_view>::value, std::string_view>
@@ -188,8 +189,7 @@ get(const basic_value<C, M, V>& v)
{
throw type_error(detail::format_underline("toml::value: "
"bad_cast to std::chrono::system_clock::time_point", {
{std::addressof(detail::get_region(v)),
concat_to_string("the actual type is ", v.type())}
{v.location(), concat_to_string("the actual type is ", v.type())}
}), v.location());
}
}
@@ -198,24 +198,25 @@ get(const basic_value<C, M, V>& v)
// ============================================================================
// forward declaration to use this recursively. ignore this and go ahead.
// array-like type with resize(N) method
// array-like type with push_back(value) method
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is container
detail::has_resize_method<T>, // T::resize(N) works
detail::negation< // but not toml::array
detail::is_container<T>, // T is a container
detail::has_push_back_method<T>, // T::push_back(value) works
detail::negation< // but not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T>
get(const basic_value<C, M, V>&);
// array-like type with resize(N) method
// array-like type without push_back(value) method
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is container
detail::negation<detail::has_resize_method<T>>, // no T::resize() exists
detail::negation< // not toml::array
detail::is_container<T>, // T is a container
detail::negation<detail::has_push_back_method<T>>, // w/o push_back(...)
detail::negation<detail::has_specialized_from<T>>, // T does not have special conversion
detail::negation< // not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T>
get(const basic_value<C, M, V>&);
@@ -255,9 +256,36 @@ get(const basic_value<C, M, V>&);
// toml::from<T>::from_toml(v)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
std::size_t S = sizeof(::toml::from<T>)>
T get(const basic_value<C, M, V>&);
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
get(const basic_value<C, M, V>&);
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
get(basic_value<C, M, V>&);
// T(const toml::value&) and T is not toml::basic_value,
// and it does not have `from<T>` nor `from_toml`.
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, const basic_value<C, M, V>&>,
detail::negation<detail::has_from_toml_method<T, C, M, V>>,
detail::negation<detail::has_specialized_from<T>>
>::value, T>
get(const basic_value<C, M, V>&);
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, basic_value<C, M, V>&>,
detail::negation<detail::has_from_toml_method<T, C, M, V>>,
detail::negation<detail::has_specialized_from<T>>
>::value, T>
get(basic_value<C, M, V>&);
// ============================================================================
// array-like types; most likely STL container, like std::vector, etc.
@@ -265,31 +293,55 @@ T get(const basic_value<C, M, V>&);
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is container
detail::has_resize_method<T>, // T::resize(N) works
detail::negation< // but not toml::array
detail::is_container<T>, // T is a container
detail::has_push_back_method<T>, // container.push_back(elem) works
detail::negation< // but not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T>
get(const basic_value<C, M, V>& v)
{
using value_type = typename T::value_type;
const auto& ar = v.as_array();
const auto& ary = v.as_array();
T container;
container.resize(ar.size());
std::transform(ar.cbegin(), ar.cend(), container.begin(),
[](const value& x){return ::toml::get<value_type>(x);});
try_reserve(container, ary.size());
for(const auto& elem : ary)
{
container.push_back(get<value_type>(elem));
}
return container;
}
// ============================================================================
// array-like types; but does not have resize(); most likely std::array.
// std::forward_list does not have push_back, insert, or emplace.
// It has insert_after, emplace_after, push_front.
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::is_std_forward_list<T>::value, T>
get(const basic_value<C, M, V>& v)
{
using value_type = typename T::value_type;
T container;
for(const auto& elem : v.as_array())
{
container.push_front(get<value_type>(elem));
}
container.reverse();
return container;
}
// ============================================================================
// array-like types, without push_back(). most likely [std|boost]::array.
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is container
detail::negation<detail::has_resize_method<T>>, // no T::resize() exists
detail::negation< // but not toml::array
detail::is_container<T>, // T is a container
detail::negation<detail::has_push_back_method<T>>, // w/o push_back
detail::negation<detail::has_specialized_from<T>>, // T does not have special conversion
detail::negation< // T is not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T>
get(const basic_value<C, M, V>& v)
@@ -303,11 +355,13 @@ get(const basic_value<C, M, V>& v)
throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified container size is ", container.size(),
" but there are ", ar.size(), " elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
{v.location(), "here"}
}));
}
std::transform(ar.cbegin(), ar.cend(), container.begin(),
[](const value& x){return ::toml::get<value_type>(x);});
for(std::size_t i=0; i<ar.size(); ++i)
{
container[i] = ::toml::get<value_type>(ar[i]);
}
return container;
}
@@ -327,9 +381,7 @@ get(const basic_value<C, M, V>& v)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified std::pair but there are ", ar.size(),
" elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
}));
" elements in toml array."), {{v.location(), "here"}}));
}
return std::make_pair(::toml::get<first_type >(ar.at(0)),
::toml::get<second_type>(ar.at(1)));
@@ -359,9 +411,7 @@ get(const basic_value<C, M, V>& v)
throw std::out_of_range(detail::format_underline(concat_to_string(
"toml::get: specified std::tuple with ",
std::tuple_size<T>::value, " elements, but there are ", ar.size(),
" elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
}));
" elements in toml array."), {{v.location(), "here"}}));
}
return detail::get_tuple_impl<T>(ar,
detail::make_index_sequence<std::tuple_size<T>::value>{});
@@ -387,7 +437,7 @@ get(const basic_value<C, M, V>& v)
T map;
for(const auto& kv : v.as_table())
{
map[key_type(kv.first)] = ::toml::get<mapped_type>(kv.second);
map.emplace(key_type(kv.first), get<mapped_type>(kv.second));
}
return map;
}
@@ -410,12 +460,45 @@ get(const basic_value<C, M, V>& v)
return ud;
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
std::size_t>
T get(const basic_value<C, M, V>& v)
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
get(const basic_value<C, M, V>& v)
{
return ::toml::from<T>::from_toml(v);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
get(basic_value<C, M, V>& v)
{
return ::toml::from<T>::from_toml(v);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>, // T is not a toml::value
std::is_constructible<T, const basic_value<C, M, V>&>, // T is constructible from toml::value
detail::negation<detail::has_from_toml_method<T, C, M, V>>, // and T does not have T.from_toml(v);
detail::negation<detail::has_specialized_from<T>> // and T does not have toml::from<T>{};
>::value, T>
get(const basic_value<C, M, V>& v)
{
return T(v);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>, // T is not a toml::value
std::is_constructible<T, basic_value<C, M, V>&>, // T is constructible from toml::value
detail::negation<detail::has_from_toml_method<T, C, M, V>>, // and T does not have T.from_toml(v);
detail::negation<detail::has_specialized_from<T>> // and T does not have toml::from<T>{};
>::value, T>
get(basic_value<C, M, V>& v)
{
return T(v);
}
// ============================================================================
// find
@@ -429,10 +512,7 @@ basic_value<C, M, V> const& find(const basic_value<C, M, V>& v, const key& ky)
const auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
detail::throw_key_not_found_error(v, ky);
}
return tab.at(ky);
}
@@ -443,10 +523,7 @@ basic_value<C, M, V>& find(basic_value<C, M, V>& v, const key& ky)
auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
detail::throw_key_not_found_error(v, ky);
}
return tab.at(ky);
}
@@ -457,14 +534,51 @@ basic_value<C, M, V> find(basic_value<C, M, V>&& v, const key& ky)
typename basic_value<C, M, V>::table_type tab = std::move(v).as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
detail::throw_key_not_found_error(v, ky);
}
return basic_value<C, M, V>(std::move(tab.at(ky)));
}
// ----------------------------------------------------------------------------
// find(value, idx)
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> const&
find(const basic_value<C, M, V>& v, const std::size_t idx)
{
const auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ary.at(idx);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V>& find(basic_value<C, M, V>& v, const std::size_t idx)
{
auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ary.at(idx);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V>
basic_value<C, M, V> find(basic_value<C, M, V>&& v, const std::size_t idx)
{
auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return basic_value<C, M, V>(std::move(ary.at(idx)));
}
// ----------------------------------------------------------------------------
// find<T>(value, key);
@@ -476,10 +590,7 @@ find(const basic_value<C, M, V>& v, const key& ky)
const auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
detail::throw_key_not_found_error(v, ky);
}
return ::toml::get<T>(tab.at(ky));
}
@@ -492,10 +603,7 @@ find(basic_value<C, M, V>& v, const key& ky)
auto& tab = v.as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
detail::throw_key_not_found_error(v, ky);
}
return ::toml::get<T>(tab.at(ky));
}
@@ -508,65 +616,151 @@ find(basic_value<C, M, V>&& v, const key& ky)
typename basic_value<C, M, V>::table_type tab = std::move(v).as_table();
if(tab.count(ky) == 0)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"key \"", ky, "\" not found"), {
{std::addressof(detail::get_region(v)), "in this table"}
}));
detail::throw_key_not_found_error(v, ky);
}
return ::toml::get<T>(std::move(tab.at(ky)));
}
// ----------------------------------------------------------------------------
// find<T>(value, idx)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))
find(const basic_value<C, M, V>& v, const std::size_t idx)
{
const auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ::toml::get<T>(ary.at(idx));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
find(basic_value<C, M, V>& v, const std::size_t idx)
{
auto& ary = v.as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ::toml::get<T>(ary.at(idx));
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
find(basic_value<C, M, V>&& v, const std::size_t idx)
{
typename basic_value<C, M, V>::array_type ary = std::move(v).as_array();
if(ary.size() <= idx)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"index ", idx, " is out of range"), {{v.location(), "in this array"}}));
}
return ::toml::get<T>(std::move(ary.at(idx)));
}
// --------------------------------------------------------------------------
// toml::find(toml::value, toml::key, Ts&& ... keys)
namespace detail
{
// It suppresses warnings by -Wsign-conversion. Let's say we have the following
// code.
// ```cpp
// const auto x = toml::find<std::string>(data, "array", 0);
// ```
// Here, the type of literal number `0` is `int`. `int` is a signed integer.
// `toml::find` takes `std::size_t` as an index. So it causes implicit sign
// conversion and `-Wsign-conversion` warns about it. Using `0u` instead of `0`
// suppresses the warning, but it makes user code messy.
// To suppress this warning, we need to be aware of type conversion caused
// by `toml::find(v, key1, key2, ... keys)`. But the thing is that the types of
// keys can be any combination of {string-like, size_t-like}. Of course we can't
// write down all the combinations. Thus we need to use some function that
// recognize the type of argument and cast it into `std::string` or
// `std::size_t` depending on the context.
// `key_cast` does the job. It has 2 overloads. One is invoked when the
// argument type is an integer and cast the argument into `std::size_t`. The
// other is invoked when the argument type is not an integer, possibly one of
// std::string, const char[N] or const char*, and construct std::string from
// the argument.
// `toml::find(v, k1, k2, ... ks)` uses `key_cast` before passing `ks` to
// `toml::find(v, k)` to suppress -Wsign-conversion.
template<typename T>
enable_if_t<conjunction<std::is_integral<remove_cvref_t<T>>,
negation<std::is_same<remove_cvref_t<T>, bool>>>::value, std::size_t>
key_cast(T&& v) noexcept
{
return std::size_t(v);
}
template<typename T>
enable_if_t<negation<conjunction<std::is_integral<remove_cvref_t<T>>,
negation<std::is_same<remove_cvref_t<T>, bool>>>>::value, std::string>
key_cast(T&& v) noexcept
{
return std::string(std::forward<T>(v));
}
} // detail
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
typename Key1, typename Key2, typename ... Keys>
const basic_value<C, M, V>&
find(const basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
find(const basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find(::toml::find(v, ky), std::forward<Ts>(keys)...);
return ::toml::find(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
typename Key1, typename Key2, typename ... Keys>
basic_value<C, M, V>&
find(basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
find(basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find(::toml::find(v, ky), std::forward<Ts>(keys)...);
return ::toml::find(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
typename Key1, typename Key2, typename ... Keys>
basic_value<C, M, V>
find(basic_value<C, M, V>&& v, const ::toml::key& ky, Ts&& ... keys)
find(basic_value<C, M, V>&& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find(::toml::find(std::move(v), ky), std::forward<Ts>(keys)...);
return ::toml::find(::toml::find(std::move(v), std::forward<Key1>(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
typename Key1, typename Key2, typename ... Keys>
decltype(::toml::get<T>(std::declval<const basic_value<C, M, V>&>()))
find(const basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
find(const basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...);
return ::toml::find<T>(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
typename Key1, typename Key2, typename ... Keys>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
find(basic_value<C, M, V>& v, const ::toml::key& ky, Ts&& ... keys)
find(basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find<T>(::toml::find(v, ky), std::forward<Ts>(keys)...);
return ::toml::find<T>(::toml::find(v, detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
typename ... Ts>
typename Key1, typename Key2, typename ... Keys>
decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
find(basic_value<C, M, V>&& v, const ::toml::key& ky, Ts&& ... keys)
find(basic_value<C, M, V>&& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
{
return ::toml::find<T>(::toml::find(std::move(v), ky), std::forward<Ts>(keys)...);
return ::toml::find<T>(::toml::find(std::move(v), detail::key_cast(k1)),
detail::key_cast(k2), std::forward<Keys>(keys)...);
}
// ============================================================================
@@ -881,6 +1075,50 @@ find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// recursive find-or with type deduction (find_or(value, keys, opt))
template<typename Value, typename ... Ks,
typename detail::enable_if_t<(sizeof...(Ks) > 1), std::nullptr_t> = nullptr>
// here we need to add SFINAE in the template parameter to avoid
// infinite recursion in type deduction on gcc
auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys)
-> decltype(find_or(std::forward<Value>(v), ky, detail::last_one(std::forward<Ks>(keys)...)))
{
if(!v.is_table())
{
return detail::last_one(std::forward<Ks>(keys)...);
}
auto&& tab = std::forward<Value>(v).as_table();
if(tab.count(ky) == 0)
{
return detail::last_one(std::forward<Ks>(keys)...);
}
return find_or(std::forward<decltype(tab)>(tab).at(ky), std::forward<Ks>(keys)...);
}
// ---------------------------------------------------------------------------
// recursive find_or with explicit type specialization, find_or<int>(value, keys...)
template<typename T, typename Value, typename ... Ks,
typename detail::enable_if_t<(sizeof...(Ks) > 1), std::nullptr_t> = nullptr>
// here we need to add SFINAE in the template parameter to avoid
// infinite recursion in type deduction on gcc
auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys)
-> decltype(find_or<T>(std::forward<Value>(v), ky, detail::last_one(std::forward<Ks>(keys)...)))
{
if(!v.is_table())
{
return detail::last_one(std::forward<Ks>(keys)...);
}
auto&& tab = std::forward<Value>(v).as_table();
if(tab.count(ky) == 0)
{
return detail::last_one(std::forward<Ks>(keys)...);
}
return find_or(std::forward<decltype(tab)>(tab).at(ky), std::forward<Ks>(keys)...);
}
// ============================================================================
// expect

View File

@@ -2,7 +2,6 @@
// Distributed under the MIT License.
#ifndef TOML11_INTO_HPP
#define TOML11_INTO_HPP
#include "traits.hpp"
namespace toml
{

View File

@@ -2,11 +2,11 @@
// Distributed under the MIT License.
#ifndef TOML11_LEXER_HPP
#define TOML11_LEXER_HPP
#include "combinator.hpp"
#include <stdexcept>
#include <istream>
#include <sstream>
#include <fstream>
#include <stdexcept>
#include "combinator.hpp"
namespace toml
{
@@ -69,15 +69,8 @@ using lex_zero_prefixable_int = sequence<lex_digit, repeat<either<lex_digit,
using lex_fractional_part = sequence<character<'.'>, lex_zero_prefixable_int>;
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
// use toml-lang/toml HEAD
using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>,
maybe<lex_sign>, lex_zero_prefixable_int>;
#else
// strictly TOML v0.5.0
using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>,
lex_dec_int>;
#endif
using lex_float = either<lex_special_float,
sequence<lex_dec_int, either<lex_exponent_part,
@@ -125,17 +118,11 @@ using lex_local_time = lex_partial_time;
// ===========================================================================
using lex_quotation_mark = character<'"'>;
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab)
in_range<0x0a, 0x1F>, // is allowed
character<0x22>, character<0x5C>,
character<0x7F>>>;
#else
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x1F>,
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab) is allowed
in_range<0x0A, 0x1F>,
character<0x22>, character<0x5C>,
character<0x7F>>>;
#endif
using lex_escape = character<'\\'>;
using lex_escape_unicode_short = sequence<character<'u'>,
repeat<lex_hex_dig, exactly<4>>>;
@@ -145,6 +132,9 @@ using lex_escape_seq_char = either<character<'"'>, character<'\\'>,
character<'b'>, character<'f'>,
character<'n'>, character<'r'>,
character<'t'>,
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
character<'e'>, // ESC (0x1B)
#endif
lex_escape_unicode_short,
lex_escape_unicode_long
>;
@@ -154,19 +144,47 @@ using lex_basic_string = sequence<lex_quotation_mark,
repeat<lex_basic_char, unlimited>,
lex_quotation_mark>;
// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings
// are allowed to be used.
// After this, the following strings are *explicitly* allowed.
// - One or two `"`s in a multi-line basic string is allowed wherever it is.
// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter.
// - One or two `"`s can appear just before or after the delimiter.
// ```toml
// str4 = """Here are two quotation marks: "". Simple enough."""
// str5 = """Here are three quotation marks: ""\"."""
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
// str7 = """"This," she said, "is just a pointless statement.""""
// ```
// In the current implementation (v3.3.0), it is difficult to parse `str7` in
// the above example. It is difficult to recognize `"` at the end of string body
// collectly. It will be misunderstood as a `"""` delimiter and an additional,
// invalid `"`. Like this:
// ```console
// what(): [error] toml::parse_table: invalid line format
// --> hoge.toml
// |
// 13 | str7 = """"This," she said, "is just a pointless statement.""""
// | ^- expected newline, but got '"'.
// ```
// As a quick workaround for this problem, `lex_ml_basic_string_delim` was
// split into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s.
// In parse_ml_basic_string() function, the trailing `"`s will be attached to
// the string body.
//
using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>;
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09
in_range<0x0a, 0x1F>, // is tab
character<0x5C>,
character<0x7F>,
using lex_ml_basic_string_open = lex_ml_basic_string_delim;
using lex_ml_basic_string_close = sequence<
repeat<lex_quotation_mark, exactly<3>>,
maybe<lex_quotation_mark>, maybe<lex_quotation_mark>
>;
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 is tab
in_range<0x0A, 0x1F>,
character<0x5C>, // backslash
character<0x7F>, // DEL
lex_ml_basic_string_delim>>;
#else // TOML v0.5.0
using lex_ml_basic_unescaped = exclude<either<in_range<0x00,0x1F>,
character<0x5C>,
character<0x7F>,
lex_ml_basic_string_delim>>;
#endif
using lex_ml_basic_escaped_newline = sequence<
lex_escape, maybe<lex_ws>, lex_newline,
@@ -176,39 +194,39 @@ using lex_ml_basic_char = either<lex_ml_basic_unescaped, lex_escaped>;
using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline,
lex_ml_basic_escaped_newline>,
unlimited>;
using lex_ml_basic_string = sequence<lex_ml_basic_string_delim,
using lex_ml_basic_string = sequence<lex_ml_basic_string_open,
lex_ml_basic_body,
lex_ml_basic_string_delim>;
lex_ml_basic_string_close>;
using lex_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x19>, character<0x27>>>;
using lex_literal_char = exclude<either<in_range<0x00, 0x08>, in_range<0x0A, 0x1F>,
character<0x7F>, character<0x27>>>;
using lex_apostrophe = character<'\''>;
using lex_literal_string = sequence<lex_apostrophe,
repeat<lex_literal_char, unlimited>,
lex_apostrophe>;
// the same reason as above.
using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>;
using lex_ml_literal_string_open = lex_ml_literal_string_delim;
using lex_ml_literal_string_close = sequence<
repeat<lex_apostrophe, exactly<3>>,
maybe<lex_apostrophe>, maybe<lex_apostrophe>
>;
using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x1F>,
in_range<0x0A, 0x1F>,
character<0x7F>,
lex_ml_literal_string_delim>>;
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
unlimited>;
using lex_ml_literal_string = sequence<lex_ml_literal_string_delim,
using lex_ml_literal_string = sequence<lex_ml_literal_string_open,
lex_ml_literal_body,
lex_ml_literal_string_delim>;
lex_ml_literal_string_close>;
using lex_string = either<lex_ml_basic_string, lex_basic_string,
lex_ml_literal_string, lex_literal_string>;
// ===========================================================================
using lex_comment_start_symbol = character<'#'>;
using lex_non_eol = either<character<'\t'>, exclude<in_range<0x00, 0x19>>>;
using lex_comment = sequence<lex_comment_start_symbol,
repeat<lex_non_eol, unlimited>>;
using lex_dot_sep = sequence<maybe<lex_ws>, character<'.'>, maybe<lex_ws>>;
using lex_unquoted_key = repeat<either<lex_alpha, lex_digit,
@@ -243,6 +261,34 @@ using lex_array_table = sequence<lex_array_table_open,
maybe<lex_ws>,
lex_array_table_close>;
using lex_utf8_1byte = in_range<0x00, 0x7F>;
using lex_utf8_2byte = sequence<
in_range<'\xC2', '\xDF'>,
in_range<'\x80', '\xBF'>
>;
using lex_utf8_3byte = sequence<either<
sequence<character<'\xE0'>, in_range<'\xA0', '\xBF'>>,
sequence<in_range<'\xE1', '\xEC'>, in_range<'\x80', '\xBF'>>,
sequence<character<'\xED'>, in_range<'\x80', '\x9F'>>,
sequence<in_range<'\xEE', '\xEF'>, in_range<'\x80', '\xBF'>>
>, in_range<'\x80', '\xBF'>>;
using lex_utf8_4byte = sequence<either<
sequence<character<'\xF0'>, in_range<'\x90', '\xBF'>>,
sequence<in_range<'\xF1', '\xF3'>, in_range<'\x80', '\xBF'>>,
sequence<character<'\xF4'>, in_range<'\x80', '\x8F'>>
>, in_range<'\x80', '\xBF'>, in_range<'\x80', '\xBF'>>;
using lex_utf8_code = either<
lex_utf8_1byte,
lex_utf8_2byte,
lex_utf8_3byte,
lex_utf8_4byte
>;
using lex_comment_start_symbol = character<'#'>;
using lex_non_eol_ascii = either<character<0x09>, in_range<0x20, 0x7E>>;
using lex_comment = sequence<lex_comment_start_symbol, repeat<either<
lex_non_eol_ascii, lex_utf8_2byte, lex_utf8_3byte, lex_utf8_4byte>, unlimited>>;
} // detail
} // toml
#endif // TOML_LEXER_HPP

View File

@@ -11,12 +11,12 @@ inline namespace literals
inline namespace toml_literals
{
inline ::toml::value operator"" _toml(const char* str, std::size_t len)
// implementation
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
literal_internal_impl(::toml::detail::location loc)
{
::toml::detail::location<std::vector<char>>
loc(/* filename = */ std::string("TOML literal encoded in a C++ code"),
/* contents = */ std::vector<char>(str, str + len));
using value_type = ::toml::basic_value<
TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>;
// if there are some comments or empty lines, skip them.
using skip_line = ::toml::detail::repeat<toml::detail::sequence<
::toml::detail::maybe<::toml::detail::lex_ws>,
@@ -53,7 +53,7 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len)
// If it is neither a table-key or a array-of-table-key, it may be a value.
if(!is_table_key && !is_aots_key)
{
if(auto data = ::toml::detail::parse_value<::toml::value>(loc))
if(auto data = ::toml::detail::parse_value<value_type>(loc, 0))
{
return data.unwrap();
}
@@ -70,17 +70,43 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len)
// It is a valid toml file.
// It should be parsed as if we parse a file with this content.
if(auto data = ::toml::detail::parse_toml_file<::toml::value>(loc))
if(auto data = ::toml::detail::parse_toml_file<value_type>(loc))
{
return data.unwrap();
}
else // none of them.
{
throw ::toml::syntax_error(data.unwrap_err(),
source_location(std::addressof(loc)));
throw ::toml::syntax_error(data.unwrap_err(), source_location(loc));
}
}
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
operator"" _toml(const char* str, std::size_t len)
{
::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"),
std::vector<char>(str, str + len));
// literal length does not include the null character at the end.
return literal_internal_impl(std::move(loc));
}
// value of __cplusplus in C++2a/20 mode is not fixed yet along compilers.
// So here we use the feature test macro for `char8_t` itself.
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
// value of u8"" literal has been changed from char to char8_t and char8_t is
// NOT compatible to char
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
operator"" _toml(const char8_t* str, std::size_t len)
{
::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"),
std::vector<char>(reinterpret_cast<const char*>(str),
reinterpret_cast<const char*>(str) + len));
return literal_internal_impl(std::move(loc));
}
#endif
} // toml_literals
} // literals
} // toml

121
toml/macros.hpp Normal file
View File

@@ -0,0 +1,121 @@
#ifndef TOML11_MACROS_HPP
#define TOML11_MACROS_HPP
#define TOML11_STRINGIZE_AUX(x) #x
#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x)
#define TOML11_CONCATENATE_AUX(x, y) x##y
#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y)
// ============================================================================
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
// ----------------------------------------------------------------------------
// TOML11_ARGS_SIZE
#define TOML11_INDEX_RSEQ() \
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \
16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define TOML11_ARGS_SIZE_IMPL(\
ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \
ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \
ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \
ARG31, ARG32, N, ...) N
#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__)
#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ())
// ----------------------------------------------------------------------------
// TOML11_FOR_EACH_VA_ARGS
#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1)
#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\
TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__)
// ----------------------------------------------------------------------------
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
// use it in the following way.
// ```cpp
// namespace foo
// {
// struct Foo
// {
// std::string s;
// double d;
// int i;
// };
// } // foo
//
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i)
// ```
// And then you can use `toml::find<foo::Foo>(file, "foo");`
//
#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\
obj.VAR_NAME = toml::find<decltype(obj.VAR_NAME)>(v, TOML11_STRINGIZE(VAR_NAME));
#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\
v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME;
#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\
namespace toml { \
template<> \
struct from<NAME> \
{ \
template<typename C, template<typename ...> class T, \
template<typename ...> class A> \
static NAME from_toml(const basic_value<C, T, A>& v) \
{ \
NAME obj; \
TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \
return obj; \
} \
}; \
template<> \
struct into<NAME> \
{ \
static value into_toml(const NAME& obj) \
{ \
::toml::value v = ::toml::table{}; \
TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \
return v; \
} \
}; \
} /* toml */
#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
#endif// TOML11_MACROS_HPP

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -2,11 +2,22 @@
// Distributed under the MIT License.
#ifndef TOML11_SERIALIZER_HPP
#define TOML11_SERIALIZER_HPP
#include "value.hpp"
#include "lexer.hpp"
#include <limits>
#include <cmath>
#include <cstdio>
#include <limits>
#if defined(_WIN32)
#include <locale.h>
#elif defined(__APPLE__) || defined(__FreeBSD__)
#include <xlocale.h>
#elif defined(__linux__)
#include <locale.h>
#endif
#include "lexer.hpp"
#include "value.hpp"
namespace toml
{
@@ -22,39 +33,82 @@ namespace toml
// Since toml restricts characters available in a bare key, generally a string
// should be escaped. But checking whether a string needs to be surrounded by
// a `"` and escaping some special character is boring.
inline std::string format_key(const toml::key& key)
template<typename charT, typename traits, typename Alloc>
std::basic_string<charT, traits, Alloc>
format_key(const std::basic_string<charT, traits, Alloc>& k)
{
detail::location<toml::key> loc(key, key);
if(k.empty())
{
return std::string("\"\"");
}
// check the key can be a bare (unquoted) key
detail::location loc(k, std::vector<char>(k.begin(), k.end()));
detail::lex_unquoted_key::invoke(loc);
if(loc.iter() == loc.end())
{
return key; // all the tokens are consumed. the key is unquoted-key.
return k; // all the tokens are consumed. the key is unquoted-key.
}
std::string token("\"");
for(const char c : key)
//if it includes special characters, then format it in a "quoted" key.
std::basic_string<charT, traits, Alloc> serialized("\"");
for(const char c : k)
{
switch(c)
{
case '\\': {token += "\\\\"; break;}
case '\"': {token += "\\\""; break;}
case '\b': {token += "\\b"; break;}
case '\t': {token += "\\t"; break;}
case '\f': {token += "\\f"; break;}
case '\n': {token += "\\n"; break;}
case '\r': {token += "\\r"; break;}
default : {token += c; break;}
case '\\': {serialized += "\\\\"; break;}
case '\"': {serialized += "\\\""; break;}
case '\b': {serialized += "\\b"; break;}
case '\t': {serialized += "\\t"; break;}
case '\f': {serialized += "\\f"; break;}
case '\n': {serialized += "\\n"; break;}
case '\r': {serialized += "\\r"; break;}
default: {
if (c >= 0x00 && c < 0x20)
{
std::array<char, 7> buf;
std::snprintf(buf.data(), buf.size(), "\\u00%02x", static_cast<int>(c));
serialized += buf.data();
}
else
{
serialized += c;
}
break;
}
}
}
token += "\"";
return token;
serialized += "\"";
return serialized;
}
template<typename Comment,
template<typename ...> class Table,
template<typename ...> class Array>
template<typename charT, typename traits, typename Alloc>
std::basic_string<charT, traits, Alloc>
format_keys(const std::vector<std::basic_string<charT, traits, Alloc>>& keys)
{
if(keys.empty())
{
return std::string("\"\"");
}
std::basic_string<charT, traits, Alloc> serialized;
for(const auto& ky : keys)
{
serialized += format_key(ky);
serialized += charT('.');
}
serialized.pop_back(); // remove the last dot '.'
return serialized;
}
template<typename Value>
struct serializer
{
using value_type = basic_value<Comment, Table, Array>;
static_assert(detail::is_basic_value<Value>::value,
"toml::serializer is for toml::value and its variants, "
"toml::basic_value<...>.");
using value_type = Value;
using key_type = typename value_type::key_type ;
using comment_type = typename value_type::comment_type ;
using boolean_type = typename value_type::boolean_type ;
@@ -72,8 +126,10 @@ struct serializer
const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
const bool can_be_inlined = false,
const bool no_comment = false,
std::vector<toml::key> ks = {})
std::vector<toml::key> ks = {},
const bool value_has_comment = false)
: can_be_inlined_(can_be_inlined), no_comment_(no_comment),
value_has_comment_(value_has_comment && !no_comment),
float_prec_(float_prec), width_(w), keys_(std::move(ks))
{}
~serializer() = default;
@@ -84,18 +140,93 @@ struct serializer
}
std::string operator()(const integer_type i) const
{
return std::to_string(i);
#if defined(_WIN32)
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
const std::string original_locale(setlocale(LC_NUMERIC, nullptr));
setlocale(LC_NUMERIC, "C");
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0));
locale_t original_locale(0);
if(c_locale != locale_t(0))
{
original_locale = uselocale(c_locale);
}
#endif
const auto str = std::to_string(i);
#if defined(_WIN32)
setlocale(LC_NUMERIC, original_locale.c_str());
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
if(original_locale != locale_t(0))
{
uselocale(original_locale);
}
#endif
return str;
}
std::string operator()(const floating_type f) const
{
if(std::isnan(f))
{
if(std::signbit(f))
{
return std::string("-nan");
}
else
{
return std::string("nan");
}
}
else if(!std::isfinite(f))
{
if(std::signbit(f))
{
return std::string("-inf");
}
else
{
return std::string("inf");
}
}
// set locale to "C".
// To make it thread-local, we use OS-specific features.
// If we set process-global locale, it can break other thread that also
// outputs something simultaneously.
#if defined(_WIN32)
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
const std::string original_locale(setlocale(LC_NUMERIC, nullptr));
setlocale(LC_NUMERIC, "C");
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0));
locale_t original_locale(0);
if(c_locale != locale_t(0))
{
original_locale = uselocale(c_locale);
}
#endif
const auto fmt = "%.*g";
const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f);
// +1 for null character(\0)
std::vector<char> buf(static_cast<std::size_t>(bsz + 1), '\0');
std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f);
// restore the original locale
#if defined(_WIN32)
setlocale(LC_NUMERIC, original_locale.c_str());
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
if(original_locale != locale_t(0))
{
uselocale(original_locale);
}
#endif
std::string token(buf.begin(), std::prev(buf.end()));
if(token.back() == '.') // 1. => 1.0
if(!token.empty() && token.back() == '.') // 1. => 1.0
{
token += '0';
}
@@ -112,35 +243,6 @@ struct serializer
{
// the resulting value does not have any float specific part!
token += ".0";
return token;
}
if(!has_exponent)
{
return token; // there is no exponent part. just return it.
}
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
// Although currently it is not released yet, TOML will allow
// zero-prefix in an exponent part such as 1.234e+01.
// The following code removes the zero prefixes.
// If the feature is activated, the following codes can be skipped.
return token;
#endif
// zero-prefix in an exponent is NOT allowed in TOML v0.5.0.
// 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(static_cast<typename std::string::size_type>(offset),
zero_prefix);
}
return token;
}
@@ -148,12 +250,24 @@ struct serializer
{
if(s.kind == string_t::basic)
{
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend())
if((std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) &&
this->width_ != (std::numeric_limits<std::size_t>::max)())
{
// 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;
// if linefeed or double-quote is contained,
// make it multiline basic string.
const auto escaped = this->escape_ml_basic_string(s.str);
std::string open("\"\"\"");
std::string close("\"\"\"");
if(escaped.find('\n') != std::string::npos ||
this->width_ < escaped.size() + 6)
{
// if the string body contains newline or is enough long,
// add newlines after and before delimiters.
open += "\n";
close = std::string("\\\n") + close;
}
return open + escaped + close;
}
// no linefeed. try to make it oneline-string.
@@ -194,7 +308,11 @@ struct serializer
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");
std::string open("'''");
if(this->width_ + 6 < s.str.size())
{
open += '\n'; // the first newline is ignored by TOML spec
}
const std::string close("'''");
return open + s.str + close;
}
@@ -233,92 +351,18 @@ struct serializer
std::string operator()(const array_type& v) const
{
if(!v.empty() && v.front().is_table())// v is an array of tables
{
// if it's not inlined, we need to add `[[table.key]]`.
// but if it can be inlined,
// ```
// table.key = [
// {...},
// # comment
// {...},
// ]
// ```
if(this->can_be_inlined_)
{
std::string token;
if(!keys_.empty())
{
token += this->serialize_key(keys_.back());
token += " = ";
}
bool failed = false;
token += "[\n";
for(const auto& item : v)
{
// if an element of the table has a comment, the table
// cannot be inlined.
if(this->has_comment_inside(item.as_table()))
{
failed = true;
break;
}
if(!no_comment_)
{
for(const auto& c : item.comments())
{
token += '#';
token += c;
token += '\n';
}
}
const auto t = this->make_inline_table(item.as_table());
if(t.size() + 1 > width_ || // +1 for the last comma {...},
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
{
failed = true;
break;
}
token += t;
token += ",\n";
}
if(!failed)
{
token += "]\n";
return token;
}
// if failed, serialize them as [[array.of.tables]].
}
std::string token;
for(const auto& item : v)
{
if(!no_comment_)
{
for(const auto& c : item.comments())
{
token += '#';
token += c;
token += '\n';
}
}
token += "[[";
token += this->serialize_dotted_key(keys_);
token += "]]\n";
token += this->make_multiline_table(item.as_table());
}
return token;
}
if(v.empty())
{
return std::string("[]");
}
if(this->is_array_of_tables(v))
{
return make_array_of_tables(v);
}
// not an array of tables. normal array.
// first, try to make it inline if none of the elements have a comment.
if(!this->has_comment_inside(v))
if( ! this->has_comment_inside(v))
{
const auto inl = this->make_inline_array(v);
if(inl.size() < this->width_ &&
@@ -339,7 +383,7 @@ struct serializer
token += "[\n";
for(const auto& item : v)
{
if(!item.comments().empty() && !no_comment_)
if( ! item.comments().empty() && !no_comment_)
{
// if comment exists, the element must be the only element in the line.
// e.g. the following is not allowed.
@@ -365,15 +409,25 @@ struct serializer
token += '\n';
}
token += toml::visit(*this, item);
if(token.back() == '\n') {token.pop_back();}
if(!token.empty() && token.back() == '\n') {token.pop_back();}
token += ",\n";
continue;
}
std::string next_elem;
next_elem += toml::visit(*this, item);
if(item.is_table())
{
serializer ser(*this);
ser.can_be_inlined_ = true;
ser.width_ = (std::numeric_limits<std::size_t>::max)();
next_elem += toml::visit(ser, item);
}
else
{
next_elem += toml::visit(*this, item);
}
// comma before newline.
if(next_elem.back() == '\n') {next_elem.pop_back();}
if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();}
// if current line does not exceeds the width limit, continue.
if(current_line.size() + next_elem.size() + 1 < this->width_)
@@ -400,7 +454,10 @@ struct serializer
}
if(!current_line.empty())
{
if(current_line.back() != '\n') {current_line += '\n';}
if(!current_line.empty() && current_line.back() != '\n')
{
current_line += '\n';
}
token += current_line;
}
token += "]\n";
@@ -417,7 +474,7 @@ struct serializer
std::string token;
if(!this->keys_.empty())
{
token += this->serialize_key(this->keys_.back());
token += format_key(this->keys_.back());
token += " = ";
}
token += this->make_inline_table(v);
@@ -432,7 +489,7 @@ struct serializer
if(!keys_.empty())
{
token += '[';
token += this->serialize_dotted_key(keys_);
token += format_keys(keys_);
token += "]\n";
}
token += this->make_multiline_table(v);
@@ -441,25 +498,6 @@ struct serializer
private:
std::string serialize_key(const toml::key& key) const
{
return ::toml::format_key(key);
}
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.
@@ -475,7 +513,19 @@ struct serializer
case '\f': {retval += "\\f"; break;}
case '\n': {retval += "\\n"; break;}
case '\r': {retval += "\\r"; break;}
default : {retval += c; break;}
default :
{
if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F)
{
retval += "\\u00";
retval += char(48 + (c / 16));
retval += char((c % 16 < 10 ? 48 : 55) + (c % 16));
}
else
{
retval += c;
}
}
}
}
return retval;
@@ -489,7 +539,9 @@ struct serializer
switch(*i)
{
case '\\': {retval += "\\\\"; break;}
case '\"': {retval += "\\\""; break;}
// One or two consecutive "s are allowed.
// Later we will check there are no three consecutive "s.
// case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;}
@@ -507,9 +559,40 @@ struct serializer
}
break;
}
default: {retval += *i; break;}
default :
{
const auto c = *i;
if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F)
{
retval += "\\u00";
retval += char(48 + (c / 16));
retval += char((c % 16 < 10 ? 48 : 55) + (c % 16));
}
else
{
retval += c;
}
}
}
}
// Only 1 or 2 consecutive `"`s are allowed in multiline basic string.
// 3 consecutive `"`s are considered as a closing delimiter.
// We need to check if there are 3 or more consecutive `"`s and insert
// backslash to break them down into several short `"`s like the `str6`
// in the following example.
// ```toml
// str4 = """Here are two quotation marks: "". Simple enough."""
// # str5 = """Here are three quotation marks: """.""" # INVALID
// str5 = """Here are three quotation marks: ""\"."""
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
// ```
auto found_3_quotes = retval.find("\"\"\"");
while(found_3_quotes != std::string::npos)
{
retval.replace(found_3_quotes, 3, "\"\"\\\"");
found_3_quotes = retval.find("\"\"\"");
}
return retval;
}
@@ -546,8 +629,10 @@ struct serializer
for(const auto& item : v)
{
if(is_first) {is_first = false;} else {token += ',';}
token += visit(serializer(std::numeric_limits<std::size_t>::max(),
this->float_prec_, true), item);
token += visit(serializer(
(std::numeric_limits<std::size_t>::max)(), this->float_prec_,
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !item.comments().empty()), item);
}
token += ']';
return token;
@@ -564,10 +649,12 @@ struct serializer
{
// 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 += format_key(kv.first);
token += '=';
token += visit(serializer(std::numeric_limits<std::size_t>::max(),
this->float_prec_, true), kv.second);
token += visit(serializer(
(std::numeric_limits<std::size_t>::max)(), this->float_prec_,
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
}
token += '}';
return token;
@@ -577,30 +664,33 @@ struct serializer
{
std::string token;
// print non-table stuff first. because after printing [foo.bar], the
// remaining non-table values will be assigned into [foo.bar], not [foo]
for(const auto kv : v)
// print non-table elements first.
// ```toml
// [foo] # a table we're writing now here
// key = "value" # <- non-table element, "key"
// # ...
// [foo.bar] # <- table element, "bar"
// ```
// because after printing [foo.bar], the remaining non-table values will
// be assigned into [foo.bar], not [foo]. Those values should be printed
// earlier.
for(const auto& kv : v)
{
if(kv.second.is_table() || is_array_of_tables(kv.second))
{
continue;
}
if(!kv.second.comments().empty() && !no_comment_)
{
for(const auto& c : kv.second.comments())
{
token += '#';
token += c;
token += '\n';
}
}
const auto key_and_sep = this->serialize_key(kv.first) + " = ";
token += write_comments(kv.second);
const auto key_and_sep = format_key(kv.first) + " = ";
const auto residual_width = (this->width_ > key_and_sep.size()) ?
this->width_ - key_and_sep.size() : 0;
token += key_and_sep;
token += visit(serializer(residual_width, this->float_prec_, true),
kv.second);
token += visit(serializer(residual_width, this->float_prec_,
/*can be inlined*/ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
if(token.back() != '\n')
{
token += '\n';
@@ -626,45 +716,172 @@ struct serializer
ks.push_back(kv.first);
auto tmp = visit(serializer(this->width_, this->float_prec_,
!multiline_table_printed, this->no_comment_, ks),
kv.second);
!multiline_table_printed, this->no_comment_, ks,
/*has_comment*/ !kv.second.comments().empty()), kv.second);
// If it is the first time to print a multi-line table, it would be
// helpful to separate normal key-value pair and subtables by a
// newline.
// (this checks if the current key-value pair contains newlines.
// but it is not perfect because multi-line string can also contain
// a newline. in such a case, an empty line will be written) TODO
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 += '\n'; // separate key-value pairs and subtables
if(!kv.second.comments().empty() && !no_comment_)
{
for(const auto& c : kv.second.comments())
token += write_comments(kv.second);
token += tmp;
// care about recursive tables (all tables in each level prints
// newline and there will be a full of newlines)
if(tmp.substr(tmp.size() - 2, 2) != "\n\n" &&
tmp.substr(tmp.size() - 4, 4) != "\r\n\r\n" )
{
token += '#';
token += c;
token += '\n';
}
}
token += tmp;
else
{
token += write_comments(kv.second);
token += tmp;
token += '\n';
}
}
return token;
}
std::string make_array_of_tables(const array_type& v) const
{
// if it's not inlined, we need to add `[[table.key]]`.
// but if it can be inlined, we can format it as the following.
// ```
// table.key = [
// {...},
// # comment
// {...},
// ]
// ```
// This function checks if inlinization is possible or not, and then
// format the array-of-tables in a proper way.
//
// Note about comments:
//
// If the array itself has a comment (value_has_comment_ == true), we
// should try to make it inline.
// ```toml
// # comment about array
// array = [
// # comment about table element
// {of = "table"}
// ]
// ```
// If it is formatted as a multiline table, the two comments becomes
// indistinguishable.
// ```toml
// # comment about array
// # comment about table element
// [[array]]
// of = "table"
// ```
// So we need to try to make it inline, and it force-inlines regardless
// of the line width limit.
// It may fail if the element of a table has comment. In that case,
// the array-of-tables will be formatted as a multiline table.
if(this->can_be_inlined_ || this->value_has_comment_)
{
std::string token;
if(!keys_.empty())
{
token += format_key(keys_.back());
token += " = ";
}
bool failed = false;
token += "[\n";
for(const auto& item : v)
{
// if an element of the table has a comment, the table
// cannot be inlined.
if(this->has_comment_inside(item.as_table()))
{
failed = true;
break;
}
// write comments for the table itself
token += write_comments(item);
const auto t = this->make_inline_table(item.as_table());
if(t.size() + 1 > width_ || // +1 for the last comma {...},
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
{
// if the value itself has a comment, ignore the line width limit
if( ! this->value_has_comment_)
{
failed = true;
break;
}
}
token += t;
token += ",\n";
}
if( ! failed)
{
token += "]\n";
return token;
}
// if failed, serialize them as [[array.of.tables]].
}
std::string token;
for(const auto& item : v)
{
token += write_comments(item);
token += "[[";
token += format_keys(keys_);
token += "]]\n";
token += this->make_multiline_table(item.as_table());
}
return token;
}
std::string write_comments(const value_type& v) const
{
std::string retval;
if(this->no_comment_) {return retval;}
for(const auto& c : v.comments())
{
retval += '#';
retval += c;
retval += '\n';
}
return retval;
}
bool is_array_of_tables(const value_type& v) const
{
if(!v.is_array()) {return false;}
const auto& a = v.as_array();
return !a.empty() && a.front().is_table();
if(!v.is_array() || v.as_array().empty()) {return false;}
return is_array_of_tables(v.as_array());
}
bool is_array_of_tables(const array_type& v) const
{
// Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to
// check all the element in an array to check if the array is an array
// of tables.
return std::all_of(v.begin(), v.end(), [](const value_type& elem) {
return elem.is_table();
});
}
private:
bool can_be_inlined_;
bool no_comment_;
bool value_has_comment_;
int float_prec_;
std::size_t width_;
std::vector<toml::key> keys_;
@@ -677,6 +894,7 @@ format(const basic_value<C, M, V>& v, std::size_t w = 80u,
int fprec = std::numeric_limits<toml::floating>::max_digits10,
bool no_comment = false, bool force_inline = false)
{
using value_type = basic_value<C, M, V>;
// if value is a table, it is considered to be a root object.
// the root object can't be an inline table.
if(v.is_table())
@@ -687,10 +905,11 @@ format(const basic_value<C, M, V>& v, std::size_t w = 80u,
oss << v.comments();
oss << '\n'; // to split the file comment from the first element
}
oss << visit(serializer<C, M, V>(w, fprec, no_comment, false), v);
const auto serialized = visit(serializer<value_type>(w, fprec, false, no_comment), v);
oss << serialized;
return oss.str();
}
return visit(serializer<C, M, V>(w, fprec, force_inline), v);
return visit(serializer<value_type>(w, fprec, force_inline), v);
}
namespace detail
@@ -707,7 +926,7 @@ template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
nocomment(std::basic_ostream<charT, traits>& os)
{
// by default, it is zero. and by defalut, it shows comments.
// by default, it is zero. and by default, it shows comments.
os.iword(detail::comment_index(os)) = 1;
return os;
}
@@ -716,7 +935,7 @@ template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
showcomment(std::basic_ostream<charT, traits>& os)
{
// by default, it is zero. and by defalut, it shows comments.
// by default, it is zero. and by default, it shows comments.
os.iword(detail::comment_index(os)) = 0;
return os;
}
@@ -726,12 +945,14 @@ template<typename charT, typename traits, typename C,
std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
{
using value_type = basic_value<C, M, V>;
// get status of std::setw().
const auto w = static_cast<std::size_t>(os.width());
const int fprec = static_cast<int>(os.precision());
os.width(0);
// by defualt, iword is initialized byl 0. And by default, toml11 outputs
// by default, iword is initialized by 0. And by default, toml11 outputs
// comments. So `0` means showcomment. 1 means nocommnet.
const bool no_comment = (1 == os.iword(detail::comment_index(os)));
@@ -741,7 +962,8 @@ operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
os << '\n'; // to split the file comment from the first element
}
// the root object can't be an inline table. so pass `false`.
os << visit(serializer<C, M, V>(w, fprec, false, no_comment), v);
const auto serialized = visit(serializer<value_type>(w, fprec, no_comment, false), v);
os << serialized;
// if v is a non-table value, and has only one comment, then
// put a comment just after a value. in the following way.

View File

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

View File

@@ -16,10 +16,9 @@ struct storage
{
using value_type = T;
storage(value_type const& v): ptr(toml::make_unique<T>(v)) {}
storage(value_type&& v): ptr(toml::make_unique<T>(std::move(v))) {}
explicit storage(value_type const& v): ptr(toml::make_unique<T>(v)) {}
explicit storage(value_type&& v): ptr(toml::make_unique<T>(std::move(v))) {}
~storage() = default;
storage(const storage& rhs): ptr(toml::make_unique<T>(*rhs.ptr)) {}
storage& operator=(const storage& rhs)
{

View File

@@ -2,11 +2,17 @@
// Distributed under the MIT License.
#ifndef TOML11_STRING_HPP
#define TOML11_STRING_HPP
#include "version.hpp"
#include <cstdint>
#include <algorithm>
#include <string>
#include <cstdint>
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>)
#define TOML11_USING_STRING_VIEW 1
#include <string_view>
#endif
#endif
@@ -51,7 +57,7 @@ struct string
string& operator+=(const std::string& rhs) {str += rhs; return *this;}
string& operator+=(const string& rhs) {str += rhs.str; return *this;}
#if __cplusplus >= 201703L
#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
explicit string(std::string_view s): kind(string_t::basic), str(s){}
string(std::string_view s, string_t k): kind(k), str(s){}

View File

@@ -2,12 +2,19 @@
// Distributed under the MIT License.
#ifndef TOML11_TRAITS_HPP
#define TOML11_TRAITS_HPP
#include "from.hpp"
#include "into.hpp"
#include "version.hpp"
#include <chrono>
#include <forward_list>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <chrono>
#include <tuple>
#include <string>
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>)
#include <string_view>
#endif // has_include(<string_view>)
@@ -43,17 +50,23 @@ struct has_mapped_type_impl
template<typename T> static std::true_type check(typename T::mapped_type*);
template<typename T> static std::false_type check(...);
};
struct has_resize_method_impl
struct has_reserve_method_impl
{
constexpr static std::size_t dummy=0;
template<typename T> static std::true_type check(decltype(std::declval<T>().resize(dummy))*);
template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>().reserve(std::declval<std::size_t>()))*);
};
struct has_push_back_method_impl
{
template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))*);
};
struct is_comparable_impl
{
template<typename T> static std::true_type check(decltype(std::declval<T>() < std::declval<T>())*);
template<typename T> static std::false_type check(...);
template<typename T> static std::true_type check(
decltype(std::declval<T>() < std::declval<T>())*);
};
struct has_from_toml_method_impl
@@ -76,6 +89,22 @@ struct has_into_toml_method_impl
static std::false_type check(...);
};
struct has_specialized_from_impl
{
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::from<T>)>
static std::true_type check(::toml::from<T>*);
};
struct has_specialized_into_impl
{
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::into<T>)>
static std::true_type check(::toml::from<T>*);
};
/// 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
@@ -91,7 +120,9 @@ struct has_key_type : decltype(has_key_type_impl::check<T>(nullptr)){};
template<typename T>
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)){};
struct has_reserve_method : decltype(has_reserve_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_push_back_method : decltype(has_push_back_method_impl::check<T>(nullptr)){};
template<typename T>
struct is_comparable : decltype(is_comparable_impl::check<T>(nullptr)){};
@@ -104,6 +135,11 @@ template<typename T>
struct has_into_toml_method
: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_specialized_from : decltype(has_specialized_from_impl::check<T>(nullptr)){};
template<typename T>
struct has_specialized_into : decltype(has_specialized_into_impl::check<T>(nullptr)){};
#ifdef __INTEL_COMPILER
#undef decltype
#endif
@@ -111,6 +147,14 @@ struct has_into_toml_method
// ---------------------------------------------------------------------------
// C++17 and/or/not
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
using std::conjunction;
using std::disjunction;
using std::negation;
#else
template<typename ...> struct conjunction : std::true_type{};
template<typename T> struct conjunction<T> : T{};
template<typename T, typename ... Ts>
@@ -128,6 +172,8 @@ struct disjunction<T, Ts...> :
template<typename T>
struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{};
#endif
// ---------------------------------------------------------------------------
// type checkers
@@ -139,6 +185,10 @@ template<typename T> struct is_std_tuple : std::false_type{};
template<typename ... Ts>
struct is_std_tuple<std::tuple<Ts...>> : std::true_type{};
template<typename T> struct is_std_forward_list : std::false_type{};
template<typename T>
struct is_std_forward_list<std::forward_list<T>> : std::true_type{};
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{};
@@ -159,8 +209,10 @@ template<typename T>
struct is_container : conjunction<
negation<is_map<T>>, // not a map
negation<std::is_same<T, std::string>>, // not a std::string
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>)
negation<std::is_same<T, std::string_view>>, // not a std::string_view
#endif // has_include(<string_view>)
#endif
has_iterator<T>, // has T::iterator
has_value_type<T> // has T::value_type
@@ -182,6 +234,13 @@ struct is_basic_value<::toml::basic_value<C, M, V>>: std::true_type{};
// ---------------------------------------------------------------------------
// C++14 index_sequence
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::index_sequence;
using std::make_index_sequence;
#else
template<std::size_t ... Ns> struct index_sequence{};
template<typename IS, std::size_t N> struct push_back_index_sequence{};
@@ -205,15 +264,26 @@ struct index_sequence_maker<0>
template<std::size_t N>
using make_index_sequence = typename index_sequence_maker<N-1>::type;
#endif // cplusplus >= 2014
// ---------------------------------------------------------------------------
// C++14 enable_if_t
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::enable_if_t;
#else
template<bool B, typename T>
using enable_if_t = typename std::enable_if<B, T>::type;
#endif // cplusplus >= 2014
// ---------------------------------------------------------------------------
// return_type_of_t
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L && defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable>=201703
template<typename F, typename ... Args>
using return_type_of_t = std::invoke_result_t<F, Args...>;

View File

@@ -2,12 +2,13 @@
// Distributed under the MIT License.
#ifndef TOML11_TYPES_HPP
#define TOML11_TYPES_HPP
#include <unordered_map>
#include <vector>
#include "comments.hpp"
#include "datetime.hpp"
#include "string.hpp"
#include "traits.hpp"
#include "comments.hpp"
#include <vector>
#include <unordered_map>
namespace toml
{
@@ -20,9 +21,14 @@ class basic_value;
using character = char;
using key = std::string;
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ <= 4
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wshadow"
#endif
using boolean = bool;
using integer = std::int64_t;
using floating = double; // "float" is a keyward, cannot use it here.
using floating = double; // "float" is a keyword, cannot use it here.
// the following stuffs are structs defined here, so aliases are not needed.
// - string
// - offset_datetime
@@ -31,12 +37,26 @@ using floating = double; // "float" is a keyward, cannot use it here.
// - local_date
// - local_time
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
// default toml::value and default array/table. these are defined after defining
// basic_value itself.
// using value = basic_value<discard_comments, std::unordered_map, std::vector>;
// using array = typename value::array_type;
// using table = typename value::table_type;
// to avoid warnings about `value_t::integer` is "shadowing" toml::integer in
// GCC -Wshadow=global.
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# if 7 <= __GNUC__
# pragma GCC diagnostic ignored "-Wshadow=global"
# else // gcc-6 or older
# pragma GCC diagnostic ignored "-Wshadow"
# endif
#endif
enum class value_t : std::uint8_t
{
empty = 0,
@@ -51,6 +71,9 @@ enum class value_t : std::uint8_t
array = 9,
table = 10,
};
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
template<typename charT, typename traits>
inline std::basic_ostream<charT, traits>&
@@ -146,4 +169,5 @@ template<typename T, typename V> struct is_exact_toml_type<T const volatile&, V>
} // detail
} // toml
#endif// TOML11_TYPES_H

View File

@@ -2,12 +2,14 @@
// Distributed under the MIT License.
#ifndef TOML11_UTILITY_HPP
#define TOML11_UTILITY_HPP
#include "traits.hpp"
#include <utility>
#include <memory>
#include <sstream>
#include <utility>
#if __cplusplus >= 201402L
#include "traits.hpp"
#include "version.hpp"
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]]
#elif defined(__GNUC__)
# define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg)))
@@ -20,38 +22,41 @@
namespace toml
{
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::make_unique;
#else
template<typename T, typename ... Ts>
inline std::unique_ptr<T> make_unique(Ts&& ... args)
{
return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
}
#endif // TOML11_CPLUSPLUS_STANDARD_VERSION >= 2014
namespace detail
{
template<typename T>
inline void resize_impl(T& container, std::size_t N, std::true_type)
template<typename Container>
void try_reserve_impl(Container& container, std::size_t N, std::true_type)
{
container.resize(N);
return ;
container.reserve(N);
return;
}
template<typename T>
inline void resize_impl(T& container, std::size_t N, std::false_type)
template<typename Container>
void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept
{
if(container.size() >= N) {return;}
throw std::invalid_argument("not resizable type");
return;
}
} // detail
template<typename T>
inline void resize(T& container, std::size_t N)
template<typename Container>
void try_reserve(Container& container, std::size_t N)
{
if(container.size() == N) {return;}
return detail::resize_impl(container, N, detail::has_resize_method<T>());
if(N <= container.size()) {return;}
detail::try_reserve_impl(container, N, detail::has_reserve_method<Container>{});
return;
}
namespace detail
@@ -76,14 +81,70 @@ std::string concat_to_string(Ts&& ... args)
return detail::concat_to_string_impl(oss, std::forward<Ts>(args) ...);
}
template<typename T, typename U>
T from_string(const std::string& str, U&& opt)
template<typename T>
T from_string(const std::string& str, T opt)
{
T v(static_cast<T>(std::forward<U>(opt)));
T v(opt);
std::istringstream iss(str);
iss >> v;
return v;
}
namespace detail
{
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
template<typename T>
decltype(auto) last_one(T&& tail) noexcept
{
return std::forward<T>(tail);
}
template<typename T, typename ... Ts>
decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept
{
return last_one(std::forward<Ts>(tail)...);
}
#else // C++11
// The following code
// ```cpp
// 1 | template<typename T, typename ... Ts>
// 2 | auto last_one(T&& /*head*/, Ts&& ... tail)
// 3 | -> decltype(last_one(std::forward<Ts>(tail)...))
// 4 | {
// 5 | return last_one(std::forward<Ts>(tail)...);
// 6 | }
// ```
// does not work because the function `last_one(...)` is not yet defined at
// line #3, so `decltype()` cannot deduce the type returned from `last_one`.
// So we need to determine return type in a different way, like a meta func.
template<typename T, typename ... Ts>
struct last_one_in_pack
{
using type = typename last_one_in_pack<Ts...>::type;
};
template<typename T>
struct last_one_in_pack<T>
{
using type = T;
};
template<typename ... Ts>
using last_one_in_pack_t = typename last_one_in_pack<Ts...>::type;
template<typename T>
T&& last_one(T&& tail) noexcept
{
return std::forward<T>(tail);
}
template<typename T, typename ... Ts>
enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t<Ts&& ...>>
last_one(T&& /*head*/, Ts&& ... tail)
{
return last_one(std::forward<Ts>(tail)...);
}
#endif
} // detail
}// toml
#endif // TOML11_UTILITY

File diff suppressed because it is too large Load Diff

42
toml/version.hpp Normal file
View File

@@ -0,0 +1,42 @@
#ifndef TOML11_VERSION_HPP
#define TOML11_VERSION_HPP
// This file checks C++ version.
#ifndef __cplusplus
# error "__cplusplus is not defined"
#endif
// Since MSVC does not define `__cplusplus` correctly unless you pass
// `/Zc:__cplusplus` when compiling, the workaround macros are added.
// Those enables you to define version manually or to use MSVC specific
// version macro automatically.
//
// The value of `__cplusplus` macro is defined in the C++ standard spec, but
// MSVC ignores the value, maybe because of backward compatibility. Instead,
// MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in
// the C++ standard. First we check the manual version definition, and then
// we check if _MSVC_LANG is defined. If neither, use normal `__cplusplus`.
//
// FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170
// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170
//
#if defined(TOML11_ENFORCE_CXX11)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201103L
#elif defined(TOML11_ENFORCE_CXX14)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201402L
#elif defined(TOML11_ENFORCE_CXX17)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201703L
#elif defined(TOML11_ENFORCE_CXX20)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 202002L
#elif defined(_MSVC_LANG) && defined(_MSC_VER) && 1910 <= _MSC_VER
# define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG
#else
# define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus
#endif
#if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L && _MSC_VER < 1900
# error "toml11 requires C++11 or later."
#endif
#endif// TOML11_VERSION_HPP