diff --git a/.circleci/config.yml b/.circleci/config.yml index 7d2481a..d942793 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,24 +1,18 @@ version: 2.1 jobs: - linux_build: + develop_build: docker: - - image: debian:bullseye + - image: marzer/misc_cpp17_dev:latest steps: - checkout - - run: - name: Installing python - command: | - apt-get -qq update && apt-get install --upgrade -y python3 - run: name: Checking toml.hpp command: | cd python && python3 ci_single_header_check.py - run: - name: Installing dependencies + name: Pulling Catch2 command: | - apt-get -qq update && apt-get install --upgrade -y git clang-9 g++-9 python3-pip ninja-build - pip3 install meson git submodule update --init --recursive extern/Catch2 - run: name: Building with clang @@ -35,29 +29,38 @@ jobs: command: | cd build-clang && ninja test && cd ../build-gcc && ninja test - generate_dox: + + master_build: docker: - - image: debian:bullseye + - image: marzer/misc_cpp17_dev:latest steps: - checkout - run: - name: Installing dependencies + name: Checking toml.hpp command: | - apt-get -qq update && apt-get install --upgrade -y python3 python3-pip make cmake clang-9 flex bison git wget tar - pip3 install beautifulsoup4 jinja2 pygments html5lib - git submodule update --init extern/mcss + cd python && python3 ci_single_header_check.py - run: - name: Building and installing Doxygen 1.8.17 + name: Pulling Catch2 command: | - wget http://doxygen.nl/files/doxygen-1.8.17.src.tar.gz - tar xvzf doxygen-1.8.17.src.tar.gz - rm doxygen-1.8.17.src.tar.gz - cd doxygen-1.8.17 - CC=clang-9 CXX=clang++-9 cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -Wno-dev -Wno-deprecated - make install + git submodule update --init --recursive extern/Catch2 + - run: + name: Building with clang + command: | + CXX=clang++-9 meson build-clang + cd build-clang && ninja -v -j 4 + - run: + name: Building with gcc + command: | + CXX=g++-9 meson build-gcc + cd build-gcc && ninja -v -j 4 + - run: + name: Running tests + command: | + cd build-clang && ninja test && cd ../build-gcc && ninja test - run: name: Generating documentation command: | + git submodule update --init extern/mcss cd python && python3 generate_documentation.py - persist_to_workspace: root: docs @@ -91,14 +94,15 @@ workflows: version: 2 build: jobs: - - linux_build - - generate_dox: - requires: - - linux_build + - master_build: filters: branches: only: master - deploy_dox: requires: - - generate_dox + - master_build + - develop_build: + filters: + branches: + ignore: master diff --git a/.editorconfig b/.editorconfig index de79ff5..f1496fb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,5 +13,5 @@ indent_style = space charset = utf-8 trim_trailing_whitespace = true -[*.{c, cpp, cxx, h, hpp, hxx, inl, py, md}] +[*.{c, cpp, cxx, h, hpp, hxx, inl, py, md, css}] end_of_line = lf diff --git a/docs/Doxyfile b/docs/Doxyfile index 8663ed4..57a4310 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -6,7 +6,7 @@ DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = toml++ PROJECT_NUMBER = -PROJECT_BRIEF = +PROJECT_BRIEF = TOML Parser for C++17 PROJECT_LOGO = OUTPUT_DIRECTORY = ./ CREATE_SUBDIRS = NO @@ -27,7 +27,7 @@ ABBREVIATE_BRIEF = "The $name class" \ an \ the ALWAYS_DETAILED_SEC = NO -INLINE_INHERITED_MEMB = YES +INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = ../ STRIP_FROM_INC_PATH = @@ -53,7 +53,7 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES TOC_INCLUDE_HEADINGS = 0 AUTOLINK_SUPPORT = YES -BUILTIN_STL_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = NO @@ -330,10 +330,13 @@ PERLMOD_MAKEVAR_PREFIX = ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = NO -SEARCH_INCLUDES = YES +SEARCH_INCLUDES = NO INCLUDE_PATH = INCLUDE_FILE_PATTERNS = -PREDEFINED = __cplusplus=201703L +PREDEFINED = TOML_DOXYGEN=1 \ + __cplusplus=201703L \ + TOML_ALWAYS_INLINE=inline \ + TOML_MAY_THROW= EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = NO #--------------------------------------------------------------------------- diff --git a/docs/Doxyfile-mcss b/docs/Doxyfile-mcss index 689053b..10fd49c 100644 --- a/docs/Doxyfile-mcss +++ b/docs/Doxyfile-mcss @@ -1,21 +1,21 @@ -@INCLUDE = ./Doxyfile -GENERATE_HTML = NO -GENERATE_XML = YES -XML_PROGRAMLISTING = NO -HTML_EXTRA_STYLESHEET = https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600 \ - ../css/m-dark+documentation.compiled.css \ - tomlplusplus.css -WARN_AS_ERROR = NO -TAB_SIZE = 4 -HTML_EXTRA_FILES = tomlplusplus.js -##! M_THEME_COLOR = #2d2d30 -##! M_LINKS_NAVBAR1 = namespaces -##! M_LINKS_NAVBAR2 = annotated -##! M_SEARCH_DOWNLOAD_BINARY = NO -##! M_CLASS_TREE_EXPAND_LEVELS = 1 -##! M_FILE_TREE_EXPAND_LEVELS = 3 -##! M_PAGE_FINE_PRINT = \ -##! Github • \ -##! Report an issue \ -##!

-##! M_HTML_HEADER = +@INCLUDE = ./Doxyfile +GENERATE_HTML = NO +GENERATE_XML = YES +XML_PROGRAMLISTING = NO +HTML_EXTRA_STYLESHEET = https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600 \ + ../css/m-dark+documentation.compiled.css \ + tomlplusplus.css +WARN_AS_ERROR = NO +TAB_SIZE = 4 +HTML_EXTRA_FILES = tomlplusplus.js +SHOW_INCLUDE_FILES = NO +##! M_THEME_COLOR = #22272e +##! M_LINKS_NAVBAR1 = pages +##! M_LINKS_NAVBAR2 = annotated +##! M_SEARCH_DOWNLOAD_BINARY = NO +##! M_CLASS_TREE_EXPAND_LEVELS = 3 +##! M_FILE_TREE_EXPAND_LEVELS = 3 +##! M_PAGE_FINE_PRINT = Github • \ +##! Report an issue \ +##!

