diff --git a/CMakeLists.txt b/CMakeLists.txt index 589949d..cf5800b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,5 +3,15 @@ project(cpp-json CXX) enable_testing() +if (MSVC) + add_compile_options(/EHsc /Zc:throwingNew) + if (MSVC_VERSION GREATER_EQUAL 1913) + add_compile_options(/Zc:externConstexpr) + endif() + if (MSVC_VERSION GREATER_EQUAL 1914) + add_compile_options(/Zc:__cplusplus) + endif() +endif() + add_subdirectory(lib) add_subdirectory(test) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 3ff0070..fa4be2f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -10,3 +10,13 @@ target_include_directories(cpp-json target_sources(cpp-json INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/cpp-json/json.h ) + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + target_compile_options(cpp-json INTERFACE + -Werror=reorder + ) +elseif(MSVC) + target_compile_options(cpp-json INTERFACE + /we5038 + ) +endif() diff --git a/lib/include/cpp-json/json_decode.h b/lib/include/cpp-json/json_decode.h index 33b1746..4d89044 100644 --- a/lib/include/cpp-json/json_decode.h +++ b/lib/include/cpp-json/json_decode.h @@ -14,6 +14,7 @@ #include "json_error.h" #include "json_reader.h" #include "json_value.h" +#include namespace json { @@ -84,7 +85,7 @@ class parser { while (reader_.peek() != '"' && reader_.peek() != '\n') { - char ch = reader_.read(); + const char ch = reader_.read(); if (ch == '\\') { switch (reader_.read()) { case '"': @@ -120,8 +121,8 @@ class parser { if (!std::isxdigit(hex[2] = reader_.read())) JSON_THROW(invalid_unicode_character(reader_.index())); if (!std::isxdigit(hex[3] = reader_.read())) JSON_THROW(invalid_unicode_character(reader_.index())); - uint16_t w1 = 0; - uint16_t w2 = 0; + std::uint_least16_t w1 = 0; + std::uint_least16_t w2 = 0; w1 |= (detail::to_hex(hex[0]) << 12); w1 |= (detail::to_hex(hex[1]) << 8); diff --git a/lib/include/cpp-json/json_detail.h b/lib/include/cpp-json/json_detail.h index d2ec9de..4b9d508 100644 --- a/lib/include/cpp-json/json_detail.h +++ b/lib/include/cpp-json/json_detail.h @@ -45,12 +45,12 @@ bool is_space(Ch ch) { * @param out */ template -void surrogate_pair_to_utf8(std::uint16_t w1, std::uint16_t w2, Out &out) { +void surrogate_pair_to_utf8(std::uint_least16_t w1, std::uint_least16_t w2, Out &out) { - std::uint32_t cp; + std::uint_least32_t cp = '\0'; if ((w1 & 0xfc00) == 0xd800) { if ((w2 & 0xfc00) == 0xdc00) { - cp = 0x10000 + (((static_cast(w1) & 0x3ff) << 10) | (w2 & 0x3ff)); + cp = 0x10000 + (((static_cast(w1) & 0x3ff) << 10) | (w2 & 0x3ff)); } else { JSON_THROW(invalid_unicode_character(0)); } @@ -59,19 +59,19 @@ void surrogate_pair_to_utf8(std::uint16_t w1, std::uint16_t w2, Out &out) { } if (cp < 0x80) { - *out++ = static_cast(cp); + *out++ = static_cast(cp); } else if (cp < 0x0800) { - *out++ = static_cast(0xc0 | ((cp >> 6) & 0x1f)); - *out++ = static_cast(0x80 | (cp & 0x3f)); + *out++ = static_cast(0xc0 | ((cp >> 6) & 0x1f)); + *out++ = static_cast(0x80 | (cp & 0x3f)); } else if (cp < 0x10000) { - *out++ = static_cast(0xe0 | ((cp >> 12) & 0x0f)); - *out++ = static_cast(0x80 | ((cp >> 6) & 0x3f)); - *out++ = static_cast(0x80 | (cp & 0x3f)); + *out++ = static_cast(0xe0 | ((cp >> 12) & 0x0f)); + *out++ = static_cast(0x80 | ((cp >> 6) & 0x3f)); + *out++ = static_cast(0x80 | (cp & 0x3f)); } else if (cp < 0x1fffff) { - *out++ = static_cast(0xf0 | ((cp >> 18) & 0x07)); - *out++ = static_cast(0x80 | ((cp >> 12) & 0x3f)); - *out++ = static_cast(0x80 | ((cp >> 6) & 0x3f)); - *out++ = static_cast(0x80 | (cp & 0x3f)); + *out++ = static_cast(0xf0 | ((cp >> 18) & 0x07)); + *out++ = static_cast(0x80 | ((cp >> 12) & 0x3f)); + *out++ = static_cast(0x80 | ((cp >> 6) & 0x3f)); + *out++ = static_cast(0x80 | (cp & 0x3f)); } } diff --git a/lib/include/cpp-json/json_encode.h b/lib/include/cpp-json/json_encode.h index ac67250..66b5f74 100644 --- a/lib/include/cpp-json/json_encode.h +++ b/lib/include/cpp-json/json_encode.h @@ -3,6 +3,8 @@ #define JSON_ENCODE_H_ #include "json_value.h" +#include +#include #include #include #include @@ -49,12 +51,10 @@ inline std::string escape_string(std::string_view s, Options options) { reserved : 24; }; - state_t shift_state = {0, 0, 0}; - char32_t result = 0; + state_t shift_state = {0, 0, 0}; + std::uint_least32_t result = 0; - for (auto it = s.begin(); it != s.end(); ++it) { - - const auto ch = static_cast(*it); + for (const unsigned char ch : s) { if (shift_state.seen == 0) { @@ -88,7 +88,7 @@ inline std::string escape_string(std::string_view s, Options options) { if (!isprint(ch)) { r += "\\u"; char buf[5]; - snprintf(buf, sizeof(buf), "%04X", ch); + snprintf(buf, std::size(buf), "%04X", ch); r += buf; } else { r += static_cast(ch); @@ -133,17 +133,17 @@ inline std::string escape_string(std::string_view s, Options options) { if (result < 0xd800 || (result >= 0xe000 && result < 0x10000)) { r += "\\u"; - snprintf(buf, sizeof(buf), "%04X", result); + snprintf(buf, std::size(buf), "%04X", result); r += buf; } else { result = (result - 0x10000); r += "\\u"; - snprintf(buf, sizeof(buf), "%04X", 0xd800 + ((result >> 10) & 0x3ff)); + snprintf(buf, std::size(buf), "%04X", 0xd800 + ((result >> 10) & 0x3ff)); r += buf; r += "\\u"; - snprintf(buf, sizeof(buf), "%04X", 0xdc00 + (result & 0x3ff)); + snprintf(buf, std::size(buf), "%04X", 0xdc00 + (result & 0x3ff)); r += buf; } @@ -161,7 +161,7 @@ inline std::string escape_string(std::string_view s, Options options) { } } else { - for (char ch : s) { + for (const char ch : s) { switch (ch) { case '\"': @@ -216,14 +216,13 @@ inline void value_to_string(std::ostream &os, const object &o, Options options, os << "{\n"; auto it = o.begin(); - auto e = o.end(); ++indent; os << std::string(indent * IndentWidth, ' ') << '"' << escape_string(it->first, options) << "\" : "; value_to_string(os, it->second, options, indent, true); ++it; - for (; it != e; ++it) { + for (auto e = o.end(); it != e; ++it) { os << ','; os << '\n'; os << std::string(indent * IndentWidth, ' ') << '"' << escape_string(it->first, options) << "\" : "; @@ -248,12 +247,11 @@ inline void value_to_string(std::ostream &os, const array &a, Options options, i os << "[\n"; auto it = a.begin(); - auto e = a.end(); ++indent; value_to_string(os, *it++, options, indent, false); - for (; it != e; ++it) { + for (auto e = a.end(); it != e; ++it) { os << ','; os << '\n'; value_to_string(os, *it, options, indent, false); @@ -315,11 +313,10 @@ inline void serialize(std::ostream &os, const array &a, Options options) { os << "["; if (!a.empty()) { auto it = a.begin(); - auto e = a.end(); serialize(os, *it++, options); - for (; it != e; ++it) { + for (const auto e = a.end(); it != e; ++it) { os << ','; serialize(os, *it, options); } @@ -331,12 +328,11 @@ inline void serialize(std::ostream &os, const object &o, Options options) { os << "{"; if (!o.empty()) { auto it = o.begin(); - auto e = o.end(); os << '"' << escape_string(it->first, options) << "\":"; serialize(os, it->second, options); ++it; - for (; it != e; ++it) { + for (const auto e = o.end(); it != e; ++it) { os << ','; os << '"' << escape_string(it->first, options) << "\":"; serialize(os, it->second, options); diff --git a/lib/include/cpp-json/json_reader.h b/lib/include/cpp-json/json_reader.h index a1f5dbc..a5857cd 100644 --- a/lib/include/cpp-json/json_reader.h +++ b/lib/include/cpp-json/json_reader.h @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -79,7 +80,7 @@ class basic_reader { * @return size_t */ size_t consume(std::basic_string_view chars) noexcept { - return consume_while([chars](Ch ch) { + return consume_while([chars](Ch ch) noexcept { return chars.find(ch) != std::basic_string_view::npos; }); } @@ -91,7 +92,7 @@ class basic_reader { * @return size_t */ size_t consume_whitespace() noexcept { - return consume_while([](Ch ch) { + return consume_while([](Ch ch) noexcept { return (ch == ' ' || ch == '\t'); }); } @@ -104,18 +105,12 @@ class basic_reader { * @return size_t */ template - size_t consume_while(Pred pred) noexcept { - size_t count = 0; - while (!eof()) { - const Ch ch = peek(); - if (!pred(ch)) { - break; - } - + size_t consume_while(Pred pred) noexcept(noexcept(pred(Ch{'\0'}))) { + const size_t start = index_; + while (!eof() && pred(peek())) { ++index_; - ++count; } - return count; + return index_ - start; } /** @@ -160,7 +155,7 @@ class basic_reader { return {}; } - std::basic_string m = input_.substr(index_); + std::basic_string m{ input_.substr(index_) }; index_ += m.size(); return m; } @@ -173,15 +168,13 @@ class basic_reader { * @return bool */ std::optional> match(const std::basic_regex ®ex) { - std::match_results matches; + std::match_results::const_iterator> matches; - const Ch *first = &input_[index_]; - const Ch *last = &input_[input_.size()]; + const auto first = std::next(input_.cbegin(), index_); - if (std::regex_search(first, last, matches, regex, std::regex_constants::match_continuous)) { - std::basic_string m(matches[0].first, matches[0].second); - index_ += m.size(); - return m; + if (std::regex_search(first, input_.cend(), matches, regex, std::regex_constants::match_continuous)) { + index_ += matches[0].length(); + return matches[0].str(); } return {}; @@ -196,20 +189,12 @@ class basic_reader { */ template std::optional> match_while(Pred pred) { + using return_type = std::optional>; - size_t start = index_; - while (!eof()) { - const Ch ch = peek(); - if (!pred(ch)) { - break; - } - - ++index_; - } + const size_t count = consume_while(std::move(pred)); - std::basic_string m(&input_[start], &input_[index_]); - if (!m.empty()) { - return m; + if (count > 0) { + return return_type{ input_.substr(index_ - count, count) }; } return {}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a0f516a..46d6d2b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,10 +15,18 @@ set_property(TARGET example2 PROPERTY CXX_STANDARD 17) set_property(TARGET example3 PROPERTY CXX_STANDARD 17) set_property(TARGET example4 PROPERTY CXX_STANDARD 17) -target_compile_options(example1 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow) -target_compile_options(example2 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow) -target_compile_options(example3 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow) -target_compile_options(example4 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow) +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + target_compile_options(example1 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow) + target_compile_options(example2 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow) + target_compile_options(example3 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow) + target_compile_options(example4 PUBLIC -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow) +elseif(MSVC) + # multi-processor compilation, warning-level 4 + target_compile_options(example1 PUBLIC /MP /W4) + target_compile_options(example2 PUBLIC /MP /W4) + target_compile_options(example3 PUBLIC /MP /W4) + target_compile_options(example4 PUBLIC /MP /W4) +endif() add_test( NAME example1