From 944b83642a614debdf52196a36f261cfdd9fcdb7 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 2 Mar 2019 17:52:00 +0900 Subject: [PATCH 01/37] feat: make location to inherit region_base To generate error message, it is better to have the same interface. Also, location can be considered as a region having only one character. --- toml/region.hpp | 259 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 178 insertions(+), 81 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index 7a0acba..62a28e1 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -28,44 +28,6 @@ inline std::string make_string(std::size_t len, char c) return std::string(len, c); } -// location in a container, normally in a file content. -// shared_ptr points the resource that the iter points. -// it can be used not only for resource handling, but also error message. -template -struct location -{ - static_assert(std::is_same::value,""); - using const_iterator = typename Container::const_iterator; - using source_ptr = std::shared_ptr; - - location(std::string name, Container cont) - : source_(std::make_shared(std::move(cont))), - source_name_(std::move(name)), iter_(source_->cbegin()) - {} - location(const location&) = default; - location(location&&) = default; - location& operator=(const location&) = default; - location& operator=(location&&) = default; - ~location() = default; - - const_iterator& iter() noexcept {return iter_;} - const_iterator iter() const noexcept {return iter_;} - - const_iterator begin() const noexcept {return source_->cbegin();} - const_iterator end() const noexcept {return source_->cend();} - - source_ptr const& source() const& noexcept {return source_;} - source_ptr&& source() && noexcept {return std::move(source_);} - - std::string const& name() const noexcept {return source_name_;} - - private: - - source_ptr source_; - std::string source_name_; - const_iterator iter_; -}; - // region in a container, normally in a file content. // shared_ptr points the resource that the iter points. // combinators returns this. @@ -86,12 +48,89 @@ struct region_base virtual std::string line() const {return std::string("unknown line");} virtual std::string line_num() const {return std::string("?");} - virtual std::size_t before() const noexcept {return 0;} virtual std::size_t size() const noexcept {return 0;} virtual std::size_t after() const noexcept {return 0;} }; +// location in a container, normally in a file content. +// shared_ptr points the resource that the iter points. +// it can be used not only for resource handling, but also error message. +// +// it can be considered as a region that contains only one character. +template +struct location final : public region_base +{ + static_assert(std::is_same::value,""); + using const_iterator = typename Container::const_iterator; + using source_ptr = std::shared_ptr; + + location(std::string name, Container cont) + : source_(std::make_shared(std::move(cont))), + source_name_(std::move(name)), iter_(source_->cbegin()) + {} + location(const location&) = default; + location(location&&) = default; + location& operator=(const location&) = default; + location& operator=(location&&) = default; + ~location() = default; + + bool is_ok() const noexcept override {return static_cast(source_);} + + const_iterator& iter() noexcept {return iter_;} + const_iterator iter() const noexcept {return iter_;} + + const_iterator begin() const noexcept {return source_->cbegin();} + const_iterator end() const noexcept {return source_->cend();} + + std::string str() const override {return make_string(1, *this->iter());} + std::string name() const override {return source_name_;} + + std::string line_num() const override + { + return std::to_string(1+std::count(this->begin(), this->iter(), '\n')); + } + + std::string line() const override + { + return make_string(this->line_begin(), this->line_end()); + } + + const_iterator line_begin() const noexcept + { + using reverse_iterator = std::reverse_iterator; + return std::find(reverse_iterator(this->iter()), + reverse_iterator(this->begin()), '\n').base(); + } + const_iterator line_end() const noexcept + { + return std::find(this->iter(), this->end(), '\n'); + } + + // location is always points a character. so the size is 1. + std::size_t size() const noexcept override + { + return 1u; + } + std::size_t before() const noexcept override + { + return std::distance(this->line_begin(), this->iter()); + } + std::size_t after() const noexcept override + { + return std::distance(this->iter(), this->line_end()); + } + + source_ptr const& source() const& noexcept {return source_;} + source_ptr&& source() && noexcept {return std::move(source_);} + + private: + + source_ptr source_; + std::string source_name_; + const_iterator iter_; +}; + template struct region final : public region_base { @@ -225,7 +264,19 @@ inline std::string format_underline(const std::string& message, retval += make_string(line_number.size() + 1, ' '); retval += " | "; retval += make_string(reg.before(), ' '); - retval += make_string(reg.size(), '~'); + if(reg.size() == 1) + { + // invalid + // ^------ + retval += '^'; + retval += make_string(reg.after(), '-'); + } + else + { + // invalid + // ~~~~~~~ + retval += make_string(reg.size(), '~'); + } retval += ' '; retval += comment_for_underline; if(helps.size() != 0) @@ -270,7 +321,19 @@ inline std::string format_underline(const std::string& message, retval << make_string(line_num_width + 1, ' '); retval << " | "; retval << make_string(reg1.before(), ' '); - retval << make_string(reg1.size(), '~'); + if(reg1.size() == 1) + { + // invalid + // ^------ + retval << '^'; + retval << make_string(reg1.after(), '-'); + } + else + { + // invalid + // ~~~~~~~ + retval << make_string(reg1.size(), '~'); + } retval << ' '; retval << comment_for_underline1 << newline; // --------------------------------------- @@ -287,7 +350,19 @@ inline std::string format_underline(const std::string& message, retval << make_string(line_num_width + 1, ' '); retval << " | "; retval << make_string(reg2.before(), ' '); - retval << make_string(reg2.size(), '~'); + if(reg2.size() == 1) + { + // invalid + // ^------ + retval << '^'; + retval << make_string(reg2.after(), '-'); + } + else + { + // invalid + // ~~~~~~~ + retval << make_string(reg2.size(), '~'); + } retval << ' '; retval << comment_for_underline2; if(helps.size() != 0) @@ -305,62 +380,84 @@ inline std::string format_underline(const std::string& message, return retval.str(); } - // to show a better error message. -template -std::string -format_underline(const std::string& message, const location& loc, - const std::string& comment_for_underline, - std::vector helps = {}) +inline std::string format_underline(const std::string& message, + std::vector> reg_com, + std::vector helps = {}) { + assert(!reg_com.empty()); + #ifdef _WIN32 const auto newline = "\r\n"; #else const char newline = '\n'; #endif - using const_iterator = typename location::const_iterator; - using reverse_iterator = std::reverse_iterator; - const auto line_begin = std::find(reverse_iterator(loc.iter()), - reverse_iterator(loc.begin()), - '\n').base(); - const auto line_end = std::find(loc.iter(), loc.end(), '\n'); - const auto line_number = std::to_string( - 1 + std::count(loc.begin(), loc.iter(), '\n')); + const auto line_num_width = std::max_element(reg_com.begin(), reg_com.end(), + [](std::pair const& lhs, + std::pair const& rhs) + { + return lhs.first->line_num().size() < rhs.first->line_num().size(); + } + )->first->line_num().size(); + + std::ostringstream retval; + retval << message << newline; + + for(std::size_t i=0; iname() == reg_com.at(i).first->name()) + { + retval << " ..." << newline; + } + else + { + retval << " --> " << reg_com.at(i).first->name() << newline; + } + + const region_base* const reg = reg_com.at(i).first; + const std::string& comment = reg_com.at(i).second; + + + retval << ' ' << std::setw(line_num_width) << reg->line_num(); + retval << " | " << reg->line() << newline; + retval << make_string(line_num_width + 1, ' '); + retval << " | " << make_string(reg->before(), ' '); + + if(reg->size() == 1) + { + // invalid + // ^------ + retval << '^'; + retval << make_string(reg->after(), '-'); + } + else + { + // invalid + // ~~~~~~~ + retval << make_string(reg->size(), '~'); + } + + retval << ' '; + retval << comment << newline; + } - std::string retval; - retval += message; - retval += newline; - retval += " --> "; - retval += loc.name(); - retval += newline; - retval += ' '; - retval += line_number; - retval += " | "; - retval += make_string(line_begin, line_end); - retval += newline; - retval += make_string(line_number.size() + 1, ' '); - retval += " | "; - retval += make_string(std::distance(line_begin, loc.iter()),' '); - retval += '^'; - retval += make_string(std::distance(loc.iter(), line_end), '-'); - retval += ' '; - retval += comment_for_underline; if(helps.size() != 0) { - retval += newline; - retval += make_string(line_number.size() + 1, ' '); - retval += " | "; + retval << newline; + retval << make_string(line_num_width + 1, ' '); + retval << " | "; for(const auto help : helps) { - retval += newline; - retval += "Hint: "; - retval += help; + retval << newline; + retval << "Hint: "; + retval << help; } } - return retval; + return retval.str(); } + } // detail } // toml #endif// TOML11_REGION_H From ae793fb6311c613b7e6c8935fe3aa445333df426 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 2 Mar 2019 17:55:51 +0900 Subject: [PATCH 02/37] feat: improve error message for invalid array --- toml/parser.hpp | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 983d54e..111f145 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -876,16 +876,39 @@ parse_array(location& loc) { if(!retval.empty() && retval.front().type() != val.as_ok().type()) { - throw syntax_error(format_underline( - "[error] toml::parse_array: type of elements should be the " - "same each other.", region(loc, first, loc.iter()), - "inhomogeneous types")); + auto array_start_loc = loc; + array_start_loc.iter() = first; + + throw syntax_error(format_underline("[error] toml::parse_array: " + "type of elements should be the same each other.", + std::vector>{ + std::make_pair( + std::addressof(array_start_loc), + std::string("array starts here") + ), + std::make_pair( + std::addressof(get_region(retval.front())), + std::string("value has type ") + + stringize(retval.front().type()) + ), + std::make_pair( + std::addressof(get_region(val.unwrap())), + std::string("value has different type, ") + + stringize(val.unwrap().type()) + ) + })); } retval.push_back(std::move(val.unwrap())); } else { - return err(val.unwrap_err()); + auto array_start_loc = loc; + array_start_loc.iter() = first; + + throw syntax_error(format_underline("[error] toml::parse_array: " + "value having invalid format appeared in an array", + array_start_loc, "array starts here", + loc, "it is not a valid value.")); } using lex_array_separator = sequence, character<','>>; @@ -901,8 +924,12 @@ parse_array(location& loc) } else { + auto array_start_loc = loc; + array_start_loc.iter() = first; + throw syntax_error(format_underline("[error] toml::parse_array:" - " missing array separator `,`", loc, "should be `,`")); + " missing array separator `,` after a value", + array_start_loc, "array starts here", loc, "should be `,`")); } } } @@ -960,6 +987,7 @@ parse_key_value_pair(location& loc) { std::string msg; loc.iter() = after_kvsp; + // check there is something not a comment/whitespace after `=` if(sequence, maybe, lex_newline>::invoke(loc)) { loc.iter() = after_kvsp; @@ -967,10 +995,9 @@ parse_key_value_pair(location& loc) "missing value after key-value separator '='", loc, "expected value, but got nothing"); } - else + else // there is something not a comment/whitespace, so invalid format. { - msg = format_underline("[error] toml::parse_key_value_pair: " - "invalid value format", loc, val.unwrap_err()); + msg = std::move(val.unwrap_err()); } loc.iter() = first; return err(msg); @@ -1114,7 +1141,7 @@ insert_nested_key(table& root, const toml::value& v, "[error] toml::insert_value: value (\"", format_dotted_keys(first, last), "\") already exists."), get_region(tab->at(k)), "value already exists here", - get_region(v), "value inserted twice")); + get_region(v), "value defined twice")); } } tab->insert(std::make_pair(k, v)); From 2accc9d22ca5d66e6ecd2fbeea2ba08d88be2534 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 3 Mar 2019 18:56:45 +0900 Subject: [PATCH 03/37] fix: diagnose, but not throw for unicode error in 2.0.x and 2.1.0, README says "it shows warning" for invalid unicode codepoints. So far, this library just show an error message in stderr for this case. It is not good to change the behavior fatal in the next minor release, 2.1.1, that includes patches and improved error msgs. I will make it throw syntax_error after 2.2.0 for invalid unicode codepoints. For now, I will keep it to be "warning". --- toml/parser.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 111f145..378be6e 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -250,10 +250,10 @@ std::string read_utf8_codepoint(const region& reg, { if(0xD800 <= codepoint && codepoint <= 0xDFFF) { - throw syntax_error(format_underline("[error] " + std::cerr << format_underline("[warning] " "toml::read_utf8_codepoint: codepoints in the range " "[0xD800, 0xDFFF] are not valid UTF-8.", - loc, "not a valid UTF-8 codepoint")); + loc, "not a valid UTF-8 codepoint") << std::endl; } assert(codepoint < 0xD800 || 0xDFFF < codepoint); // 1110yyyy 10yxxxxx 10xxxxxx @@ -265,10 +265,10 @@ std::string read_utf8_codepoint(const region& reg, { if(0x10FFFF < codepoint) // out of Unicode region { - throw syntax_error(format_underline("[error] " + std::cerr << format_underline("[error] " "toml::read_utf8_codepoint: input codepoint is too large to " "decode as a unicode character.", loc, - "should be in [0x00..0x10FFFF]")); + "should be in [0x00..0x10FFFF]") << std::endl; } // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx character += static_cast(0xF0| codepoint >> 18); From d88521d63c2f29b0e6e91ddd60ec4e99728a307a Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Mon, 4 Mar 2019 15:01:28 +0900 Subject: [PATCH 04/37] feat: enable to change region of value To allow the following toml file, we need to replace the region after the more precise region is found. ```toml [a.b.c] d = 42 [a] e = 2.71 ``` If the precise region (here, [a]) is found, the region of `a` should be `[a]`, not `[a.b.c]`. After `[a]` is defined, toml does not allow to write `[a]` twice. To check it, we need to replace the region of values to the precise one. --- toml/value.hpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/toml/value.hpp b/toml/value.hpp index d6f5f0e..03812c7 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -21,6 +21,8 @@ namespace detail { // to show error messages. not recommended for users. region_base const& get_region(const value&); +template +void change_region(value&, Region&&); }// detail template @@ -560,6 +562,9 @@ class value // for error messages friend region_base const& detail::get_region(const value&); + template + friend void detail::change_region(value&, Region&&); + template struct switch_cast; @@ -594,6 +599,20 @@ inline region_base const& get_region(const value& v) { return *(v.region_info_); } + +template +void change_region(value& v, Region&& reg) +{ + using region_type = typename std::remove_reference< + typename std::remove_cv::type + >::type; + + std::shared_ptr new_reg = + std::make_shared(std::forward(reg)); + v.region_info_ = new_reg; + return; +} + }// detail template<> struct value::switch_cast From b0ed122214db80a80f7ef740e063200b3aea7ca7 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 5 Mar 2019 23:25:25 +0900 Subject: [PATCH 05/37] fix: allow deeper table appeared before allow the following toml file. ```toml [a.b.c] d = 10 [a] e = 2.718 ``` --- toml/parser.hpp | 99 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index faa7213..8512643 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -987,6 +987,75 @@ std::string format_dotted_keys(InputIterator first, const InputIterator last) return retval; } +// forward decl for is_valid_forward_table_definition +template +result, region>, std::string> +parse_table_key(location& loc); +// The following toml file is allowed. +// ```toml +// [a.b.c] # here, table `a` has element `b`. +// foo = "bar" +// [a] # merge a = {baz = "qux"} to a = {b = {...}} +// baz = "qux" +// ``` +// But the following is not allowed. +// ```toml +// [a] +// b.c.foo = "bar" +// [a] # error! the same table [a] defined! +// baz = "qux" +// ``` +// The following is neither allowed. +// ```toml +// a = { b.c.foo = "bar"} +// [a] # error! the same table [a] defined! +// baz = "qux" +// ``` +// Here, it parses region of `tab->at(k)` as a table key and check the depth +// of the key. If the key region points deeper node, it would be allowed. +// Otherwise, the key points the same node. It would be rejected. +template +bool is_valid_forward_table_definition(const value& fwd, + Iterator key_first, Iterator key_curr, Iterator key_last) +{ + location def("internal", detail::get_region(fwd).str()); + if(const auto tabkeys = parse_table_key(def)) + { + // table keys always contains all the nodes from the root. + const auto& tks = tabkeys.unwrap().first; + if(std::distance(key_first, key_last) == tks.size() && + std::equal(tks.begin(), tks.end(), key_first)) + { + // the keys are equivalent. it is not allowed. + return false; + } + // the keys are not equivalent. it is allowed. + return true; + } + if(const auto dotkeys = parse_key(def)) + { + // consider the following case. + // [a] + // b.c = {d = 42} + // [a.b.c] + // e = 2.71 + // this defines the table [a.b.c] twice. no? + + // a dotted key starts from the node representing a table in which the + // dotted key belongs to. + const auto& dks = dotkeys.unwrap().first; + if(std::distance(key_curr, key_last) == dks.size() && + std::equal(dks.begin(), dks.end(), key_curr)) + { + // the keys are equivalent. it is not allowed. + return false; + } + // the keys are not equivalent. it is allowed. + return true; + } + return false; +} + template result insert_nested_key(table& root, const toml::value& v, @@ -1077,16 +1146,34 @@ insert_nested_key(table& root, const toml::value& v, tab->insert(std::make_pair(k, aot)); return ok(true); } - } + } // end if(array of table) + if(tab->count(k) == 1) { if(tab->at(k).is(value_t::Table) && v.is(value_t::Table)) { - throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: table (\"", - format_dotted_keys(first, last), "\") already exists."), - get_region(tab->at(k)), "table already exists here", - get_region(v), "table defined twice")); + if(!is_valid_forward_table_definition( + tab->at(k), first, iter, last)) + { + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: table (\"", + format_dotted_keys(first, last), + "\") already exists."), + get_region(tab->at(k)), "table already exists here", + get_region(v), "table defined twice")); + } + // to allow the following toml file. + // [a.b.c] + // d = 42 + // [a] + // e = 2.71 + auto& t = tab->at(k).cast(); + for(const auto& kv : v.cast()) + { + t[kv.first] = kv.second; + } + detail::change_region(tab->at(k), key_reg); + return ok(true); } else if(v.is(value_t::Table) && tab->at(k).is(value_t::Array) && From 084e82a8a9e0a28c389b8687d3641e5513f437b2 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 7 Mar 2019 14:09:02 +0900 Subject: [PATCH 06/37] chore: update README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 43b6f8c..2861b34 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ toml11 [![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136) -C++11 header-only toml parser depending only on C++ standard library. +C++11 header-only toml parser/encoder depending only on C++ standard library. compatible to the latest version of [TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md) @@ -642,6 +642,7 @@ I thank the contributor for providing great feature to this repository. - Intel Compiler support - Quentin Khan (@xaxousis) - Found & Fixed a bug around ODR + - Improved error message to show the location where the parser fails ## Licensing terms From dc112bd6c152174bb44c6e475486f30987594d20 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 12 Mar 2019 20:43:07 +0900 Subject: [PATCH 07/37] feat: add is_[boolean|integer|...]() member func it is an alias to is --- toml/value.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/toml/value.hpp b/toml/value.hpp index 03812c7..fc38dbf 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -537,6 +537,18 @@ class value bool is() const noexcept {return value_traits::type_index == this->type_;} bool is(value_t t) const noexcept {return t == this->type_;} + bool is_uninitialized() const noexcept {return this->is(value_t::Empty );} + bool is_boolean() const noexcept {return this->is(value_t::Boolean );} + bool is_integer() const noexcept {return this->is(value_t::Integer );} + bool is_float() const noexcept {return this->is(value_t::Float );} + bool is_string() const noexcept {return this->is(value_t::String );} + bool is_offset_datetime() const noexcept {return this->is(value_t::OffsetDatetime);} + bool is_local_datetime() const noexcept {return this->is(value_t::LocalDatetime );} + bool is_local_date() const noexcept {return this->is(value_t::LocalDate );} + bool is_local_time() const noexcept {return this->is(value_t::LocalTime );} + bool is_array() const noexcept {return this->is(value_t::Array );} + bool is_table() const noexcept {return this->is(value_t::Table );} + value_t type() const {return type_;} template From 6693ec78f4fcd4d5916368f4789b3c335dcd4867 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 12 Mar 2019 20:44:27 +0900 Subject: [PATCH 08/37] test: add test for toml::value::is_something() --- tests/test_value.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/tests/test_value.cpp b/tests/test_value.cpp index 702fa8d..a0b38e4 100644 --- a/tests/test_value.cpp +++ b/tests/test_value.cpp @@ -20,6 +20,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean) BOOST_CHECK(v2.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_boolean()); + BOOST_CHECK(v2.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); BOOST_CHECK_EQUAL(v2.cast(), false); @@ -33,6 +35,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean) BOOST_CHECK(v2.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_boolean()); + BOOST_CHECK(v2.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), false); BOOST_CHECK_EQUAL(v2.cast(), true); @@ -48,6 +52,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean) BOOST_CHECK(v4.is(toml::value_t::Boolean)); BOOST_CHECK(v3.is()); BOOST_CHECK(v4.is()); + BOOST_CHECK(v3.is_boolean()); + BOOST_CHECK(v4.is_boolean()); BOOST_CHECK_EQUAL(v3.cast(), false); BOOST_CHECK_EQUAL(v4.cast(), true); @@ -61,6 +67,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean) BOOST_CHECK(v6.is(toml::value_t::Boolean)); BOOST_CHECK(v5.is()); BOOST_CHECK(v6.is()); + BOOST_CHECK(v3.is_boolean()); + BOOST_CHECK(v4.is_boolean()); BOOST_CHECK_EQUAL(v5.cast(), false); BOOST_CHECK_EQUAL(v6.cast(), true); @@ -74,6 +82,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean) BOOST_CHECK(v2.is(toml::value_t::Float)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_integer()); + BOOST_CHECK(v2.is_float()); BOOST_CHECK_EQUAL(v1.cast(), 42); BOOST_CHECK_EQUAL(v2.cast(), 3.14); @@ -90,6 +100,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer) BOOST_CHECK(v2.is(toml::value_t::Integer)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_integer()); + BOOST_CHECK(v2.is_integer()); BOOST_CHECK_EQUAL(v1.cast(), -42); BOOST_CHECK_EQUAL(v2.cast(), 42u); @@ -103,6 +115,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer) BOOST_CHECK(v2.is(toml::value_t::Integer)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_integer()); + BOOST_CHECK(v2.is_integer()); BOOST_CHECK_EQUAL(v1.cast(), 54); BOOST_CHECK_EQUAL(v2.cast(), -54); @@ -118,6 +132,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer) BOOST_CHECK(v4.is(toml::value_t::Integer)); BOOST_CHECK(v3.is()); BOOST_CHECK(v4.is()); + BOOST_CHECK(v3.is_integer()); + BOOST_CHECK(v4.is_integer()); BOOST_CHECK_EQUAL(v3.cast(), 54); BOOST_CHECK_EQUAL(v4.cast(), -54); @@ -131,6 +147,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer) BOOST_CHECK(v6.is(toml::value_t::Integer)); BOOST_CHECK(v5.is()); BOOST_CHECK(v6.is()); + BOOST_CHECK(v5.is_integer()); + BOOST_CHECK(v6.is_integer()); BOOST_CHECK_EQUAL(v5.cast(), 54); BOOST_CHECK_EQUAL(v6.cast(), -54); @@ -144,6 +162,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer) BOOST_CHECK(v2.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_boolean()); + BOOST_CHECK(v2.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); BOOST_CHECK_EQUAL(v2.cast(), false); @@ -160,6 +180,8 @@ BOOST_AUTO_TEST_CASE(test_value_float) BOOST_CHECK(v2.is(toml::value_t::Float)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_float()); + BOOST_CHECK(v2.is_float()); BOOST_CHECK_EQUAL(v1.cast(), 3.14); BOOST_CHECK_CLOSE_FRACTION(v2.cast(), 3.14, 1e-2); @@ -173,6 +195,8 @@ BOOST_AUTO_TEST_CASE(test_value_float) BOOST_CHECK(v2.is(toml::value_t::Float)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_float()); + BOOST_CHECK(v2.is_float()); BOOST_CHECK_CLOSE_FRACTION(v1.cast(), 2.718, 1e-3); BOOST_CHECK_EQUAL(v2.cast(), 2.718); @@ -188,6 +212,8 @@ BOOST_AUTO_TEST_CASE(test_value_float) BOOST_CHECK(v4.is(toml::value_t::Float)); BOOST_CHECK(v3.is()); BOOST_CHECK(v4.is()); + BOOST_CHECK(v3.is_float()); + BOOST_CHECK(v4.is_float()); BOOST_CHECK_CLOSE_FRACTION(v3.cast(), 2.718, 1e-3); BOOST_CHECK_EQUAL(v4.cast(), 2.718); @@ -201,6 +227,8 @@ BOOST_AUTO_TEST_CASE(test_value_float) BOOST_CHECK(v6.is(toml::value_t::Float)); BOOST_CHECK(v5.is()); BOOST_CHECK(v6.is()); + BOOST_CHECK(v5.is_float()); + BOOST_CHECK(v6.is_float()); BOOST_CHECK_CLOSE_FRACTION(v5.cast(), 2.718, 1e-3); BOOST_CHECK_EQUAL(v6.cast(), 2.718); @@ -214,6 +242,8 @@ BOOST_AUTO_TEST_CASE(test_value_float) BOOST_CHECK(v2.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_boolean()); + BOOST_CHECK(v2.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); BOOST_CHECK_EQUAL(v2.cast(), false); @@ -234,6 +264,9 @@ BOOST_AUTO_TEST_CASE(test_value_string) BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); BOOST_CHECK(v3.is()); + BOOST_CHECK(v1.is_string()); + BOOST_CHECK(v2.is_string()); + BOOST_CHECK(v3.is_string()); BOOST_CHECK_EQUAL(v1.cast(), "foo"); BOOST_CHECK_EQUAL(v2.cast(), "foo"); @@ -249,9 +282,9 @@ BOOST_AUTO_TEST_CASE(test_value_string) BOOST_CHECK(v1.is(toml::value_t::String)); BOOST_CHECK(v2.is(toml::value_t::String)); BOOST_CHECK(v3.is(toml::value_t::String)); - BOOST_CHECK(v1.is()); - BOOST_CHECK(v2.is()); - BOOST_CHECK(v3.is()); + BOOST_CHECK(v1.is_string()); + BOOST_CHECK(v2.is_string()); + BOOST_CHECK(v3.is_string()); BOOST_CHECK_EQUAL(v1.cast(), "bar"); BOOST_CHECK_EQUAL(v2.cast(), "bar"); @@ -273,6 +306,9 @@ BOOST_AUTO_TEST_CASE(test_value_string) BOOST_CHECK(v4.is()); BOOST_CHECK(v5.is()); BOOST_CHECK(v6.is()); + BOOST_CHECK(v4.is_string()); + BOOST_CHECK(v5.is_string()); + BOOST_CHECK(v6.is_string()); BOOST_CHECK_EQUAL(v4.cast(), "bar"); BOOST_CHECK_EQUAL(v5.cast(), "bar"); @@ -291,6 +327,9 @@ BOOST_AUTO_TEST_CASE(test_value_string) BOOST_CHECK(v4.is()); BOOST_CHECK(v5.is()); BOOST_CHECK(v6.is()); + BOOST_CHECK(v4.is_string()); + BOOST_CHECK(v5.is_string()); + BOOST_CHECK(v6.is_string()); BOOST_CHECK_EQUAL(v4.cast(), "baz"); BOOST_CHECK_EQUAL(v5.cast(), "baz"); @@ -309,6 +348,9 @@ BOOST_AUTO_TEST_CASE(test_value_string) BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); BOOST_CHECK(v3.is()); + BOOST_CHECK(v1.is_boolean()); + BOOST_CHECK(v2.is_boolean()); + BOOST_CHECK(v3.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); BOOST_CHECK_EQUAL(v2.cast(), true); @@ -322,6 +364,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); BOOST_CHECK(v1.is(toml::value_t::LocalDate)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_local_date()); BOOST_CHECK_EQUAL(v1.cast(), toml::local_date(2018, toml::month_t::Jan, 31)); @@ -331,6 +374,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); BOOST_CHECK(v1.is(toml::value_t::LocalDate)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_local_date()); BOOST_CHECK_EQUAL(v1.cast(), toml::local_date(2018, toml::month_t::Apr, 1)); @@ -341,6 +385,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date) BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDate); BOOST_CHECK(v2.is(toml::value_t::LocalDate)); BOOST_CHECK(v2.is()); + BOOST_CHECK(v2.is_local_date()); BOOST_CHECK_EQUAL(v2.cast(), toml::local_date(2018, toml::month_t::Apr, 1)); @@ -349,6 +394,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK(v1.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); } @@ -364,6 +410,8 @@ BOOST_AUTO_TEST_CASE(test_value_local_time) BOOST_CHECK(v2.is(toml::value_t::LocalTime)); BOOST_CHECK(v1.is()); BOOST_CHECK(v2.is()); + BOOST_CHECK(v1.is_local_time()); + BOOST_CHECK(v2.is_local_time()); BOOST_CHECK_EQUAL(v1.cast(), toml::local_time(12, 30, 45)); @@ -377,6 +425,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_time) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime); BOOST_CHECK(v1.is(toml::value_t::LocalTime)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_local_time()); BOOST_CHECK_EQUAL(v1.cast(), toml::local_time(1, 30, 0, 100, 0)); @@ -386,6 +435,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_time) BOOST_CHECK_EQUAL(v3.type(), toml::value_t::LocalTime); BOOST_CHECK(v3.is(toml::value_t::LocalTime)); BOOST_CHECK(v3.is()); + BOOST_CHECK(v3.is_local_time()); BOOST_CHECK_EQUAL(v3.cast(), toml::local_time(1, 30, 0, 100, 0)); @@ -394,6 +444,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_time) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK(v1.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); } @@ -407,6 +458,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_local_datetime()); BOOST_CHECK_EQUAL(v1.cast(), toml::local_datetime( @@ -420,6 +472,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_local_datetime()); BOOST_CHECK_EQUAL(v1.cast(), toml::local_datetime( @@ -432,6 +485,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime) BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDatetime); BOOST_CHECK(v2.is(toml::value_t::LocalDatetime)); BOOST_CHECK(v2.is()); + BOOST_CHECK(v2.is_local_datetime()); BOOST_CHECK_EQUAL(v2.cast(), toml::local_datetime( @@ -442,6 +496,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK(v1.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); } @@ -456,6 +511,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_offset_datetime()); BOOST_CHECK_EQUAL(v1.cast(), toml::offset_datetime( @@ -472,6 +528,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_offset_datetime()); BOOST_CHECK_EQUAL(v1.cast(), toml::offset_datetime( @@ -485,6 +542,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime) BOOST_CHECK_EQUAL(v2.type(), toml::value_t::OffsetDatetime); BOOST_CHECK(v2.is(toml::value_t::OffsetDatetime)); BOOST_CHECK(v2.is()); + BOOST_CHECK(v2.is_offset_datetime()); BOOST_CHECK_EQUAL(v2.cast(), toml::offset_datetime( @@ -495,6 +553,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK(v1.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); } @@ -507,10 +566,12 @@ BOOST_AUTO_TEST_CASE(test_value_array) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); BOOST_CHECK(v1.is(toml::value_t::Array)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_array()); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); BOOST_CHECK(v2.is(toml::value_t::Array)); BOOST_CHECK(v2.is()); + BOOST_CHECK(v2.is_array()); BOOST_CHECK_EQUAL(v1.cast().at(0).cast(), 1); BOOST_CHECK_EQUAL(v1.cast().at(1).cast(), 2); @@ -530,10 +591,12 @@ BOOST_AUTO_TEST_CASE(test_value_array) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); BOOST_CHECK(v1.is(toml::value_t::Array)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_array()); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); BOOST_CHECK(v2.is(toml::value_t::Array)); BOOST_CHECK(v2.is()); + BOOST_CHECK(v2.is_array()); BOOST_CHECK_EQUAL(v1.cast().at(0).cast(), 6); BOOST_CHECK_EQUAL(v1.cast().at(1).cast(), 7); @@ -553,6 +616,7 @@ BOOST_AUTO_TEST_CASE(test_value_array) BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Array); BOOST_CHECK(v3.is(toml::value_t::Array)); BOOST_CHECK(v3.is()); + BOOST_CHECK(v3.is_array()); BOOST_CHECK_EQUAL(v3.cast().at(0).cast(), 6); BOOST_CHECK_EQUAL(v3.cast().at(1).cast(), 7); @@ -564,6 +628,7 @@ BOOST_AUTO_TEST_CASE(test_value_array) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK(v1.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); } @@ -574,6 +639,7 @@ BOOST_AUTO_TEST_CASE(test_value_table) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); BOOST_CHECK(v1.is(toml::value_t::Table)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_table()); BOOST_CHECK_EQUAL(v1.cast().at("foo").cast(), 42); BOOST_CHECK_EQUAL(v1.cast().at("bar").cast(), 3.14); @@ -584,6 +650,7 @@ BOOST_AUTO_TEST_CASE(test_value_table) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); BOOST_CHECK(v1.is(toml::value_t::Table)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_table()); BOOST_CHECK_EQUAL(v1.cast().at("foo").cast(), 2.71); BOOST_CHECK_EQUAL(v1.cast().at("bar").cast(), 54); @@ -595,6 +662,7 @@ BOOST_AUTO_TEST_CASE(test_value_table) BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Table); BOOST_CHECK(v3.is(toml::value_t::Table)); BOOST_CHECK(v3.is()); + BOOST_CHECK(v3.is_table()); BOOST_CHECK_EQUAL(v3.cast().at("foo").cast(), 2.71); BOOST_CHECK_EQUAL(v3.cast().at("bar").cast(), 54); @@ -604,5 +672,13 @@ BOOST_AUTO_TEST_CASE(test_value_table) BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); BOOST_CHECK(v1.is(toml::value_t::Boolean)); BOOST_CHECK(v1.is()); + BOOST_CHECK(v1.is_boolean()); BOOST_CHECK_EQUAL(v1.cast(), true); } + +BOOST_AUTO_TEST_CASE(test_value_empty) +{ + toml::value v1; + BOOST_CHECK(v1.is_uninitialized()); + BOOST_CHECK(v1.is(toml::value_t::Empty)); +} From c4c416e8b21714f4b05f87131b85802fc61a1f0e Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 12 Mar 2019 22:18:25 +0900 Subject: [PATCH 09/37] doc: add is_* function to README --- README.md | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2861b34..84f6d28 100644 --- a/README.md +++ b/README.md @@ -387,7 +387,31 @@ If it's not a `toml::table`, the same error as "invalid type" would be thrown. ### Checking value type -When you don't know the exact type of toml-value, you can get `enum` type from `toml::value`. +You can check what type of value does `toml::value` contains by `is_*` function. + +```cpp +toml::value v = /* ... */; +if(v.is_integer() && toml::get(v) == 42) +{ + std::cout << "value is 42" << std::endl; +} +``` + +The complete list of the functions is below. + +- `bool toml::value::is_boolean() const noexcept;` +- `bool toml::value::is_integer() const noexcept;` +- `bool toml::value::is_float() const noexcept;` +- `bool toml::value::is_string() const noexcept;` +- `bool toml::value::is_offset_datetime() const noexcept;` +- `bool toml::value::is_local_datetime() const noexcept;` +- `bool toml::value::is_local_date() const noexcept;` +- `bool toml::value::is_local_time() const noexcept;` +- `bool toml::value::is_array() const noexcept;` +- `bool toml::value::is_table() const noexcept;` +- `bool toml::value::is_uninitialized() const noexcept;` + +Also, you can get `enum class` value from `toml::value`. ```cpp switch(data.at("something").type()) @@ -400,6 +424,16 @@ switch(data.at("something").type()) } ``` +The complete list of the `enum`s can be found in the section +[underlying types](#underlying-types). + +The `enum`s can be used as a parameter of `toml::value::is` function like the following. + +```cpp +toml::value v = /* ... */; +if(v.is(toml::value_t::Boolean)) // ... +``` + ### Fill only the matched value The more sophisticated way is using `toml::from_toml` and `std::tie`. From dddcecb0340f58fca9d45703a8d1efb6f0e946cc Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 12 Mar 2019 23:37:46 +0900 Subject: [PATCH 10/37] fix: use snprintf instead of stringstream to avoid the effect of locale --- toml/serializer.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/toml/serializer.hpp b/toml/serializer.hpp index cb150bb..c4fb689 100644 --- a/toml/serializer.hpp +++ b/toml/serializer.hpp @@ -5,6 +5,7 @@ #include "value.hpp" #include "lexer.hpp" #include +#include namespace toml { @@ -30,13 +31,12 @@ struct serializer } std::string operator()(const toml::floating f) const { - std::string token = [=] { - // every float value needs decimal point (or exponent). - std::ostringstream oss; - oss << std::setprecision(float_prec_) << std::showpoint << f; - return oss.str(); - }(); + const auto fmt = "%.*g"; + const auto bsz = std::snprintf(nullptr, 0, fmt, int(this->float_prec_), f); + std::vector buf(bsz + 1, '\0'); // +1 for null character(\0) + std::snprintf(buf.data(), buf.size(), fmt, int(this->float_prec_), f); + std::string token(buf.begin(), buf.end()); if(token.back() == '.') // 1. => 1.0 { token += '0'; From 46b35870c55356824a11078b483c40f0dea20d1d Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 13 Mar 2019 01:17:27 +0900 Subject: [PATCH 11/37] style: remove needless type casting --- toml/serializer.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toml/serializer.hpp b/toml/serializer.hpp index c4fb689..c25f8b2 100644 --- a/toml/serializer.hpp +++ b/toml/serializer.hpp @@ -32,9 +32,9 @@ struct serializer std::string operator()(const toml::floating f) const { const auto fmt = "%.*g"; - const auto bsz = std::snprintf(nullptr, 0, fmt, int(this->float_prec_), f); + const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f); std::vector buf(bsz + 1, '\0'); // +1 for null character(\0) - std::snprintf(buf.data(), buf.size(), fmt, int(this->float_prec_), f); + std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); std::string token(buf.begin(), buf.end()); if(token.back() == '.') // 1. => 1.0 From 74ceceef73675bb870c712b040cd71c84efb9aff Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 13 Mar 2019 14:03:04 +0900 Subject: [PATCH 12/37] fix: suppress warning about sign-unsign comparison The solution is not ideal, but it's okay at the line --- toml/parser.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 4bd4b60..26b13b1 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1058,7 +1058,7 @@ bool is_valid_forward_table_definition(const value& fwd, { // table keys always contains all the nodes from the root. const auto& tks = tabkeys.unwrap().first; - if(std::distance(key_first, key_last) == tks.size() && + if(std::size_t(std::distance(key_first, key_last)) == tks.size() && std::equal(tks.begin(), tks.end(), key_first)) { // the keys are equivalent. it is not allowed. @@ -1079,7 +1079,7 @@ bool is_valid_forward_table_definition(const value& fwd, // a dotted key starts from the node representing a table in which the // dotted key belongs to. const auto& dks = dotkeys.unwrap().first; - if(std::distance(key_curr, key_last) == dks.size() && + if(std::size_t(std::distance(key_curr, key_last)) == dks.size() && std::equal(dks.begin(), dks.end(), key_curr)) { // the keys are equivalent. it is not allowed. From 5b2ce267213cbcd900aed6fde1fda87b9d706da3 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 14 Mar 2019 00:56:35 +0900 Subject: [PATCH 13/37] refactor: remove redundant function since N region format_underline() has been implemented, overloads for 1 and 2 region(s) are not needed. --- toml/region.hpp | 149 +----------------------------------------------- 1 file changed, 3 insertions(+), 146 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index 62a28e1..0601bce 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -237,149 +237,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 region_base& reg, const std::string& comment_for_underline, - std::vector helps = {}) -{ -#ifdef _WIN32 - const auto newline = "\r\n"; -#else - const char newline = '\n'; -#endif - const auto line = reg.line(); - const auto line_number = reg.line_num(); - - std::string retval; - retval += message; - retval += newline; - retval += " --> "; - retval += reg.name(); - retval += newline; - retval += ' '; - retval += line_number; - retval += " | "; - retval += line; - retval += newline; - retval += make_string(line_number.size() + 1, ' '); - retval += " | "; - retval += make_string(reg.before(), ' '); - if(reg.size() == 1) - { - // invalid - // ^------ - retval += '^'; - retval += make_string(reg.after(), '-'); - } - else - { - // invalid - // ~~~~~~~ - retval += make_string(reg.size(), '~'); - } - retval += ' '; - retval += comment_for_underline; - if(helps.size() != 0) - { - retval += newline; - retval += make_string(line_number.size() + 1, ' '); - retval += " | "; - for(const auto help : helps) - { - retval += newline; - retval += "Hint: "; - retval += help; - } - } - return retval; -} - -// to show a better error message. -inline std::string format_underline(const std::string& message, - const region_base& reg1, const std::string& comment_for_underline1, - const region_base& reg2, const std::string& comment_for_underline2, - std::vector helps = {}) -{ -#ifdef _WIN32 - const auto newline = "\r\n"; -#else - const char newline = '\n'; -#endif - const auto line1 = reg1.line(); - const auto line_number1 = reg1.line_num(); - const auto line2 = reg2.line(); - const auto line_number2 = reg2.line_num(); - const auto line_num_width = - std::max(line_number1.size(), line_number2.size()); - - std::ostringstream retval; - retval << message << newline; - retval << " --> " << reg1.name() << newline; -// --------------------------------------- - retval << ' ' << std::setw(line_num_width) << line_number1; - retval << " | " << line1 << newline; - retval << make_string(line_num_width + 1, ' '); - retval << " | "; - retval << make_string(reg1.before(), ' '); - if(reg1.size() == 1) - { - // invalid - // ^------ - retval << '^'; - retval << make_string(reg1.after(), '-'); - } - else - { - // invalid - // ~~~~~~~ - retval << make_string(reg1.size(), '~'); - } - retval << ' '; - retval << comment_for_underline1 << newline; -// --------------------------------------- - if(reg2.name() != reg1.name()) - { - retval << " --> " << reg2.name() << newline; - } - else - { - retval << " ..." << newline; - } - retval << ' ' << std::setw(line_num_width) << line_number2; - retval << " | " << line2 << newline; - retval << make_string(line_num_width + 1, ' '); - retval << " | "; - retval << make_string(reg2.before(), ' '); - if(reg2.size() == 1) - { - // invalid - // ^------ - retval << '^'; - retval << make_string(reg2.after(), '-'); - } - else - { - // invalid - // ~~~~~~~ - retval << make_string(reg2.size(), '~'); - } - retval << ' '; - retval << comment_for_underline2; - if(helps.size() != 0) - { - retval << newline; - retval << make_string(line_num_width + 1, ' '); - retval << " | "; - for(const auto help : helps) - { - retval << newline; - retval << "Hint: "; - retval << help; - } - } - return retval.str(); -} - // to show a better error message. inline std::string format_underline(const std::string& message, std::vector> reg_com, @@ -408,10 +265,11 @@ inline std::string format_underline(const std::string& message, { if(i!=0 && reg_com.at(i-1).first->name() == reg_com.at(i).first->name()) { - retval << " ..." << newline; + retval << newline << " ..." << newline; } else { + if(i != 0) {retval << newline;} retval << " --> " << reg_com.at(i).first->name() << newline; } @@ -439,7 +297,7 @@ inline std::string format_underline(const std::string& message, } retval << ' '; - retval << comment << newline; + retval << comment; } if(helps.size() != 0) @@ -457,7 +315,6 @@ inline std::string format_underline(const std::string& message, return retval.str(); } - } // detail } // toml #endif// TOML11_REGION_H From 0babe8d589a8a527b39eb23b99b3741bfa97fe1b Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 14 Mar 2019 00:59:10 +0900 Subject: [PATCH 14/37] fix: use format_underline for N regions everywhere --- toml/get.hpp | 28 ++-- toml/parser.hpp | 352 +++++++++++++++++++++++++++--------------------- toml/value.hpp | 36 +++-- 3 files changed, 236 insertions(+), 180 deletions(-) diff --git a/toml/get.hpp b/toml/get.hpp index fe51622..80336ce 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -210,8 +210,9 @@ T get(const value& v) { throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified container size is ", container.size(), - " but there are ", ar.size(), " elements in toml array."), - detail::get_region(v), "here")); + " but there are ", ar.size(), " elements in toml array."), { + {std::addressof(detail::get_region(v)), "here"} + })); } std::transform(ar.cbegin(), ar.cend(), container.begin(), [](const value& x){return ::toml::get(x);}); @@ -233,7 +234,9 @@ T get(const value& v) { throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified std::pair but there are ", ar.size(), - " elements in toml array."), detail::get_region(v), "here")); + " elements in toml array."), { + {std::addressof(detail::get_region(v)), "here"} + })); } return std::make_pair(::toml::get(ar.at(0)), ::toml::get(ar.at(1))); @@ -264,7 +267,9 @@ T get(const value& v) throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified std::tuple with ", std::tuple_size::value, "elements, but there are ", ar.size(), - " elements in toml array."), detail::get_region(v), "here")); + " elements in toml array."), { + {std::addressof(detail::get_region(v)), "here"} + })); } return detail::get_tuple_impl(ar, detail::make_index_sequence::value>{}); @@ -340,8 +345,9 @@ find(const toml::value& v, const toml::key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), detail::get_region(v), - "in this table")); + "[error] key \"", ky, "\" not found"), { + {std::addressof(detail::get_region(v)), "in this table"} + })); } return ::toml::get(tab.at(ky)); } @@ -353,8 +359,9 @@ find(toml::value& v, const toml::key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), detail::get_region(v), - "in this table")); + "[error] key \"", ky, "\" not found"), { + {std::addressof(detail::get_region(v)), "in this table"} + })); } return ::toml::get(tab.at(ky)); } @@ -366,8 +373,9 @@ find(toml::value&& v, const toml::key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), detail::get_region(v), - "in this table")); + "[error] key \"", ky, "\" not found"), { + {std::addressof(detail::get_region(v)), "in this table"} + })); } return ::toml::get(std::move(tab[ky])); } diff --git a/toml/parser.hpp b/toml/parser.hpp index 26b13b1..9c1d562 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -29,13 +29,13 @@ parse_boolean(location& loc) else // internal error. { throw toml::internal_error(format_underline( - "[error] toml::parse_boolean: internal error", reg, - "invalid token")); + "[error] toml::parse_boolean: internal error", + {{std::addressof(reg), "invalid token"}})); } } loc.iter() = first; //rollback - return err(format_underline("[error] toml::parse_boolean: ", loc, - "the next token is not a boolean")); + return err(format_underline("[error] toml::parse_boolean: ", + {{std::addressof(loc), "the next token is not a boolean"}})); } template @@ -57,14 +57,14 @@ parse_binary_integer(location& loc) { throw toml::internal_error(format_underline( "[error] toml::parse_integer: internal error", - token.unwrap(), "invalid token")); + {{std::addressof(token.unwrap()), "invalid token"}})); } } return ok(std::make_pair(retval, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_binary_integer:", loc, - "the next token is not an integer")); + return err(format_underline("[error] toml::parse_binary_integer:", + {{std::addressof(loc), "the next token is not an integer"}})); } template @@ -84,8 +84,8 @@ parse_octal_integer(location& loc) return ok(std::make_pair(retval, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_octal_integer:", loc, - "the next token is not an integer")); + return err(format_underline("[error] toml::parse_octal_integer:", + {{std::addressof(loc), "the next token is not an integer"}})); } template @@ -105,8 +105,8 @@ parse_hexadecimal_integer(location& loc) return ok(std::make_pair(retval, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_hexadecimal_integer", loc, - "the next token is not an integer")); + return err(format_underline("[error] toml::parse_hexadecimal_integer", + {{std::addressof(loc), "the next token is not an integer"}})); } template @@ -133,8 +133,8 @@ parse_integer(location& loc) return ok(std::make_pair(retval, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_integer: ", loc, - "the next token is not an integer")); + return err(format_underline("[error] toml::parse_integer: ", + {{std::addressof(loc), "the next token is not an integer"}})); } template @@ -222,8 +222,8 @@ parse_floating(location& loc) return ok(std::make_pair(v, token.unwrap())); } loc.iter() = first; - return err(format_underline("[error] toml::parse_floating: ", loc, - "the next token is not a float")); + return err(format_underline("[error] toml::parse_floating: ", + {{std::addressof(loc), "the next token is not a float"}})); } template @@ -252,8 +252,9 @@ std::string read_utf8_codepoint(const region& reg, { std::cerr << format_underline("[warning] " "toml::read_utf8_codepoint: codepoints in the range " - "[0xD800, 0xDFFF] are not valid UTF-8.", - loc, "not a valid UTF-8 codepoint") << std::endl; + "[0xD800, 0xDFFF] are not valid UTF-8.", {{ + std::addressof(loc), "not a valid UTF-8 codepoint" + }}) << std::endl; } assert(codepoint < 0xD800 || 0xDFFF < codepoint); // 1110yyyy 10yxxxxx 10xxxxxx @@ -267,8 +268,8 @@ std::string read_utf8_codepoint(const region& reg, { std::cerr << format_underline("[error] " "toml::read_utf8_codepoint: input codepoint is too large to " - "decode as a unicode character.", loc, - "should be in [0x00..0x10FFFF]") << std::endl; + "decode as a unicode character.", {{std::addressof(loc), + "should be in [0x00..0x10FFFF]"}}) << std::endl; } // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx character += static_cast(0xF0| codepoint >> 18); @@ -280,7 +281,7 @@ std::string read_utf8_codepoint(const region& reg, { throw std::range_error(format_underline(concat_to_string("[error] " "input codepoint (", str, ") is too large to encode as utf-8."), - reg, "should be in [0x00..0x10FFFF]")); + {{std::addressof(reg), "should be in [0x00..0x10FFFF]"}})); } return character; } @@ -291,8 +292,8 @@ result parse_escape_sequence(location& loc) const auto first = loc.iter(); if(first == loc.end() || *first != '\\') { - return err(format_underline("[error]: toml::parse_escape_sequence: ", loc, - "the next token is not an escape sequence \"\\\"")); + return err(format_underline("[error]: toml::parse_escape_sequence: ", {{ + std::addressof(loc), "the next token is not a backslash \"\\\""}})); } ++loc.iter(); switch(*loc.iter()) @@ -314,7 +315,7 @@ result parse_escape_sequence(location& loc) { return err(format_underline("[error] parse_escape_sequence: " "invalid token found in UTF-8 codepoint uXXXX.", - loc, token.unwrap_err())); + {{std::addressof(loc), token.unwrap_err()}})); } } case 'U': @@ -327,16 +328,16 @@ result parse_escape_sequence(location& loc) { return err(format_underline("[error] parse_escape_sequence: " "invalid token found in UTF-8 codepoint Uxxxxxxxx", - loc, token.unwrap_err())); + {{std::addressof(loc), token.unwrap_err()}})); } } } const auto msg = format_underline("[error] parse_escape_sequence: " - "unknown escape sequence appeared.", loc, "escape sequence is one of" - " \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx", {"if you want to write " - "backslash as just one backslash, use literal string like:", - "regex = '<\\i\\c*\\s*>'"}); + "unknown escape sequence appeared.", {{std::addressof(loc), + "escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}}, + /* Hints = */{"if you want to write backslash as just one backslash, " + "use literal string like: regex = '<\\i\\c*\\s*>'"}); loc.iter() = first; return err(msg); } @@ -359,7 +360,7 @@ parse_ml_basic_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_basic_string: invalid token", - inner_loc, "should be \"\"\"")); + {{std::addressof(inner_loc), "should be \"\"\""}})); } // immediate newline is ignored (if exists) /* discard return value */ lex_newline::invoke(inner_loc); @@ -385,7 +386,7 @@ parse_ml_basic_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_basic_string: unexpected end of region", - inner_loc, "not sufficient token")); + {{std::addressof(inner_loc), "not sufficient token"}})); } delim = lex_ml_basic_string_delim::invoke(inner_loc); } @@ -412,7 +413,7 @@ parse_basic_string(location& loc) if(!quot) { throw internal_error(format_underline("[error] parse_basic_string: " - "invalid token", inner_loc, "should be \"")); + "invalid token", {{std::addressof(inner_loc), "should be \""}})); } std::string retval; @@ -434,7 +435,7 @@ parse_basic_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_basic_string: unexpected end of region", - inner_loc, "not sufficient token")); + {{std::addressof(inner_loc), "not sufficient token"}})); } quot = lex_quotation_mark::invoke(inner_loc); } @@ -461,7 +462,7 @@ parse_ml_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_literal_string: invalid token", - inner_loc, "should be '''")); + {{std::addressof(inner_loc), "should be '''"}})); } // immediate newline is ignored (if exists) /* discard return value */ lex_newline::invoke(inner_loc); @@ -473,7 +474,7 @@ parse_ml_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_literal_string: invalid token", - inner_loc, "should be '''")); + {{std::addressof(inner_loc), "should be '''"}})); } return ok(std::make_pair( toml::string(body.unwrap().str(), toml::string_t::literal), @@ -500,7 +501,7 @@ parse_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_literal_string: invalid token", - inner_loc, "should be '")); + {{std::addressof(inner_loc), "should be '"}})); } const auto body = repeat::invoke(inner_loc); @@ -510,7 +511,7 @@ parse_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_literal_string: invalid token", - inner_loc, "should be '")); + {{std::addressof(inner_loc), "should be '"}})); } return ok(std::make_pair( toml::string(body.unwrap().str(), toml::string_t::literal), @@ -531,8 +532,8 @@ parse_string(location& loc) if(const auto rslt = parse_ml_literal_string(loc)) {return rslt;} if(const auto rslt = parse_basic_string(loc)) {return rslt;} if(const auto rslt = parse_literal_string(loc)) {return rslt;} - return err(format_underline("[error] toml::parse_string: ", loc, - "the next token is not a string")); + return err(format_underline("[error] toml::parse_string: ", + {{std::addressof(loc), "the next token is not a string"}})); } template @@ -547,21 +548,23 @@ parse_local_date(location& loc) const auto y = lex_date_fullyear::invoke(inner_loc); if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') { + const std::string msg = y.map_err_or_else( + [](const std::string& msg) {return msg;}, "should be `-`"); + throw internal_error(format_underline("[error]: " "toml::parse_inner_local_date: invalid year format", - inner_loc, y.map_err_or_else([](const std::string& msg) { - return msg; - }, "should be `-`"))); + {{std::addressof(inner_loc), msg}})); } ++inner_loc.iter(); const auto m = lex_date_month::invoke(inner_loc); if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') { + const std::string msg = m.map_err_or_else( + [](const std::string& msg) {return msg;}, "should be `-`"); + throw internal_error(format_underline("[error]: " "toml::parse_local_date: invalid month format", - inner_loc, m.map_err_or_else([](const std::string& msg) { - return msg; - }, "should be `-`"))); + {{std::addressof(inner_loc), msg}})); } ++inner_loc.iter(); const auto d = lex_date_mday::invoke(inner_loc); @@ -569,7 +572,7 @@ parse_local_date(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_date: invalid day format", - inner_loc, d.unwrap_err())); + {{std::addressof(inner_loc), d.unwrap_err()}})); } return ok(std::make_pair(local_date( static_cast(from_string(y.unwrap().str(), 0)), @@ -581,8 +584,8 @@ parse_local_date(location& loc) else { loc.iter() = first; - return err(format_underline("[error]: toml::parse_local_date: ", loc, - "the next token is not a local_date")); + return err(format_underline("[error]: toml::parse_local_date: ", + {{std::addressof(loc), "the next token is not a local_date"}})); } } @@ -598,21 +601,23 @@ parse_local_time(location& loc) const auto h = lex_time_hour::invoke(inner_loc); if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') { + const std::string msg = h.map_err_or_else( + [](const std::string& msg) {return msg;}, "should be `:`"); + throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid year format", - inner_loc, h.map_err_or_else([](const std::string& msg) { - return msg; - }, "should be `:`"))); + {{std::addressof(inner_loc), msg}})); } ++inner_loc.iter(); const auto m = lex_time_minute::invoke(inner_loc); if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') { + const std::string msg = m.map_err_or_else( + [](const std::string& msg) {return msg;}, "should be `:`"); + throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid month format", - inner_loc, m.map_err_or_else([](const std::string& msg) { - return msg; - }, "should be `:`"))); + {{std::addressof(inner_loc), msg}})); } ++inner_loc.iter(); const auto s = lex_time_second::invoke(inner_loc); @@ -620,7 +625,7 @@ parse_local_time(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid second format", - inner_loc, s.unwrap_err())); + {{std::addressof(inner_loc), s.unwrap_err()}})); } local_time time( static_cast(from_string(h.unwrap().str(), 0)), @@ -656,7 +661,7 @@ parse_local_time(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid subsecond format", - inner_loc, secfrac.unwrap_err())); + {{std::addressof(inner_loc), secfrac.unwrap_err()}})); } } return ok(std::make_pair(time, token.unwrap())); @@ -664,8 +669,8 @@ parse_local_time(location& loc) else { loc.iter() = first; - return err(format_underline("[error]: toml::parse_local_time: ", loc, - "the next token is not a local_time")); + return err(format_underline("[error]: toml::parse_local_time: ", + {{std::addressof(loc), "the next token is not a local_time"}})); } } @@ -680,25 +685,26 @@ parse_local_datetime(location& loc) const auto date = parse_local_date(inner_loc); if(!date || inner_loc.iter() == inner_loc.end()) { + const std::string msg = date.map_err_or_else( + [](const std::string& msg) {return msg;}, "date, not datetime"); + throw internal_error(format_underline("[error]: " "toml::parse_local_datetime: invalid datetime format", - inner_loc, date.map_err_or_else([](const std::string& msg){ - return msg; - }, "date, not datetime"))); + {{std::addressof(inner_loc), msg}})); } const char delim = *(inner_loc.iter()++); if(delim != 'T' && delim != 't' && delim != ' ') { throw internal_error(format_underline("[error]: " "toml::parse_local_datetime: invalid datetime format", - inner_loc, "should be `T` or ` ` (space)")); + {{std::addressof(inner_loc), "should be `T` or ` ` (space)"}})); } const auto time = parse_local_time(inner_loc); if(!time) { throw internal_error(format_underline("[error]: " "toml::parse_local_datetime: invalid datetime format", - inner_loc, "invalid time fomrat")); + {{std::addressof(inner_loc), "invalid time fomrat"}})); } return ok(std::make_pair( local_datetime(date.unwrap().first, time.unwrap().first), @@ -707,8 +713,8 @@ parse_local_datetime(location& loc) else { loc.iter() = first; - return err(format_underline("[error]: toml::parse_local_datetime: ", loc, - "the next token is not a local_datetime")); + return err(format_underline("[error]: toml::parse_local_datetime: ", + {{std::addressof(loc), "the next token is not a local_datetime"}})); } } @@ -723,11 +729,12 @@ parse_offset_datetime(location& loc) const auto datetime = parse_local_datetime(inner_loc); if(!datetime || inner_loc.iter() == inner_loc.end()) { + const std::string msg = datetime.map_err_or_else( + [](const std::string& msg){return msg;}, "date, not datetime"); + throw internal_error(format_underline("[error]: " "toml::parse_offset_datetime: invalid datetime format", - inner_loc, datetime.map_err_or_else([](const std::string& msg){ - return msg; - }, "date, not datetime"))); + {{std::addressof(inner_loc), msg}})); } time_offset offset(0, 0); if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) @@ -748,7 +755,7 @@ parse_offset_datetime(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_offset_datetime: invalid datetime format", - inner_loc, "should be `Z` or `+HH:MM`")); + {{std::addressof(inner_loc), "should be `Z` or `+HH:MM`"}})); } return ok(std::make_pair(offset_datetime(datetime.unwrap().first, offset), token.unwrap())); @@ -756,8 +763,8 @@ parse_offset_datetime(location& loc) else { loc.iter() = first; - return err(format_underline("[error]: toml::parse_offset_datetime: ", loc, - "the next token is not a local_datetime")); + return err(format_underline("[error]: toml::parse_offset_datetime: ", + {{std::addressof(loc), "the next token is not a local_datetime"}})); } } @@ -778,8 +785,8 @@ parse_simple_key(location& loc) const auto reg = bare.unwrap(); return ok(std::make_pair(reg.str(), reg)); } - return err(format_underline("[error] toml::parse_simple_key: ", loc, - "the next token is not a simple key")); + return err(format_underline("[error] toml::parse_simple_key: ", + {{std::addressof(loc), "the next token is not a simple key"}})); } // dotted key become vector of keys @@ -806,7 +813,7 @@ parse_key(location& loc) { throw internal_error(format_underline("[error] " "toml::detail::parse_key: dotted key contains invalid key", - inner_loc, k.unwrap_err())); + {{std::addressof(inner_loc), k.unwrap_err()}})); } lex_ws::invoke(inner_loc); @@ -821,8 +828,8 @@ parse_key(location& loc) else { throw internal_error(format_underline("[error] toml::parse_key: " - "dotted key contains invalid key ", inner_loc, - "should be `.`")); + "dotted key contains invalid key ", + {{std::addressof(inner_loc), "should be `.`"}})); } } return ok(std::make_pair(keys, reg)); @@ -835,7 +842,8 @@ parse_key(location& loc) return ok(std::make_pair(std::vector(1, smpl.unwrap().first), smpl.unwrap().second)); } - return err(format_underline("[error] toml::parse_key: ", loc, "is not a valid key")); + return err(format_underline("[error] toml::parse_key: ", + {{std::addressof(loc), "is not a valid key"}})); } // forward-decl to implement parse_array and parse_table @@ -880,22 +888,16 @@ parse_array(location& loc) array_start_loc.iter() = first; throw syntax_error(format_underline("[error] toml::parse_array: " - "type of elements should be the same each other.", - std::vector>{ - std::make_pair( - std::addressof(array_start_loc), - std::string("array starts here") - ), - std::make_pair( + "type of elements should be the same each other.", { + {std::addressof(array_start_loc), "array starts here"}, + { std::addressof(get_region(retval.front())), - std::string("value has type ") + - stringize(retval.front().type()) - ), - std::make_pair( + "value has type " + stringize(retval.front().type()) + }, + { std::addressof(get_region(val.unwrap())), - std::string("value has different type, ") + - stringize(val.unwrap().type()) - ) + "value has different type, " + stringize(val.unwrap().type()) + } })); } retval.push_back(std::move(val.unwrap())); @@ -906,9 +908,10 @@ parse_array(location& loc) array_start_loc.iter() = first; throw syntax_error(format_underline("[error] toml::parse_array: " - "value having invalid format appeared in an array", - array_start_loc, "array starts here", - loc, "it is not a valid value.")); + "value having invalid format appeared in an array", { + {std::addressof(array_start_loc), "array starts here"}, + {std::addressof(loc), "it is not a valid value."} + })); } using lex_array_separator = sequence, character<','>>; @@ -928,14 +931,17 @@ parse_array(location& loc) array_start_loc.iter() = first; throw syntax_error(format_underline("[error] toml::parse_array:" - " missing array separator `,` after a value", - array_start_loc, "array starts here", loc, "should be `,`")); + " missing array separator `,` after a value", { + {std::addressof(array_start_loc), "array starts here"}, + {std::addressof(loc), "should be `,`"} + })); } } } loc.iter() = first; throw syntax_error(format_underline("[error] toml::parse_array: " - "array did not closed by `]`", loc, "should be closed")); + "array did not closed by `]`", + {{std::addressof(loc), "should be closed"}})); } template @@ -953,7 +959,8 @@ parse_key_value_pair(location& loc) { loc.iter() = first; msg = format_underline("[error] toml::parse_key_value_pair: " - "empty key is not allowed.", loc, "key expected before '='"); + "empty key is not allowed.", + {{std::addressof(loc), "key expected before '='"}}); } return err(std::move(msg)); } @@ -968,14 +975,16 @@ parse_key_value_pair(location& loc) if(std::find(loc.iter(), line_end, '=') != line_end) { msg = format_underline("[error] toml::parse_key_value_pair: " - "invalid format for key", loc, "invalid character in key", { - "Did you forget '.' to separate dotted-key?", + "invalid format for key", + {{std::addressof(loc), "invalid character in key"}}, + {"Did you forget '.' to separate dotted-key?", "Allowed characters for bare key are [0-9a-zA-Z_-]."}); } else // if not, the error is lack of key-value separator. { msg = format_underline("[error] toml::parse_key_value_pair: " - "missing key-value separator `=`", loc, "should be `=`"); + "missing key-value separator `=`", + {{std::addressof(loc), "should be `=`"}}); } loc.iter() = first; return err(std::move(msg)); @@ -992,8 +1001,8 @@ parse_key_value_pair(location& loc) { loc.iter() = after_kvsp; msg = format_underline("[error] toml::parse_key_value_pair: " - "missing value after key-value separator '='", loc, - "expected value, but got nothing"); + "missing value after key-value separator '='", + {{std::addressof(loc), "expected value, but got nothing"}}); } else // there is something not a comment/whitespace, so invalid format. { @@ -1122,19 +1131,26 @@ insert_nested_key(table& root, const toml::value& v, // show special err msg for conflicting table throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") cannot insert" - "ed"), get_region(tab->at(k)), "table already defined", - get_region(v), "this conflicts with the previous table")); + format_dotted_keys(first, last), + "\") cannot be defined"), { + {std::addressof(get_region(tab->at(k))), + "table already defined"}, + {std::addressof(get_region(v)), + "this conflicts with the previous table"} + })); } else if(!(tab->at(k).is(value_t::Array))) { throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" - " existing value"), get_region(tab->at(k)), - concat_to_string("this ", tab->at(k).type(), - " value already exists"), get_region(v), - "while inserting this array-of-tables")); + " existing value"), { + {std::addressof(get_region(tab->at(k))), + concat_to_string("this ", tab->at(k).type(), + " value already exists")}, + {std::addressof(get_region(v)), + "while inserting this array-of-tables"} + })); } array& a = tab->at(k).template cast(); if(!(a.front().is(value_t::Table))) @@ -1142,10 +1158,13 @@ insert_nested_key(table& root, const toml::value& v, throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" - " existing value"), get_region(tab->at(k)), - concat_to_string("this ", tab->at(k).type(), - " value already exists"), get_region(v), - "while inserting this array-of-tables")); + " existing value"), { + {std::addressof(get_region(tab->at(k))), + concat_to_string("this ", tab->at(k).type(), + " value already exists")}, + {std::addressof(get_region(v)), + "while inserting this array-of-tables"} + })); } // avoid conflicting array of table like the following. // ```toml @@ -1167,10 +1186,13 @@ insert_nested_key(table& root, const toml::value& v, throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" - " existing array-of-tables"), get_region(tab->at(k)), - concat_to_string("this ", tab->at(k).type(), - " value has static size"), get_region(v), - "appending this to the statically sized array")); + " existing array-of-tables"), { + {std::addressof(get_region(tab->at(k))), + concat_to_string("this ", tab->at(k).type(), + " value has static size")}, + {std::addressof(get_region(v)), + "appending it to the statically sized array"} + })); } a.push_back(v); return ok(true); @@ -1191,11 +1213,14 @@ insert_nested_key(table& root, const toml::value& v, tab->at(k), first, iter, last)) { throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: table (\"", - format_dotted_keys(first, last), - "\") already exists."), - get_region(tab->at(k)), "table already exists here", - get_region(v), "table defined twice")); + "[error] toml::insert_value: table (\"", + format_dotted_keys(first, last), + "\") already exists."), { + {std::addressof(get_region(tab->at(k))), + "table already exists here"}, + {std::addressof(get_region(v)), + "table defined twice"} + })); } // to allow the following toml file. // [a.b.c] @@ -1217,18 +1242,23 @@ insert_nested_key(table& root, const toml::value& v, { throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: array of tables (\"", - format_dotted_keys(first, last), "\") already exists."), - get_region(tab->at(k)), "array of tables defined here", - get_region(v), "table conflicts with the previous array" - " of table")); + format_dotted_keys(first, last), "\") already exists."), { + {std::addressof(get_region(tab->at(k))), + "array of tables defined here"}, + {std::addressof(get_region(v)), + "table conflicts with the previous array of table"} + })); } else { throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: value (\"", - format_dotted_keys(first, last), "\") already exists."), - get_region(tab->at(k)), "value already exists here", - get_region(v), "value defined twice")); + format_dotted_keys(first, last), "\") already exists."), { + {std::addressof(get_region(tab->at(k))), + "value already exists here"}, + {std::addressof(get_region(v)), + "value defined twice"} + })); } } tab->insert(std::make_pair(k, v)); @@ -1260,9 +1290,11 @@ insert_nested_key(table& root, const toml::value& v, throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: target (", format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), - get_region(a.back()), concat_to_string("actual type is ", - a.back().type()), get_region(v), "inserting this")); + ") is neither table nor an array of tables"), { + {std::addressof(get_region(a.back())), + concat_to_string("actual type is ", a.back().type())}, + {std::addressof(get_region(v)), "inserting this"} + })); } tab = std::addressof(a.back().template cast()); } @@ -1271,9 +1303,11 @@ insert_nested_key(table& root, const toml::value& v, throw syntax_error(format_underline(concat_to_string( "[error] toml::insert_value: target (", format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), - get_region(tab->at(k)), concat_to_string("actual type is ", - tab->at(k).type()), get_region(v), "inserting this")); + ") is neither table nor an array of tables"), { + {std::addressof(get_region(tab->at(k))), + concat_to_string("actual type is ", tab->at(k).type())}, + {std::addressof(get_region(v)), "inserting this"} + })); } } } @@ -1288,8 +1322,8 @@ parse_inline_table(location& loc) table retval; if(!(loc.iter() != loc.end() && *loc.iter() == '{')) { - return err(format_underline("[error] toml::parse_inline_table: ", loc, - "the next token is not an inline table")); + return err(format_underline("[error] toml::parse_inline_table: ", + {{std::addressof(loc), "the next token is not an inline table"}})); } ++loc.iter(); // it starts from "{". it should be formatted as inline-table @@ -1335,13 +1369,14 @@ parse_inline_table(location& loc) { throw syntax_error(format_underline("[error] " "toml:::parse_inline_table: missing table separator `,` ", - loc, "should be `,`")); + {{std::addressof(loc), "should be `,`"}})); } } } loc.iter() = first; throw syntax_error(format_underline("[error] toml::parse_inline_table: " - "inline table did not closed by `}`", loc, "should be closed")); + "inline table did not closed by `}`", + {{std::addressof(loc), "should be closed"}})); } template @@ -1350,7 +1385,8 @@ result parse_value(location& loc) const auto first = loc.iter(); if(first == loc.end()) { - return err(format_underline("[error] toml::parse_value: input is empty", loc, "")); + return err(format_underline("[error] toml::parse_value: input is empty", + {{std::addressof(loc), ""}})); } if(auto r = parse_string (loc)) {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} @@ -1374,7 +1410,7 @@ result parse_value(location& loc) {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} const auto msg = format_underline("[error] toml::parse_value: " - "unknown token appeared", loc, "unknown"); + "unknown token appeared", {{std::addressof(loc), "unknown"}}); loc.iter() = first; return err(msg); } @@ -1391,7 +1427,8 @@ parse_table_key(location& loc) if(!open || inner_loc.iter() == inner_loc.end()) { throw internal_error(format_underline("[error] " - "toml::parse_table_key: no `[`", inner_loc, "should be `[`")); + "toml::parse_table_key: no `[`", + {{std::addressof(inner_loc), "should be `[`"}})); } // to skip [ a . b . c ] // ^----------- this whitespace @@ -1400,7 +1437,8 @@ parse_table_key(location& loc) if(!keys) { throw internal_error(format_underline("[error] " - "toml::parse_table_key: invalid key", inner_loc, "not key")); + "toml::parse_table_key: invalid key", + {{std::addressof(inner_loc), "not key"}})); } // to skip [ a . b . c ] // ^-- this whitespace @@ -1409,7 +1447,8 @@ parse_table_key(location& loc) if(!close) { throw internal_error(format_underline("[error] " - "toml::parse_table_key: no `]`", inner_loc, "should be `]`")); + "toml::parse_table_key: no `]`", + {{std::addressof(inner_loc), "should be `]`"}})); } // after [table.key], newline or EOF(empty table) requried. @@ -1422,7 +1461,7 @@ parse_table_key(location& loc) { throw syntax_error(format_underline("[error] " "toml::parse_table_key: newline required after [table.key]", - loc, "expected newline")); + {{std::addressof(loc), "expected newline"}})); } } return ok(std::make_pair(keys.unwrap().first, token.unwrap())); @@ -1445,23 +1484,24 @@ parse_array_table_key(location& loc) if(!open || inner_loc.iter() == inner_loc.end()) { throw internal_error(format_underline("[error] " - "toml::parse_array_table_key: no `[[`", inner_loc, - "should be `[[`")); + "toml::parse_array_table_key: no `[[`", + {{std::addressof(inner_loc), "should be `[[`"}})); } lex_ws::invoke(inner_loc); const auto keys = parse_key(inner_loc); if(!keys) { throw internal_error(format_underline("[error] " - "toml::parse_array_table_key: invalid key", inner_loc, - "not key")); + "toml::parse_array_table_key: invalid key", + {{std::addressof(inner_loc), "not a key"}})); } lex_ws::invoke(inner_loc); const auto close = lex_array_table_close::invoke(inner_loc); if(!close) { throw internal_error(format_underline("[error] " - "toml::parse_table_key: no `]]`", inner_loc, "should be `]]`")); + "toml::parse_table_key: no `]]`", + {{std::addressof(inner_loc), "should be `]]`"}})); } // after [[table.key]], newline or EOF(empty table) requried. @@ -1472,9 +1512,9 @@ parse_array_table_key(location& loc) const auto nl = lex_newline_after_table_key::invoke(loc); if(!nl) { - throw syntax_error(format_underline("[error] " - "toml::parse_array_table_key: newline required after " - "[[table.key]]", loc, "expected newline")); + throw syntax_error(format_underline("[error] toml::" + "parse_array_table_key: newline required after [[table.key]]", + {{std::addressof(loc), "expected newline"}})); } } return ok(std::make_pair(keys.unwrap().first, token.unwrap())); @@ -1550,8 +1590,8 @@ result parse_ml_table(location& loc) const auto before = loc.iter(); lex_ws::invoke(loc); // skip whitespace const auto msg = format_underline("[error] toml::parse_table: " - "invalid line format", loc, concat_to_string( - "expected newline, but got '", show_char(*loc.iter()), "'.")); + "invalid line format", {{std::addressof(loc), concat_to_string( + "expected newline, but got '", show_char(*loc.iter()), "'.")}}); loc.iter() = before; return err(msg); } @@ -1621,7 +1661,7 @@ result parse_toml_file(location& loc) continue; } return err(format_underline("[error]: toml::parse_toml_file: " - "unknown line appeared", loc, "unknown format")); + "unknown line appeared", {{std::addressof(loc), "unknown format"}})); } return ok(data); } diff --git a/toml/value.hpp b/toml/value.hpp index 03812c7..27c2d8a 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -681,9 +681,11 @@ typename detail::toml_default_type::type& value::cast() & { if(T != this->type_) { - throw type_error(format_underline(concat_to_string( - "[error] toml::value bad_cast to ", T), *region_info_, - concat_to_string("the actual type is ", this->type_))); + throw type_error(detail::format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + })); } return switch_cast::invoke(*this); } @@ -692,9 +694,11 @@ typename detail::toml_default_type::type const& value::cast() const& { if(T != this->type_) { - throw type_error(format_underline(concat_to_string( - "[error] toml::value bad_cast to ", T), *region_info_, - concat_to_string("the actual type is ", this->type_))); + throw type_error(detail::format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + })); } return switch_cast::invoke(*this); } @@ -703,9 +707,11 @@ typename detail::toml_default_type::type&& value::cast() && { if(T != this->type_) { - throw type_error(format_underline(concat_to_string( - "[error] toml::value bad_cast to ", T), *region_info_, - concat_to_string("the actual type is ", this->type_))); + throw type_error(detail::format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), { + {this->region_info_.get(), + concat_to_string("the actual type is ", this->type_)} + })); } return switch_cast::invoke(std::move(*this)); } @@ -792,8 +798,9 @@ inline std::string format_error(const std::string& err_msg, const toml::value& v, const std::string& comment, std::vector hints = {}) { - return detail::format_underline(err_msg, detail::get_region(v), comment, - std::move(hints)); + return detail::format_underline(err_msg, { + {std::addressof(detail::get_region(v)), comment} + }, std::move(hints)); } inline std::string format_error(const std::string& err_msg, @@ -801,9 +808,10 @@ inline std::string format_error(const std::string& err_msg, const toml::value& v2, const std::string& comment2, std::vector hints = {}) { - return detail::format_underline(err_msg, detail::get_region(v1), comment1, - detail::get_region(v2), comment2, - std::move(hints)); + return detail::format_underline(err_msg, { + {std::addressof(detail::get_region(v1)), comment1}, + {std::addressof(detail::get_region(v2)), comment2} + }, std::move(hints)); } template From 510e10de955bf4799bd38545bc9147379341ae4f Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 14 Mar 2019 22:58:44 +0900 Subject: [PATCH 15/37] ci: test numerous compilers on CI --- .travis.yml | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index b549f32..840eabe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,39 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-5 - - build-essential - - cmake + - libboost-all-dev + - os: linux + language: cpp + compiler: gcc + env: COMPILER="g++-6" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-6 + - libboost-all-dev + - os: linux + language: cpp + compiler: gcc + env: COMPILER="g++-7" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + - libboost-all-dev + - os: linux + language: cpp + compiler: gcc + env: COMPILER="g++-8" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-8 - libboost-all-dev - os: linux language: cpp @@ -26,8 +57,66 @@ matrix: - llvm-toolchain-precise-3.7 packages: - clang-3.7 - - build-essential - - cmake + - libboost-all-dev + - os: linux + language: cpp + compiler: clang + env: COMPILER="clang++-4.0" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-4.0 + packages: + - clang-4.0 + - libboost-all-dev + - os: linux + language: cpp + compiler: clang + env: COMPILER="clang++-5.0" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + packages: + - clang-5.0 + - libboost-all-dev + - os: linux + language: cpp + compiler: clang + env: COMPILER="clang++-6.0" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-6.0 + packages: + - clang-6.0 + - libboost-all-dev + - os: linux + language: cpp + compiler: clang + env: COMPILER="clang++-7" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-7 + packages: + - clang-7 + - libboost-all-dev + - os: linux + language: cpp + compiler: clang + env: COMPILER="clang++-8" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-8 + packages: + - clang-8 - libboost-all-dev - os: osx language: cpp From 61dfa4a2dce2f8a328f7ec3d5c3d1e735b8049d8 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 15 Mar 2019 12:38:37 +0900 Subject: [PATCH 16/37] feat: format any number of values into an err msg ```cpp toml::format_error("[error] message", v1, "v1", v2, "v2", ...); ``` --- toml/value.hpp | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/toml/value.hpp b/toml/value.hpp index 27c2d8a..a866b04 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -794,24 +794,38 @@ inline bool operator>=(const toml::value& lhs, const toml::value& rhs) return !(lhs < rhs); } -inline std::string format_error(const std::string& err_msg, - const toml::value& v, const std::string& comment, - std::vector hints = {}) +namespace detail { - return detail::format_underline(err_msg, { - {std::addressof(detail::get_region(v)), comment} - }, std::move(hints)); +inline std::string format_error_impl(const std::string& err_msg, + std::vector> val, + std::vector hints) +{ + return format_underline(err_msg, std::move(val), std::move(hints)); +} +inline std::string format_error_impl(const std::string& err_msg, + std::vector> val) +{ + return format_underline(err_msg, std::move(val)); } -inline std::string format_error(const std::string& err_msg, - const toml::value& v1, const std::string& comment1, - const toml::value& v2, const std::string& comment2, - std::vector hints = {}) +template +std::string format_error_impl(const std::string& err_msg, + std::vector> val, + const toml::value& v, const std::string& comment, + Ts&& ... args) { - return detail::format_underline(err_msg, { - {std::addressof(detail::get_region(v1)), comment1}, - {std::addressof(detail::get_region(v2)), comment2} - }, std::move(hints)); + val.push_back(std::make_pair(std::addressof(get_region(v)), comment)); + return format_error_impl(err_msg, std::move(val), std::forward(args)...); +} +} // detail + +template +std::string format_error(const std::string& err_msg, Ts&& ... args) +{ + std::vector> val; + val.reserve(sizeof...(args) / 2); + return detail::format_error_impl(err_msg, std::move(val), + std::forward(args)...); } template From 59aaaab4360bb6c03d2b3b1c8d6ed7b04dfa61d2 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 15 Mar 2019 12:40:01 +0900 Subject: [PATCH 17/37] test: add test to check format_error compiles --- tests/CMakeLists.txt | 1 + tests/test_format_error.cpp | 75 +++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 tests/test_format_error.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0664150..8db2420 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,6 +28,7 @@ set(TEST_NAMES test_serialize_file test_parse_unicode test_error_detection + test_format_error ) CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL) diff --git a/tests/test_format_error.cpp b/tests/test_format_error.cpp new file mode 100644 index 0000000..36cb171 --- /dev/null +++ b/tests/test_format_error.cpp @@ -0,0 +1,75 @@ +#define BOOST_TEST_MODULE "test_value" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include + +// to check it successfully compiles. it does not check the formatted string. + +BOOST_AUTO_TEST_CASE(test_1_value) +{ + toml::value val(42); + + { + const std::string pretty_error = + toml::format_error("[error] test error", val, "this is a value"); + std::cout << pretty_error << std::endl; + } + + { + const std::string pretty_error = + toml::format_error("[error] test error", val, "this is a value", + std::vector{"this is a hint"}); + std::cout << pretty_error << std::endl; + } +} + +BOOST_AUTO_TEST_CASE(test_2_values) +{ + toml::value v1(42); + toml::value v2(3.14); + { + const std::string pretty_error = + toml::format_error("[error] test error with two values", + v1, "this is the answer", + v2, "this is the pi"); + std::cout << pretty_error << std::endl; + } + + { + const std::string pretty_error = + toml::format_error("[error] test error with two values", + v1, "this is the answer", + v2, "this is the pi", + std::vector{"hint"}); + std::cout << pretty_error << std::endl; + } +} + +BOOST_AUTO_TEST_CASE(test_3_values) +{ + toml::value v1(42); + toml::value v2(3.14); + toml::value v3("foo"); + { + const std::string pretty_error = + toml::format_error("[error] test error with two values", + v1, "this is the answer", + v2, "this is the pi", + v3, "this is a meta-syntactic variable"); + std::cout << pretty_error << std::endl; + } + + { + const std::string pretty_error = + toml::format_error("[error] test error with two values", + v1, "this is the answer", + v2, "this is the pi", + v3, "this is a meta-syntactic variable", + std::vector{"hint 1", "hint 2"}); + std::cout << pretty_error << std::endl; + } +} From a04544637b53881631711c6bc031df1f2203ac33 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 15 Mar 2019 14:29:32 +0900 Subject: [PATCH 18/37] feat: mark to_toml as deprecated because the constructor of `toml::value()` supports all the stuff that are supported by `to_toml`. --- toml/to_toml.hpp | 6 ++++++ toml/utility.hpp | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/toml/to_toml.hpp b/toml/to_toml.hpp index 348ce47..eb7eff1 100644 --- a/toml/to_toml.hpp +++ b/toml/to_toml.hpp @@ -8,32 +8,38 @@ namespace toml { template +TOML11_MARK_AS_DEPRECATED inline value to_toml(T&& x) { return value(std::forward(x)); } template +TOML11_MARK_AS_DEPRECATED inline value to_toml(T&& x, string_t kind) { return value(std::forward(x), kind); } +TOML11_MARK_AS_DEPRECATED inline value to_toml(local_date d, local_time t) { return value(local_datetime(d, t)); } +TOML11_MARK_AS_DEPRECATED inline value to_toml(local_date d, local_time t, time_offset ofs) { return value(offset_datetime(d, t, ofs)); } template +TOML11_MARK_AS_DEPRECATED inline value to_toml(Ts&& ... xs) { return value(toml::array{toml::value(std::forward(xs)) ... }); } +TOML11_MARK_AS_DEPRECATED inline value to_toml(std::initializer_list> xs) { return value(toml::table(xs.begin(), xs.end())); diff --git a/toml/utility.hpp b/toml/utility.hpp index 6202163..a560f5d 100644 --- a/toml/utility.hpp +++ b/toml/utility.hpp @@ -7,6 +7,16 @@ #include #include +#if __cplusplus >= 201402L +# define TOML11_MARK_AS_DEPRECATED [[deprecated]] +#elif defined(__GNUC__) +# define TOML11_MARK_AS_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +# define TOML11_MARK_AS_DEPRECATED __declspec(deprecated) +#else +# define TOML11_MARK_AS_DEPRECATED +#endif + namespace toml { From 514df99e40e6b602301cd0856a12b11a2e7a2d1d Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 15 Mar 2019 17:39:31 +0900 Subject: [PATCH 19/37] feat: consider invalid UTF-8 as syntax_error the following codepoints are considered to be a syntax_error - [0xD800, 0xDFFF] - larger than 0x10FFFF --- toml/parser.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 9c1d562..45ae3b4 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -250,11 +250,11 @@ std::string read_utf8_codepoint(const region& reg, { if(0xD800 <= codepoint && codepoint <= 0xDFFF) { - std::cerr << format_underline("[warning] " + throw syntax_error(format_underline("[error] " "toml::read_utf8_codepoint: codepoints in the range " "[0xD800, 0xDFFF] are not valid UTF-8.", {{ std::addressof(loc), "not a valid UTF-8 codepoint" - }}) << std::endl; + }})); } assert(codepoint < 0xD800 || 0xDFFF < codepoint); // 1110yyyy 10yxxxxx 10xxxxxx @@ -266,10 +266,10 @@ std::string read_utf8_codepoint(const region& reg, { if(0x10FFFF < codepoint) // out of Unicode region { - std::cerr << format_underline("[error] " + throw syntax_error(format_underline("[error] " "toml::read_utf8_codepoint: input codepoint is too large to " "decode as a unicode character.", {{std::addressof(loc), - "should be in [0x00..0x10FFFF]"}}) << std::endl; + "should be in [0x00..0x10FFFF]"}})); } // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx character += static_cast(0xF0| codepoint >> 18); From 76863cb27f6488206ed39b2613321e74aade0c06 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 15 Mar 2019 17:48:47 +0900 Subject: [PATCH 20/37] refactor: simplify branches about utf8 codepoint --- toml/parser.hpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 45ae3b4..765d8b5 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -262,15 +262,8 @@ std::string read_utf8_codepoint(const region& reg, character += static_cast(0x80|(codepoint >> 6 & 0x3F)); character += static_cast(0x80|(codepoint & 0x3F)); } - else if(codepoint < 0x200000) // U+010000 ... U+1FFFFF + else if(codepoint < 0x110000) // U+010000 ... U+10FFFF { - if(0x10FFFF < codepoint) // out of Unicode region - { - throw syntax_error(format_underline("[error] " - "toml::read_utf8_codepoint: input codepoint is too large to " - "decode as a unicode character.", {{std::addressof(loc), - "should be in [0x00..0x10FFFF]"}})); - } // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx character += static_cast(0xF0| codepoint >> 18); character += static_cast(0x80|(codepoint >> 12 & 0x3F)); @@ -279,9 +272,9 @@ std::string read_utf8_codepoint(const region& reg, } else // out of UTF-8 region { - throw std::range_error(format_underline(concat_to_string("[error] " - "input codepoint (", str, ") is too large to encode as utf-8."), - {{std::addressof(reg), "should be in [0x00..0x10FFFF]"}})); + throw syntax_error(format_underline("[error] toml::read_utf8_codepoint:" + " input codepoint is too large.", + {{std::addressof(loc), "should be in [0x00..0x10FFFF]"}})); } return character; } From 9b8db6a2258ffd3f4f120c85d0fe32a7cfbfbe6a Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 15 Mar 2019 19:30:36 +0900 Subject: [PATCH 21/37] fix: remove extraneous null character after float the bug was introduced by snprintf --- toml/serializer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toml/serializer.hpp b/toml/serializer.hpp index c25f8b2..68bdbcc 100644 --- a/toml/serializer.hpp +++ b/toml/serializer.hpp @@ -36,7 +36,7 @@ struct serializer std::vector buf(bsz + 1, '\0'); // +1 for null character(\0) std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); - std::string token(buf.begin(), buf.end()); + std::string token(buf.begin(), std::prev(buf.end())); if(token.back() == '.') // 1. => 1.0 { token += '0'; From df6dcbc4ed04dd5d46fb91c2c73eb9ba51f561e1 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 16 Mar 2019 14:16:31 +0900 Subject: [PATCH 22/37] feat: check a class has from/into_toml member fn to support better serialization --- toml/traits.hpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/toml/traits.hpp b/toml/traits.hpp index 79bb8b2..1991728 100644 --- a/toml/traits.hpp +++ b/toml/traits.hpp @@ -9,6 +9,9 @@ namespace toml { + +class value; // forward decl + namespace detail { @@ -45,6 +48,22 @@ struct has_resize_method_impl template static std::false_type check(...); }; +struct has_from_toml_method_impl +{ + template + static std::true_type check( + decltype(std::declval().from_toml(std::declval<::toml::value>()))*); + template + static std::false_type check(...); +}; +struct has_into_toml_method_impl +{ + template + static std::true_type check(decltype(std::declval().into_toml())*); + template + static std::false_type check(...); +}; + /// Intel C++ compiler can not use decltype in parent class declaration, here /// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 #ifdef __INTEL_COMPILER @@ -62,6 +81,14 @@ struct has_mapped_type : decltype(has_mapped_type_impl::check(nullptr)){}; template struct has_resize_method : decltype(has_resize_method_impl::check(nullptr)){}; + +template +struct has_from_toml_method +: decltype(has_from_toml_method_impl::check(nullptr)){}; +template +struct has_into_toml_method +: decltype(has_into_toml_method_impl::check(nullptr)){}; + #ifdef __INTEL_COMPILER #undef decltype(...) #endif From fd063af7ce01e0add05c38e78ec97d9340f14444 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 16 Mar 2019 14:19:47 +0900 Subject: [PATCH 23/37] refactor: make include guard style uniform --- toml/datetime.hpp | 4 ++-- toml/exception.hpp | 4 ++-- toml/from_toml.hpp | 4 ++-- toml/get.hpp | 4 ++-- toml/region.hpp | 4 ++-- toml/result.hpp | 4 ++-- toml/string.hpp | 4 ++-- toml/to_toml.hpp | 4 ++-- toml/traits.hpp | 4 ++-- toml/types.hpp | 4 ++-- toml/utility.hpp | 4 ++-- toml/value.hpp | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/toml/datetime.hpp b/toml/datetime.hpp index 8c4cc3b..d3ae16d 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_DATETIME -#define TOML11_DATETIME +#ifndef TOML11_DATETIME_HPP +#define TOML11_DATETIME_HPP #include #include #include diff --git a/toml/exception.hpp b/toml/exception.hpp index f92a7c1..1a3cd0c 100644 --- a/toml/exception.hpp +++ b/toml/exception.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_EXCEPTION -#define TOML11_EXCEPTION +#ifndef TOML11_EXCEPTION_HPP +#define TOML11_EXCEPTION_HPP #include #include diff --git a/toml/from_toml.hpp b/toml/from_toml.hpp index a9348eb..f75b24b 100644 --- a/toml/from_toml.hpp +++ b/toml/from_toml.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_FROM_TOML -#define TOML11_FROM_TOML +#ifndef TOML11_FROM_TOML_HPP +#define TOML11_FROM_TOML_HPP #include "get.hpp" namespace toml diff --git a/toml/get.hpp b/toml/get.hpp index 80336ce..94b58f9 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_GET -#define TOML11_GET +#ifndef TOML11_GET_HPP +#define TOML11_GET_HPP #include "result.hpp" #include "value.hpp" #include diff --git a/toml/region.hpp b/toml/region.hpp index 0601bce..db830c7 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_REGION_H -#define TOML11_REGION_H +#ifndef TOML11_REGION_HPP +#define TOML11_REGION_HPP #include "exception.hpp" #include #include diff --git a/toml/result.hpp b/toml/result.hpp index 949b6f8..4930140 100644 --- a/toml/result.hpp +++ b/toml/result.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_RESULT_H -#define TOML11_RESULT_H +#ifndef TOML11_RESULT_HPP +#define TOML11_RESULT_HPP #include "traits.hpp" #include #include diff --git a/toml/string.hpp b/toml/string.hpp index eef932d..89e6d39 100644 --- a/toml/string.hpp +++ b/toml/string.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_STRING_H -#define TOML11_STRING_H +#ifndef TOML11_STRING_HPP +#define TOML11_STRING_HPP #include #include diff --git a/toml/to_toml.hpp b/toml/to_toml.hpp index eb7eff1..8fe2f69 100644 --- a/toml/to_toml.hpp +++ b/toml/to_toml.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_TO_TOML -#define TOML11_TO_TOML +#ifndef TOML11_TO_TOML_HPP +#define TOML11_TO_TOML_HPP #include "value.hpp" namespace toml diff --git a/toml/traits.hpp b/toml/traits.hpp index 79bb8b2..bf4917b 100644 --- a/toml/traits.hpp +++ b/toml/traits.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_TRAITS -#define TOML11_TRAITS +#ifndef TOML11_TRAITS_HPP +#define TOML11_TRAITS_HPP #include #include #include diff --git a/toml/types.hpp b/toml/types.hpp index 361ed10..db93320 100644 --- a/toml/types.hpp +++ b/toml/types.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_TYPES_H -#define TOML11_TYPES_H +#ifndef TOML11_TYPES_HPP +#define TOML11_TYPES_HPP #include "datetime.hpp" #include "string.hpp" #include "traits.hpp" diff --git a/toml/utility.hpp b/toml/utility.hpp index a560f5d..01fe6d7 100644 --- a/toml/utility.hpp +++ b/toml/utility.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_UTILITY -#define TOML11_UTILITY +#ifndef TOML11_UTILITY_HPP +#define TOML11_UTILITY_HPP #include "traits.hpp" #include #include diff --git a/toml/value.hpp b/toml/value.hpp index a866b04..397f347 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -1,7 +1,7 @@ // Copyright Toru Niina 2017. // Distributed under the MIT License. -#ifndef TOML11_VALUE -#define TOML11_VALUE +#ifndef TOML11_VALUE_HPP +#define TOML11_VALUE_HPP #include "traits.hpp" #include "utility.hpp" #include "exception.hpp" From 6929bcdf78899fac8c5fc900b7d1b4c563776d4d Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 16 Mar 2019 14:27:05 +0900 Subject: [PATCH 24/37] feat: add from and into --- toml/from.hpp | 20 ++++++++++++++++++++ toml/into.hpp | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 toml/from.hpp create mode 100644 toml/into.hpp diff --git a/toml/from.hpp b/toml/from.hpp new file mode 100644 index 0000000..8251973 --- /dev/null +++ b/toml/from.hpp @@ -0,0 +1,20 @@ +// Copyright Toru Niina 2019. +// Distributed under the MIT License. +#ifndef TOML11_FROM_HPP +#define TOML11_FROM_HPP +#include "traits.hpp" + +namespace toml +{ + +template +struct from; +// { +// static T from_toml(const toml::value& v) +// { +// // User-defined conversions ... +// } +// }; + +} // toml +#endif // TOML11_FROM_HPP diff --git a/toml/into.hpp b/toml/into.hpp new file mode 100644 index 0000000..17f2248 --- /dev/null +++ b/toml/into.hpp @@ -0,0 +1,20 @@ +// Copyright Toru Niina 2019. +// Distributed under the MIT License. +#ifndef TOML11_INTO_HPP +#define TOML11_INTO_HPP +#include "traits.hpp" + +namespace toml +{ + +template +struct into; +// { +// static toml::value into_toml(const T& user_defined_type) +// { +// // User-defined conversions ... +// } +// }; + +} // toml +#endif // TOML11_INTO_HPP From b1b72a94a8656609284c006b1687217284689a27 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 16 Mar 2019 14:44:04 +0900 Subject: [PATCH 25/37] feat: support conversion with external types --- toml/get.hpp | 26 ++++++++++++++++++++++++++ toml/value.hpp | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/toml/get.hpp b/toml/get.hpp index 80336ce..dc1b495 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -2,6 +2,7 @@ // Distributed under the MIT License. #ifndef TOML11_GET #define TOML11_GET +#include "from.hpp" #include "result.hpp" #include "value.hpp" #include @@ -297,6 +298,31 @@ T get(const toml::value& v) return map; } + +// ============================================================================ +// user-defined, but compatible types. + +template>, // not a toml::value + detail::has_from_toml_method, // but has from_toml(toml::value) memfn + std::is_default_constructible // and default constructible + >::value, std::nullptr_t>::type = nullptr> +T get(const toml::value& v) +{ + T ud; + ud.from_toml(v); + return ud; +} +template> // not a toml::value + >::value, std::nullptr_t>::type = nullptr, + std::size_t = sizeof(::toml::from) // and has from specialization + > +T get(const toml::value& v) +{ + return ::toml::from::from_toml(v); +} + // ============================================================================ // find and get diff --git a/toml/value.hpp b/toml/value.hpp index a866b04..00b790a 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -3,6 +3,7 @@ #ifndef TOML11_VALUE #define TOML11_VALUE #include "traits.hpp" +#include "into.hpp" #include "utility.hpp" #include "exception.hpp" #include "storage.hpp" @@ -533,6 +534,46 @@ class value return *this; } + // user-defined ========================================================= + + // convert using into_toml() method ------------------------------------- + + template>, // not a toml::value + detail::has_into_toml_method // but has `into_toml` method + >::value, std::nullptr_t>::type = nullptr> + value(const T& ud): value(ud.into_toml()) {} + + template>, // not a toml::value + detail::has_into_toml_method // but has `into_toml` method + >::value, std::nullptr_t>::type = nullptr> + value& operator=(const T& ud) + { + *this = ud.into_toml(); + return *this; + } + + // convert using into struct ----------------------------------------- + + template>::value, + std::nullptr_t>::type = nullptr, + std::size_t S = sizeof(::toml::into)> + value(const T& ud): value(::toml::into::into_toml(ud)) {} + + template>::value, + std::nullptr_t>::type = nullptr, + std::size_t S = sizeof(::toml::into)> + value& operator=(const T& ud) + { + *this = ::toml::into::into_toml(ud); + return *this; + } + + // type checking and casting ============================================ + template bool is() const noexcept {return value_traits::type_index == this->type_;} bool is(value_t t) const noexcept {return t == this->type_;} From 31e450f9aff2267094085322167f921522383be5 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 16 Mar 2019 15:46:21 +0900 Subject: [PATCH 26/37] test: add test for `from/into` based conversions --- tests/CMakeLists.txt | 1 + tests/test_extended_conversions.cpp | 82 +++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 tests/test_extended_conversions.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8db2420..cb3c790 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -29,6 +29,7 @@ set(TEST_NAMES test_parse_unicode test_error_detection test_format_error + test_extended_conversions ) CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL) diff --git a/tests/test_extended_conversions.cpp b/tests/test_extended_conversions.cpp new file mode 100644 index 0000000..dd20758 --- /dev/null +++ b/tests/test_extended_conversions.cpp @@ -0,0 +1,82 @@ +#define BOOST_TEST_MODULE "test_extended_conversions" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include + +namespace extlib +{ +struct foo +{ + int a; + std::string b; +}; +struct bar +{ + int a; + std::string b; + + void from_toml(const toml::value& v) + { + this->a = toml::find(v, "a"); + this->b = toml::find(v, "b"); + return ; + } + + toml::table into_toml() const + { + return toml::table{{"a", this->a}, {"b", this->b}}; + } +}; +} // extlib + +namespace toml +{ +template<> +struct from +{ + static extlib::foo from_toml(const toml::value& v) + { + return extlib::foo{toml::find(v, "a"), toml::find(v, "b")}; + } +}; + +template<> +struct into +{ + static toml::table into_toml(const extlib::foo& f) + { + return toml::table{{"a", f.a}, {"b", f.b}}; + } +}; +} // toml + + +BOOST_AUTO_TEST_CASE(test_conversion_by_member_methods) +{ + const toml::value v{{"a", 42}, {"b", "baz"}}; + + const auto foo = toml::get(v); + BOOST_TEST(foo.a == 42); + BOOST_TEST(foo.b == "baz"); + + const toml::value v2(foo); + + BOOST_TEST(v == v2); +} + +BOOST_AUTO_TEST_CASE(test_conversion_by_specialization) +{ + const toml::value v{{"a", 42}, {"b", "baz"}}; + + const auto bar = toml::get(v); + BOOST_TEST(bar.a == 42); + BOOST_TEST(bar.b == "baz"); + + const toml::value v2(bar); + + BOOST_TEST(v == v2); +} From 190636b79193910aa119d508aae709474a91b7b9 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 16 Mar 2019 15:52:22 +0900 Subject: [PATCH 27/37] fix: support getting a container of external types --- tests/test_extended_conversions.cpp | 34 +++++++++++++++++++++++++++++ toml/get.hpp | 18 +++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/tests/test_extended_conversions.cpp b/tests/test_extended_conversions.cpp index dd20758..0d4ff25 100644 --- a/tests/test_extended_conversions.cpp +++ b/tests/test_extended_conversions.cpp @@ -80,3 +80,37 @@ BOOST_AUTO_TEST_CASE(test_conversion_by_specialization) BOOST_TEST(v == v2); } + +BOOST_AUTO_TEST_CASE(test_recursive_conversion) +{ + const toml::value v{ + toml::table{{"a", 42}, {"b", "baz"}}, + toml::table{{"a", 43}, {"b", "qux"}}, + toml::table{{"a", 44}, {"b", "quux"}}, + toml::table{{"a", 45}, {"b", "foobar"}}, + }; + + const auto foos = toml::get>(v); + BOOST_TEST(foos.size() == 4ul); + BOOST_TEST(foos.at(0).a == 42); + BOOST_TEST(foos.at(1).a == 43); + BOOST_TEST(foos.at(2).a == 44); + BOOST_TEST(foos.at(3).a == 45); + + BOOST_TEST(foos.at(0).b == "baz"); + BOOST_TEST(foos.at(1).b == "qux"); + BOOST_TEST(foos.at(2).b == "quux"); + BOOST_TEST(foos.at(3).b == "foobar"); + + const auto bars = toml::get>(v); + BOOST_TEST(bars.size() == 4ul); + BOOST_TEST(bars.at(0).a == 42); + BOOST_TEST(bars.at(1).a == 43); + BOOST_TEST(bars.at(2).a == 44); + BOOST_TEST(bars.at(3).a == 45); + + BOOST_TEST(bars.at(0).b == "baz"); + BOOST_TEST(bars.at(1).b == "qux"); + BOOST_TEST(bars.at(2).b == "quux"); + BOOST_TEST(bars.at(3).b == "foobar"); +} diff --git a/toml/get.hpp b/toml/get.hpp index dc1b495..9d14e8c 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -174,6 +174,20 @@ template::value, std::nullptr_t>::type = nullptr> T get(const toml::value& v); +template>, // not a toml::value + detail::has_from_toml_method, // but has from_toml(toml::value) memfn + std::is_default_constructible // and default constructible + >::value, std::nullptr_t>::type = nullptr> +T get(const toml::value& v); + +template> // not a toml::value + >::value, std::nullptr_t>::type = nullptr, + std::size_t = sizeof(::toml::from) // and has from specialization + > +T get(const toml::value& v); + // ============================================================================ // array-like types; most likely STL container, like std::vector, etc. @@ -306,7 +320,7 @@ template>, // not a toml::value detail::has_from_toml_method, // but has from_toml(toml::value) memfn std::is_default_constructible // and default constructible - >::value, std::nullptr_t>::type = nullptr> + >::value, std::nullptr_t>::type> T get(const toml::value& v) { T ud; @@ -315,7 +329,7 @@ T get(const toml::value& v) } template> // not a toml::value - >::value, std::nullptr_t>::type = nullptr, + >::value, std::nullptr_t>::type, std::size_t = sizeof(::toml::from) // and has from specialization > T get(const toml::value& v) From 30a41aa71078f9197bb61dd0013a8defefe6f53d Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 16 Mar 2019 16:15:01 +0900 Subject: [PATCH 28/37] fix: use older style in BOOST_TEST --- tests/test_extended_conversions.cpp | 48 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/test_extended_conversions.cpp b/tests/test_extended_conversions.cpp index 0d4ff25..fd5d88d 100644 --- a/tests/test_extended_conversions.cpp +++ b/tests/test_extended_conversions.cpp @@ -60,12 +60,12 @@ BOOST_AUTO_TEST_CASE(test_conversion_by_member_methods) const toml::value v{{"a", 42}, {"b", "baz"}}; const auto foo = toml::get(v); - BOOST_TEST(foo.a == 42); - BOOST_TEST(foo.b == "baz"); + BOOST_CHECK_EQUAL(foo.a, 42); + BOOST_CHECK_EQUAL(foo.b, "baz"); const toml::value v2(foo); - BOOST_TEST(v == v2); + BOOST_CHECK_EQUAL(v, v2); } BOOST_AUTO_TEST_CASE(test_conversion_by_specialization) @@ -73,12 +73,12 @@ BOOST_AUTO_TEST_CASE(test_conversion_by_specialization) const toml::value v{{"a", 42}, {"b", "baz"}}; const auto bar = toml::get(v); - BOOST_TEST(bar.a == 42); - BOOST_TEST(bar.b == "baz"); + BOOST_CHECK_EQUAL(bar.a, 42); + BOOST_CHECK_EQUAL(bar.b, "baz"); const toml::value v2(bar); - BOOST_TEST(v == v2); + BOOST_CHECK_EQUAL(v, v2); } BOOST_AUTO_TEST_CASE(test_recursive_conversion) @@ -91,26 +91,26 @@ BOOST_AUTO_TEST_CASE(test_recursive_conversion) }; const auto foos = toml::get>(v); - BOOST_TEST(foos.size() == 4ul); - BOOST_TEST(foos.at(0).a == 42); - BOOST_TEST(foos.at(1).a == 43); - BOOST_TEST(foos.at(2).a == 44); - BOOST_TEST(foos.at(3).a == 45); + BOOST_CHECK_EQUAL(foos.size() , 4ul); + BOOST_CHECK_EQUAL(foos.at(0).a , 42); + BOOST_CHECK_EQUAL(foos.at(1).a , 43); + BOOST_CHECK_EQUAL(foos.at(2).a , 44); + BOOST_CHECK_EQUAL(foos.at(3).a , 45); - BOOST_TEST(foos.at(0).b == "baz"); - BOOST_TEST(foos.at(1).b == "qux"); - BOOST_TEST(foos.at(2).b == "quux"); - BOOST_TEST(foos.at(3).b == "foobar"); + BOOST_CHECK_EQUAL(foos.at(0).b , "baz"); + BOOST_CHECK_EQUAL(foos.at(1).b , "qux"); + BOOST_CHECK_EQUAL(foos.at(2).b , "quux"); + BOOST_CHECK_EQUAL(foos.at(3).b , "foobar"); const auto bars = toml::get>(v); - BOOST_TEST(bars.size() == 4ul); - BOOST_TEST(bars.at(0).a == 42); - BOOST_TEST(bars.at(1).a == 43); - BOOST_TEST(bars.at(2).a == 44); - BOOST_TEST(bars.at(3).a == 45); + BOOST_CHECK_EQUAL(bars.size() , 4ul); + BOOST_CHECK_EQUAL(bars.at(0).a , 42); + BOOST_CHECK_EQUAL(bars.at(1).a , 43); + BOOST_CHECK_EQUAL(bars.at(2).a , 44); + BOOST_CHECK_EQUAL(bars.at(3).a , 45); - BOOST_TEST(bars.at(0).b == "baz"); - BOOST_TEST(bars.at(1).b == "qux"); - BOOST_TEST(bars.at(2).b == "quux"); - BOOST_TEST(bars.at(3).b == "foobar"); + BOOST_CHECK_EQUAL(bars.at(0).b , "baz"); + BOOST_CHECK_EQUAL(bars.at(1).b , "qux"); + BOOST_CHECK_EQUAL(bars.at(2).b , "quux"); + BOOST_CHECK_EQUAL(bars.at(3).b , "foobar"); } From 43014c6619232166b1883e9944477dfdf65cc3cf Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 16 Mar 2019 16:24:10 +0900 Subject: [PATCH 29/37] fix: remove redefined default template argument --- toml/get.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/toml/get.hpp b/toml/get.hpp index ffc8747..6aad019 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -329,9 +329,7 @@ T get(const toml::value& v) } template> // not a toml::value - >::value, std::nullptr_t>::type, - std::size_t = sizeof(::toml::from) // and has from specialization - > + >::value, std::nullptr_t>::type, std::size_t> // and has from T get(const toml::value& v) { return ::toml::from::from_toml(v); From cad8f5125651149e3f4297a8111839ad8bf7acb2 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 16 Mar 2019 16:56:37 +0900 Subject: [PATCH 30/37] doc: add explanation of conversions to README --- README.md | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/README.md b/README.md index 2861b34..2695491 100644 --- a/README.md +++ b/README.md @@ -423,6 +423,148 @@ int i = 0; toml::from_toml(i, data.at("something")); ``` +### Conversion between toml value and your class + +You can also use `toml::get` and other related functions with the types you defined +after you implement some stuff. + +```cpp +namespace ext +{ +struct foo +{ + int a; + double b; + std::string c; +}; +} // ext + +const auto data = toml::parse("example.toml"); + +const foo f = toml::get(data.at("foo")); +``` + +There are 2 ways to use `toml::get` with the types that you defined. + +The first one is to implement `from_toml(const toml::value&)` member function. + +```cpp +namespace ext +{ +struct foo +{ + int a; + double b; + std::string c; + + void from_toml(const toml::value& v) + { + this->a = toml::find(v, "a"); + this->b = toml::find(v, "b"); + this->c = toml::find(v, "c"); + return; + } +}; +} // ext +``` + +In this way, because `toml::get` first constructs `foo` without arguments, +the type should be default-constructible. + +The second is to implement specialization of `toml::from` for your type. + +```cpp +namespace ext +{ +struct foo +{ + int a; + double b; + std::string c; +}; +} // ext + +namespace toml +{ +template<> +struct from +{ + ext::foo from_toml(const toml::value& v) + { + ext::foo f; + f.a = toml::find(v, "a"); + f.b = toml::find(v, "b"); + f.c = toml::find(v, "c"); + return f; + } +}; +} // toml +``` + +In this way, since the conversion function is introduced from out 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` become ambiguous. + +---- + +The opposite direction is also supported in a similar way. You can directly +pass your type to `toml::value`'s constructor by introducing `into_iter` or +`toml::into`. + +```cpp +namespace ext +{ +struct foo +{ + int a; + double b; + std::string c; + + toml::table into_toml() const // you need to mark it const. + { + return toml::table{{"a", this->a}, {"b", this->b}, {"c", this->c}}; + } +}; +} // ext + +ext::foo f{42, 3.14, "foobar"}; +toml::value v(f); +``` + +The definition of `toml::into` is similar to `from_toml()`. + +```cpp +namespace ext +{ +struct foo +{ + int a; + double b; + std::string c; +}; +} // ext + +namespace toml +{ +template<> +struct into +{ + toml::table into_toml(const ext::foo& v) + { + return toml::table{{"a", this->a}, {"b", this->b}, {"c", this->c}}; + } +}; +} // toml + +ext::foo f{42, 3.14, "foobar"}; +toml::value v(f); +``` + +Any type that can be converted to `toml::value`, e.g. `toml::table`, `toml::array`, +is okay to return from `into_toml`. + ### visiting toml::value TOML v2.1.0+ provides `toml::visit` to apply a function to `toml::value` in the From d17c1926812e83fcf5b9eb24c10a4a5f3f1d6010 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 16 Mar 2019 17:05:58 +0900 Subject: [PATCH 31/37] refactor: remove to_toml and related tests --- tests/CMakeLists.txt | 1 - tests/test_to_toml.cpp | 189 ----------------------------------------- toml.hpp | 1 - toml/to_toml.hpp | 49 ----------- 4 files changed, 240 deletions(-) delete mode 100644 tests/test_to_toml.cpp delete mode 100644 toml/to_toml.hpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8db2420..f3bc0bb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -22,7 +22,6 @@ set(TEST_NAMES test_parse_table_key test_get test_get_related_func - test_to_toml test_from_toml test_parse_file test_serialize_file diff --git a/tests/test_to_toml.cpp b/tests/test_to_toml.cpp deleted file mode 100644 index 464a760..0000000 --- a/tests/test_to_toml.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#define BOOST_TEST_MODULE "test_to_toml" -#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST -#include -#else -#define BOOST_TEST_NO_LIB -#include -#endif -#include -#include -#include - -BOOST_AUTO_TEST_CASE(test_value_boolean) -{ - toml::value v1 = toml::to_toml(true); - toml::value v2 = toml::to_toml(false); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); - BOOST_CHECK(v1.is(toml::value_t::Boolean)); - BOOST_CHECK(v2.is(toml::value_t::Boolean)); - BOOST_CHECK(v1.is()); - BOOST_CHECK(v2.is()); - - BOOST_CHECK_EQUAL(v1.cast(), true); - BOOST_CHECK_EQUAL(v2.cast(), false); -} - -BOOST_AUTO_TEST_CASE(test_value_integer) -{ - toml::value v1 = toml::to_toml(-42); - toml::value v2 = toml::to_toml(42u); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Integer); - BOOST_CHECK(v1.is(toml::value_t::Integer)); - BOOST_CHECK(v2.is(toml::value_t::Integer)); - BOOST_CHECK(v1.is()); - BOOST_CHECK(v2.is()); - - BOOST_CHECK_EQUAL(v1.cast(), -42); - BOOST_CHECK_EQUAL(v2.cast(), 42u); -} - -BOOST_AUTO_TEST_CASE(test_value_float) -{ - toml::value v1 = toml::to_toml(3.14); - toml::value v2 = toml::to_toml(3.14f); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float); - BOOST_CHECK(v1.is(toml::value_t::Float)); - BOOST_CHECK(v2.is(toml::value_t::Float)); - BOOST_CHECK(v1.is()); - BOOST_CHECK(v2.is()); - - BOOST_CHECK_EQUAL(v1.cast(), 3.14); - BOOST_CHECK_CLOSE_FRACTION(v2.cast(), 3.14, 1e-2); -} - -BOOST_AUTO_TEST_CASE(test_value_string) -{ - toml::value v1 = toml::to_toml(std::string("foo")); - toml::value v2 = toml::to_toml(std::string("foo"), toml::string_t::literal); - toml::value v3 = toml::to_toml("foo"); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String); - BOOST_CHECK(v1.is(toml::value_t::String)); - BOOST_CHECK(v2.is(toml::value_t::String)); - BOOST_CHECK(v3.is(toml::value_t::String)); - BOOST_CHECK(v1.is()); - BOOST_CHECK(v2.is()); - BOOST_CHECK(v3.is()); - - BOOST_CHECK_EQUAL(v1.cast(), "foo"); - BOOST_CHECK_EQUAL(v2.cast(), "foo"); - BOOST_CHECK_EQUAL(v3.cast(), "foo"); -} - -BOOST_AUTO_TEST_CASE(test_value_local_date) -{ - toml::value v1 = toml::to_toml(toml::local_date(2018, toml::month_t::Jan, 31)); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); - BOOST_CHECK(v1.is(toml::value_t::LocalDate)); - BOOST_CHECK(v1.is()); - - BOOST_CHECK_EQUAL(v1.cast(), - toml::local_date(2018, toml::month_t::Jan, 31)); -} - -BOOST_AUTO_TEST_CASE(test_value_local_time) -{ - toml::value v1 = toml::to_toml(toml::local_time(12, 30, 45)); - toml::value v2 = toml::to_toml(std::chrono::hours(12) + std::chrono::minutes(30) + - std::chrono::seconds(45)); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalTime); - BOOST_CHECK(v1.is(toml::value_t::LocalTime)); - BOOST_CHECK(v2.is(toml::value_t::LocalTime)); - BOOST_CHECK(v1.is()); - BOOST_CHECK(v2.is()); - - BOOST_CHECK_EQUAL(v1.cast(), - toml::local_time(12, 30, 45)); - BOOST_CHECK_EQUAL(v2.cast(), - toml::local_time(12, 30, 45)); - BOOST_CHECK_EQUAL(v1.cast(), - v2.cast()); -} - -BOOST_AUTO_TEST_CASE(test_value_local_datetime) -{ - toml::value v1 = toml::to_toml(toml::local_datetime( - toml::local_date(2018, toml::month_t::Jan, 31), - toml::local_time(12, 30, 45) - )); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); - BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); - BOOST_CHECK(v1.is()); - - BOOST_CHECK_EQUAL(v1.cast(), - toml::local_datetime( - toml::local_date(2018, toml::month_t::Jan, 31), - toml::local_time(12, 30, 45))); -} - -BOOST_AUTO_TEST_CASE(test_value_offset_datetime) -{ - toml::value v1 = toml::to_toml(toml::offset_datetime( - toml::local_date(2018, toml::month_t::Jan, 31), - toml::local_time(12, 30, 45), - toml::time_offset(9, 0) - )); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); - BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); - BOOST_CHECK(v1.is()); - - BOOST_CHECK_EQUAL(v1.cast(), - toml::offset_datetime( - toml::local_date(2018, toml::month_t::Jan, 31), - toml::local_time(12, 30, 45), - toml::time_offset(9, 0) - )); -} - -BOOST_AUTO_TEST_CASE(test_value_array) -{ - std::vector v{1,2,3,4,5}; - toml::value v1 = toml::to_toml(v); - toml::value v2 = toml::to_toml(6,7,8,9,0); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); - BOOST_CHECK(v1.is(toml::value_t::Array)); - BOOST_CHECK(v1.is()); - - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); - BOOST_CHECK(v2.is(toml::value_t::Array)); - BOOST_CHECK(v2.is()); - - BOOST_CHECK_EQUAL(v1.cast().at(0).cast(), 1); - BOOST_CHECK_EQUAL(v1.cast().at(1).cast(), 2); - BOOST_CHECK_EQUAL(v1.cast().at(2).cast(), 3); - BOOST_CHECK_EQUAL(v1.cast().at(3).cast(), 4); - BOOST_CHECK_EQUAL(v1.cast().at(4).cast(), 5); - - BOOST_CHECK_EQUAL(v2.cast().at(0).cast(), 6); - BOOST_CHECK_EQUAL(v2.cast().at(1).cast(), 7); - BOOST_CHECK_EQUAL(v2.cast().at(2).cast(), 8); - BOOST_CHECK_EQUAL(v2.cast().at(3).cast(), 9); - BOOST_CHECK_EQUAL(v2.cast().at(4).cast(), 0); -} - -BOOST_AUTO_TEST_CASE(test_value_table) -{ - toml::value v1 = toml::to_toml({{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}); - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); - BOOST_CHECK(v1.is(toml::value_t::Table)); - BOOST_CHECK(v1.is()); - - BOOST_CHECK_EQUAL(v1.cast().at("foo").cast(), 42); - BOOST_CHECK_EQUAL(v1.cast().at("bar").cast(), 3.14); - BOOST_CHECK_EQUAL(v1.cast().at("baz").cast().str, "qux"); -} diff --git a/toml.hpp b/toml.hpp index 80e62e2..8cd95af 100644 --- a/toml.hpp +++ b/toml.hpp @@ -35,7 +35,6 @@ #include "toml/parser.hpp" #include "toml/serializer.hpp" -#include "toml/to_toml.hpp" #include "toml/from_toml.hpp" #include "toml/get.hpp" diff --git a/toml/to_toml.hpp b/toml/to_toml.hpp deleted file mode 100644 index 8fe2f69..0000000 --- a/toml/to_toml.hpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_TO_TOML_HPP -#define TOML11_TO_TOML_HPP -#include "value.hpp" - -namespace toml -{ - -template -TOML11_MARK_AS_DEPRECATED -inline value to_toml(T&& x) -{ - return value(std::forward(x)); -} - -template -TOML11_MARK_AS_DEPRECATED -inline value to_toml(T&& x, string_t kind) -{ - return value(std::forward(x), kind); -} - -TOML11_MARK_AS_DEPRECATED -inline value to_toml(local_date d, local_time t) -{ - return value(local_datetime(d, t)); -} -TOML11_MARK_AS_DEPRECATED -inline value to_toml(local_date d, local_time t, time_offset ofs) -{ - return value(offset_datetime(d, t, ofs)); -} - -template -TOML11_MARK_AS_DEPRECATED -inline value to_toml(Ts&& ... xs) -{ - return value(toml::array{toml::value(std::forward(xs)) ... }); -} - -TOML11_MARK_AS_DEPRECATED -inline value to_toml(std::initializer_list> xs) -{ - return value(toml::table(xs.begin(), xs.end())); -} - -} // toml -#endif // TOML11_TO_TOML From 6628fe5ace9da7bd39e46f2fb8629103799c34fb Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 17 Mar 2019 19:12:13 +0900 Subject: [PATCH 32/37] test: add language agnostic toml-test --- .circleci/config.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..7adb470 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,24 @@ +version: 2.1 + +jobs: + test: + docker: + - image: circleci/buildpack-deps:bionic + steps: + - checkout + - run: g++ --version + - run: cd test/ + - run: g++ -std=c++11 -O2 -Wall -Wextra -Werror check.cpp -o check + - run: git clone https://github.com/BurntSushi/toml-test.git + - run: cp check toml-test/tests/invalid + - run: cp check toml-test/tests/valid + - run: cd toml-test/tests/invalid + - run: for f in $(ls ./*.toml); do echo "==> ${f}"; cat ${f}; ./check ${f} invalid; done + - run: cd ../valid + - run: for f in $(ls ./*.toml); do echo "==> ${f}"; cat ${f}; ./check ${f} valid; done + +workflows: + version: 2.1 + test: + jobs: + - test From 77ab391885677945b1566b825cf7a5b1f260076f Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 17 Mar 2019 19:20:24 +0900 Subject: [PATCH 33/37] ci: fix name of directory and add test script --- .circleci/config.yml | 2 +- tests/check.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/check.cpp diff --git a/.circleci/config.yml b/.circleci/config.yml index 7adb470..277302d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ jobs: steps: - checkout - run: g++ --version - - run: cd test/ + - run: cd tests/ - run: g++ -std=c++11 -O2 -Wall -Wextra -Werror check.cpp -o check - run: git clone https://github.com/BurntSushi/toml-test.git - run: cp check toml-test/tests/invalid diff --git a/tests/check.cpp b/tests/check.cpp new file mode 100644 index 0000000..a271f3f --- /dev/null +++ b/tests/check.cpp @@ -0,0 +1,41 @@ +#include "toml.hpp" +#include +#include + +int main(int argc, char **argv) +{ + if(argc != 3) + { + std::cerr << "usage: ./check [filename] [valid|invalid]" << std::endl; + return 1; + } + + const std::string file_kind(argv[2]); + + try + { + const auto data = toml::parse(argv[1]); + std::cout << std::setprecision(16) << std::setw(80) << data; + if(file_kind == "valid") + { + return 0; + } + else + { + return 1; + } + } + catch(const toml::syntax_error& err) + { + std::cout << "what(): " << err.what() << std::endl; + if(file_kind == "invalid") + { + return 0; + } + else + { + return 1; + } + } + return 127; +} From cdf209d7f6c9445fcfe5f00ce7eb28ca80916bd0 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 17 Mar 2019 19:23:52 +0900 Subject: [PATCH 34/37] ci: show the status on CI --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 277302d..4401166 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,6 +8,7 @@ jobs: - checkout - run: g++ --version - run: cd tests/ + - run: ls - run: g++ -std=c++11 -O2 -Wall -Wextra -Werror check.cpp -o check - run: git clone https://github.com/BurntSushi/toml-test.git - run: cp check toml-test/tests/invalid From 209ad79a8f145531c1e2b578c7a4f76f9ca1df76 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 17 Mar 2019 19:26:22 +0900 Subject: [PATCH 35/37] ci: fix config file of circleci --- .circleci/config.yml | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4401166..c9c7f2f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,17 +6,19 @@ jobs: - image: circleci/buildpack-deps:bionic steps: - checkout - - run: g++ --version - - run: cd tests/ - - run: ls - - run: g++ -std=c++11 -O2 -Wall -Wextra -Werror check.cpp -o check - - run: git clone https://github.com/BurntSushi/toml-test.git - - run: cp check toml-test/tests/invalid - - run: cp check toml-test/tests/valid - - run: cd toml-test/tests/invalid - - run: for f in $(ls ./*.toml); do echo "==> ${f}"; cat ${f}; ./check ${f} invalid; done - - run: cd ../valid - - run: for f in $(ls ./*.toml); do echo "==> ${f}"; cat ${f}; ./check ${f} valid; done + - run: + command: | + g++ --version + cd tests/ + ls + g++ -std=c++11 -O2 -Wall -Wextra -Werror check.cpp -o check + git clone https://github.com/BurntSushi/toml-test.git + cp check toml-test/tests/invalid + cp check toml-test/tests/valid + cd toml-test/tests/invalid + for f in $(ls ./*.toml); do echo "==> ${f}"; cat ${f}; ./check ${f} invalid; done + cd ../valid + for f in $(ls ./*.toml); do echo "==> ${f}"; cat ${f}; ./check ${f} valid; done workflows: version: 2.1 From e761a503c00830a5c4546226671150d230c73e64 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 17 Mar 2019 19:27:58 +0900 Subject: [PATCH 36/37] ci: fix silly mistake in circleci script --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c9c7f2f..4b36c9d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,7 +11,7 @@ jobs: g++ --version cd tests/ ls - g++ -std=c++11 -O2 -Wall -Wextra -Werror check.cpp -o check + g++ -std=c++11 -O2 -Wall -Wextra -Werror -I../ check.cpp -o check git clone https://github.com/BurntSushi/toml-test.git cp check toml-test/tests/invalid cp check toml-test/tests/valid From 227688ec638bfc596a153d290883456e11970510 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 17 Mar 2019 19:36:23 +0900 Subject: [PATCH 37/37] ci: make result clearer a bit --- .circleci/config.yml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4b36c9d..421b8d1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,9 +16,27 @@ jobs: cp check toml-test/tests/invalid cp check toml-test/tests/valid cd toml-test/tests/invalid - for f in $(ls ./*.toml); do echo "==> ${f}"; cat ${f}; ./check ${f} invalid; done + for f in $(ls ./*.toml); + do echo "==> ${f}"; + cat ${f}; + echo "---------------------------------------"; + ./check ${f} invalid; + if [ $? -ne 0 ] ; then + exit 1 + fi + echo "======================================="; + done cd ../valid - for f in $(ls ./*.toml); do echo "==> ${f}"; cat ${f}; ./check ${f} valid; done + for f in $(ls ./*.toml); + do echo "==> ${f}"; + cat ${f}; + echo "---------------------------------------"; + ./check ${f} valid; + if [ $? -ne 0 ] ; then + exit 1 + fi + echo "======================================="; + done workflows: version: 2.1