Documentation generated using m.css +##! M_HTML_HEADER = diff --git a/docs/tomlplusplus.css b/docs/tomlplusplus.css index 3bf6971..806982d 100644 --- a/docs/tomlplusplus.css +++ b/docs/tomlplusplus.css @@ -1,3 +1,4 @@ +/* table.m-table th { color: #ffe698; @@ -49,71 +50,16 @@ dl.m-doc dd margin-bottom: 0.8rem; } -a.tpp-external -{ - font-weight: normal; -} - .m-doc-template a, dl.m-doc dd a, ul.m-doc li > span.m-doc a { - color: #858585; + color: #858585; } .m-doc-template a.tpp-external:hover, dl.m-doc dd a.tpp-external:hover, ul.m-doc li > span.m-doc a.tpp-external:hover { - color: inherit; -} - - -.tpp-swatch, .tpp-enable-if > * -{ - display: inline-block; - border-radius: 0.2rem; -} - -.tpp-enable-if -{ - margin-bottom: 2px; -} - -.tpp-enable-if > * -{ - background-clip: padding-box !important; - padding: 0px 2px; - text-decoration: none; -} - -.tpp-enable-if > a -{ - white-space: nowrap; - font-size: 0.8rem; - font-weight: bold; - background-color: #858585; - color: #050505; - padding-bottom: 0px; - margin: 0px 1px; - margin-bottom: 2px; -} - -.tpp-enable-if > a:hover -{ - background-color: #747474; - color: initial; -} - -.tpp-enable-if > span -{ - display: none; - padding-left: 2em; - -} - -.tpp-swatch -{ - min-width: 3em; - min-height: 1em; + color: inherit; } body > header > nav @@ -126,12 +72,12 @@ html background-color: #2d2d30; } -pre, code, .m-label/*.m-flat.m-success*/, .tpp-enable-if > a +pre, code, .m-label, .tpp-enable-if > a { font-family: 'Consolas', monospace; } -pre, .m-doc-search-content /*, .tpp-enable-if > full*/ +pre, .m-doc-search-content { background-color: #1e1e1e; } @@ -141,31 +87,6 @@ pre.m-code, code background-color: #1e1e1e88; } -.m-code .c1 /* comments */ -{ - color: rgb(87,166,74); -} - -.m-code .mi, .m-code .mf /* literals */ -{ - color: rgb(181,206,168); -} - -.m-code .k /* keywords */ -{ - color: rgb(86,156,214); -} - -.m-code .n /* names */ -{ - color: rgb(220,220,220); -} - -.m-code .p /* punctuation */ -{ - color: rgb(120,120,120); -} - article section.m-doc-details > div > h3:first-child, article section > h2, body > footer > nav { background-color: #252526; @@ -178,21 +99,7 @@ article section.m-doc-details > div > h3:first-child, article section > h2, body .m-label:not(.m-flat) { - font-weight: bold; -} - -header -{ - position: fixed; - left: 0; - right: 0; - top: 0; - z-index: 100; -} - -body -{ - margin-top: 3rem; + font-weight: bold; } div.m-doc-include @@ -210,9 +117,91 @@ div.m-doc-include color: #9999AA; } -article, article > header, article section +*/ + +html, body { - margin-bottom: 3em; - background-color: red; - background-color: inherit; + scroll-padding-top: 3.5rem; +} + +body +{ + margin-top: 3rem; +} + +header { + position: fixed; + left: 0; + right: 0; + top: 0; + z-index: 100; +} + +article, article > header, article section { + margin-bottom: 3em; +} + +pre, code, .tpp-enable-if > a { + font-family: 'Consolas', 'Source Sans Pro', monospace; +} + +a.tpp-external { + font-weight: normal; +} + +.tpp-enable-if { + margin-bottom: 2px; +} + +.tpp-enable-if > * { + display: inline-block; + border-radius: 0.2rem; + background-clip: padding-box !important; + padding: 0px 2px; + text-decoration: none; +} + +.tpp-enable-if > a { + white-space: nowrap; + font-size: 0.8rem; + font-weight: bold; + background-color: #858585; + color: #050505; + padding-bottom: 0px; + margin: 0px 1px; + margin-bottom: 2px; +} + + .tpp-enable-if > a:hover { + background-color: #747474; + color: initial; + } + +.tpp-enable-if > span { + display: none; + padding-left: 2em; +} + +nav .m-thin { + margin-left: 0.5em; +} + +.m-code .c1 { + color: rgb(87,166,74); +} + +.m-code .mi, .m-code .mf { + color: rgb(181,206,168); +} + +.m-code .k { + color: rgb(86,156,214); +} + +.m-code .n { + color: rgb(220,220,220); +} + +.m-code .p { + color: rgb(120,120,120); } diff --git a/examples/parse_file.cpp b/examples/parse_file.cpp index 2c8a436..e7df419 100644 --- a/examples/parse_file.cpp +++ b/examples/parse_file.cpp @@ -22,8 +22,6 @@ int main(int argc, char** argv) } try { - //const auto table = toml::parse(file); //works but parse_errors would not include the source path - const auto table = toml::parse(file, std::move(path)); std::cout << table << std::endl; diff --git a/include/toml++/toml.h b/include/toml++/toml.h index 10924f2..6a79606 100644 --- a/include/toml++/toml.h +++ b/include/toml++/toml.h @@ -6,6 +6,7 @@ #include "toml_common.h" #include "toml_date_time.h" +#include "toml_print_to_stream.h" #include "toml_node.h" #include "toml_table.h" #include "toml_array.h" @@ -13,7 +14,6 @@ #include "toml_node_view.h" #include "toml_utf8.h" #include "toml_parser.h" -#include "toml_print_to_stream.h" #include "toml_formatter.h" #include "toml_default_formatter.h" #include "toml_json_formatter.h" @@ -51,4 +51,5 @@ #undef TOML_STRING_PREFIX_1 #undef TOML_STRING_PREFIX #undef TOML_UNDEF_MACROS + #undef TOML_DOXYGEN #endif diff --git a/include/toml++/toml_array.h b/include/toml++/toml_array.h index d7579b7..d8c6db8 100644 --- a/include/toml++/toml_array.h +++ b/include/toml++/toml_array.h @@ -79,6 +79,25 @@ namespace toml::impl namespace toml { + /// \brief A TOML array. + /// \detail The interface of this type is modeled after std::vector so things + /// mostly work as you'd expect them to with a vector: \cpp + /// + /// auto table = toml::parse("arr = [1, 2, 3, 4, 'five']"sv); + /// + /// auto& arr = *table.get_as("arr"); + /// + /// for (size_t i = 0; i < arr.size(); i++) + /// { + /// arr[i].visit([=](auto&& el) noexcept + /// { + /// std::cout << el << ", "; + /// }); + /// } + /// + /// // prints: 1, 2, 3, 4, "five" + /// + /// \ecpp class array final : public node { @@ -88,6 +107,11 @@ namespace toml public: + using value_type = node; + using size_type = size_t; + using difference_type = ptrdiff_t; + using reference = node&; + using const_reference = const node&; using iterator = impl::array_iterator; using const_iterator = impl::array_iterator; @@ -108,23 +132,20 @@ namespace toml } [[nodiscard]] node_type type() const noexcept override { return node_type::array; } - [[nodiscard]] bool is_table() const noexcept override { return false; } [[nodiscard]] bool is_array() const noexcept override { return true; } [[nodiscard]] bool is_value() const noexcept override { return false; } + [[nodiscard]] array* as_array() noexcept override { return this; } + [[nodiscard]] const array* as_array() const noexcept override { return this; } - [[nodiscard]] bool is_array_of_tables() const noexcept override - { - if (values.empty()) - return false; - - for (auto& val : values) - if (!val->is_table()) - return false; - - return true; - } - + /// \brief Checks if the array contains values of only one type. + /// + /// \tparam T A TOML node type. Provide an explicit type for "is every element a T?". + /// Leave it as the default `void` for "is every element the same type?". + /// + /// \returns True if the array was homogeneous. + /// + /// \attention Empty arrays are _not_ regarded as homogeneous. template [[nodiscard]] bool is_homogeneous() const noexcept { @@ -147,34 +168,102 @@ namespace toml return true; } - [[nodiscard]] array* as_array() noexcept override { return this; } - [[nodiscard]] const array* as_array() const noexcept override { return this; } + /// \brief Returns true if this array contains only tables. + [[nodiscard]] TOML_ALWAYS_INLINE + bool is_array_of_tables() const noexcept override + { + return is_homogeneous(); + } + /// \brief Gets a reference to the node element at a specific index. + [[nodiscard]] node& operator[] (size_t index) noexcept { return *values[index]; } + /// \brief Gets a reference to the node element at a specific index. + [[nodiscard]] const node& operator[] (size_t index) const noexcept { return *values[index]; } + + /// \brief Returns a reference to the first element in the array. + [[nodiscard]] node& front() noexcept { return *values.front(); } + /// \brief Returns a reference to the first element in the array. + [[nodiscard]] const node& front() const noexcept { return *values.front(); } + /// \brief Returns a reference to the last element in the array. + [[nodiscard]] node& back() noexcept { return *values.back(); } + /// \brief Returns a reference to the last element in the array. + [[nodiscard]] const node& back() const noexcept { return *values.back(); } + + /// \brief Returns an iterator to the first element. + [[nodiscard]] iterator begin() noexcept { return { values.begin() }; } + /// \brief Returns an iterator to the first element. + [[nodiscard]] const_iterator begin() const noexcept { return { values.begin() }; } + /// \brief Returns an iterator to the first element. + [[nodiscard]] const_iterator cbegin() const noexcept { return { values.cbegin() }; } + + /// \brief Returns an iterator to one-past-the-last element. + [[nodiscard]] iterator end() noexcept { return { values.end() }; } + /// \brief Returns an iterator to one-past-the-last element. + [[nodiscard]] const_iterator end() const noexcept { return { values.end() }; } + /// \brief Returns an iterator to one-past-the-last element. + [[nodiscard]] const_iterator cend() const noexcept { return { values.cend() }; } + + /// \brief Returns true if the array is empty. [[nodiscard]] bool empty() const noexcept { return values.empty(); } + /// \brief Returns the number of elements in the array. [[nodiscard]] size_t size() const noexcept { return values.size(); } + /// \brief Reserves internal storage capacity up to a pre-determined number of elements. + void reserve(size_t new_capacity) TOML_MAY_THROW { values.reserve(new_capacity); } - [[nodiscard]] node& operator[] (size_t index) & noexcept { return *values[index].get(); } - [[nodiscard]] node&& operator[] (size_t index) && noexcept { return std::move(*values[index].get()); } - [[nodiscard]] const node& operator[] (size_t index) const& noexcept { return *values[index].get(); } + /// \brief Removes all elements from the array. + void clear() noexcept { values.clear(); } + + // insert() + // emplace() + + /// \brief Removes the specified element from the array. + /// + /// \returns The position following the Iterator following the removed element. + iterator erase(const_iterator pos) noexcept + { + return iterator{ values.erase(pos.raw_) }; + } + + /// \brief Removes the specified range of elements elements from the array. + /// + /// \returns The position following the Iterator following the last removed element. + iterator erase(const_iterator first, const_iterator last) noexcept + { + return iterator{ values.erase(first.raw_, last.raw_) }; + } + + // push_back() + // emplace_back() + + /// \brief Removes the last element from the array. + void pop_back() noexcept { values.pop_back(); } + + /// \brief Gets an element at a specific index if it is a particular type. + /// + /// \tparam T The node's type. + /// \param index The element index. + /// + /// \returns A pointer to the selected element if it was of the specified type, or nullptr. template [[nodiscard]] node_of* get_as(size_t index) noexcept { return values[index]->as(); } + /// \brief Gets an element at a specific index if it is a particular type. + /// + /// \tparam T The node's type. + /// \param index The element index. + /// + /// \returns A pointer to the selected element if it was of the specified type, or nullptr. template [[nodiscard]] const node_of* get_as(size_t index) const noexcept { return values[index]->as(); } - [[nodiscard]] iterator begin() noexcept { return { values.begin() }; } - [[nodiscard]] const_iterator begin() const noexcept { return { values.begin() }; } - [[nodiscard]] const_iterator cbegin() const noexcept { return { values.cbegin() }; } - - [[nodiscard]] iterator end() noexcept { return { values.end() }; } - [[nodiscard]] const_iterator end() const noexcept { return { values.end() }; } - [[nodiscard]] const_iterator cend() const noexcept { return { values.cend() }; } + template + friend inline std::basic_ostream& operator << (std::basic_ostream&, const array&) TOML_MAY_THROW; }; } diff --git a/include/toml++/toml_common.h b/include/toml++/toml_common.h index 0f73017..7d2ccdf 100644 --- a/include/toml++/toml_common.h +++ b/include/toml++/toml_common.h @@ -114,8 +114,6 @@ #define TOML_USE_STREAMS_FOR_FLOATS 1 #endif -#elif defined (DOXYGEN) - #define TOML_EXCEPTIONS 0 #endif #ifndef TOML_CPP_VERSION @@ -138,6 +136,9 @@ #ifndef TOML_EXCEPTIONS #define TOML_EXCEPTIONS 1 #endif +#ifndef TOML_DOXYGEN + #define TOML_DOXYGEN 0 +#endif #if TOML_EXCEPTIONS #define TOML_CONDITIONAL_NOEXCEPT(...) noexcept(__VA_ARGS__) #define TOML_MAY_THROW @@ -273,20 +274,17 @@ TOML_POP_WARNINGS /// \brief The root namespace for all toml++ functions and types. namespace toml { - /// \brief User-defined literals. inline namespace literals { using namespace std::string_literals; using namespace std::string_view_literals; - /// \brief Specifies a uint8_t literal. [[nodiscard]] TOML_ALWAYS_INLINE TOML_CONSTEVAL uint8_t operator"" _u8(unsigned long long n) noexcept { return static_cast(n); } - /// \brief Specifies a size_t literal. [[nodiscard]] TOML_ALWAYS_INLINE TOML_CONSTEVAL size_t operator"" _sz(unsigned long long n) noexcept { @@ -302,16 +300,16 @@ namespace toml #else - /// \brief The base character type for toml++ keys and string values. - /// \remarks This will be `char8_t` if `TOML_CHAR_8_STRINGS = 1`, otherwise it will be `char`. + /// \brief The base character type for keys and string values. + /// \attention This will be `char8_t` if `TOML_CHAR_8_STRINGS` is `1`. using string_char = char; - /// \brief The string type for toml++ keys and string values. - /// \remarks This will be `std::u8string` if `TOML_CHAR_8_STRINGS = 1`, otherwise it will be `std::string`. + /// \brief The string type for keys and string values. + /// \attention This will be `std::u8string` if `TOML_CHAR_8_STRINGS` is `1`. using string = std::string; - /// \brief The string type for toml++ keys and string values. - /// \remarks This will be `std::u8string_view` if `TOML_CHAR_8_STRINGS = 1`, otherwise it will be `std::string_view`. + /// \brief The string type for keys and string values. + /// \attention This will be `std::u8string_view` if `TOML_CHAR_8_STRINGS` is `1`. using string_view = std::string_view; #endif @@ -348,13 +346,13 @@ namespace toml #else /// \brief The integer type used to tally line numbers and columns. - /// \remarks This will be `uint32_t` if `TOML_LARGE_FILES = 1`, otherwise it will be `uint16_t`. + /// \attention This will be `uint32_t` if `TOML_LARGE_FILES` is `1`. using source_index = uint16_t; #endif /// \brief A source document line-and-column pair. - struct source_position final + struct source_position { /// \brief The line number. /// \remarks Valid line numbers start at 1. @@ -371,7 +369,6 @@ namespace toml return line > source_index{} && column > source_index{}; } - [[nodiscard]] friend constexpr bool operator == (const source_position& lhs, const source_position& rhs) noexcept { @@ -412,7 +409,7 @@ namespace toml using source_path_ptr = std::shared_ptr; /// \brief A source document region. - struct source_region final + struct source_region { /// \brief The beginning of the region (inclusive). source_position begin; @@ -429,7 +426,7 @@ namespace toml TOML_PUSH_WARNINGS TOML_DISABLE_INIT_WARNINGS - #if TOML_EXCEPTIONS + #if !TOML_DOXYGEN && TOML_EXCEPTIONS class parse_error final : public std::runtime_error @@ -472,8 +469,8 @@ namespace toml /// \brief An error thrown/returned when parsing fails. /// - /// \remarks This will inherit from `std::runtime_exception` when `TOML_EXCEPTIONS = 1`. - /// The public interface will be exactly the same either way. + /// \remarks This class inherits from `std::runtime_error` when `TOML_EXCEPTIONS` is `1`. + /// The public interface remains the same regardless of exception mode. class parse_error final { private: @@ -537,6 +534,13 @@ namespace toml::impl #endif + template + [[nodiscard]] TOML_ALWAYS_INLINE + constexpr std::underlying_type_t unwrap_enum(T val) noexcept + { + return static_cast>(val); + } + // Q: "why not use std::find??" // A: Because is _huge_ and std::find would be the only thing I used from it. // I don't want to impose such a heavy burden on users. @@ -551,20 +555,6 @@ namespace toml::impl return {}; } - template - struct is_generic_invocable - { - template - static constexpr auto test(U&&) -> decltype(std::declval()(std::declval()), std::true_type{}); - static constexpr std::false_type test(...); - - struct tester {}; - static constexpr auto value = decltype(test(tester{}))::value; - }; - - template - inline constexpr bool is_generic_invocable_v = is_generic_invocable::value; - class parser; template @@ -692,12 +682,17 @@ namespace toml::impl namespace toml { + + /// \brief Helper alias that wraps a type up as it's TOML node equivalent. template using node_of = typename impl::node_wrapper::type; + /// \brief Helper alias that unwraps TOML node type to it's raw value equivalent. template using value_of = typename impl::node_unwrapper::type; + + /// \brief Pretty-prints the value of a node_type to a stream. template inline std::basic_ostream& operator << (std::basic_ostream& lhs, node_type rhs) TOML_MAY_THROW { diff --git a/include/toml++/toml_default_formatter.h b/include/toml++/toml_default_formatter.h index 9147920..a917caa 100644 --- a/include/toml++/toml_default_formatter.h +++ b/include/toml++/toml_default_formatter.h @@ -6,6 +6,9 @@ namespace toml::impl { + TOML_PUSH_WARNINGS + TOML_DISABLE_ALL_WARNINGS + [[nodiscard]] inline toml::string default_formatter_make_key_segment(const toml::string& str) noexcept { @@ -32,7 +35,16 @@ namespace toml::impl s.reserve(str.length() + 2_sz); s += TOML_STRING_PREFIX('"'); for (auto c : str) - s.append(escape_string_character(c)); + { + if (c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F')) TOML_UNLIKELY + s.append(low_character_escape_table[c]); + else if (c == TOML_STRING_PREFIX('\x7F')) TOML_UNLIKELY + s.append(TOML_STRING_PREFIX("\\u007F"sv)); + else if (c == TOML_STRING_PREFIX('"')) TOML_UNLIKELY + s.append(TOML_STRING_PREFIX("\\\""sv)); + else + s += c; + } s += TOML_STRING_PREFIX('"'); return s; } @@ -41,6 +53,8 @@ namespace toml::impl } } + TOML_POP_WARNINGS + [[nodiscard]] inline size_t default_formatter_inline_columns(const node& node) noexcept { @@ -177,7 +191,7 @@ namespace toml const auto original_indent = base::indent(); const auto multiline = impl::default_formatter_forces_multiline( arr, - base::indent_columns() * static_cast(original_indent < 0 ? 0 : original_indent) + base::indent_columns * static_cast(original_indent < 0 ? 0 : original_indent) ); impl::print_to_stream("["sv, base::stream()); if (multiline) @@ -341,17 +355,37 @@ namespace toml } } + void print() TOML_MAY_THROW + { + switch (auto source_type = base::source().type()) + { + case node_type::table: + { + auto& tbl = *reinterpret_cast(&base::source()); + if (tbl.is_inline() || (base::flags() & format_flags::always_print_as_inline) != format_flags::none) + print_inline(tbl); + else + { + base::decrease_indent(); // so root kvps and tables have the same indent + print(tbl); + } + break; + } + + case node_type::array: + print(*reinterpret_cast(&base::source())); + break; + + default: + base::print(base::source(), source_type); + } + } + public: TOML_NODISCARD_CTOR - default_formatter(const toml::table& source_, toml::string_view indent_string = {}) noexcept - : base{ - source_, - impl::formatter_options{ - indent_string, - false //quote_dates_and_times - } - } + explicit default_formatter(const toml::node& source, format_flags flags = {}) noexcept + : base{ source, flags } {} template @@ -359,9 +393,8 @@ namespace toml TOML_MAY_THROW { rhs.attach(lhs); - rhs.base::decrease_indent(); //starts at -1 so root kvps and first-level child tables have the same indent rhs.key_path.clear(); - rhs.print(rhs.source()); + rhs.print(); rhs.detach(); return lhs; } @@ -413,4 +446,10 @@ namespace toml { return lhs << default_formatter{ rhs }; } + + template + inline std::basic_ostream& operator << (std::basic_ostream& lhs, const array& rhs) TOML_MAY_THROW + { + return lhs << default_formatter{ rhs }; + } } diff --git a/include/toml++/toml_formatter.h b/include/toml++/toml_formatter.h index 89cf54e..6ffebec 100644 --- a/include/toml++/toml_formatter.h +++ b/include/toml++/toml_formatter.h @@ -1,53 +1,45 @@ #pragma once #include "toml_print_to_stream.h" +namespace toml +{ + enum class format_flags : uint8_t + { + none, + always_print_as_inline = 1, + quote_dates_and_times = 2 + }; + [[nodiscard]] constexpr format_flags operator & (format_flags lhs, format_flags rhs) noexcept + { + return static_cast(impl::unwrap_enum(lhs) & impl::unwrap_enum(rhs)); + } + [[nodiscard]] constexpr format_flags operator | (format_flags lhs, format_flags rhs) noexcept + { + return static_cast( impl::unwrap_enum(lhs) | impl::unwrap_enum(rhs) ); + } +} + namespace toml::impl { - TOML_PUSH_WARNINGS - TOML_DISABLE_ALL_WARNINGS // some compilers will complain about a tautological unsigned >= 0. - // TINAE - char can have signed _or_ unsigned semantics and I can't - // be arsed handling this differently - - [[nodiscard]] - inline toml::string_view escape_string_character(const toml::string_char& c) noexcept - { - if (c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F')) TOML_UNLIKELY - return low_character_escape_table[c]; - else if (c == TOML_STRING_PREFIX('\x7F')) TOML_UNLIKELY - return TOML_STRING_PREFIX("\\u007F"sv); - else if (c == TOML_STRING_PREFIX('"')) TOML_UNLIKELY - return TOML_STRING_PREFIX("\\\""sv); - else - return toml::string_view{ &c, 1_sz }; - } - - TOML_POP_WARNINGS - - struct formatter_options final - { - toml::string_view indent_string; - bool quote_dates_and_times; - }; - template class formatter { private: - const toml::table& source_; - std::basic_ostream* stream_ = nullptr; - formatter_options options_; + const toml::node* source_; + format_flags flags_; int indent_; bool naked_newline_; - size_t indent_columns_; + std::basic_ostream* stream_ = nullptr; protected: - [[nodiscard]] const toml::table& source() const noexcept { return source_; } - [[nodiscard]] const formatter_options& options() const noexcept { return options_; } + [[nodiscard]] const toml::node& source() const noexcept { return *source_; } + [[nodiscard]] format_flags flags() const noexcept { return flags_; } [[nodiscard]] std::basic_ostream& stream() const noexcept { return *stream_; } + static constexpr size_t indent_columns = 4; + static constexpr toml::string_view indent_string = TOML_STRING_PREFIX(" "sv); [[nodiscard]] int indent() const noexcept { return indent_; } - [[nodiscard]] size_t indent_columns() const noexcept { return indent_columns_; } void indent(int level) noexcept { indent_ = level; } void increase_indent() noexcept { indent_++; } void decrease_indent() noexcept { indent_--; } @@ -79,7 +71,7 @@ namespace toml::impl { for (int i = 0; i < indent_; i++) { - print_to_stream(options_.indent_string, *stream_); + print_to_stream(indent_string, *stream_); naked_newline_ = false; } } @@ -91,8 +83,7 @@ namespace toml::impl else { print_to_stream('"', *stream_); - for (auto c : str) - print_to_stream(escape_string_character(c), *stream_); + print_to_stream_with_escapes(str, *stream_); print_to_stream('"', *stream_); } naked_newline_ = false; @@ -114,7 +105,7 @@ namespace toml::impl if constexpr (is_date_time) { - if (options_.quote_dates_and_times) + if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none) print_to_stream('"', *stream_); } @@ -122,7 +113,7 @@ namespace toml::impl if constexpr (is_date_time) { - if (options_.quote_dates_and_times) + if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none) print_to_stream('"', *stream_); } @@ -145,22 +136,9 @@ namespace toml::impl } } - formatter(const toml::table& source, formatter_options&& options) noexcept - : source_{ source }, - options_{ std::move(options) } - - { - if (options_.indent_string.empty()) - { - options_.indent_string = TOML_STRING_PREFIX(" "sv); - indent_columns_ = 4_sz; - } - else - { - indent_columns_ = {}; - for (auto c : options_.indent_string) - indent_columns_ += c == '\t' ? 4_sz : 1_sz; - } - } + formatter(const toml::node& source, format_flags flags) noexcept + : source_{ &source }, + flags_{ flags } + {} }; } diff --git a/include/toml++/toml_json_formatter.h b/include/toml++/toml_json_formatter.h index a1f927c..94cebae 100644 --- a/include/toml++/toml_json_formatter.h +++ b/include/toml++/toml_json_formatter.h @@ -45,17 +45,23 @@ namespace toml base::clear_naked_newline(); } + void print() TOML_MAY_THROW + { + switch (auto source_type = base::source().type()) + { + case node_type::table: print(*reinterpret_cast(&base::source())); break; + case node_type::array: print(*reinterpret_cast(&base::source())); break; + default: base::print(base::source(), source_type); + } + } + public: TOML_NODISCARD_CTOR - json_formatter(const toml::table& source_, toml::string_view indent_string = {}) noexcept - : base{ - source_, - impl::formatter_options{ - indent_string, - true //quote_dates_and_times - } - } + explicit json_formatter( + const toml::node& source, + format_flags flags = format_flags::quote_dates_and_times) noexcept + : base{ source, flags } {} template @@ -63,7 +69,7 @@ namespace toml TOML_MAY_THROW { rhs.attach(lhs); - rhs.print(rhs.source()); + rhs.print(); rhs.detach(); return lhs; } diff --git a/include/toml++/toml_node.h b/include/toml++/toml_node.h index a7606a4..cb227d6 100644 --- a/include/toml++/toml_node.h +++ b/include/toml++/toml_node.h @@ -3,6 +3,10 @@ namespace toml { + /// \brief A TOML node. + /// + /// \detail A parsed TOML document forms a tree made up of tables, arrays and values. + /// This type is the base of each of those, providing a lot of the polymorphic plumbing. class TOML_INTERFACE node { private: @@ -43,25 +47,46 @@ namespace toml virtual ~node() noexcept = default; + + /// \brief Returns the node's type identifier. [[nodiscard]] virtual node_type type() const noexcept = 0; + + /// \brief Returns true if this node is a table. [[nodiscard]] virtual bool is_table() const noexcept = 0; + /// \brief Returns true if this node is an array. [[nodiscard]] virtual bool is_array() const noexcept = 0; + /// \brief Returns true if this node is a value. [[nodiscard]] virtual bool is_value() const noexcept = 0; + /// \brief Returns true if this node is a string value. [[nodiscard]] virtual bool is_string() const noexcept { return false; } + /// \brief Returns true if this node is an integer value. [[nodiscard]] virtual bool is_integer() const noexcept { return false; } + /// \brief Returns true if this node is an floating-point value. [[nodiscard]] virtual bool is_floating_point() const noexcept { return false; } + /// \brief Returns true if this node is a boolean value. [[nodiscard]] virtual bool is_boolean() const noexcept { return false; } + /// \brief Returns true if this node is a local date value. [[nodiscard]] virtual bool is_date() const noexcept { return false; } + /// \brief Returns true if this node is a local time value. [[nodiscard]] virtual bool is_time() const noexcept { return false; } + /// \brief Returns true if this node is a date-time value. [[nodiscard]] virtual bool is_date_time() const noexcept { return false; } + /// \brief Returns true if this node is an array containing only tables. [[nodiscard]] virtual bool is_array_of_tables() const noexcept { return false; } + + /// \brief Checks if a node is a specific type. + /// + /// \tparam T The + /// + /// \returns Returns true if this node is an instance template - [[nodiscard]] bool is() const noexcept + [[nodiscard]] TOML_ALWAYS_INLINE + bool is() const noexcept { - using type = value_of; + using type = value_of>; static_assert( impl::is_value_or_node, "Template type parameter must be one of the basic value types, a toml::table, or a toml::array" @@ -155,8 +180,7 @@ namespace toml static decltype(auto) do_visit(N* node, FUNC&& visitor) TOML_MAY_THROW { static_assert( - impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as())> + std::is_invocable_vtemplate reinterpret_as
())> || std::is_invocable_vtemplate reinterpret_as())> || std::is_invocable_vtemplate reinterpret_as())> || std::is_invocable_vtemplate reinterpret_as())> @@ -168,7 +192,7 @@ namespace toml "Visitors must be invocable for at least one of the toml::node specializations" ); - static constexpr auto is_exhaustive = impl::is_generic_invocable_v || ( + static constexpr auto is_exhaustive = std::is_invocable_vtemplate reinterpret_as
())> && std::is_invocable_vtemplate reinterpret_as())> && std::is_invocable_vtemplate reinterpret_as())> @@ -177,54 +201,44 @@ namespace toml && std::is_invocable_vtemplate reinterpret_as())> && std::is_invocable_vtemplate reinterpret_as())> && std::is_invocable_vtemplate reinterpret_as
())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as
())>) return std::forward(visitor)(*node->template reinterpret_as
()); break; case node_type::array: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as())>) return std::forward(visitor)(*node->template reinterpret_as()); break; case node_type::string: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as())>) return std::forward(visitor)(*node->template reinterpret_as()); break; case node_type::integer: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as())>) return std::forward(visitor)(*node->template reinterpret_as()); break; case node_type::floating_point: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as())>) return std::forward(visitor)(*node->template reinterpret_as()); break; case node_type::boolean: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as())>) return std::forward(visitor)(*node->template reinterpret_as()); break; case node_type::date: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as())>) return std::forward(visitor)(*node->template reinterpret_as()); break; case node_type::time: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as
operator[] (string_view) noexcept; [[nodiscard]] inline node_view operator[] (string_view) const noexcept; + + template + friend inline std::basic_ostream& operator << (std::basic_ostream&, const table&) TOML_MAY_THROW; }; } diff --git a/include/toml++/toml_value.h b/include/toml++/toml_value.h index 3d3e0dd..19022fa 100644 --- a/include/toml++/toml_value.h +++ b/include/toml++/toml_value.h @@ -1,5 +1,6 @@ #pragma once #include "toml_node.h" +#include "toml_print_to_stream.h" namespace toml { @@ -102,7 +103,18 @@ namespace toml template friend std::basic_ostream& operator << (std::basic_ostream& lhs, const value& rhs) TOML_MAY_THROW { - impl::print_to_stream(rhs.val_, lhs); + // this is the same behaviour as default_formatter, but it's so simple that there's + // no need to spin up a new instance of it just for individual values. + + if constexpr (std::is_same_v) + { + impl::print_to_stream('"', lhs); + impl::print_to_stream_with_escapes(rhs.val_, lhs); + impl::print_to_stream('"', lhs); + } + else + impl::print_to_stream(rhs.val_, lhs); + return lhs; } }; diff --git a/python/ci_single_header_check.py b/python/ci_single_header_check.py index 19c55a7..cd3b20f 100644 --- a/python/ci_single_header_check.py +++ b/python/ci_single_header_check.py @@ -22,7 +22,7 @@ def get_script_folder(): def read_all_text_from_file(path): print("Reading {}".format(path)) - with open(path, 'r') as file: + with open(path, 'r', encoding='utf-8') as file: text = file.read() return text diff --git a/python/generate_documentation.py b/python/generate_documentation.py index 96d9414..b7a5d11 100644 --- a/python/generate_documentation.py +++ b/python/generate_documentation.py @@ -57,28 +57,6 @@ def get_all_files(dir, all=None, any=None): -def print_value(name, val): - print('{}:\n {}'.format(name, val)) - - - -def error_message(err, prefix="Error"): - if (isinstance(err, Exception) or (is_collection(err) and isinstance(err[0], Exception))): - exc = err[0] if is_collection(err) else err - trace = err[1] if (is_collection(err) and len(err) > 1) else traceback.format_exc(err) - print('{}: [{}] {}\n{}'.format( - prefix, - type(err).__name__, - str(err), - traceback.format_exc(err) - ), - file=sys.stderr - ) - else: - print("{}: {}".format(prefix, err), file=sys.stderr) - - - class HTMLDocument(object): def __init__(self, path): @@ -257,14 +235,14 @@ class NavBarFix(object): -# changes any links to index.html to link to namespaces.html instead (index.html is blank/unused) +# changes any links to index.html to link to annotated.html instead (index.html is blank/unused) class IndexHrefFix(object): def __call__(self, file, doc): links = doc.body('a', href='index.html') if (len(links) > 0): for link in links: - link['href'] = 'namespaces.html' + link['href'] = 'annotated.html' return True return False @@ -475,31 +453,11 @@ class InlineNamespaceFix3(InlineNamespaceFixBase): -# adds a custom footer to the main index pages. -class FooterFix(object): - __replacement = ''.format( - datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC") - ) - - def __call__(self, file, doc): - footer = doc.body.find(id='tpp-custom-footer') - if (footer is None): - return False - html_replace_tag(footer, self.__replacement) - return True - - - -#======================================================================================================================= - - - # adds links to external sources where appropriate class ExtDocLinksFix(object): __types = [ (r'(?:std::)?size_t', 'https://en.cppreference.com/w/cpp/types/size_t'), - (r'(?:std::)?u?int(?:8|16|32|64)_ts?', 'https://en.cppreference.com/w/cpp/types/integer'), + (r'(?:std::)?u?int(?:8|16|32|64)(fast|least)?_ts?', 'https://en.cppreference.com/w/cpp/types/integer'), (r'std::pairs?', 'https://en.cppreference.com/w/cpp/utility/pair'), (r'std::bytes?', 'https://en.cppreference.com/w/cpp/types/byte'), (r'std::optionals?', 'https://en.cppreference.com/w/cpp/utility/optional'), @@ -554,6 +512,8 @@ class ExtDocLinksFix(object): (r'std::add_[lr]value_reference(?:_t)?', 'https://en.cppreference.com/w/cpp/types/add_reference'), (r'std::remove_reference(?:_t)?', 'https://en.cppreference.com/w/cpp/types/remove_reference'), (r'std::remove_cv(?:_t)?', 'https://en.cppreference.com/w/cpp/types/remove_cv'), + (r'std::exceptions?', 'https://en.cppreference.com/w/cpp/error/exception'), + (r'std::runtime_errors?', 'https://en.cppreference.com/w/cpp/error/runtime_error'), ( r'(?:L?P)?(?:' + r'D?WORD(?:32|64|_PTR)?|HANDLE|HMODULE|BOOL(?:EAN)?' @@ -661,27 +621,44 @@ class EnableIfFix(object): -_threadError = None +_threadError = False def postprocess_file(dir, file, fixes): + global _threadError - if (_threadError is not None): + if (_threadError): return False print("Post-processing {}".format(file)) - doc = HTMLDocument(path.join(dir, file)) - file = file.lower() changed = False - for fix in fixes: - if (fix(file, doc)): - changed = True - if (changed): - doc.flush() + + try: + doc = HTMLDocument(path.join(dir, file)) + file = file.lower() + for fix in fixes: + if (fix(file, doc)): + changed = True + if (changed): + doc.flush() + + except Exception as err: + print( + 'Error: [{}] {}'.format( + type(err).__name__, + str(err) + ), + file=sys.stderr + ) + traceback.print_exc(file=sys.stderr) + _threadError = True + return changed + + def delete_directory(dir_path): if (path.exists(dir_path)): print('Deleting {}'.format(dir_path)) @@ -712,12 +689,6 @@ def main(): mcss_dir = path.join(root_dir, 'extern', 'mcss') doxygen = path.join(mcss_dir, 'documentation', 'doxygen.py') - print_value('doc', docs_dir) - print_value('xml', xml_dir) - print_value('html', html_dir) - print_value('m.css', mcss_dir) - print_value('doxygen', doxygen) - # delete any previously generated html and xml delete_directory(xml_dir) delete_directory(html_dir) @@ -744,26 +715,22 @@ def main(): , InlineNamespaceFix1() , InlineNamespaceFix2() , InlineNamespaceFix3() - , FooterFix() , ExtDocLinksFix() , EnableIfFix() ] files = [path.split(f) for f in get_all_files(html_dir, any=('*.html', '*.htm'))] - print_value("Files", files) if files: with concurrent.futures.ThreadPoolExecutor(max_workers=min(len(files), num_threads)) as executor: jobs = { executor.submit(postprocess_file, dir, file, fixes) : file for dir, file in files } for job in concurrent.futures.as_completed(jobs): - file = jobs[job] - try: - print('Finished processing {}.'.format(file)) - except Exception as e: - _threadError = (e, traceback.format_exc(e)) + if _threadError: executor.shutdown(False) break - if (_threadError is not None): - error_message(_threadError, prefix="Fatal error") - sys.exit(-1) + else: + file = jobs[job] + print('Finished processing {}.'.format(file)) + if _threadError: + sys.exit(1) @@ -772,7 +739,7 @@ if __name__ == '__main__': main() except Exception as err: print( - 'Fatal error: [{}] {}'.format( + 'Error: [{}] {}'.format( type(err).__name__, str(err) ), diff --git a/python/generate_single_header.py b/python/generate_single_header.py index 780693f..e35d55b 100644 --- a/python/generate_single_header.py +++ b/python/generate_single_header.py @@ -16,7 +16,7 @@ def get_script_folder(): def read_all_text_from_file(path): print("Reading {}".format(path)) - with open(path, 'r') as file: + with open(path, 'r', encoding='utf-8') as file: text = file.read() return text @@ -78,11 +78,19 @@ def main(): source_text = Preprocessor()('toml.h') source_text = re.sub('\r\n', '\n', source_text, 0, re.I | re.M) # convert windows newlines source_text = re.sub('(?:\n[ \t]*//[/#!<]+[^\n]*)+\n', '\n', source_text, 0, re.I | re.M) # remove 'magic' comment blocks - source_text = re.sub('^[ \t]*//[/#!<]+.+?$', '', source_text, 0, re.I | re.M) # remove 'magic' comments - source_text = re.sub('\n([ \t]*\n[ \t]*)+\n', '\n\n', source_text, 0, re.I | re.M) # remove double newlines + source_text = re.sub('\n(?:[ \t]*\n[ \t]*)+\n', '\n\n', source_text, 0, re.I | re.M) # remove double newlines source_text = re.sub('([^ \t])[ \t]+\n', '\\1\n', source_text, 0, re.I | re.M) # remove trailing whitespace + for i in range(0, 5): # remove blank lines between simple one-liner definitions + source_text = re.sub('(using .+?;)\n\n([ \t]*using)', '\\1\n\\2', source_text, 0, re.I | re.M) + source_text = re.sub( + '([a-zA-Z_][a-zA-Z0-9_]*[ \t]+[a-zA-Z_][a-zA-Z0-9_]*[ \t]*;)' \ + + '\n\n([ \t]*[a-zA-Z_][a-zA-Z0-9_]*[ \t]+[a-zA-Z_][a-zA-Z0-9_]*[ \t]*;)', '\\1\n\\2', + source_text, 0, re.I | re.M) + source_text = re.sub('(\[\[nodiscard\]\][^\n]+)\n\n([ \t]*\[\[nodiscard\]\])', '\\1\n\\2', source_text, 0, re.I | re.M) source_text = source_text.strip() + + # extract library version library_version = [0,0,0] match = re.search(r'^\s*#\s*define\s+TOML_LIB_MAJOR\s+([0-9]+)\s*$', source_text, re.I | re.M) @@ -101,7 +109,7 @@ def main(): toml++ v{} https://github.com/marzer/tomlplusplus'''.format('.'.join(str(x) for x in library_version))) preamble.append(''' -- THIS FILE WAS ASSEMBLED FROM MULTIPLE HEADER FILES BY A SCRIPT - PLEASE DON'T EDIT IT DIRECTLY - +- THIS FILE WAS ASSEMBLED FROM MULTIPLE HEADER FILES BY A SCRIPT - PLEASE DON'T EDIT IT DIRECTLY - If you wish to submit a contribution to toml++, hooray and thanks! Before you crack on, please be aware that this file was assembled from a number of smaller files by a python script, and code contributions should not be made diff --git a/python/generate_unicode_functions.py b/python/generate_unicode_functions.py index 71e5e7f..c10cedd 100644 --- a/python/generate_unicode_functions.py +++ b/python/generate_unicode_functions.py @@ -467,7 +467,7 @@ def main(): print(codepoint_list, end='', file=codepoint_file) else: print("Reading unicode database file into memory") - with open(codepoint_file_path, 'r') as codepoint_file: + with open(codepoint_file_path, 'r', encoding='utf-8') as codepoint_file: codepoint_list = codepoint_file.read() diff --git a/toml.hpp b/toml.hpp index 001de60..f62cbae 100644 --- a/toml.hpp +++ b/toml.hpp @@ -5,7 +5,7 @@ // //---------------------------------------------------------------------------------------------------------------------- // -// - THIS FILE WAS ASSEMBLED FROM MULTIPLE HEADER FILES BY A SCRIPT - PLEASE DON'T EDIT IT DIRECTLY - +// - THIS FILE WAS ASSEMBLED FROM MULTIPLE HEADER FILES BY A SCRIPT - PLEASE DON'T EDIT IT DIRECTLY - // // If you wish to submit a contribution to toml++, hooray and thanks! Before you crack on, please be aware that this // file was assembled from a number of smaller files by a python script, and code contributions should not be made @@ -157,8 +157,6 @@ #define TOML_USE_STREAMS_FOR_FLOATS 1 #endif -#elif defined (DOXYGEN) - #define TOML_EXCEPTIONS 0 #endif #ifndef TOML_CPP_VERSION @@ -181,6 +179,9 @@ #ifndef TOML_EXCEPTIONS #define TOML_EXCEPTIONS 1 #endif +#ifndef TOML_DOXYGEN + #define TOML_DOXYGEN 0 +#endif #if TOML_EXCEPTIONS #define TOML_CONDITIONAL_NOEXCEPT(...) noexcept(__VA_ARGS__) #define TOML_MAY_THROW @@ -343,9 +344,7 @@ namespace toml #else using string_char = char; - using string = std::string; - using string_view = std::string_view; #endif @@ -354,7 +353,6 @@ namespace toml struct time; struct time_offset; struct date_time; - class node; template class node_view; template class value; @@ -384,10 +382,9 @@ namespace toml #endif - struct source_position final + struct source_position { source_index line; - source_index column; [[nodiscard]] @@ -434,19 +431,17 @@ namespace toml using source_path_ptr = std::shared_ptr; - struct source_region final + struct source_region { source_position begin; - source_position end; - source_path_ptr path; }; TOML_PUSH_WARNINGS TOML_DISABLE_INIT_WARNINGS - #if TOML_EXCEPTIONS + #if !TOML_DOXYGEN && TOML_EXCEPTIONS class parse_error final : public std::runtime_error @@ -546,6 +541,13 @@ namespace toml::impl #endif + template + [[nodiscard]] TOML_ALWAYS_INLINE + constexpr std::underlying_type_t unwrap_enum(T val) noexcept + { + return static_cast>(val); + } + // Q: "why not use std::find??" // A: Because is _huge_ and std::find would be the only thing I used from it. // I don't want to impose such a heavy burden on users. @@ -560,20 +562,6 @@ namespace toml::impl return {}; } - template - struct is_generic_invocable - { - template - static constexpr auto test(U&&) -> decltype(std::declval()(std::declval()), std::true_type{}); - static constexpr std::false_type test(...); - - struct tester {}; - static constexpr auto value = decltype(test(tester{}))::value; - }; - - template - inline constexpr bool is_generic_invocable_v = is_generic_invocable::value; - class parser; template @@ -700,6 +688,7 @@ namespace toml::impl namespace toml { + template using node_of = typename impl::node_wrapper::type; @@ -864,7 +853,288 @@ namespace toml #pragma endregion //---------------------------------------- ↑ toml_date_time.h -------------------------------------------------------- -//-------------------------------------------------------------------- ↓ toml_node.h --------------------------------- +//-------------------------------------------------------------- ↓ toml_print_to_stream.h ---------------------------- +#pragma region + +namespace toml::impl +{ + // Q: "why does print_to_stream() exist? why not just use ostream::write(), ostream::put() etc?" + // A: - I'm supporting C++20's char8_t as well; wrapping streams allows switching string modes transparently. + // - is locale-independent. + // - I can avoid forcing users to drag in and . + + template + TOML_ALWAYS_INLINE + void print_to_stream(std::basic_string_view str, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert(sizeof(CHAR1) == 1); + static_assert(sizeof(CHAR2) == 1); + stream.write(str.data(), str.length()); + } + + template + TOML_ALWAYS_INLINE + void print_to_stream(const std::basic_string& str, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert(sizeof(CHAR1) == 1); + static_assert(sizeof(CHAR2) == 1); + stream.write(str.data(), str.length()); + } + + template + TOML_ALWAYS_INLINE + void print_to_stream(char character, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert(sizeof(CHAR) == 1); + stream.put(static_cast(character)); + } + + template + TOML_GCC_ATTR(nonnull) TOML_ALWAYS_INLINE + void print_to_stream(const char* str, size_t len, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert(sizeof(CHAR) == 1); + stream.write(reinterpret_cast(str), static_cast(len)); + } + + #if defined(__cpp_lib_char8_t) + + template + TOML_ALWAYS_INLINE + void print_to_stream(char8_t character, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert(sizeof(CHAR) == 1); + stream.put(static_cast(character)); + } + + template + TOML_GCC_ATTR(nonnull) TOML_ALWAYS_INLINE + void print_to_stream(const char8_t* str, size_t len, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert(sizeof(CHAR) == 1); + stream.write(reinterpret_cast(str), static_cast(len)); + } + + #endif + + template inline constexpr size_t charconv_buffer_length = 0; + template <> inline constexpr size_t charconv_buffer_length = 60; + template <> inline constexpr size_t charconv_buffer_length = 40; + template <> inline constexpr size_t charconv_buffer_length = 20; //strlen("18446744073709551615") + template <> inline constexpr size_t charconv_buffer_length = 20; //strlen("-9223372036854775808") + template <> inline constexpr size_t charconv_buffer_length = 11; //strlen("-2147483648") + template <> inline constexpr size_t charconv_buffer_length = 6; //strlen("-32768") + template <> inline constexpr size_t charconv_buffer_length = 4; //strlen("-128") + template <> inline constexpr size_t charconv_buffer_length = 10; //strlen("4294967295") + template <> inline constexpr size_t charconv_buffer_length = 5; //strlen("65535") + template <> inline constexpr size_t charconv_buffer_length = 3; //strlen("255") + + template + inline void print_integer_to_stream(T val, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert( + sizeof(CHAR) == 1, + "The stream's underlying character type must be 1 byte in size." + ); + + TOML_GCC_ATTR(uninitialized) char buf[charconv_buffer_length]; + const auto res = std::to_chars(buf, buf + sizeof(buf), val); + print_to_stream(buf, static_cast(res.ptr - buf), stream); + } + + #define TOML_P2S_OVERLOAD(type) \ + template \ + TOML_ALWAYS_INLINE \ + void print_to_stream(type val, std::basic_ostream& stream) TOML_MAY_THROW \ + { \ + static_assert(sizeof(CHAR) == 1); \ + print_integer_to_stream(val, stream); \ + } + + TOML_P2S_OVERLOAD(int8_t) + TOML_P2S_OVERLOAD(int16_t) + TOML_P2S_OVERLOAD(int32_t) + TOML_P2S_OVERLOAD(int64_t) + TOML_P2S_OVERLOAD(uint8_t) + TOML_P2S_OVERLOAD(uint16_t) + TOML_P2S_OVERLOAD(uint32_t) + TOML_P2S_OVERLOAD(uint64_t) + + #undef TOML_P2S_OVERLOAD + + template + inline void print_floating_point_to_stream(T val, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert( + sizeof(CHAR) == 1, + "The stream's underlying character type must be 1 byte in size." + ); + + static constexpr auto needs_decimal_point = [](auto&& s) noexcept + { + for (auto c : s) + if (c == '.' || c == 'E' || c == 'e') + return false; + return true; + }; + + #if TOML_USE_STREAMS_FOR_FLOATS + { + std::ostringstream oss; + oss << val; + const auto str = oss.str(); + print_to_stream(str, stream); + if (needs_decimal_point(str)) + print_to_stream(".0"sv, stream); + } + #else + { + TOML_GCC_ATTR(uninitialized) char buf[charconv_buffer_length]; + const auto res = std::to_chars(buf, buf + sizeof(buf), val); + const auto str = std::string_view{ buf, static_cast(res.ptr - buf) }; + print_to_stream(str, stream); + if (needs_decimal_point(str)) + print_to_stream(".0"sv, stream); + } + #endif + } + + #define TOML_P2S_OVERLOAD(type) \ + template \ + TOML_ALWAYS_INLINE \ + void print_to_stream(type val, std::basic_ostream& stream) TOML_MAY_THROW \ + { \ + static_assert(sizeof(CHAR) == 1); \ + print_floating_point_to_stream(val, stream); \ + } + + TOML_P2S_OVERLOAD(float) + TOML_P2S_OVERLOAD(double) + + #undef TOML_P2S_OVERLOAD + + template + TOML_ALWAYS_INLINE + void print_to_stream(bool& val, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert(sizeof(CHAR) == 1); + print_to_stream(val ? "true"sv : "false"sv, stream); + } + + template + inline void print_to_stream(T val, std::basic_ostream& stream, size_t zero_pad_to_digits) TOML_MAY_THROW + { + static_assert(sizeof(CHAR) == 1); + TOML_GCC_ATTR(uninitialized) char buf[charconv_buffer_length]; + const auto res = std::to_chars(buf, buf + sizeof(buf), val); + const auto len = static_cast(res.ptr - buf); + for (size_t i = len; i < zero_pad_to_digits; i++) + print_to_stream('0', stream); + print_to_stream(buf, static_cast(res.ptr - buf), stream); + } + + template + inline void print_to_stream(const toml::date& val, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert(sizeof(CHAR) == 1); + print_to_stream(val.year, stream, 4_sz); + print_to_stream('-', stream); + print_to_stream(val.month, stream, 2_sz); + print_to_stream('-', stream); + print_to_stream(val.day, stream, 2_sz); + } + + template + inline void print_to_stream(const toml::time& val, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert(sizeof(CHAR) == 1); + print_to_stream(val.hour, stream, 2_sz); + print_to_stream(':', stream); + print_to_stream(val.minute, stream, 2_sz); + print_to_stream(':', stream); + print_to_stream(val.second, stream, 2_sz); + if (val.nanosecond && val.nanosecond <= 999999999u) + { + print_to_stream('.', stream); + auto ns = val.nanosecond; + size_t digits = 9_sz; + while (ns % 10u == 0u) + { + ns /= 10u; + digits--; + } + print_to_stream(ns, stream, digits); + } + } + + template + inline void print_to_stream(toml::time_offset val, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert(sizeof(CHAR) == 1); + if (!val.minutes) + print_to_stream('Z', stream); + else + { + auto mins = static_cast(val.minutes); + if (mins < 0) + { + print_to_stream('-', stream); + mins = -mins; + } + else + print_to_stream('+', stream); + const auto hours = mins / 60; + if (hours) + { + print_to_stream(static_cast(hours), stream, 2_sz); + mins -= hours * 60; + } + else + print_to_stream("00"sv, stream); + print_to_stream(':', stream); + print_to_stream(static_cast(mins), stream, 2_sz); + } + } + + template + inline void print_to_stream(const toml::date_time& val, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert(sizeof(CHAR) == 1); + + print_to_stream(val.date, stream); + print_to_stream('T', stream); + print_to_stream(val.time, stream); + if (val.time_offset) + print_to_stream(*val.time_offset, stream); + } + + TOML_PUSH_WARNINGS + TOML_DISABLE_ALL_WARNINGS + + template + void print_to_stream_with_escapes(T && str, std::basic_ostream& stream) TOML_MAY_THROW + { + static_assert(sizeof(CHAR) == 1); + for (auto c : str) + { + if (c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F')) TOML_UNLIKELY + print_to_stream(low_character_escape_table[c], stream); + else if (c == TOML_STRING_PREFIX('\x7F')) TOML_UNLIKELY + print_to_stream(TOML_STRING_PREFIX("\\u007F"sv), stream); + else if (c == TOML_STRING_PREFIX('"')) TOML_UNLIKELY + print_to_stream(TOML_STRING_PREFIX("\\\""sv), stream); + else + print_to_stream(c, stream); + } + } + + TOML_POP_WARNINGS +} + +#pragma endregion +//-------------------------------------------------------------- ↑ toml_print_to_stream.h ---------------------------- + +//--------------------------------------------------------------------------------------------- ↓ toml_node.h -------- #pragma region namespace toml @@ -910,11 +1180,9 @@ namespace toml virtual ~node() noexcept = default; [[nodiscard]] virtual node_type type() const noexcept = 0; - [[nodiscard]] virtual bool is_table() const noexcept = 0; [[nodiscard]] virtual bool is_array() const noexcept = 0; [[nodiscard]] virtual bool is_value() const noexcept = 0; - [[nodiscard]] virtual bool is_string() const noexcept { return false; } [[nodiscard]] virtual bool is_integer() const noexcept { return false; } [[nodiscard]] virtual bool is_floating_point() const noexcept { return false; } @@ -925,9 +1193,10 @@ namespace toml [[nodiscard]] virtual bool is_array_of_tables() const noexcept { return false; } template - [[nodiscard]] bool is() const noexcept + [[nodiscard]] TOML_ALWAYS_INLINE + bool is() const noexcept { - using type = value_of; + using type = value_of>; static_assert( impl::is_value_or_node, "Template type parameter must be one of the basic value types, a toml::table, or a toml::array" @@ -953,7 +1222,6 @@ namespace toml [[nodiscard]] virtual value* as_date() noexcept { return nullptr; } [[nodiscard]] virtual value
())> + std::is_invocable_vtemplate reinterpret_as
())> || std::is_invocable_vtemplate reinterpret_as())> || std::is_invocable_vtemplate reinterpret_as())> || std::is_invocable_vtemplate reinterpret_as())> @@ -1034,7 +1301,7 @@ namespace toml "Visitors must be invocable for at least one of the toml::node specializations" ); - static constexpr auto is_exhaustive = impl::is_generic_invocable_v || ( + static constexpr auto is_exhaustive = std::is_invocable_vtemplate reinterpret_as
())> && std::is_invocable_vtemplate reinterpret_as())> && std::is_invocable_vtemplate reinterpret_as())> @@ -1043,54 +1310,44 @@ namespace toml && std::is_invocable_vtemplate reinterpret_as())> && std::is_invocable_vtemplate reinterpret_as())> && std::is_invocable_vtemplate reinterpret_as
())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as
())>) return std::forward(visitor)(*node->template reinterpret_as
()); break; case node_type::array: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as())>) return std::forward(visitor)(*node->template reinterpret_as()); break; case node_type::string: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as())>) return std::forward(visitor)(*node->template reinterpret_as()); break; case node_type::integer: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as())>) return std::forward(visitor)(*node->template reinterpret_as()); break; case node_type::floating_point: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as())>) return std::forward(visitor)(*node->template reinterpret_as()); break; case node_type::boolean: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as())>) return std::forward(visitor)(*node->template reinterpret_as()); break; case node_type::date: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as())>) + if constexpr (std::is_invocable_vtemplate reinterpret_as())>) return std::forward(visitor)(*node->template reinterpret_as()); break; case node_type::time: - if constexpr (impl::is_generic_invocable_v - || std::is_invocable_vtemplate reinterpret_as
operator[] (string_view) noexcept; [[nodiscard]] inline node_view operator[] (string_view) const noexcept; + + template + friend inline std::basic_ostream& operator << (std::basic_ostream&, const table&) TOML_MAY_THROW; }; } #pragma endregion -//-------------------------------------------------------------------------------------------- ↑ toml_table.h -------- +//----------------- ↑ toml_table.h ----------------------------------------------------------------------------------- -//----------------- ↓ toml_array.h ----------------------------------------------------------------------------------- +//------------------------------------------ ↓ toml_array.h ---------------------------------------------------------- #pragma region namespace toml::impl @@ -1441,6 +1692,11 @@ namespace toml public: + using value_type = node; + using size_type = size_t; + using difference_type = ptrdiff_t; + using reference = node&; + using const_reference = const node&; using iterator = impl::array_iterator; using const_iterator = impl::array_iterator; @@ -1461,22 +1717,11 @@ namespace toml } [[nodiscard]] node_type type() const noexcept override { return node_type::array; } - [[nodiscard]] bool is_table() const noexcept override { return false; } [[nodiscard]] bool is_array() const noexcept override { return true; } [[nodiscard]] bool is_value() const noexcept override { return false; } - - [[nodiscard]] bool is_array_of_tables() const noexcept override - { - if (values.empty()) - return false; - - for (auto& val : values) - if (!val->is_table()) - return false; - - return true; - } + [[nodiscard]] array* as_array() noexcept override { return this; } + [[nodiscard]] const array* as_array() const noexcept override { return this; } template [[nodiscard]] bool is_homogeneous() const noexcept @@ -1500,15 +1745,47 @@ namespace toml return true; } - [[nodiscard]] array* as_array() noexcept override { return this; } - [[nodiscard]] const array* as_array() const noexcept override { return this; } + [[nodiscard]] TOML_ALWAYS_INLINE + bool is_array_of_tables() const noexcept override + { + return is_homogeneous(); + } + [[nodiscard]] node& operator[] (size_t index) noexcept { return *values[index]; } + [[nodiscard]] const node& operator[] (size_t index) const noexcept { return *values[index]; } + [[nodiscard]] node& front() noexcept { return *values.front(); } + [[nodiscard]] const node& front() const noexcept { return *values.front(); } + [[nodiscard]] node& back() noexcept { return *values.back(); } + [[nodiscard]] const node& back() const noexcept { return *values.back(); } + [[nodiscard]] iterator begin() noexcept { return { values.begin() }; } + [[nodiscard]] const_iterator begin() const noexcept { return { values.begin() }; } + [[nodiscard]] const_iterator cbegin() const noexcept { return { values.cbegin() }; } + [[nodiscard]] iterator end() noexcept { return { values.end() }; } + [[nodiscard]] const_iterator end() const noexcept { return { values.end() }; } + [[nodiscard]] const_iterator cend() const noexcept { return { values.cend() }; } [[nodiscard]] bool empty() const noexcept { return values.empty(); } [[nodiscard]] size_t size() const noexcept { return values.size(); } + void reserve(size_t new_capacity) TOML_MAY_THROW { values.reserve(new_capacity); } - [[nodiscard]] node& operator[] (size_t index) & noexcept { return *values[index].get(); } - [[nodiscard]] node&& operator[] (size_t index) && noexcept { return std::move(*values[index].get()); } - [[nodiscard]] const node& operator[] (size_t index) const& noexcept { return *values[index].get(); } + void clear() noexcept { values.clear(); } + + // insert() + // emplace() + + iterator erase(const_iterator pos) noexcept + { + return iterator{ values.erase(pos.raw_) }; + } + + iterator erase(const_iterator first, const_iterator last) noexcept + { + return iterator{ values.erase(first.raw_, last.raw_) }; + } + + // push_back() + // emplace_back() + + void pop_back() noexcept { values.pop_back(); } template [[nodiscard]] node_of* get_as(size_t index) noexcept @@ -1522,20 +1799,15 @@ namespace toml return values[index]->as(); } - [[nodiscard]] iterator begin() noexcept { return { values.begin() }; } - [[nodiscard]] const_iterator begin() const noexcept { return { values.begin() }; } - [[nodiscard]] const_iterator cbegin() const noexcept { return { values.cbegin() }; } - - [[nodiscard]] iterator end() noexcept { return { values.end() }; } - [[nodiscard]] const_iterator end() const noexcept { return { values.end() }; } - [[nodiscard]] const_iterator cend() const noexcept { return { values.cend() }; } + template + friend inline std::basic_ostream& operator << (std::basic_ostream&, const array&) TOML_MAY_THROW; }; } #pragma endregion -//----------------- ↑ toml_array.h ----------------------------------------------------------------------------------- +//------------------------------------------ ↑ toml_array.h ---------------------------------------------------------- -//------------------------------------------ ↓ toml_value.h ---------------------------------------------------------- +//------------------------------------------------------------------- ↓ toml_value.h --------------------------------- #pragma region namespace toml @@ -1599,7 +1871,6 @@ namespace toml [[nodiscard]] bool is_table() const noexcept override { return false; } [[nodiscard]] bool is_array() const noexcept override { return false; } [[nodiscard]] bool is_value() const noexcept override { return true; } - [[nodiscard]] bool is_string() const noexcept override { return std::is_same_v; } [[nodiscard]] bool is_integer() const noexcept override { return std::is_same_v; } [[nodiscard]] bool is_floating_point() const noexcept override { return std::is_same_v; } @@ -1607,7 +1878,6 @@ namespace toml [[nodiscard]] bool is_date() const noexcept override { return std::is_same_v; } [[nodiscard]] bool is_time() const noexcept override { return std::is_same_v; } [[nodiscard]] bool is_date_time() const noexcept override { return std::is_same_v; } - [[nodiscard]] value* as_string() noexcept override { return as_value(this); } [[nodiscard]] value* as_integer() noexcept override { return as_value(this); } [[nodiscard]] value* as_floating_point() noexcept override { return as_value(this); } @@ -1615,7 +1885,6 @@ namespace toml [[nodiscard]] value* as_date() noexcept override { return as_value(this); } [[nodiscard]] value
(); } - [[nodiscard]] const value* as_string() const noexcept { return as(); } [[nodiscard]] const value* as_integer() const noexcept { return as(); } [[nodiscard]] const value* as_floating_point() const noexcept { return as(); } @@ -1951,9 +2226,9 @@ namespace toml } #pragma endregion -//----------------------------------------------------------------- ↑ toml_node_view.h ------------------------------- +//------------------------------------------------------------------------------------------ ↑ toml_node_view.h ------ -//--------------------------------------------------------------------------------------------- ↓ toml_utf8.h -------- +//------------------ ↓ toml_utf8.h ----------------------------------------------------------------------------------- #pragma region #if TOML_LANG_HIGHER_THAN(0, 5, 0) // toml/issues/687 @@ -3534,9 +3809,9 @@ namespace toml::impl } #pragma endregion -//--------------------------------------------------------------------------------------------- ↑ toml_utf8.h -------- +//------------------ ↑ toml_utf8.h ----------------------------------------------------------------------------------- -//----------------- ↓ toml_parser.h ---------------------------------------------------------------------------------- +//------------------------------------------ ↓ toml_parser.h --------------------------------------------------------- #pragma region namespace toml @@ -3569,7 +3844,6 @@ namespace toml [[nodiscard]] bool succeeded() const noexcept { return !is_err; } [[nodiscard]] bool failed() const noexcept { return is_err; } [[nodiscard]] explicit operator bool() const noexcept { return !is_err; } - [[nodiscard]] table& get() & noexcept { TOML_ASSERT(!is_err); @@ -3605,14 +3879,11 @@ namespace toml [[nodiscard]] table& operator* () & noexcept { return get(); } [[nodiscard]] table&& operator* () && noexcept { return std::move(get()); } [[nodiscard]] const table& operator* () const & noexcept { return get(); } - [[nodiscard]] table* operator-> () noexcept { return &get(); } [[nodiscard]] const table* operator-> () const noexcept { return &get(); } - [[nodiscard]] operator table& () noexcept { return get(); } [[nodiscard]] operator table&& () noexcept { return std::move(get()); } [[nodiscard]] operator const table& () const noexcept { return get(); } - [[nodiscard]] explicit operator parse_error& () noexcept { return error(); } [[nodiscard]] explicit operator parse_error && () noexcept { return std::move(error()); } [[nodiscard]] explicit operator const parse_error& () const noexcept { return error(); } @@ -6605,317 +6876,50 @@ namespace toml } #pragma endregion -//----------------- ↑ toml_parser.h ---------------------------------------------------------------------------------- - -//------------------------------------- ↓ toml_print_to_stream.h ----------------------------------------------------- -#pragma region - -namespace toml::impl -{ - // Q: "why does print_to_stream() exist? why not just use ostream::write(), ostream::put() etc?" - // A: - I'm supporting C++20's char8_t as well; wrapping streams allows switching string modes transparently. - // - is locale-independent. - // - I can avoid forcing users to drag in and . - - template - TOML_ALWAYS_INLINE - void print_to_stream(std::basic_string_view str, std::basic_ostream& stream) TOML_MAY_THROW - { - static_assert(sizeof(CHAR1) == 1); - static_assert(sizeof(CHAR2) == 1); - stream.write(str.data(), str.length()); - } - - template - TOML_ALWAYS_INLINE - void print_to_stream(const std::basic_string& str, std::basic_ostream& stream) TOML_MAY_THROW - { - static_assert(sizeof(CHAR1) == 1); - static_assert(sizeof(CHAR2) == 1); - stream.write(str.data(), str.length()); - } - - template - TOML_ALWAYS_INLINE - void print_to_stream(char character, std::basic_ostream& stream) TOML_MAY_THROW - { - static_assert(sizeof(CHAR) == 1); - stream.put(static_cast(character)); - } - - template - TOML_GCC_ATTR(nonnull) TOML_ALWAYS_INLINE - void print_to_stream(const char* str, size_t len, std::basic_ostream& stream) TOML_MAY_THROW - { - static_assert(sizeof(CHAR) == 1); - stream.write(reinterpret_cast(str), static_cast(len)); - } - - #if defined(__cpp_lib_char8_t) - - template - TOML_ALWAYS_INLINE - void print_to_stream(char8_t character, std::basic_ostream& stream) TOML_MAY_THROW - { - static_assert(sizeof(CHAR) == 1); - stream.put(static_cast(character)); - } - - template - TOML_GCC_ATTR(nonnull) TOML_ALWAYS_INLINE - void print_to_stream(const char8_t* str, size_t len, std::basic_ostream& stream) TOML_MAY_THROW - { - static_assert(sizeof(CHAR) == 1); - stream.write(reinterpret_cast(str), static_cast(len)); - } - - #endif - - template inline constexpr size_t charconv_buffer_length = 0; - template <> inline constexpr size_t charconv_buffer_length = 60; - template <> inline constexpr size_t charconv_buffer_length = 40; - template <> inline constexpr size_t charconv_buffer_length = 20; //strlen("18446744073709551615") - template <> inline constexpr size_t charconv_buffer_length = 20; //strlen("-9223372036854775808") - template <> inline constexpr size_t charconv_buffer_length = 11; //strlen("-2147483648") - template <> inline constexpr size_t charconv_buffer_length = 6; //strlen("-32768") - template <> inline constexpr size_t charconv_buffer_length = 4; //strlen("-128") - template <> inline constexpr size_t charconv_buffer_length = 10; //strlen("4294967295") - template <> inline constexpr size_t charconv_buffer_length = 5; //strlen("65535") - template <> inline constexpr size_t charconv_buffer_length = 3; //strlen("255") - - template - inline void print_integer_to_stream(T val, std::basic_ostream& stream) TOML_MAY_THROW - { - static_assert( - sizeof(CHAR) == 1, - "The stream's underlying character type must be 1 byte in size." - ); - - TOML_GCC_ATTR(uninitialized) char buf[charconv_buffer_length]; - const auto res = std::to_chars(buf, buf + sizeof(buf), val); - print_to_stream(buf, static_cast(res.ptr - buf), stream); - } - - #define TOML_P2S_OVERLOAD(type) \ - template \ - TOML_ALWAYS_INLINE \ - void print_to_stream(type val, std::basic_ostream& stream) TOML_MAY_THROW \ - { \ - static_assert(sizeof(CHAR) == 1); \ - print_integer_to_stream(val, stream); \ - } - - TOML_P2S_OVERLOAD(int8_t) - TOML_P2S_OVERLOAD(int16_t) - TOML_P2S_OVERLOAD(int32_t) - TOML_P2S_OVERLOAD(int64_t) - TOML_P2S_OVERLOAD(uint8_t) - TOML_P2S_OVERLOAD(uint16_t) - TOML_P2S_OVERLOAD(uint32_t) - TOML_P2S_OVERLOAD(uint64_t) - - #undef TOML_P2S_OVERLOAD - - template - inline void print_floating_point_to_stream(T val, std::basic_ostream& stream) TOML_MAY_THROW - { - static_assert( - sizeof(CHAR) == 1, - "The stream's underlying character type must be 1 byte in size." - ); - - static constexpr auto needs_decimal_point = [](auto&& s) noexcept - { - for (auto c : s) - if (c == '.' || c == 'E' || c == 'e') - return false; - return true; - }; - - #if TOML_USE_STREAMS_FOR_FLOATS - { - std::ostringstream oss; - oss << val; - const auto str = oss.str(); - print_to_stream(str, stream); - if (needs_decimal_point(str)) - print_to_stream(".0"sv, stream); - } - #else - { - TOML_GCC_ATTR(uninitialized) char buf[charconv_buffer_length]; - const auto res = std::to_chars(buf, buf + sizeof(buf), val); - const auto str = std::string_view{ buf, static_cast(res.ptr - buf) }; - print_to_stream(str, stream); - if (needs_decimal_point(str)) - print_to_stream(".0"sv, stream); - } - #endif - } - - #define TOML_P2S_OVERLOAD(type) \ - template \ - TOML_ALWAYS_INLINE \ - void print_to_stream(type val, std::basic_ostream& stream) TOML_MAY_THROW \ - { \ - static_assert(sizeof(CHAR) == 1); \ - print_floating_point_to_stream(val, stream); \ - } - - TOML_P2S_OVERLOAD(float) - TOML_P2S_OVERLOAD(double) - - #undef TOML_P2S_OVERLOAD - - template - TOML_ALWAYS_INLINE - void print_to_stream(bool& val, std::basic_ostream& stream) TOML_MAY_THROW - { - static_assert(sizeof(CHAR) == 1); - print_to_stream(val ? "true"sv : "false"sv, stream); - } - - template - inline void print_to_stream(T val, std::basic_ostream& stream, size_t zero_pad_to_digits) TOML_MAY_THROW - { - static_assert(sizeof(CHAR) == 1); - TOML_GCC_ATTR(uninitialized) char buf[charconv_buffer_length]; - const auto res = std::to_chars(buf, buf + sizeof(buf), val); - const auto len = static_cast(res.ptr - buf); - for (size_t i = len; i < zero_pad_to_digits; i++) - print_to_stream('0', stream); - print_to_stream(buf, static_cast(res.ptr - buf), stream); - } - - template - inline void print_to_stream(const toml::date& val, std::basic_ostream& stream) TOML_MAY_THROW - { - static_assert(sizeof(CHAR) == 1); - print_to_stream(val.year, stream, 4_sz); - print_to_stream('-', stream); - print_to_stream(val.month, stream, 2_sz); - print_to_stream('-', stream); - print_to_stream(val.day, stream, 2_sz); - } - - template - inline void print_to_stream(const toml::time& val, std::basic_ostream& stream) TOML_MAY_THROW - { - static_assert(sizeof(CHAR) == 1); - print_to_stream(val.hour, stream, 2_sz); - print_to_stream(':', stream); - print_to_stream(val.minute, stream, 2_sz); - print_to_stream(':', stream); - print_to_stream(val.second, stream, 2_sz); - if (val.nanosecond && val.nanosecond <= 999999999u) - { - print_to_stream('.', stream); - auto ns = val.nanosecond; - size_t digits = 9_sz; - while (ns % 10u == 0u) - { - ns /= 10u; - digits--; - } - print_to_stream(ns, stream, digits); - } - } - - template - inline void print_to_stream(toml::time_offset val, std::basic_ostream& stream) TOML_MAY_THROW - { - static_assert(sizeof(CHAR) == 1); - if (!val.minutes) - print_to_stream('Z', stream); - else - { - auto mins = static_cast(val.minutes); - if (mins < 0) - { - print_to_stream('-', stream); - mins = -mins; - } - else - print_to_stream('+', stream); - const auto hours = mins / 60; - if (hours) - { - print_to_stream(static_cast(hours), stream, 2_sz); - mins -= hours * 60; - } - else - print_to_stream("00"sv, stream); - print_to_stream(':', stream); - print_to_stream(static_cast(mins), stream, 2_sz); - } - } - - template - inline void print_to_stream(const toml::date_time& val, std::basic_ostream& stream) TOML_MAY_THROW - { - static_assert(sizeof(CHAR) == 1); - - print_to_stream(val.date, stream); - print_to_stream('T', stream); - print_to_stream(val.time, stream); - if (val.time_offset) - print_to_stream(*val.time_offset, stream); - } -} - -#pragma endregion -//------------------------------------- ↑ toml_print_to_stream.h ----------------------------------------------------- +//------------------------------------------ ↑ toml_parser.h --------------------------------------------------------- //----------------------------------------------------------------- ↓ toml_formatter.h ------------------------------- #pragma region +namespace toml +{ + enum class format_flags : uint8_t + { + none, + always_print_as_inline = 1, + quote_dates_and_times = 2 + }; + [[nodiscard]] constexpr format_flags operator & (format_flags lhs, format_flags rhs) noexcept + { + return static_cast(impl::unwrap_enum(lhs) & impl::unwrap_enum(rhs)); + } + [[nodiscard]] constexpr format_flags operator | (format_flags lhs, format_flags rhs) noexcept + { + return static_cast( impl::unwrap_enum(lhs) | impl::unwrap_enum(rhs) ); + } +} + namespace toml::impl { - TOML_PUSH_WARNINGS - TOML_DISABLE_ALL_WARNINGS // some compilers will complain about a tautological unsigned >= 0. - // TINAE - char can have signed _or_ unsigned semantics and I can't - // be arsed handling this differently - - [[nodiscard]] - inline toml::string_view escape_string_character(const toml::string_char& c) noexcept - { - if (c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F')) TOML_UNLIKELY - return low_character_escape_table[c]; - else if (c == TOML_STRING_PREFIX('\x7F')) TOML_UNLIKELY - return TOML_STRING_PREFIX("\\u007F"sv); - else if (c == TOML_STRING_PREFIX('"')) TOML_UNLIKELY - return TOML_STRING_PREFIX("\\\""sv); - else - return toml::string_view{ &c, 1_sz }; - } - - TOML_POP_WARNINGS - - struct formatter_options final - { - toml::string_view indent_string; - bool quote_dates_and_times; - }; - template class formatter { private: - const toml::table& source_; - std::basic_ostream* stream_ = nullptr; - formatter_options options_; + const toml::node* source_; + format_flags flags_; int indent_; bool naked_newline_; - size_t indent_columns_; + std::basic_ostream* stream_ = nullptr; protected: - [[nodiscard]] const toml::table& source() const noexcept { return source_; } - [[nodiscard]] const formatter_options& options() const noexcept { return options_; } + [[nodiscard]] const toml::node& source() const noexcept { return *source_; } + [[nodiscard]] format_flags flags() const noexcept { return flags_; } [[nodiscard]] std::basic_ostream& stream() const noexcept { return *stream_; } + static constexpr size_t indent_columns = 4; + static constexpr toml::string_view indent_string = TOML_STRING_PREFIX(" "sv); [[nodiscard]] int indent() const noexcept { return indent_; } - [[nodiscard]] size_t indent_columns() const noexcept { return indent_columns_; } void indent(int level) noexcept { indent_ = level; } void increase_indent() noexcept { indent_++; } void decrease_indent() noexcept { indent_--; } @@ -6947,7 +6951,7 @@ namespace toml::impl { for (int i = 0; i < indent_; i++) { - print_to_stream(options_.indent_string, *stream_); + print_to_stream(indent_string, *stream_); naked_newline_ = false; } } @@ -6959,8 +6963,7 @@ namespace toml::impl else { print_to_stream('"', *stream_); - for (auto c : str) - print_to_stream(escape_string_character(c), *stream_); + print_to_stream_with_escapes(str, *stream_); print_to_stream('"', *stream_); } naked_newline_ = false; @@ -6982,7 +6985,7 @@ namespace toml::impl if constexpr (is_date_time) { - if (options_.quote_dates_and_times) + if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none) print_to_stream('"', *stream_); } @@ -6990,7 +6993,7 @@ namespace toml::impl if constexpr (is_date_time) { - if (options_.quote_dates_and_times) + if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none) print_to_stream('"', *stream_); } @@ -7013,23 +7016,10 @@ namespace toml::impl } } - formatter(const toml::table& source, formatter_options&& options) noexcept - : source_{ source }, - options_{ std::move(options) } - - { - if (options_.indent_string.empty()) - { - options_.indent_string = TOML_STRING_PREFIX(" "sv); - indent_columns_ = 4_sz; - } - else - { - indent_columns_ = {}; - for (auto c : options_.indent_string) - indent_columns_ += c == '\t' ? 4_sz : 1_sz; - } - } + formatter(const toml::node& source, format_flags flags) noexcept + : source_{ &source }, + flags_{ flags } + {} }; } @@ -7041,6 +7031,9 @@ namespace toml::impl namespace toml::impl { + TOML_PUSH_WARNINGS + TOML_DISABLE_ALL_WARNINGS + [[nodiscard]] inline toml::string default_formatter_make_key_segment(const toml::string& str) noexcept { @@ -7067,7 +7060,16 @@ namespace toml::impl s.reserve(str.length() + 2_sz); s += TOML_STRING_PREFIX('"'); for (auto c : str) - s.append(escape_string_character(c)); + { + if (c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F')) TOML_UNLIKELY + s.append(low_character_escape_table[c]); + else if (c == TOML_STRING_PREFIX('\x7F')) TOML_UNLIKELY + s.append(TOML_STRING_PREFIX("\\u007F"sv)); + else if (c == TOML_STRING_PREFIX('"')) TOML_UNLIKELY + s.append(TOML_STRING_PREFIX("\\\""sv)); + else + s += c; + } s += TOML_STRING_PREFIX('"'); return s; } @@ -7076,6 +7078,8 @@ namespace toml::impl } } + TOML_POP_WARNINGS + [[nodiscard]] inline size_t default_formatter_inline_columns(const node& node) noexcept { @@ -7212,7 +7216,7 @@ namespace toml const auto original_indent = base::indent(); const auto multiline = impl::default_formatter_forces_multiline( arr, - base::indent_columns() * static_cast(original_indent < 0 ? 0 : original_indent) + base::indent_columns * static_cast(original_indent < 0 ? 0 : original_indent) ); impl::print_to_stream("["sv, base::stream()); if (multiline) @@ -7376,17 +7380,37 @@ namespace toml } } + void print() TOML_MAY_THROW + { + switch (auto source_type = base::source().type()) + { + case node_type::table: + { + auto& tbl = *reinterpret_cast(&base::source()); + if (tbl.is_inline() || (base::flags() & format_flags::always_print_as_inline) != format_flags::none) + print_inline(tbl); + else + { + base::decrease_indent(); // so root kvps and tables have the same indent + print(tbl); + } + break; + } + + case node_type::array: + print(*reinterpret_cast(&base::source())); + break; + + default: + base::print(base::source(), source_type); + } + } + public: TOML_NODISCARD_CTOR - default_formatter(const toml::table& source_, toml::string_view indent_string = {}) noexcept - : base{ - source_, - impl::formatter_options{ - indent_string, - false //quote_dates_and_times - } - } + explicit default_formatter(const toml::node& source, format_flags flags = {}) noexcept + : base{ source, flags } {} template @@ -7394,9 +7418,8 @@ namespace toml TOML_MAY_THROW { rhs.attach(lhs); - rhs.base::decrease_indent(); //starts at -1 so root kvps and first-level child tables have the same indent rhs.key_path.clear(); - rhs.print(rhs.source()); + rhs.print(); rhs.detach(); return lhs; } @@ -7448,6 +7471,12 @@ namespace toml { return lhs << default_formatter{ rhs }; } + + template + inline std::basic_ostream& operator << (std::basic_ostream& lhs, const array& rhs) TOML_MAY_THROW + { + return lhs << default_formatter{ rhs }; + } } #pragma endregion @@ -7500,17 +7529,23 @@ namespace toml base::clear_naked_newline(); } + void print() TOML_MAY_THROW + { + switch (auto source_type = base::source().type()) + { + case node_type::table: print(*reinterpret_cast(&base::source())); break; + case node_type::array: print(*reinterpret_cast(&base::source())); break; + default: base::print(base::source(), source_type); + } + } + public: TOML_NODISCARD_CTOR - json_formatter(const toml::table& source_, toml::string_view indent_string = {}) noexcept - : base{ - source_, - impl::formatter_options{ - indent_string, - true //quote_dates_and_times - } - } + explicit json_formatter( + const toml::node& source, + format_flags flags = format_flags::quote_dates_and_times) noexcept + : base{ source, flags } {} template @@ -7518,7 +7553,7 @@ namespace toml TOML_MAY_THROW { rhs.attach(lhs); - rhs.print(rhs.source()); + rhs.print(); rhs.detach(); return lhs; } @@ -7607,6 +7642,7 @@ namespace toml #undef TOML_STRING_PREFIX_1 #undef TOML_STRING_PREFIX #undef TOML_UNDEF_MACROS + #undef TOML_DOXYGEN #endif #ifdef __GNUC__ diff --git a/vs/toml++.vcxproj b/vs/toml++.vcxproj index 6c2dc7e..dc6be77 100644 --- a/vs/toml++.vcxproj +++ b/vs/toml++.vcxproj @@ -75,6 +75,10 @@ + + + + diff --git a/vs/toml++.vcxproj.filters b/vs/toml++.vcxproj.filters index f8bd4e9..087c99e 100644 --- a/vs/toml++.vcxproj.filters +++ b/vs/toml++.vcxproj.filters @@ -1,29 +1,92 @@  - - - - - - - - - - - - - - - - + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + + + include + - - - - + + python + + + python + + + python + + + python + + + doc + + + doc + + + doc + + + doc + + + + + {98f8e2f6-ce87-435a-bd9a-4f02b8ac66e7} + + + {5ba46476-31a3-484e-aee7-f840dd2202b1} + + + {5ed0949f-6855-4664-ad86-2b38ceeb400b} + \ No newline at end of file