From b728387109f7598ccda7faa1f3d08b6f51ff1050 Mon Sep 17 00:00:00 2001 From: Jacyking <791026912@qq.com> Date: Mon, 6 May 2024 22:57:00 +0800 Subject: [PATCH 01/12] add section field update --- iguana/detail/charconv.h | 36 +++++++-- iguana/detail/traits.hpp | 10 ++- iguana/json_reader.hpp | 154 +++++++++++++++++++++++++---------- iguana/json_util.hpp | 29 ++++++- iguana/json_writer.hpp | 78 +++++++++--------- iguana/reflection.hpp | 114 ++++++++++++++++++++++++++ iguana/util.hpp | 136 +++++++++++++++++++++---------- iguana/version.hpp | 8 ++ iguana/xml_reader.hpp | 170 ++++++++++++++++++++++++--------------- iguana/xml_util.hpp | 151 +++++++++++++++++++++++++++++++++- iguana/xml_writer.hpp | 64 +++++++++++++-- iguana/yaml_reader.hpp | 6 +- ormpp/dbng.hpp | 10 +++ ormpp/mysql.hpp | 73 ++++++++++------- ormpp/postgresql.hpp | 67 ++++++++------- ormpp/sqlite.hpp | 92 ++++++++++++--------- ormpp/utility.hpp | 52 ++++++++---- tests/test_ormpp.cpp | 79 +++++++++++++++++- 18 files changed, 1005 insertions(+), 324 deletions(-) create mode 100644 iguana/version.hpp diff --git a/iguana/detail/charconv.h b/iguana/detail/charconv.h index 7bf2e6a0..a5b2d0c7 100644 --- a/iguana/detail/charconv.h +++ b/iguana/detail/charconv.h @@ -3,27 +3,48 @@ #include "dragonbox_to_chars.h" #include "fast_float.h" +#include "iguana/define.h" #include "itoa.hpp" - namespace iguana { template struct is_char_type : std::disjunction, std::is_same, std::is_same, std::is_same> {}; +inline void *to_chars_float(...) { + throw std::runtime_error("not allowed to invoke"); + return {}; +} + +template (), std::declval()))> +using return_of_tochars = std::conditional_t, + std::true_type, std::false_type>; +// here std::true_type is used as a type , any other type is also ok. +using has_to_chars_float = iguana::return_of_tochars; + namespace detail { -template + +// check_number==true: check if the string [first, last) is a legal number +template std::pair from_chars(const char *first, - const char *last, - U &value) noexcept { + const char *last, U &value) { using T = std::decay_t; if constexpr (std::is_floating_point_v) { auto [p, ec] = fast_float::from_chars(first, last, value); + if constexpr (check_number) { + if (p != last || ec != std::errc{}) + IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); } + } return {p, ec}; } else { auto [p, ec] = std::from_chars(first, last, value); + if constexpr (check_number) { + if (p != last || ec != std::errc{}) + IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); } + } return {p, ec}; } } @@ -33,7 +54,12 @@ template char *to_chars(char *buffer, T value) noexcept { using U = std::decay_t; if constexpr (std::is_floating_point_v) { - return jkj::dragonbox::to_chars(value, buffer); + if constexpr (has_to_chars_float::value) { + return static_cast(to_chars_float(value, buffer)); + } + else { + return jkj::dragonbox::to_chars(value, buffer); + } } else if constexpr (std::is_signed_v && (sizeof(U) >= 8)) { return xtoa(value, buffer, 10, 1); // int64_t diff --git a/iguana/detail/traits.hpp b/iguana/detail/traits.hpp index d3b9aac8..64f082e0 100644 --- a/iguana/detail/traits.hpp +++ b/iguana/detail/traits.hpp @@ -15,7 +15,6 @@ #include "iguana/define.h" - namespace iguana { template @@ -68,6 +67,15 @@ template struct has_type> : std::disjunction...> {}; +template +struct member_tratis {}; + +template +struct member_tratis { + using owner_type = Owner; + using value_type = T; +}; + template inline constexpr bool is_int64_v = std::is_same_v || std::is_same_v; diff --git a/iguana/json_reader.hpp b/iguana/json_reader.hpp index 6feef512..05018349 100644 --- a/iguana/json_reader.hpp +++ b/iguana/json_reader.hpp @@ -11,13 +11,13 @@ namespace detail { template , int> = 0> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end); +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end); template , int> = 0> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end); +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end); template , int> = 0> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { from_json(value, it, end); } @@ -61,15 +61,15 @@ IGUANA_INLINE void parse_escape(U &value, It &&it, It &&end) { } template , int> = 0> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { skip_ws(it, end); if constexpr (contiguous_iterator>) { const auto size = std::distance(it, end); if (size == 0) IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); } const auto start = &*it; - auto [p, ec] = detail::from_chars(start, start + size, value); - if (ec != std::errc{}) + auto [p, ec] = detail::from_chars(start, start + size, value); + if (ec != std::errc{} || !can_follow_number(*p)) IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); } it += (p - &*it); } @@ -82,14 +82,12 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { buffer[i] = *it++; ++i; } - auto [p, ec] = detail::from_chars(buffer, buffer + i, value); - if (ec != std::errc{}) - IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); } + detail::from_chars(buffer, buffer + i, value); } } template , int> = 0> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { skip_ws(it, end); auto start = it; while (it != end && is_numeric(*it)) { @@ -101,7 +99,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { template , int> = 0> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { if constexpr (!skip) { skip_ws(it, end); match<'"'>(it, end); @@ -140,7 +138,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { } template , int> = 0> -IGUANA_INLINE void parse_item(U &&value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &&value, It &&it, It &&end) { skip_ws(it, end); if (it < end) @@ -166,7 +164,7 @@ IGUANA_INLINE void parse_item(U &&value, It &&it, It &&end) { template , int> = 0> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { if constexpr (!skip) { skip_ws(it, end); match<'"'>(it, end); @@ -208,7 +206,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { template , int> = 0> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { static_assert(contiguous_iterator>, "must be contiguous"); if constexpr (!skip) { skip_ws(it, end); @@ -229,16 +227,16 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { } template , int> = 0> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { static constexpr auto str_to_enum = get_enum_map>(); if constexpr (bool_v) { // not defined a specialization template using T = std::underlying_type_t>; - parse_item(reinterpret_cast(value), it, end); + from_json_impl(reinterpret_cast(value), it, end); } else { std::string_view enum_names; - parse_item(enum_names, it, end); + from_json_impl(enum_names, it, end); auto it = str_to_enum.find(enum_names); if (it != str_to_enum.end()) IGUANA_LIKELY { value = it->second; } @@ -250,7 +248,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { } template , int> = 0> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { using T = std::remove_reference_t; constexpr auto n = sizeof(T) / sizeof(decltype(std::declval()[0])); skip_ws(it, end); @@ -262,7 +260,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { auto value_it = std::begin(value); for (size_t i = 0; i < n; ++i) { if (*it != '"') - IGUANA_LIKELY { parse_item(*value_it++, it, end); } + IGUANA_LIKELY { from_json_impl(*value_it++, it, end); } } match<'"'>(it, end); return; @@ -280,7 +278,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { } auto value_it = std::begin(value); for (size_t i = 0; i < n; ++i) { - parse_item(*value_it++, it, end); + from_json_impl(*value_it++, it, end); skip_ws(it, end); if (it == end) { throw std::runtime_error("Unexpected end"); @@ -301,7 +299,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { template , int>> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { value.clear(); skip_ws(it, end); @@ -316,7 +314,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { if (i > 0) IGUANA_LIKELY { match<','>(it, end); } - parse_item(value.emplace_back(), it, end); + from_json_impl(value.emplace_back(), it, end); skip_ws(it, end); } throw std::runtime_error("Expected ]"); @@ -337,7 +335,7 @@ IGUANA_INLINE auto get_key(It &&it, It &&end) { // compile time versions of keys it = start; static thread_local std::string static_key{}; - detail::parse_item(static_key, it, end); + detail::from_json_impl(static_key, it, end); return std::string_view(static_key); } else @@ -352,14 +350,14 @@ IGUANA_INLINE auto get_key(It &&it, It &&end) { } else { static thread_local std::string static_key{}; - detail::parse_item(static_key, it, end); + detail::from_json_impl(static_key, it, end); return std::string_view(static_key); } } template , int> = 0> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { using T = std::remove_reference_t; using key_type = typename T::key_type; skip_ws(it, end); @@ -384,19 +382,19 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { match<':'>(it, end); if constexpr (string_v || string_view_v) { - parse_item(value[key_type(key)], it, end); + from_json_impl(value[key_type(key)], it, end); } else { static thread_local key_type key_value{}; - parse_item(key_value, key.begin(), key.end()); - parse_item(value[key_value], it, end); + from_json_impl(key_value, key.begin(), key.end()); + from_json_impl(value[key_value], it, end); } skip_ws(it, end); } } template , int> = 0> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { skip_ws(it, end); match<'['>(it, end); skip_ws(it, end); @@ -410,7 +408,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { match<','>(it, end); skip_ws(it, end); } - parse_item(v, it, end); + from_json_impl(v, it, end); skip_ws(it, end); }); @@ -418,7 +416,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { } template , int> = 0> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { skip_ws(it, end); if (it < end && *it == '"') IGUANA_LIKELY { ++it; } @@ -439,17 +437,17 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { using value_type = typename T::value_type; value_type t; if constexpr (string_v || string_view_v) { - parse_item(t, it, end); + from_json_impl(t, it, end); } else { - parse_item(t, it, end); + from_json_impl(t, it, end); } value = std::move(t); } } template , int>> -IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { skip_ws(it, end); if (it == end) IGUANA_UNLIKELY { throw std::runtime_error("Unexexpected eof"); } @@ -465,7 +463,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) { else { value = std::make_shared(); } - parse_item(*value, it, end); + from_json_impl(*value, it, end); } } @@ -499,6 +497,44 @@ IGUANA_INLINE void skip_object_value(It &&it, It &&end) { } } +template +IGUANA_INLINE bool from_json_variant_impl(U &value, It it, It end, It &temp_it, + It &temp_end) { + try { + value_type val; + from_json_impl(val, it, end); + value = val; + temp_it = it; + temp_end = end; + return true; + } catch (std::exception &ex) { + return false; + } +} + +template +IGUANA_INLINE void from_json_variant(U &value, It &it, It &end, + std::index_sequence) { + static_assert(!has_duplicate_type_v>, + "don't allow same type in std::variant"); + bool r = false; + It temp_it = it; + It temp_end = end; + ((void)(!r && (r = from_json_variant_impl< + variant_element_t>>( + value, it, end, temp_it, temp_end), + true)), + ...); + it = temp_it; + end = temp_end; +} + +template , int> = 0> +IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) { + from_json_variant(value, it, end, + std::make_index_sequence< + std::variant_size_v>>{}); +} } // namespace detail template , int>> @@ -522,7 +558,10 @@ IGUANA_INLINE void from_json(T &value, It &&it, It &&end) { IGUANA_UNLIKELY { return; } skip_ws(it, end); match<':'>(it, end); - detail::parse_item(value.*member_ptr, it, end); + { + using namespace detail; + from_json_impl(value.*member_ptr, it, end); + } skip_ws(it, end); if (*it == '}') @@ -551,7 +590,8 @@ IGUANA_INLINE void from_json(T &value, It &&it, It &&end) { [&](auto &&member_ptr) IGUANA__INLINE_LAMBDA { using V = std::decay_t; if constexpr (std::is_member_pointer_v) { - detail::parse_item(value.*member_ptr, it, end); + using namespace detail; + from_json_impl(value.*member_ptr, it, end); } else { static_assert(!sizeof(V), "type not supported"); @@ -583,7 +623,8 @@ IGUANA_INLINE void from_json(T &value, It &&it, It &&end) { template , int> = 0> IGUANA_INLINE void from_json(T &value, It &&it, It &&end) { - detail::parse_item(value, it, end); + using namespace detail; + from_json_impl(value, it, end); } template @@ -603,6 +644,31 @@ IGUANA_INLINE void from_json(T &value, const View &view) { from_json(value, std::begin(view), std::end(view)); } +template < + auto member, + typename Parant = typename member_tratis::owner_type, + typename T> +IGUANA_INLINE void from_json(T &value, std::string_view str) { + constexpr size_t duplicate_count = + iguana::duplicate_count, member>(); + static_assert(duplicate_count != 1, "the member is not belong to the object"); + static_assert(duplicate_count == 2, "has duplicate field name"); + + constexpr auto name = name_of(); + constexpr size_t index = index_of(); + constexpr size_t member_count = member_count_of(); + str = str.substr(str.find(name) + name.size()); + size_t pos = str.find(":") + 1; + if constexpr (index == member_count - 1) { // last field + str = str.substr(pos, str.find("}") - pos + 1); + } + else { + str = str.substr(pos, str.find(",") - pos); + } + + detail::from_json_impl(value.*member, std::begin(str), std::end(str)); +} + template , int> = 0> IGUANA_INLINE void from_json(T &value, const View &view, @@ -683,7 +749,8 @@ inline void parse(jobject &result, It &&it, It &&end, if (it == end) IGUANA_UNLIKELY { throw std::runtime_error("Expected }"); } std::string key; - detail::parse_item(key, it, end); + using namespace detail; + from_json_impl(key, it, end); auto emplaced = result.try_emplace(key); if (!emplaced.second) @@ -705,6 +772,7 @@ inline void parse(jobject &result, It &&it, It &&end, template inline void parse(jvalue &result, It &&it, It &&end, bool parse_as_double) { + using namespace detail; skip_ws(it, end); switch (*it) { case 'n': @@ -715,7 +783,7 @@ inline void parse(jvalue &result, It &&it, It &&end, bool parse_as_double) { case 'f': case 't': - detail::parse_item(result.template emplace(), it, end); + from_json_impl(result.template emplace(), it, end); break; case '0': case '1': @@ -729,7 +797,7 @@ inline void parse(jvalue &result, It &&it, It &&end, bool parse_as_double) { case '9': case '-': { double d{}; - detail::parse_item(d, it, end); + from_json_impl(d, it, end); if (!parse_as_double && (static_cast(d) == d)) result.emplace(d); else @@ -739,11 +807,11 @@ inline void parse(jvalue &result, It &&it, It &&end, bool parse_as_double) { case '"': if constexpr (Is_view) { result.template emplace(); - detail::parse_item(std::get(result), it, end); + from_json_impl(std::get(result), it, end); } else { result.template emplace(); - detail::parse_item(std::get(result), it, end); + from_json_impl(std::get(result), it, end); } break; case '[': diff --git a/iguana/json_util.hpp b/iguana/json_util.hpp index 5c00c077..f1bf3a33 100644 --- a/iguana/json_util.hpp +++ b/iguana/json_util.hpp @@ -18,10 +18,7 @@ class numeric_str { if (val_.empty()) IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); } T res; - auto [_, ec] = - detail::from_chars(val_.data(), val_.data() + val_.size(), res); - if (ec != std::errc{}) - IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); } + detail::from_chars(val_.data(), val_.data() + val_.size(), res); return res; } @@ -214,4 +211,28 @@ IGUANA_INLINE bool is_numeric(char c) noexcept { return static_cast(is_num[static_cast(c)]); } +// '\t' '\r' '\n' '"' '}' ']' ',' ' ' '\0' +IGUANA_INLINE bool can_follow_number(char c) noexcept { + static constexpr int can_follow_num[256] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // 2 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F + }; + return static_cast(can_follow_num[static_cast(c)]); +} + } // namespace iguana diff --git a/iguana/json_writer.hpp b/iguana/json_writer.hpp index 2df2bfe8..eeaeaa39 100644 --- a/iguana/json_writer.hpp +++ b/iguana/json_writer.hpp @@ -11,33 +11,33 @@ namespace iguana { template , int> = 0> IGUANA_INLINE void to_json(T &&t, Stream &s); - +namespace detail { template -IGUANA_INLINE void render_json_value(Stream &ss, std::optional &val); +IGUANA_INLINE void to_json_impl(Stream &ss, std::optional &val); template , int> = 0> -IGUANA_INLINE void render_json_value(Stream &ss, const T &t); +IGUANA_INLINE void to_json_impl(Stream &ss, const T &t); template , int> = 0> -IGUANA_INLINE void render_json_value(Stream &ss, const T &v); +IGUANA_INLINE void to_json_impl(Stream &ss, const T &v); template , int> = 0> -IGUANA_INLINE void render_json_value(Stream &ss, const T &v); +IGUANA_INLINE void to_json_impl(Stream &ss, const T &v); template , int> = 0> -IGUANA_INLINE void render_json_value(Stream &ss, const T &o); +IGUANA_INLINE void to_json_impl(Stream &ss, const T &o); template , int> = 0> -IGUANA_INLINE void render_json_value(Stream &s, T &&t); +IGUANA_INLINE void to_json_impl(Stream &s, T &&t); template , int> = 0> -IGUANA_INLINE void render_json_value(Stream &s, T &&t); +IGUANA_INLINE void to_json_impl(Stream &s, T &&t); template IGUANA_INLINE void join(Stream &ss, InputIt first, InputIt last, const T &delim, @@ -52,17 +52,17 @@ IGUANA_INLINE void join(Stream &ss, InputIt first, InputIt last, const T &delim, } template -IGUANA_INLINE void render_json_value(Stream &ss, std::nullptr_t) { +IGUANA_INLINE void to_json_impl(Stream &ss, std::nullptr_t) { ss.append("null"); } template -IGUANA_INLINE void render_json_value(Stream &ss, bool b) { +IGUANA_INLINE void to_json_impl(Stream &ss, bool b) { ss.append(b ? "true" : "false"); }; template -IGUANA_INLINE void render_json_value(Stream &ss, char value) { +IGUANA_INLINE void to_json_impl(Stream &ss, char value) { ss.append("\""); ss.push_back(value); ss.append("\""); @@ -70,7 +70,7 @@ IGUANA_INLINE void render_json_value(Stream &ss, char value) { template , int> = 0> -IGUANA_INLINE void render_json_value(Stream &ss, T value) { +IGUANA_INLINE void to_json_impl(Stream &ss, T value) { char temp[65]; auto p = detail::to_chars(temp, value); ss.append(temp, p - temp); @@ -78,13 +78,13 @@ IGUANA_INLINE void render_json_value(Stream &ss, T value) { template , int> = 0> -IGUANA_INLINE void render_json_value(Stream &ss, T v) { +IGUANA_INLINE void to_json_impl(Stream &ss, T v) { ss.append(v.value().data(), v.value().size()); } template , int> = 0> -IGUANA_INLINE void render_json_value(Stream &ss, T &&t) { +IGUANA_INLINE void to_json_impl(Stream &ss, T &&t) { ss.push_back('"'); if constexpr (Is_writing_escape) { write_string_with_escape(t.data(), t.size(), ss); @@ -99,28 +99,28 @@ template , int> = 0> IGUANA_INLINE void render_key(Stream &ss, T &t) { ss.push_back('"'); - render_json_value(ss, t); + to_json_impl(ss, t); ss.push_back('"'); } template , int> = 0> IGUANA_INLINE void render_key(Stream &ss, T &&t) { - render_json_value(ss, std::forward(t)); + to_json_impl(ss, std::forward(t)); } template , int> = 0> -IGUANA_INLINE void render_json_value(Stream &ss, T &&t) { +IGUANA_INLINE void to_json_impl(Stream &ss, T &&t) { to_json(std::forward(t), ss); } template , int> = 0> -IGUANA_INLINE void render_json_value(Stream &ss, T val) { +IGUANA_INLINE void to_json_impl(Stream &ss, T val) { static constexpr auto enum_to_str = get_enum_map>(); if constexpr (bool_v) { - render_json_value( + to_json_impl( ss, static_cast>(val)); } else { @@ -128,7 +128,7 @@ IGUANA_INLINE void render_json_value(Stream &ss, T val) { if (it != enum_to_str.end()) IGUANA_LIKELY { auto str = it->second; - render_json_value( + to_json_impl( ss, std::string_view(str.data(), str.size())); } else { @@ -140,12 +140,12 @@ IGUANA_INLINE void render_json_value(Stream &ss, T val) { } template -IGUANA_INLINE void render_json_value(Stream &ss, std::optional &val) { +IGUANA_INLINE void to_json_impl(Stream &ss, std::optional &val) { if (!val) { ss.append("null"); } else { - render_json_value(ss, *val); + to_json_impl(ss, *val); } } @@ -154,14 +154,14 @@ IGUANA_INLINE void render_array(Stream &ss, const T &v) { ss.push_back('['); join(ss, std::begin(v), std::end(v), ',', [&ss](const auto &jsv) IGUANA__INLINE_LAMBDA { - render_json_value(ss, jsv); + to_json_impl(ss, jsv); }); ss.push_back(']'); } template , int>> -IGUANA_INLINE void render_json_value(Stream &ss, const T &t) { +IGUANA_INLINE void to_json_impl(Stream &ss, const T &t) { if constexpr (std::is_same_v()[0])>>) { constexpr size_t n = sizeof(T) / sizeof(decltype(std::declval()[0])); @@ -184,24 +184,24 @@ IGUANA_INLINE void render_json_value(Stream &ss, const T &t) { template , int>> -IGUANA_INLINE void render_json_value(Stream &ss, const T &o) { +IGUANA_INLINE void to_json_impl(Stream &ss, const T &o) { ss.push_back('{'); join(ss, o.cbegin(), o.cend(), ',', [&ss](const auto &jsv) IGUANA__INLINE_LAMBDA { render_key(ss, jsv.first); ss.push_back(':'); - render_json_value(ss, jsv.second); + to_json_impl(ss, jsv.second); }); ss.push_back('}'); } template , int>> -IGUANA_INLINE void render_json_value(Stream &ss, const T &v) { +IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) { ss.push_back('['); join(ss, v.cbegin(), v.cend(), ',', [&ss](const auto &jsv) IGUANA__INLINE_LAMBDA { - render_json_value(ss, jsv); + to_json_impl(ss, jsv); }); ss.push_back(']'); } @@ -217,9 +217,9 @@ constexpr auto write_json_key = [](auto &s, auto i, template , int>> -IGUANA_INLINE void render_json_value(Stream &ss, const T &v) { +IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) { if (v) { - render_json_value(ss, *v); + to_json_impl(ss, *v); } else { ss.append("null"); @@ -228,13 +228,13 @@ IGUANA_INLINE void render_json_value(Stream &ss, const T &v) { template , int>> -IGUANA_INLINE void render_json_value(Stream &s, T &&t) { +IGUANA_INLINE void to_json_impl(Stream &s, T &&t) { using U = typename std::decay_t; s.push_back('['); constexpr size_t size = std::tuple_size_v; for_each(std::forward(t), [&s, size](auto &v, auto i) IGUANA__INLINE_LAMBDA { - render_json_value(s, v); + to_json_impl(s, v); if (i != size - 1) IGUANA_LIKELY { s.push_back(','); } @@ -244,17 +244,20 @@ IGUANA_INLINE void render_json_value(Stream &s, T &&t) { template , int>> -IGUANA_INLINE void render_json_value(Stream &s, T &&t) { +IGUANA_INLINE void to_json_impl(Stream &s, T &&t) { + static_assert(!has_duplicate_type_v>, + "don't allow same type in std::variant"); std::visit( [&s](auto value) { - render_json_value(s, value); + to_json_impl(s, value); }, t); } - +} // namespace detail template , int>> IGUANA_INLINE void to_json(T &&t, Stream &s) { + using namespace detail; s.push_back('{'); for_each(std::forward(t), [&t, &s](const auto &v, auto i) IGUANA__INLINE_LAMBDA { @@ -265,7 +268,7 @@ IGUANA_INLINE void to_json(T &&t, Stream &s) { write_json_key(s, i, t); s.push_back(':'); - render_json_value(s, t.*v); + to_json_impl(s, t.*v); if (Idx < Count - 1) IGUANA_LIKELY { s.push_back(','); } }); @@ -275,7 +278,8 @@ IGUANA_INLINE void to_json(T &&t, Stream &s) { template , int> = 0> IGUANA_INLINE void to_json(T &&t, Stream &s) { - render_json_value(s, t); + using namespace detail; + to_json_impl(s, t); } } // namespace iguana diff --git a/iguana/reflection.hpp b/iguana/reflection.hpp index 41e15423..234da95d 100644 --- a/iguana/reflection.hpp +++ b/iguana/reflection.hpp @@ -910,6 +910,120 @@ constexpr const std::string_view get_name() { return M::name(); } +namespace detail { +template +constexpr bool get_index_imple(T ptr, U ele) { + if constexpr (std::is_same_v) { + if (ele == ptr) { + return true; + } + else { + return false; + } + } + else { + return false; + } +} + +template +constexpr size_t member_index_impl(T ptr, Tuple &tp, + std::index_sequence) { + bool r = false; + size_t index = 0; + ((void)(!r && (r = get_index_imple(ptr, std::get(tp)), + !r ? index++ : index, true)), + ...); + return index; +} + +template +constexpr size_t member_index(T ptr, Tuple &tp) { + return member_index_impl( + ptr, tp, + std::make_index_sequence< + std::tuple_size_v>>{}); +} +} // namespace detail + +template +constexpr size_t index_of() { + using namespace detail; + using T = typename member_tratis::owner_type; + using M = Reflect_members; + constexpr auto tp = M::apply_impl(); + constexpr size_t Size = std::tuple_size_v; + constexpr size_t index = member_index(member, tp); + static_assert(index < Size, "out of range"); + return index; +} + +template +constexpr std::array indexs_of() { + return std::array{index_of()...}; +} + +template +constexpr auto name_of() { + using T = typename member_tratis::owner_type; + using M = Reflect_members; + constexpr auto s = M::arr()[index_of()]; + return std::string_view(s.data(), s.size()); +} + +template +constexpr std::array names_of() { + return std::array{ + name_of()...}; +} + +template +constexpr auto member_count_of() { + using T = typename member_tratis::owner_type; + using M = Reflect_members; + return M::value(); +} + +template +constexpr size_t duplicate_count(); + +template +constexpr void check_duplicate(Member member, size_t &index) { + using value_type = typename member_tratis::value_type; + + if (detail::get_index_imple(ptr, member)) { + index++; + } + + if constexpr (is_reflection_v) { + index += iguana::duplicate_count(); + } +} + +template +constexpr size_t duplicate_count() { + using M = Reflect_members; + constexpr auto name = name_of(); + constexpr auto arr = M::arr(); + + constexpr auto tp = M::apply_impl(); + size_t index = 0; + std::apply( + [&](auto... ele) { + (check_duplicate(ele, index), ...); + }, + tp); + + for (auto &s : arr) { + if (s == name) { + index++; + break; + } + } + + return index; +} + template constexpr const std::string_view get_fields() { using M = Reflect_members; diff --git a/iguana/util.hpp b/iguana/util.hpp index 3d2605cd..47959618 100644 --- a/iguana/util.hpp +++ b/iguana/util.hpp @@ -139,15 +139,16 @@ struct is_variant> : std::true_type {}; template constexpr inline bool variant_v = is_variant>::value; -template -constexpr inline bool non_refletable_v = - container_v || c_array_v || tuple_v || optional_v || - smart_ptr_v || std::is_fundamental_v> || - variant_v; +template +using variant_element_t = std::remove_reference_t( + std::declval>()))>; template constexpr inline bool refletable_v = is_reflection_v>; +template +constexpr inline bool non_refletable_v = !refletable_v; + template constexpr inline bool plain_v = string_container_v || num_v || char_v || bool_v || enum_v; @@ -204,10 +205,59 @@ inline constexpr auto has_qoute = [](uint64_t chunk) IGUANA__INLINE_LAMBDA { 0b0010001000100010001000100010001000100010001000100010001000100010); }; +template +IGUANA_INLINE void write_unicode_to_string(Ch& it, Stream& ss) { + static const char hexDigits[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + unsigned codepoint = 0; + if (!decode_utf8(it, codepoint)) + IGUANA_UNLIKELY { throw std::runtime_error("illegal unicode character"); } + if constexpr (is_xml_serialization) { + ss.append("&#x"); + } + else { + ss.push_back('\\'); + ss.push_back('u'); + } + + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + ss.push_back(hexDigits[(codepoint >> 12) & 15]); + ss.push_back(hexDigits[(codepoint >> 8) & 15]); + ss.push_back(hexDigits[(codepoint >> 4) & 15]); + ss.push_back(hexDigits[(codepoint)&15]); + } + else { + if (codepoint < 0x010000 || codepoint > 0x10FFFF) + IGUANA_UNLIKELY { throw std::runtime_error("illegal codepoint"); } + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + ss.push_back(hexDigits[(lead >> 12) & 15]); + ss.push_back(hexDigits[(lead >> 8) & 15]); + ss.push_back(hexDigits[(lead >> 4) & 15]); + ss.push_back(hexDigits[(lead)&15]); + if constexpr (is_xml_serialization) { + ss.append(";&#x"); + } + else { + ss.push_back('\\'); + ss.push_back('u'); + } + ss.push_back(hexDigits[(trail >> 12) & 15]); + ss.push_back(hexDigits[(trail >> 8) & 15]); + ss.push_back(hexDigits[(trail >> 4) & 15]); + ss.push_back(hexDigits[(trail)&15]); + } + if constexpr (is_xml_serialization) { + ss.push_back(';'); + } +} + // https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/writer.h template -inline void write_string_with_escape(const Ch* it, SizeType length, - Stream& ss) { +IGUANA_INLINE void write_string_with_escape(const Ch* it, SizeType length, + Stream& ss) { static const char hexDigits[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; static const char escape[256] = { @@ -230,40 +280,7 @@ inline void write_string_with_escape(const Ch* it, SizeType length, std::advance(end, length); while (it < end) { if (static_cast(*it) >= 0x80) - IGUANA_UNLIKELY { - unsigned codepoint = 0; - if (!decode_utf8(it, codepoint)) - IGUANA_UNLIKELY { - throw std::runtime_error("illegal unicode character"); - } - ss.push_back('\\'); - ss.push_back('u'); - if (codepoint <= 0xD7FF || - (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { - ss.push_back(hexDigits[(codepoint >> 12) & 15]); - ss.push_back(hexDigits[(codepoint >> 8) & 15]); - ss.push_back(hexDigits[(codepoint >> 4) & 15]); - ss.push_back(hexDigits[(codepoint)&15]); - } - else { - if (codepoint < 0x010000 || codepoint > 0x10FFFF) - IGUANA_UNLIKELY { throw std::runtime_error("illegal codepoint"); } - // Surrogate pair - unsigned s = codepoint - 0x010000; - unsigned lead = (s >> 10) + 0xD800; - unsigned trail = (s & 0x3FF) + 0xDC00; - ss.push_back(hexDigits[(lead >> 12) & 15]); - ss.push_back(hexDigits[(lead >> 8) & 15]); - ss.push_back(hexDigits[(lead >> 4) & 15]); - ss.push_back(hexDigits[(lead)&15]); - ss.push_back('\\'); - ss.push_back('u'); - ss.push_back(hexDigits[(trail >> 12) & 15]); - ss.push_back(hexDigits[(trail >> 8) & 15]); - ss.push_back(hexDigits[(trail >> 4) & 15]); - ss.push_back(hexDigits[(trail)&15]); - } - } + IGUANA_UNLIKELY { write_unicode_to_string(it, ss); } else if (escape[static_cast(*it)]) IGUANA_UNLIKELY { ss.push_back('\\'); @@ -284,4 +301,41 @@ inline void write_string_with_escape(const Ch* it, SizeType length, } } +template +IGUANA_INLINE constexpr bool has_duplicate(const std::array& arr) { + for (int i = 0; i < arr.size(); i++) { + for (int j = i + 1; j < arr.size(); j++) { + if (arr[i] == arr[j]) { + return true; + } + } + } + return false; +} + +#if defined(__clang__) || defined(_MSC_VER) || \ + (defined(__GNUC__) && __GNUC__ > 8) +template +IGUANA_INLINE constexpr bool has_duplicate_type() { + std::array arr{ + iguana::type_string()...}; + return has_duplicate(arr); +} + +template +struct has_duplicate_type_in_variant : std::false_type {}; + +template +struct has_duplicate_type_in_variant> { + inline constexpr static bool value = has_duplicate_type(); +}; + +template +constexpr inline bool has_duplicate_type_v = + has_duplicate_type_in_variant::value; +#else +template +constexpr inline bool has_duplicate_type_v = false; +#endif + } // namespace iguana diff --git a/iguana/version.hpp b/iguana/version.hpp new file mode 100644 index 00000000..202ea4a3 --- /dev/null +++ b/iguana/version.hpp @@ -0,0 +1,8 @@ +#pragma once + +// Note: Update the version when release a new version. + +// IGUANA_VERSION % 100 is the sub-minor version +// IGUANA_VERSION / 100 % 1000 is the minor version +// IGUANA_VERSION / 100000 is the major version +#define IGUANA_VERSION 100004 // 1.0.4 \ No newline at end of file diff --git a/iguana/xml_reader.hpp b/iguana/xml_reader.hpp index bca3fe4b..209b4f89 100644 --- a/iguana/xml_reader.hpp +++ b/iguana/xml_reader.hpp @@ -5,7 +5,6 @@ #include "detail/utf.hpp" #include "xml_util.hpp" - namespace iguana { namespace detail { @@ -29,15 +28,26 @@ template , int> = 0> IGUANA_INLINE void parse_value(U &&value, It &&begin, It &&end) { using T = std::decay_t; if constexpr (string_container_v) { - value = T(&*begin, static_cast(std::distance(begin, end))); + if constexpr (string_view_v) { + value = T(&*begin, static_cast(std::distance(begin, end))); + } + else { + // TODO: When not parsing the value in the attribute, it is not necessary + // to unescape'and " + value.clear(); + auto pre = begin; + while (advance_until_character<'&'>(begin, end)) { + value.append(T(&*pre, static_cast(std::distance(pre, begin)))); + parse_escape_xml(value, begin, end); + pre = begin; + } + value.append(T(&*pre, static_cast(std::distance(pre, begin)))); + } } else if constexpr (num_v) { auto size = std::distance(begin, end); const auto start = &*begin; - auto [p, ec] = detail::from_chars(start, start + size, value); - if (ec != std::errc{}) - IGUANA_UNLIKELY - throw std::runtime_error("Failed to parse number"); + detail::from_chars(start, start + size, value); } else if constexpr (char_v) { if (static_cast(std::distance(begin, end)) != 1) @@ -91,9 +101,19 @@ IGUANA_INLINE void parse_attr(U &&value, It &&it, It &&end) { parse_value(key, key_begin, key_end); skip_sapces_and_newline(it, end); - match<'"'>(it, end); - auto value_begin = it; - auto value_end = skip_pass<'"'>(it, end); + auto value_begin = it + 1; + auto value_end = value_begin; + if (*it == '"') + IGUANA_LIKELY { + ++it; + value_end = skip_pass<'"'>(it, end); + } + else if (*it == '\'') { + ++it; + value_end = skip_pass<'\''>(it, end); + } + else + IGUANA_UNLIKELY { throw std::runtime_error("expected quote or apos"); } value_type v; parse_value(v, value_begin, value_end); value.emplace(std::move(key), std::move(v)); @@ -122,17 +142,8 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end, match<'<'>(it, end); if (*it == '?' || *it == '!') IGUANA_UNLIKELY { - // skip '>(it, end); - ++it; - skip_sapces_and_newline(it, end); - continue; - } + --it; + return; } auto start = it; skip_till_greater_or_space(it, end); @@ -224,24 +235,48 @@ IGUANA_INLINE void skip_object_value(It &&it, It &&end, std::string_view name) { throw std::runtime_error("unclosed tag: " + std::string(name)); } +// skip +template +IGUANA_INLINE void skip_instructions(It &&it, It &&end) { + while (*(it - 1) != '?') { + ++it; + skip_till<'>'>(it, end); + } + ++it; +} + +template +IGUANA_INLINE void skip_cdata(It &&it, It &&end) { + ++it; + skip_till<']'>(it, end); + ++it; + match<']', '>'>(it, end); +} + +template +IGUANA_INLINE void skip_comment(It &&it, It &&end) { + while (*(it - 1) != '-' || *(it - 2) != '-') { + ++it; + skip_till<'>'>(it, end); + } + ++it; +} + // return true means reach the close tag template , int> = 0> -IGUANA_INLINE auto skip_till_key(T &value, It &&it, It &&end) { - skip_sapces_and_newline(it, end); +IGUANA_INLINE auto skip_till_close_tag(T &value, It &&it, It &&end) { while (true) { + skip_sapces_and_newline(it, end); match<'<'>(it, end); if (*it == '/') IGUANA_UNLIKELY { - // - return true; // reach the close tag + // reach the close tag + return true; } else if (*it == '?') IGUANA_UNLIKELY { - // - skip_till<'>'>(it, end); - ++it; - skip_sapces_and_newline(it, end); + skip_instructions(it, end); continue; } else if (*it == '!') @@ -250,12 +285,7 @@ IGUANA_INLINE auto skip_till_key(T &value, It &&it, It &&end) { if (*it == '[') { // >()) { - ++it; - skip_till<']'>(it, end); - ++it; - match<']', '>'>(it, end); - skip_sapces_and_newline(it, end); - continue; + skip_cdata(it, end); } else { // if parse cdata @@ -275,23 +305,53 @@ IGUANA_INLINE auto skip_till_key(T &value, It &&it, It &&end) { &*vb, static_cast(std::distance(vb, ve))); } match<']', '>'>(it, end); - skip_sapces_and_newline(it, end); - continue; } } - else { + else if (*it == '-') { // - // skip_till<'>'>(it, end); ++it; - skip_sapces_and_newline(it, end); - continue; } + continue; } return false; } } +template +IGUANA_INLINE void skip_till_first_key(It &&it, It &&end) { + while (it != end) { + skip_sapces_and_newline(it, end); + match<'<'>(it, end); + if (*it == '?') + IGUANA_UNLIKELY { + skip_instructions(it, end); + continue; + } + else if (*it == '!') + IGUANA_UNLIKELY { + ++it; + if (*it == '-') { + // + skip_comment(it, end); + } + else { + // + skip_till<'>'>(it, end); + ++it; + } + continue; + } + else { + break; + } + } +} + template IGUANA_INLINE void check_required(std::string_view key_set) { if constexpr (iguana::has_iguana_required_arr_v) { @@ -314,7 +374,7 @@ IGUANA_INLINE void parse_item(T &value, It &&it, It &&end, constexpr auto cdata_idx = get_type_index(); skip_till<'>'>(it, end); ++it; - if (skip_till_key(value, it, end)) { + if (skip_till_close_tag(value, it, end)) { match_close_tag(it, end, name); return; } @@ -345,7 +405,7 @@ IGUANA_INLINE void parse_item(T &value, It &&it, It &&end, key_set.append(key).append(", "); } } - if (skip_till_key(value, it, end)) + if (skip_till_close_tag(value, it, end)) IGUANA_UNLIKELY { match_close_tag(it, end, name); parse_done = true; @@ -390,7 +450,7 @@ IGUANA_INLINE void parse_item(T &value, It &&it, It &&end, skip_object_value(it, end, key); #endif } - if (skip_till_key(value, it, end)) { + if (skip_till_close_tag(value, it, end)) { match_close_tag(it, end, name); check_required(key_set); return; @@ -406,17 +466,7 @@ IGUANA_INLINE void parse_item(T &value, It &&it, It &&end, template , int> = 0> IGUANA_INLINE void from_xml(U &value, It &&it, It &&end) { - while (it != end) { - skip_sapces_and_newline(it, end); - match<'<'>(it, end); - if (*it == '?') { - skip_till<'>'>(it, end); - ++it; - } - else { - break; - } - } + detail::skip_till_first_key(it, end); auto start = it; skip_till_greater_or_space(it, end); std::string_view key = @@ -427,17 +477,7 @@ IGUANA_INLINE void from_xml(U &value, It &&it, It &&end) { template , int> = 0> IGUANA_INLINE void from_xml(U &value, It &&it, It &&end) { - while (it != end) { - skip_sapces_and_newline(it, end); - match<'<'>(it, end); - if (*it == '?') { - skip_till<'>'>(it, end); - ++it; // skip > - } - else { - break; - } - } + detail::skip_till_first_key(it, end); auto start = it; skip_till_greater_or_space(it, end); std::string_view key = diff --git a/iguana/xml_util.hpp b/iguana/xml_util.hpp index cd2df709..dabd47a3 100644 --- a/iguana/xml_util.hpp +++ b/iguana/xml_util.hpp @@ -2,8 +2,8 @@ #include "util.hpp" namespace iguana { -template > +template > class xml_attr_t { public: T &value() { return val_; } @@ -17,6 +17,10 @@ class xml_attr_t { map_type attr_; }; +template +using xml_attr_view_t = + xml_attr_t>; + template , int> = 0> class xml_cdata_t { @@ -72,12 +76,24 @@ inline constexpr auto has_square_bracket = 0b0101110101011101010111010101110101011101010111010101110101011101); }; +inline constexpr auto has_and = [](uint64_t chunk) IGUANA__INLINE_LAMBDA { + return has_zero( + chunk ^ + 0b0010011000100110001001100010011000100110001001100010011000100110); +}; + inline constexpr auto has_equal = [](uint64_t chunk) IGUANA__INLINE_LAMBDA { return has_zero( chunk ^ 0b0011110100111101001111010011110100111101001111010011110100111101); }; +inline constexpr auto has_apos = [](uint64_t chunk) IGUANA__INLINE_LAMBDA { + return has_zero( + chunk ^ + 0b0010011100100111001001110010011100100111001001110010011100100111); +}; + template IGUANA_INLINE void skip_sapces_and_newline(It &&it, It &&end) { while (it != end && (static_cast(*it) < 33)) { @@ -104,6 +120,35 @@ IGUANA_INLINE void match_close_tag(It &&it, It &&end, std::string_view key) { // ++it; } +// returns true if the specified character 'c' is found, false otherwise. +template +IGUANA_INLINE bool advance_until_character(It &&it, It &&end) { + static_assert(contiguous_iterator>); + if (std::distance(it, end) >= 7) + IGUANA_LIKELY { + const auto end_m7 = end - 7; + for (; it < end_m7; it += 8) { + const auto chunk = *reinterpret_cast(&*it); + uint64_t test; + if constexpr (c == '&') + test = has_and(chunk); + else + static_assert(!c, "not support this character"); + if (test != 0) { + it += (countr_zero(test) >> 3); + return true; + } + } + } + // Tail end of buffer. Should be rare we even get here + while (it < end) { + if (*it == c) + return true; + ++it; + } + return false; +} + template IGUANA_INLINE void skip_till(It &&it, It &&end) { static_assert(contiguous_iterator>); @@ -126,6 +171,8 @@ IGUANA_INLINE void skip_till(It &&it, It &&end) { test = has_square_bracket(chunk); else if constexpr (c == '=') test = has_equal(chunk); + else if constexpr (c == '\'') + test = has_apos(chunk); else static_assert(!c, "not support this character"); if (test != 0) { @@ -186,4 +233,104 @@ IGUANA_INLINE auto skip_pass(It &&it, It &&end) { return res + 1; } +template +IGUANA_INLINE bool is_match(It &&it, const It &end) { + const auto n = static_cast(std::distance(it, end)); + if ((n < sizeof...(C)) || (... || (*it++ != C))) { + return false; + } + return true; +} + +template , int> = 0> +IGUANA_INLINE void parse_escape_xml(U &value, It &&it, It &&end) { + static const unsigned char lookup_digits[256] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, + 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255}; + switch (*(it + 1)) { + // & ' + case 'a': + if (is_match<'m', 'p', ';'>(it + 2, end)) { + value.push_back('&'); + it += 5; + return; + } + if (is_match<'p', 'o', 's', ';'>(it + 2, end)) { + value.push_back('\''); + it += 6; + } + break; + // " + case 'q': + if (is_match<'u', 'o', 't', ';'>(it + 2, end)) { + value.push_back('\"'); + it += 6; + } + break; + // > + case 'g': + if (is_match<'t', ';'>(it + 2, end)) { + value.push_back('>'); + it += 4; + } + break; + // < + case 'l': + if (is_match<'t', ';'>(it + 2, end)) { + value.push_back('<'); + it += 4; + } + break; + case '#': + if (*(it + 2) == 'x') { + // &#x + unsigned long codepoint = 0; + it += 3; + while (true) { + auto digit = lookup_digits[static_cast(*it)]; + if (digit == 0xFF) + break; + codepoint = codepoint * 16 + digit; + ++it; + } + encode_utf8(value, codepoint); + } + else { + unsigned long codepoint = 0; + it += 2; + while (true) { + auto digit = lookup_digits[static_cast(*it)]; + if (digit == 0xFF) + break; + codepoint = codepoint * 10 + digit; + ++it; + } + encode_utf8(value, codepoint); + } + match<';'>(it, end); + break; + default: + // skip '&' + // loose policy: allow '&' + value.push_back(*(it++)); + break; + } +} + } // namespace iguana diff --git a/iguana/xml_writer.hpp b/iguana/xml_writer.hpp index b83a2c8b..94e35f37 100644 --- a/iguana/xml_writer.hpp +++ b/iguana/xml_writer.hpp @@ -6,6 +6,58 @@ namespace iguana { +#ifdef XML_ATTR_USE_APOS +#define XML_ATTR_DELIMITER '\'' +#else +#define XML_ATTR_DELIMITER '\"' +#endif + +// TODO: improve by precaculate size +template +IGUANA_INLINE void render_string_with_escape_xml(const Ch *it, SizeType length, + Stream &ss) { + auto end = it; + std::advance(end, length); + while (it < end) { +#ifdef XML_ESCAPE_UNICODE + if (static_cast(*it) >= 0x80) + IGUANA_UNLIKELY { + write_unicode_to_string(it, ss); + continue; + } +#endif + if constexpr (escape_quote_apos) { + if constexpr (XML_ATTR_DELIMITER == '\"') { + if (*it == '"') + IGUANA_UNLIKELY { + ss.append("""); + ++it; + continue; + } + } + else { + if (*it == '\'') + IGUANA_UNLIKELY { + ss.append("'"); + ++it; + continue; + } + } + } + if (*it == '&') + IGUANA_UNLIKELY { ss.append("&"); } + else if (*it == '>') + IGUANA_UNLIKELY { ss.append(">"); } + else if (*it == '<') + IGUANA_UNLIKELY { ss.append("<"); } + else { + ss.push_back(*it); + } + ++it; + } +} + template , int> = 0> IGUANA_INLINE void render_xml_value(Stream &ss, const T &value, @@ -39,10 +91,12 @@ IGUANA_INLINE void render_head(Stream &ss, std::string_view str) { ss.push_back('>'); } -template , int> = 0> +template , int> = 0> IGUANA_INLINE void render_value(Stream &ss, const T &value) { if constexpr (string_container_v) { - ss.append(value.data(), value.size()); + render_string_with_escape_xml(value.data(), value.size(), + ss); } else if constexpr (num_v) { char temp[65]; @@ -91,9 +145,9 @@ inline void render_xml_attr(Stream &ss, const T &value, std::string_view name) { ss.push_back(' '); render_value(ss, k); ss.push_back('='); - ss.push_back('"'); - render_value(ss, v); - ss.push_back('"'); + ss.push_back(XML_ATTR_DELIMITER); + render_value(ss, v); + ss.push_back(XML_ATTR_DELIMITER); } ss.push_back('>'); } diff --git a/iguana/yaml_reader.hpp b/iguana/yaml_reader.hpp index c2987a6e..0a0d47fa 100644 --- a/iguana/yaml_reader.hpp +++ b/iguana/yaml_reader.hpp @@ -6,7 +6,6 @@ #include "detail/utf.hpp" #include "yaml_util.hpp" - namespace iguana { template , int> = 0> @@ -114,10 +113,7 @@ IGUANA_INLINE void parse_value(U &value, It &&value_begin, It &&value_end) { IGUANA_UNLIKELY { return; } auto size = std::distance(value_begin, value_end); const auto start = &*value_begin; - auto [p, ec] = detail::from_chars(start, start + size, value); - if (ec != std::errc{}) - IGUANA_UNLIKELY - throw std::runtime_error("Failed to parse number"); + detail::from_chars(start, start + size, value); } // string_view should be used for string with ' " ? diff --git a/ormpp/dbng.hpp b/ormpp/dbng.hpp index aae2859c..723dc365 100644 --- a/ormpp/dbng.hpp +++ b/ormpp/dbng.hpp @@ -63,6 +63,16 @@ class dbng { return db_.update(v, std::forward(args)...); } + template + int update_s(const T &t, Args &&...args) { + return db_.update(t, std::forward(args)...); + } + + template + int update_s(const std::vector &v, Args &&...args) { + return db_.update(v, std::forward(args)...); + } + template uint64_t get_insert_id_after_insert(const T &t, Args &&...args) { return db_.get_insert_id_after_insert(t, std::forward(args)...); diff --git a/ormpp/mysql.hpp b/ormpp/mysql.hpp index 2e9a61e7..a46a4b80 100644 --- a/ormpp/mysql.hpp +++ b/ormpp/mysql.hpp @@ -118,14 +118,14 @@ class mysql { return insert_impl(OptType::replace, v, std::forward(args)...); } - template + template int update(const T &t, Args &&...args) { - return update_impl(t, std::forward(args)...); + return update_impl(t, std::forward(args)...); } - template + template int update(const std::vector &v, Args &&...args) { - return update_impl(v, std::forward(args)...); + return update_impl(v, std::forward(args)...); } template @@ -913,26 +913,37 @@ class mysql { return sql; } - template + template int stmt_execute(const T &t, OptType type, bool condition) { + bool r = false; std::vector param_binds; - iguana::for_each(t, [&t, ¶m_binds, type, this](auto item, auto i) { + iguana::for_each(t, [&t, &r, ¶m_binds, type, this](auto item, auto i) { if (type == OptType::insert && is_auto_key(iguana::get_name(i).data())) { return; } - set_param_bind(param_binds, t.*item); + if constexpr (sizeof...(members) > 0) { + ((void)(!r && + (r = is_member(i), + !r ? set_param_bind(param_binds, t.*item) : nullptr, true)), + ...); + } + else { + set_param_bind(param_binds, t.*item); + } }); - if (condition && type == OptType::update) { - iguana::for_each(t, [&t, ¶m_binds, this](auto item, auto i) { - std::string field_name = "`"; - field_name += iguana::get_name(i).data(); - field_name += "`"; - if (is_conflict_key(field_name)) { - set_param_bind(param_binds, t.*item); - } - }); + if constexpr (sizeof...(Args) == 0) { + if (type == OptType::update) { + iguana::for_each(t, [&t, ¶m_binds, this](auto item, auto i) { + std::string field_name = "`"; + field_name += iguana::get_name(i).data(); + field_name += "`"; + if (is_conflict_key(field_name)) { + set_param_bind(param_binds, t.*item); + } + }); + } } if (mysql_stmt_bind_param(stmt_, ¶m_binds[0])) { @@ -967,28 +978,28 @@ class mysql { return res.has_value() ? res.value() : INT_MIN; } - template + template int update_impl(const T &t, Args &&...args) { - bool condition = true; - auto sql = generate_update_sql(condition, std::forward(args)...); - auto res = insert_or_update_impl(t, sql, OptType::update, false, condition); + auto sql = generate_update_sql(std::forward(args)...); + auto res = insert_or_update_impl(t, sql, OptType::update, false, + std::forward(args)...); return res.has_value() ? res.value() : INT_MIN; } - template + template int update_impl(const std::vector &v, Args &&...args) { - bool condition = true; - auto sql = generate_update_sql(condition, std::forward(args)...); - auto res = insert_or_update_impl(v, sql, OptType::update, false, condition); + auto sql = generate_update_sql(std::forward(args)...); + auto res = insert_or_update_impl(v, sql, OptType::update, false, + std::forward(args)...); return res.has_value() ? res.value() : INT_MIN; } - template + template std::optional insert_or_update_impl(const T &t, const std::string &sql, OptType type, bool get_insert_id = false, - bool condition = true) { + Args &&...args) { #ifdef ORMPP_ENABLE_LOG std::cout << sql << std::endl; #endif @@ -1005,19 +1016,20 @@ class mysql { auto guard = guard_statment(stmt_); - if (stmt_execute(t, type, condition) == INT_MIN) { + if (stmt_execute(t, type, std::forward(args)...) == + INT_MIN) { return std::nullopt; } return get_insert_id ? stmt_->mysql->insert_id : 1; } - template + template std::optional insert_or_update_impl(const std::vector &v, const std::string &sql, OptType type, bool get_insert_id = false, - bool condition = true) { + Args &&...args) { #ifdef ORMPP_ENABLE_LOG std::cout << sql << std::endl; #endif @@ -1039,7 +1051,8 @@ class mysql { } for (auto &item : v) { - if (stmt_execute(item, type, condition) == INT_MIN) { + if (stmt_execute(item, type, std::forward(args)...) == + INT_MIN) { rollback(); return std::nullopt; } diff --git a/ormpp/postgresql.hpp b/ormpp/postgresql.hpp index a8727c68..15920e65 100644 --- a/ormpp/postgresql.hpp +++ b/ormpp/postgresql.hpp @@ -94,14 +94,14 @@ class postgresql { return insert_impl(OptType::replace, v, std::forward(args)...); } - template + template int update(const T &t, Args &&...args) { - return update_impl(t, std::forward(args)...); + return update_impl(t, std::forward(args)...); } - template + template int update(const std::vector &v, Args &&...args) { - return update_impl(v, std::forward(args)...); + return update_impl(v, std::forward(args)...); } template @@ -520,24 +520,35 @@ class postgresql { return PQresultStatus(res_) == PGRES_COMMAND_OK; } - template + template std::optional stmt_execute(const T &t, OptType type, - bool condition) { + Args &&...args) { + bool r = false; std::vector> param_values; - iguana::for_each(t, [&t, ¶m_values, type, this](auto item, auto i) { + iguana::for_each(t, [&t, &r, ¶m_values, type, this](auto item, auto i) { if (type == OptType::insert && is_auto_key(iguana::get_name(i).data())) { return; } - set_param_values(param_values, t.*item); + if constexpr (sizeof...(members) > 0) { + ((void)(!r && + (r = is_member(i), + !r ? set_param_values(param_values, t.*item) : nullptr, true)), + ...); + } + else { + set_param_values(param_values, t.*item); + } }); - if (condition && type == OptType::update) { - iguana::for_each(t, [&t, ¶m_values, this](auto item, auto i) { - if (is_conflict_key(iguana::get_name(i).data())) { - set_param_values(param_values, t.*item); - } - }); + if constexpr (sizeof...(Args) == 0) { + if (type == OptType::update) { + iguana::for_each(t, [&t, ¶m_values, this](auto item, auto i) { + if (is_conflict_key(iguana::get_name(i).data())) { + set_param_values(param_values, t.*item); + } + }); + } } if (param_values.empty()) { @@ -585,43 +596,43 @@ class postgresql { return res.has_value() ? res.value() : INT_MIN; } - template + template int update_impl(const T &t, Args &&...args) { - bool condition = true; - auto sql = generate_update_sql(condition, std::forward(args)...); - auto res = insert_or_update_impl(t, sql, OptType::update, false, condition); + auto sql = generate_update_sql(std::forward(args)...); + auto res = insert_or_update_impl(t, sql, OptType::update, false, + std::forward(args)...); return res.has_value() ? res.value() : INT_MIN; } - template + template int update_impl(const std::vector &v, Args &&...args) { - bool condition = true; - auto sql = generate_update_sql(condition, std::forward(args)...); - auto res = insert_or_update_impl(v, sql, OptType::update, false, condition); + auto sql = generate_update_sql(std::forward(args)...); + auto res = insert_or_update_impl(v, sql, OptType::update, false, + std::forward(args)...); return res.has_value() ? res.value() : INT_MIN; } - template + template std::optional insert_or_update_impl(const T &t, const std::string &sql, OptType type, bool get_insert_id = false, - bool condition = true) { + Args &&...args) { if (!prepare(get_insert_id ? sql + "returning " + get_auto_key().data() : sql)) { return std::nullopt; } - return stmt_execute(t, type, condition); + return stmt_execute(t, type, std::forward(args)...); } - template + template std::optional insert_or_update_impl(const std::vector &v, const std::string &sql, OptType type, bool get_insert_id = false, - bool condition = true) { + Args &&...args) { if (!begin()) { return std::nullopt; } @@ -634,7 +645,7 @@ class postgresql { std::optional res = {0}; for (auto &item : v) { - res = stmt_execute(item, type, condition); + res = stmt_execute(item, type, std::forward(args)...); if (!res.has_value()) { rollback(); return std::nullopt; diff --git a/ormpp/sqlite.hpp b/ormpp/sqlite.hpp index a2083e7d..4773b00f 100644 --- a/ormpp/sqlite.hpp +++ b/ormpp/sqlite.hpp @@ -93,14 +93,14 @@ class sqlite { return insert_impl(OptType::replace, v, std::forward(args)...); } - template + template int update(const T &t, Args &&...args) { - return update_impl(t, std::forward(args)...); + return update_impl(t, std::forward(args)...); } - template + template int update(const std::vector &v, Args &&...args) { - return update_impl(v, std::forward(args)...); + return update_impl(v, std::forward(args)...); } template @@ -488,30 +488,39 @@ class sqlite { return sql; } - template - int stmt_execute(const T &t, OptType type, bool condition) { - int index = 0; + template + int stmt_execute(const T &t, OptType type, Args &&...args) { + int index = 1; + bool r = false; bool bind_ok = true; - iguana::for_each(t, [&t, &bind_ok, &index, type, this](auto item, auto i) { - if ((type == OptType::insert && - is_auto_key(iguana::get_name(i).data())) || - !bind_ok) { - return; + iguana::for_each( + t, [&t, &r, &bind_ok, &index, type, this](auto item, auto i) { + if ((type == OptType::insert && + is_auto_key(iguana::get_name(i).data())) || + !bind_ok) { + return; + } + if constexpr (sizeof...(members) > 0) { + ((void)(!r && (r = is_member(i), + !r ? set_param_bind(t.*item, index++) : 0, true)), + ...); + } + else { + bind_ok = set_param_bind(t.*item, index++); + } + }); + + if constexpr (sizeof...(Args) == 0) { + if (type == OptType::update) { + iguana::for_each(t, [&t, &bind_ok, &index, this](auto item, auto i) { + if (!bind_ok) { + return; + } + if (is_conflict_key(iguana::get_name(i).data())) { + bind_ok = set_param_bind(t.*item, index++); + } + }); } - bind_ok = set_param_bind(t.*item, index + 1); - index++; - }); - - if (condition && type == OptType::update) { - iguana::for_each(t, [&t, &bind_ok, &index, this](auto item, auto i) { - if (!bind_ok) { - return; - } - if (is_conflict_key(iguana::get_name(i).data())) { - bind_ok = set_param_bind(t.*item, index + 1); - index++; - } - }); } if (!bind_ok) { @@ -639,31 +648,32 @@ class sqlite { return res.has_value() ? res.value() : INT_MIN; } - template + template int update_impl(const T &t, Args &&...args) { - bool condition = true; - auto sql = generate_update_sql(condition, std::forward(args)...); - auto res = insert_or_update_impl(t, sql, OptType::update, false, condition); + auto sql = generate_update_sql(std::forward(args)...); + auto res = insert_or_update_impl(t, sql, OptType::update, false, + std::forward(args)...); return res.has_value() ? res.value() : INT_MIN; } - template + template int update_impl(const std::vector &v, Args &&...args) { - bool condition = true; - auto sql = generate_update_sql(condition, std::forward(args)...); - auto res = insert_or_update_impl(v, sql, OptType::update, false, condition); + auto sql = generate_update_sql(std::forward(args)...); + auto res = insert_or_update_impl(v, sql, OptType::update, false, + std::forward(args)...); return res.has_value() ? res.value() : INT_MIN; } - template + template std::optional insert_or_update_impl(const T &t, const std::string &sql, OptType type, bool get_insert_id = false, - bool condition = true) { + Args &&...args) { #ifdef ORMPP_ENABLE_LOG std::cout << sql << std::endl; #endif + std::cout << sql << std::endl; if (sqlite3_prepare_v2(handle_, sql.data(), (int)sql.size(), &stmt_, nullptr) != SQLITE_OK) { set_last_error(sqlite3_errmsg(handle_)); @@ -672,19 +682,20 @@ class sqlite { auto guard = guard_statment(stmt_); - if (stmt_execute(t, type, condition) == INT_MIN) { + if (stmt_execute(t, type, std::forward(args)...) == + INT_MIN) { return std::nullopt; } return get_insert_id ? sqlite3_last_insert_rowid(handle_) : 1; } - template + template std::optional insert_or_update_impl(const std::vector &v, const std::string &sql, OptType type, bool get_insert_id = false, - bool condition = true) { + Args &&...args) { #ifdef ORMPP_ENABLE_LOG std::cout << sql << std::endl; #endif @@ -701,7 +712,8 @@ class sqlite { } for (auto &item : v) { - if (stmt_execute(item, type, condition) == INT_MIN) { + if (stmt_execute(item, type, std::forward(args)...) == + INT_MIN) { rollback(); return std::nullopt; } diff --git a/ormpp/utility.hpp b/ormpp/utility.hpp index e04c80d7..0bcfb2b5 100644 --- a/ormpp/utility.hpp +++ b/ormpp/utility.hpp @@ -261,6 +261,11 @@ inline auto is_conflict_key(std::string_view field_name) { return false; } +template +constexpr bool is_member(size_t i) { + return iguana::index_of() == i; +} + template inline std::string generate_insert_sql(bool insert, Args &&...args) { #ifdef ORMPP_ENABLE_PG @@ -346,46 +351,58 @@ inline std::string generate_insert_sql(bool insert, Args &&...args) { return sql; } -template -inline std::string generate_update_sql(bool &condition, Args &&...args) { +template +inline std::string generate_update_sql(Args &&...args) { constexpr auto SIZE = iguana::get_value(); std::string sql = "update "; auto name = get_name(); append(sql, name.data()); append(sql, "set"); - - int index = 0; std::string fields; - for (size_t i = 0; i < SIZE; ++i) { - std::string field_name = iguana::get_name(i).data(); +#ifdef ORMPP_ENABLE_PG + int index = 0; +#endif + + if constexpr (sizeof...(members) > 0) { +#ifdef ORMPP_ENABLE_PG + (fields.append(iguana::name_of()) + .append("=$" + std::to_string(++index) + ","), + ...); +#else + (fields.append(iguana::name_of()).append("=?,"), ...); +#endif + } + else { + for (size_t i = 0; i < SIZE; ++i) { + std::string field_name = iguana::get_name(i).data(); #ifdef ORMPP_ENABLE_MYSQL - fields += "`" + field_name + "`"; + fields.append("`").append(field_name).append("`"); #else - fields += field_name; + fields.append(field_name); #endif #ifdef ORMPP_ENABLE_PG - append(fields, " =", "$" + std::to_string(++index)); + fields.append("=$").append(std::to_string(++index)).append(","); #else - fields += " = ?"; + fields.append("=?,"); #endif - if (i < SIZE - 1) { - fields += ", "; } } + fields.pop_back(); + std::string conflict = "where 1=1"; if constexpr (sizeof...(Args) > 0) { append(conflict, " and", args...); - condition = false; } else { for (const auto &it : get_conflict_keys()) { #ifdef ORMPP_ENABLE_PG - append(conflict, " and", it, "=", "$" + std::to_string(++index)); + append(conflict, " and", it + "=$" + std::to_string(++index)); #else - append(conflict, " and", it, "= ?"); + append(conflict, " and", it + "=?"); #endif } } + append(sql, fields, conflict); return sql; } @@ -393,8 +410,9 @@ inline std::string generate_update_sql(bool &condition, Args &&...args) { inline bool is_empty(const std::string &t) { return t.empty(); } template -constexpr bool is_char_array_v = std::is_array_v - &&std::is_same_v>>; +constexpr bool is_char_array_v = + std::is_array_v && + std::is_same_v>>; template inline constexpr size_t char_array_size(char (&)[N]) { diff --git a/tests/test_ormpp.cpp b/tests/test_ormpp.cpp index 4132ccec..5a42894d 100644 --- a/tests/test_ormpp.cpp +++ b/tests/test_ormpp.cpp @@ -1771,4 +1771,81 @@ TEST_CASE("pg update") { CHECK(vec1.front().id == vec2.front().id); } } -#endif \ No newline at end of file +#endif + +TEST_CASE("update section filed") { +#ifdef ORMPP_ENABLE_MYSQL + // dbng mysql; + // if (mysql.connect(ip, username, password, db)) { + // mysql.execute("drop table if exists person"); + // mysql.create_datatable(ormpp_auto_key{"id"}); + // mysql.insert({"purecpp1", 1}); + // mysql.insert({"purecpp2", 2}); + // mysql.update<&person::name>(person{"111", 0, 1}); + // mysql.update<&person::name>(person{"222"}, "id=2"); + // auto vec1 = mysql.query_s("id=?", 1); + // auto vec2 = mysql.query_s("id=?", 2); + // CHECK(vec1.size() == 1); + // CHECK(vec2.size() == 1); + // CHECK(vec1.front().name == "111"); + // CHECK(vec2.front().name == "222"); + // mysql.update<&person::name, &person::age>(person{"666", 666, 1}); + // vec1 = mysql.query_s("id=?", 1); + // CHECK(vec1.size() == 1); + // CHECK(vec1.front().age == 666); + // CHECK(vec1.front().name == "666"); + // } +#endif +#ifdef ORMPP_ENABLE_PG + // dbng postgres; + // if (postgres.connect(ip, username, password, db)) { + // postgres.execute("drop table if exists person"); + // postgres.create_datatable(ormpp_auto_key{"id"}); + // postgres.insert({"purecpp1", 1}); + // postgres.insert({"purecpp2", 2}); + // postgres.update<&person::name>(person{"111", 0, 1}); + // postgres.update<&person::name>(person{"222"}, "id=2"); + // auto vec1 = postgres.query_s("id=?", 1); + // auto vec2 = postgres.query_s("id=?", 2); + // CHECK(vec1.size() == 1); + // CHECK(vec2.size() == 1); + // CHECK(vec1.front().name == "111"); + // CHECK(vec2.front().name == "222"); + // postgres.update<&person::name, &person::age>(person{"666", 666, 1}); + // vec1 = postgres.query_s("id=?", 1); + // CHECK(vec1.size() == 1); + // CHECK(vec1.front().age == 666); + // CHECK(vec1.front().name == "666"); + // } +#endif +#ifdef ORMPP_ENABLE_SQLITE3 + dbng sqlite; + if (sqlite.connect(db)) { + sqlite.execute("drop table if exists person"); + sqlite.create_datatable(ormpp_auto_key{"id"}); + sqlite.insert({"person_a", 1}); + sqlite.insert({"person_b", 2}); + auto vec3 = sqlite.query_s(); + sqlite.update_s<&person::name>(person{"purecpp_a", 0, 1}); + sqlite.update_s<&person::name>(person{"purecpp_b"}, "id=2"); + auto vec1 = sqlite.query_s("id=?", 1); + auto vec2 = sqlite.query_s("id=?", 2); + CHECK(vec1.size() == 1); + CHECK(vec2.size() == 1); + CHECK(vec1.front().name == "purecpp_a"); + CHECK(vec2.front().name == "purecpp_b"); + sqlite.update_s<&person::name, &person::age>(person{"purecpp", 100, 1}); + sqlite.update_s<&person::name, &person::age>(person{"purecpp", 200}, + "id=2"); + auto vec = sqlite.query_s(); + vec1 = sqlite.query_s("id=?", 1); + vec2 = sqlite.query_s("id=?", 2); + CHECK(vec1.size() == 1); + CHECK(vec2.size() == 1); + CHECK(vec1.front().age == 100); + CHECK(vec2.front().age == 200); + CHECK(vec1.front().name == "purecpp"); + CHECK(vec2.front().name == "purecpp"); + } +#endif +} \ No newline at end of file From 4607eb522d5727817fb422f23c37617a204fdd87 Mon Sep 17 00:00:00 2001 From: Jacyking <791026912@qq.com> Date: Tue, 7 May 2024 11:30:44 +0800 Subject: [PATCH 02/12] add section field update --- ormpp/mysql.hpp | 13 ++--- ormpp/postgresql.hpp | 34 ++++++------ ormpp/sqlite.hpp | 13 ++--- ormpp/utility.hpp | 5 -- tests/test_ormpp.cpp | 124 +++++++++++++++++++++++++++++-------------- 5 files changed, 115 insertions(+), 74 deletions(-) diff --git a/ormpp/mysql.hpp b/ormpp/mysql.hpp index a46a4b80..8061693d 100644 --- a/ormpp/mysql.hpp +++ b/ormpp/mysql.hpp @@ -915,18 +915,19 @@ class mysql { template int stmt_execute(const T &t, OptType type, bool condition) { - bool r = false; std::vector param_binds; - iguana::for_each(t, [&t, &r, ¶m_binds, type, this](auto item, auto i) { + constexpr auto arr = iguana::indexs_of(); + iguana::for_each(t, [&t, arr, ¶m_binds, type, this](auto item, auto i) { if (type == OptType::insert && is_auto_key(iguana::get_name(i).data())) { return; } if constexpr (sizeof...(members) > 0) { - ((void)(!r && - (r = is_member(i), - !r ? set_param_bind(param_binds, t.*item) : nullptr, true)), - ...); + for (auto idx : arr) { + if (idx == decltype(i)::value) { + set_param_bind(param_binds, t.*item); + } + } } else { set_param_bind(param_binds, t.*item); diff --git a/ormpp/postgresql.hpp b/ormpp/postgresql.hpp index 15920e65..700e7b67 100644 --- a/ormpp/postgresql.hpp +++ b/ormpp/postgresql.hpp @@ -523,23 +523,25 @@ class postgresql { template std::optional stmt_execute(const T &t, OptType type, Args &&...args) { - bool r = false; std::vector> param_values; - iguana::for_each(t, [&t, &r, ¶m_values, type, this](auto item, auto i) { - if (type == OptType::insert && - is_auto_key(iguana::get_name(i).data())) { - return; - } - if constexpr (sizeof...(members) > 0) { - ((void)(!r && - (r = is_member(i), - !r ? set_param_values(param_values, t.*item) : nullptr, true)), - ...); - } - else { - set_param_values(param_values, t.*item); - } - }); + constexpr auto arr = iguana::indexs_of(); + iguana::for_each(t, + [&t, arr, ¶m_values, type, this](auto item, auto i) { + if (type == OptType::insert && + is_auto_key(iguana::get_name(i).data())) { + return; + } + if constexpr (sizeof...(members) > 0) { + for (auto idx : arr) { + if (idx == decltype(i)::value) { + set_param_values(param_values, t.*item); + } + } + } + else { + set_param_values(param_values, t.*item); + } + }); if constexpr (sizeof...(Args) == 0) { if (type == OptType::update) { diff --git a/ormpp/sqlite.hpp b/ormpp/sqlite.hpp index 4773b00f..ff03da9d 100644 --- a/ormpp/sqlite.hpp +++ b/ormpp/sqlite.hpp @@ -491,19 +491,21 @@ class sqlite { template int stmt_execute(const T &t, OptType type, Args &&...args) { int index = 1; - bool r = false; bool bind_ok = true; + constexpr auto arr = iguana::indexs_of(); iguana::for_each( - t, [&t, &r, &bind_ok, &index, type, this](auto item, auto i) { + t, [&t, arr, &bind_ok, &index, type, this](auto item, auto i) { if ((type == OptType::insert && is_auto_key(iguana::get_name(i).data())) || !bind_ok) { return; } if constexpr (sizeof...(members) > 0) { - ((void)(!r && (r = is_member(i), - !r ? set_param_bind(t.*item, index++) : 0, true)), - ...); + for (auto idx : arr) { + if (idx == decltype(i)::value) { + bind_ok = set_param_bind(t.*item, index++); + } + } } else { bind_ok = set_param_bind(t.*item, index++); @@ -673,7 +675,6 @@ class sqlite { #ifdef ORMPP_ENABLE_LOG std::cout << sql << std::endl; #endif - std::cout << sql << std::endl; if (sqlite3_prepare_v2(handle_, sql.data(), (int)sql.size(), &stmt_, nullptr) != SQLITE_OK) { set_last_error(sqlite3_errmsg(handle_)); diff --git a/ormpp/utility.hpp b/ormpp/utility.hpp index 0bcfb2b5..edc15421 100644 --- a/ormpp/utility.hpp +++ b/ormpp/utility.hpp @@ -261,11 +261,6 @@ inline auto is_conflict_key(std::string_view field_name) { return false; } -template -constexpr bool is_member(size_t i) { - return iguana::index_of() == i; -} - template inline std::string generate_insert_sql(bool insert, Args &&...args) { #ifdef ORMPP_ENABLE_PG diff --git a/tests/test_ormpp.cpp b/tests/test_ormpp.cpp index 5a42894d..1b76bb85 100644 --- a/tests/test_ormpp.cpp +++ b/tests/test_ormpp.cpp @@ -1775,48 +1775,81 @@ TEST_CASE("pg update") { TEST_CASE("update section filed") { #ifdef ORMPP_ENABLE_MYSQL - // dbng mysql; - // if (mysql.connect(ip, username, password, db)) { - // mysql.execute("drop table if exists person"); - // mysql.create_datatable(ormpp_auto_key{"id"}); - // mysql.insert({"purecpp1", 1}); - // mysql.insert({"purecpp2", 2}); - // mysql.update<&person::name>(person{"111", 0, 1}); - // mysql.update<&person::name>(person{"222"}, "id=2"); - // auto vec1 = mysql.query_s("id=?", 1); - // auto vec2 = mysql.query_s("id=?", 2); - // CHECK(vec1.size() == 1); - // CHECK(vec2.size() == 1); - // CHECK(vec1.front().name == "111"); - // CHECK(vec2.front().name == "222"); - // mysql.update<&person::name, &person::age>(person{"666", 666, 1}); - // vec1 = mysql.query_s("id=?", 1); - // CHECK(vec1.size() == 1); - // CHECK(vec1.front().age == 666); - // CHECK(vec1.front().name == "666"); - // } + dbng mysql; + if (mysql.connect(ip, username, password, db)) { + mysql.execute("drop table if exists person"); + mysql.create_datatable(ormpp_auto_key{"id"}); + mysql.insert({"person_a", 1}); + mysql.insert({"person_b", 2}); + mysql.update_s<&person::name>(person{"purecpp_a", 0, 1}); + mysql.update_s<&person::name>(person{"purecpp_b"}, "id=2"); + auto vec1 = mysql.query_s("id=?", 1); + auto vec2 = mysql.query_s("id=?", 2); + CHECK(vec1.size() == 1); + CHECK(vec2.size() == 1); + CHECK(vec1.front().name == "purecpp_a"); + CHECK(vec2.front().name == "purecpp_b"); + mysql.update_s<&person::name, &person::age>(person{"purecpp", 100, 1}); + mysql.update_s<&person::name, &person::age>(person{"purecpp", 200}, "id=2"); + auto vec = mysql.query_s(); + vec1 = mysql.query_s("id=?", 1); + vec2 = mysql.query_s("id=?", 2); + CHECK(vec1.size() == 1); + CHECK(vec2.size() == 1); + CHECK(vec1.front().age == 100); + CHECK(vec2.front().age == 200); + CHECK(vec1.front().name == "purecpp"); + CHECK(vec2.front().name == "purecpp"); + mysql.update_s<&person::name, &person::age>( + std::vector{{"purecpp_aa", 111, 1}, {"purecpp_bb", 222, 2}}); + vec1 = mysql.query_s("id=?", 1); + vec2 = mysql.query_s("id=?", 2); + CHECK(vec1.size() == 1); + CHECK(vec2.size() == 1); + CHECK(vec1.front().age == 111); + CHECK(vec2.front().age == 222); + CHECK(vec1.front().name == "purecpp_aa"); + CHECK(vec2.front().name == "purecpp_bb"); + } #endif #ifdef ORMPP_ENABLE_PG - // dbng postgres; - // if (postgres.connect(ip, username, password, db)) { - // postgres.execute("drop table if exists person"); - // postgres.create_datatable(ormpp_auto_key{"id"}); - // postgres.insert({"purecpp1", 1}); - // postgres.insert({"purecpp2", 2}); - // postgres.update<&person::name>(person{"111", 0, 1}); - // postgres.update<&person::name>(person{"222"}, "id=2"); - // auto vec1 = postgres.query_s("id=?", 1); - // auto vec2 = postgres.query_s("id=?", 2); - // CHECK(vec1.size() == 1); - // CHECK(vec2.size() == 1); - // CHECK(vec1.front().name == "111"); - // CHECK(vec2.front().name == "222"); - // postgres.update<&person::name, &person::age>(person{"666", 666, 1}); - // vec1 = postgres.query_s("id=?", 1); - // CHECK(vec1.size() == 1); - // CHECK(vec1.front().age == 666); - // CHECK(vec1.front().name == "666"); - // } + dbng postgres; + if (postgres.connect(ip, username, password, db)) { + postgres.execute("drop table if exists person"); + postgres.create_datatable(ormpp_auto_key{"id"}); + postgres.insert({"person_a", 1}); + postgres.insert({"person_b", 2}); + postgres.update_s<&person::name>(person{"purecpp_a", 0, 1}); + postgres.update_s<&person::name>(person{"purecpp_b"}, "id=2"); + auto vec1 = postgres.query_s("id=?", 1); + auto vec2 = postgres.query_s("id=?", 2); + CHECK(vec1.size() == 1); + CHECK(vec2.size() == 1); + CHECK(vec1.front().name == "purecpp_a"); + CHECK(vec2.front().name == "purecpp_b"); + postgres.update_s<&person::name, &person::age>(person{"purecpp", 100, 1}); + postgres.update_s<&person::name, &person::age>(person{"purecpp", 200}, + "id=2"); + auto vec = postgres.query_s(); + vec1 = postgres.query_s("id=?", 1); + vec2 = postgres.query_s("id=?", 2); + CHECK(vec1.size() == 1); + CHECK(vec2.size() == 1); + CHECK(vec1.front().age == 100); + CHECK(vec2.front().age == 200); + CHECK(vec1.front().name == "purecpp"); + CHECK(vec2.front().name == "purecpp"); + postgres.update_s<&person::name, &person::age>( + std::vector{{"purecpp_aa", 111, 1}, {"purecpp_bb", 222, 2}}); + vec1 = postgres.query_s("id=?", 1); + vec2 = postgres.query_s("id=?", 2); + CHECK(vec1.size() == 1); + CHECK(vec2.size() == 1); + CHECK(vec1.front().age == 111); + CHECK(vec2.front().age == 222); + CHECK(vec1.front().name == "purecpp_aa"); + CHECK(vec2.front().name == "purecpp_bb"); + } #endif #ifdef ORMPP_ENABLE_SQLITE3 dbng sqlite; @@ -1825,7 +1858,6 @@ TEST_CASE("update section filed") { sqlite.create_datatable(ormpp_auto_key{"id"}); sqlite.insert({"person_a", 1}); sqlite.insert({"person_b", 2}); - auto vec3 = sqlite.query_s(); sqlite.update_s<&person::name>(person{"purecpp_a", 0, 1}); sqlite.update_s<&person::name>(person{"purecpp_b"}, "id=2"); auto vec1 = sqlite.query_s("id=?", 1); @@ -1846,6 +1878,16 @@ TEST_CASE("update section filed") { CHECK(vec2.front().age == 200); CHECK(vec1.front().name == "purecpp"); CHECK(vec2.front().name == "purecpp"); + sqlite.update_s<&person::name, &person::age>( + std::vector{{"purecpp_aa", 111, 1}, {"purecpp_bb", 222, 2}}); + vec1 = sqlite.query_s("id=?", 1); + vec2 = sqlite.query_s("id=?", 2); + CHECK(vec1.size() == 1); + CHECK(vec2.size() == 1); + CHECK(vec1.front().age == 111); + CHECK(vec2.front().age == 222); + CHECK(vec1.front().name == "purecpp_aa"); + CHECK(vec2.front().name == "purecpp_bb"); } #endif } \ No newline at end of file From 24aff3bdebcea31a5b7d993433cb1408ce82f7d1 Mon Sep 17 00:00:00 2001 From: Jacyking <791026912@qq.com> Date: Tue, 7 May 2024 11:36:52 +0800 Subject: [PATCH 03/12] add section field update --- ormpp/sqlite.hpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ormpp/sqlite.hpp b/ormpp/sqlite.hpp index ff03da9d..a3269940 100644 --- a/ormpp/sqlite.hpp +++ b/ormpp/sqlite.hpp @@ -490,7 +490,7 @@ class sqlite { template int stmt_execute(const T &t, OptType type, Args &&...args) { - int index = 1; + int index = 0; bool bind_ok = true; constexpr auto arr = iguana::indexs_of(); iguana::for_each( @@ -503,12 +503,14 @@ class sqlite { if constexpr (sizeof...(members) > 0) { for (auto idx : arr) { if (idx == decltype(i)::value) { - bind_ok = set_param_bind(t.*item, index++); + bind_ok = set_param_bind(t.*item, index + 1); + index++; } } } else { - bind_ok = set_param_bind(t.*item, index++); + bind_ok = set_param_bind(t.*item, index + 1); + index++; } }); @@ -519,7 +521,8 @@ class sqlite { return; } if (is_conflict_key(iguana::get_name(i).data())) { - bind_ok = set_param_bind(t.*item, index++); + bind_ok = set_param_bind(t.*item, index + 1); + index++; } }); } From cbe16b8684a312f2ffe920f2ce40866500a69776 Mon Sep 17 00:00:00 2001 From: Jacyking <791026912@qq.com> Date: Wed, 8 May 2024 23:07:08 +0800 Subject: [PATCH 04/12] add section field update --- ormpp/dbng.hpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/ormpp/dbng.hpp b/ormpp/dbng.hpp index 723dc365..b4e660fe 100644 --- a/ormpp/dbng.hpp +++ b/ormpp/dbng.hpp @@ -35,52 +35,54 @@ class dbng { template int insert(const T &t, Args &&...args) { - return db_.insert(t, std::forward(args)...); + return db_.template insert(t, std::forward(args)...); } template int insert(const std::vector &v, Args &&...args) { - return db_.insert(v, std::forward(args)...); + return db_.template insert(v, std::forward(args)...); } template int replace(const T &t, Args &&...args) { - return db_.replace(t, std::forward(args)...); + return db_.template replace(t, std::forward(args)...); } template int replace(const std::vector &v, Args &&...args) { - return db_.replace(v, std::forward(args)...); + return db_.template replace(v, std::forward(args)...); } template int update(const T &t, Args &&...args) { - return db_.update(t, std::forward(args)...); + return db_.template update(t, std::forward(args)...); } template int update(const std::vector &v, Args &&...args) { - return db_.update(v, std::forward(args)...); + return db_.template update(v, std::forward(args)...); } template int update_s(const T &t, Args &&...args) { - return db_.update(t, std::forward(args)...); + return db_.template update(t, std::forward(args)...); } template int update_s(const std::vector &v, Args &&...args) { - return db_.update(v, std::forward(args)...); + return db_.template update(v, std::forward(args)...); } template uint64_t get_insert_id_after_insert(const T &t, Args &&...args) { - return db_.get_insert_id_after_insert(t, std::forward(args)...); + return db_.template get_insert_id_after_insert(t, + std::forward(args)...); } template uint64_t get_insert_id_after_insert(const std::vector &v, Args &&...args) { - return db_.get_insert_id_after_insert(v, std::forward(args)...); + return db_.template get_insert_id_after_insert(v, + std::forward(args)...); } template From eb22cbfafb618e2e4aaa6cf94ed58148a5ebc3cb Mon Sep 17 00:00:00 2001 From: Jacyking <791026912@qq.com> Date: Wed, 8 May 2024 23:10:19 +0800 Subject: [PATCH 05/12] add section field update --- ormpp/dbng.hpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/ormpp/dbng.hpp b/ormpp/dbng.hpp index b4e660fe..6b711a91 100644 --- a/ormpp/dbng.hpp +++ b/ormpp/dbng.hpp @@ -35,32 +35,32 @@ class dbng { template int insert(const T &t, Args &&...args) { - return db_.template insert(t, std::forward(args)...); + return db_.insert(t, std::forward(args)...); } template int insert(const std::vector &v, Args &&...args) { - return db_.template insert(v, std::forward(args)...); + return db_.insert(v, std::forward(args)...); } template int replace(const T &t, Args &&...args) { - return db_.template replace(t, std::forward(args)...); + return db_.replace(t, std::forward(args)...); } template int replace(const std::vector &v, Args &&...args) { - return db_.template replace(v, std::forward(args)...); + return db_.replace(v, std::forward(args)...); } template int update(const T &t, Args &&...args) { - return db_.template update(t, std::forward(args)...); + return db_.update(t, std::forward(args)...); } template int update(const std::vector &v, Args &&...args) { - return db_.template update(v, std::forward(args)...); + return db_.update(v, std::forward(args)...); } template @@ -75,14 +75,12 @@ class dbng { template uint64_t get_insert_id_after_insert(const T &t, Args &&...args) { - return db_.template get_insert_id_after_insert(t, - std::forward(args)...); + return db_.get_insert_id_after_insert(t, std::forward(args)...); } template uint64_t get_insert_id_after_insert(const std::vector &v, Args &&...args) { - return db_.template get_insert_id_after_insert(v, - std::forward(args)...); + return db_.get_insert_id_after_insert(v, std::forward(args)...); } template From 45ce2bc92e44e0d685bd0caba0f3c987a6d9c0ef Mon Sep 17 00:00:00 2001 From: Jacyking <791026912@qq.com> Date: Wed, 8 May 2024 23:31:37 +0800 Subject: [PATCH 06/12] add section field update --- ormpp/mysql.hpp | 2 +- tests/test_ormpp.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ormpp/mysql.hpp b/ormpp/mysql.hpp index 8061693d..0475b35c 100644 --- a/ormpp/mysql.hpp +++ b/ormpp/mysql.hpp @@ -914,7 +914,7 @@ class mysql { } template - int stmt_execute(const T &t, OptType type, bool condition) { + int stmt_execute(const T &t, OptType type, Args &&...args) { std::vector param_binds; constexpr auto arr = iguana::indexs_of(); iguana::for_each(t, [&t, arr, ¶m_binds, type, this](auto item, auto i) { diff --git a/tests/test_ormpp.cpp b/tests/test_ormpp.cpp index 1b76bb85..c679d115 100644 --- a/tests/test_ormpp.cpp +++ b/tests/test_ormpp.cpp @@ -1821,8 +1821,8 @@ TEST_CASE("update section filed") { postgres.insert({"person_b", 2}); postgres.update_s<&person::name>(person{"purecpp_a", 0, 1}); postgres.update_s<&person::name>(person{"purecpp_b"}, "id=2"); - auto vec1 = postgres.query_s("id=?", 1); - auto vec2 = postgres.query_s("id=?", 2); + auto vec1 = postgres.query_s("id=$1", 1); + auto vec2 = postgres.query_s("id=$1", 2); CHECK(vec1.size() == 1); CHECK(vec2.size() == 1); CHECK(vec1.front().name == "purecpp_a"); @@ -1831,8 +1831,8 @@ TEST_CASE("update section filed") { postgres.update_s<&person::name, &person::age>(person{"purecpp", 200}, "id=2"); auto vec = postgres.query_s(); - vec1 = postgres.query_s("id=?", 1); - vec2 = postgres.query_s("id=?", 2); + vec1 = postgres.query_s("id=$1", 1); + vec2 = postgres.query_s("id=$1", 2); CHECK(vec1.size() == 1); CHECK(vec2.size() == 1); CHECK(vec1.front().age == 100); @@ -1841,8 +1841,8 @@ TEST_CASE("update section filed") { CHECK(vec2.front().name == "purecpp"); postgres.update_s<&person::name, &person::age>( std::vector{{"purecpp_aa", 111, 1}, {"purecpp_bb", 222, 2}}); - vec1 = postgres.query_s("id=?", 1); - vec2 = postgres.query_s("id=?", 2); + vec1 = postgres.query_s("id=$1", 1); + vec2 = postgres.query_s("id=$1", 2); CHECK(vec1.size() == 1); CHECK(vec2.size() == 1); CHECK(vec1.front().age == 111); From ca9980cf0dac54540c8c1a86d91ad9b6311364ee Mon Sep 17 00:00:00 2001 From: Jacyking <791026912@qq.com> Date: Wed, 8 May 2024 23:36:36 +0800 Subject: [PATCH 07/12] add section field update --- cmake/mysql.cmake | 2 ++ ormpp/sqlite.hpp | 11 ++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cmake/mysql.cmake b/cmake/mysql.cmake index ffc75853..b7c3a236 100644 --- a/cmake/mysql.cmake +++ b/cmake/mysql.cmake @@ -17,6 +17,7 @@ IF (WIN32) ELSE (WIN32) FIND_PATH(MYSQL_INCLUDE_DIR mysql.h /usr/local/Cellar/mysql@8.0/*/include/mysql + /opt/homebrew/opt/mysql@8.0/include /opt/homebrew/include/mysql /usr/local/include/mysql /usr/include/mysql) @@ -35,6 +36,7 @@ ELSE (WIN32) PATHS /usr/lib /usr/local/lib /opt/homebrew/lib + /opt/homebrew/opt/mysql@8.0/lib /usr/local/Cellar/mysql@8.0/*/lib PATH_SUFFIXES mysql) ENDIF(WIN32) diff --git a/ormpp/sqlite.hpp b/ormpp/sqlite.hpp index a3269940..ff03da9d 100644 --- a/ormpp/sqlite.hpp +++ b/ormpp/sqlite.hpp @@ -490,7 +490,7 @@ class sqlite { template int stmt_execute(const T &t, OptType type, Args &&...args) { - int index = 0; + int index = 1; bool bind_ok = true; constexpr auto arr = iguana::indexs_of(); iguana::for_each( @@ -503,14 +503,12 @@ class sqlite { if constexpr (sizeof...(members) > 0) { for (auto idx : arr) { if (idx == decltype(i)::value) { - bind_ok = set_param_bind(t.*item, index + 1); - index++; + bind_ok = set_param_bind(t.*item, index++); } } } else { - bind_ok = set_param_bind(t.*item, index + 1); - index++; + bind_ok = set_param_bind(t.*item, index++); } }); @@ -521,8 +519,7 @@ class sqlite { return; } if (is_conflict_key(iguana::get_name(i).data())) { - bind_ok = set_param_bind(t.*item, index + 1); - index++; + bind_ok = set_param_bind(t.*item, index++); } }); } From 51747eac280405cc1ff5eba19e681b993c703b63 Mon Sep 17 00:00:00 2001 From: Jacyking <43704572+Jacyking@users.noreply.github.com> Date: Wed, 8 May 2024 23:44:56 +0800 Subject: [PATCH 08/12] test --- .github/workflows/ci-mysql.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index b4ec6ea3..c7a11242 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -26,7 +26,13 @@ jobs: - name: Configure cmake run : cmake -B${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=${{ matrix.configuration }} -DENABLE_MYSQL=ON - + + - name: Configure cmake and build on windows + if : startsWith(matrix.os, 'macos') + run : | + sudo find / -name "mysql.h" + sudo find / -name "*mysqlclient*" + - name: Build run : cmake --build ${{ github.workspace }}/build --config ${{ matrix.configuration }} @@ -34,4 +40,4 @@ jobs: working-directory: ${{ github.workspace }}/build env : CTEST_OUTPUT_ON_FAILURE: 1 - run : ctest -C ${{ matrix.configuration }} -j 1 -V \ No newline at end of file + run : ctest -C ${{ matrix.configuration }} -j 1 -V From f3f4d7988042504ba1443d3dc26ae857f6a984cb Mon Sep 17 00:00:00 2001 From: Jacyking <43704572+Jacyking@users.noreply.github.com> Date: Wed, 8 May 2024 23:46:59 +0800 Subject: [PATCH 09/12] test --- .github/workflows/ci-mysql.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index c7a11242..1b3c4931 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -24,14 +24,14 @@ jobs: mysql-version: 8.0 database: test_ormppdb - - name: Configure cmake - run : cmake -B${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=${{ matrix.configuration }} -DENABLE_MYSQL=ON - - - name: Configure cmake and build on windows + - name: Test if : startsWith(matrix.os, 'macos') run : | sudo find / -name "mysql.h" sudo find / -name "*mysqlclient*" + + - name: Configure cmake + run : cmake -B${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=${{ matrix.configuration }} -DENABLE_MYSQL=ON - name: Build run : cmake --build ${{ github.workspace }}/build --config ${{ matrix.configuration }} From 65bca5ca910171975dc295593a45984d3f82b2b2 Mon Sep 17 00:00:00 2001 From: Jacyking <791026912@qq.com> Date: Wed, 8 May 2024 23:52:51 +0800 Subject: [PATCH 10/12] add section field update --- .github/workflows/ci-mysql.yml | 10 ++-------- cmake/mariadb.cmake | 8 ++++---- cmake/mysql.cmake | 25 ++++++++++++++++--------- cmake/pgsql.cmake | 8 ++++---- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml index 1b3c4931..b4ec6ea3 100644 --- a/.github/workflows/ci-mysql.yml +++ b/.github/workflows/ci-mysql.yml @@ -24,15 +24,9 @@ jobs: mysql-version: 8.0 database: test_ormppdb - - name: Test - if : startsWith(matrix.os, 'macos') - run : | - sudo find / -name "mysql.h" - sudo find / -name "*mysqlclient*" - - name: Configure cmake run : cmake -B${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=${{ matrix.configuration }} -DENABLE_MYSQL=ON - + - name: Build run : cmake --build ${{ github.workspace }}/build --config ${{ matrix.configuration }} @@ -40,4 +34,4 @@ jobs: working-directory: ${{ github.workspace }}/build env : CTEST_OUTPUT_ON_FAILURE: 1 - run : ctest -C ${{ matrix.configuration }} -j 1 -V + run : ctest -C ${{ matrix.configuration }} -j 1 -V \ No newline at end of file diff --git a/cmake/mariadb.cmake b/cmake/mariadb.cmake index 5b4cff0d..33d15c37 100644 --- a/cmake/mariadb.cmake +++ b/cmake/mariadb.cmake @@ -14,11 +14,11 @@ IF (WIN32) FIND_PATH(MARIADB_INCLUDE_DIR mysql.h $ENV{PROGRAMFILES}/MariaDB*/include $ENV{SYSTEMDRIVE}/MariaDB*/include) -ELSE (WIN32) +ELSE () FIND_PATH(MARIADB_INCLUDE_DIR mysql.h /usr/local/include/mariadb /usr/include/mariadb) -ENDIF(WIN32) +ENDIF() SET(MARIADB_NAMES mariadb) IF (WIN32) @@ -27,13 +27,13 @@ IF (WIN32) PATHS $ENV{PROGRAMFILES}/MariaDB*/lib $ENV{SYSTEMDRIVE}/MariaDB*/lib PATH_SUFFIXES mariadb) -ELSE (WIN32) +ELSE () FIND_LIBRARY(MARIADB_LIBRARY NAMES ${MARIADB_NAMES} PATHS /usr/lib /usr/local/lib PATH_SUFFIXES mariadb) -ENDIF(WIN32) +ENDIF() IF (MARIADB_INCLUDE_DIR AND MARIADB_LIBRARY) SET(MARIADB_FOUND TRUE) diff --git a/cmake/mysql.cmake b/cmake/mysql.cmake index b7c3a236..ed339841 100644 --- a/cmake/mysql.cmake +++ b/cmake/mysql.cmake @@ -14,14 +14,17 @@ IF (WIN32) FIND_PATH(MYSQL_INCLUDE_DIR mysql.h $ENV{PROGRAMFILES}/MySQL/*/include $ENV{SYSTEMDRIVE}/MySQL/*/include) -ELSE (WIN32) +ELSEIF (LINUX) FIND_PATH(MYSQL_INCLUDE_DIR mysql.h - /usr/local/Cellar/mysql@8.0/*/include/mysql - /opt/homebrew/opt/mysql@8.0/include - /opt/homebrew/include/mysql /usr/local/include/mysql /usr/include/mysql) -ENDIF(WIN32) +ELSEIF (APPLE) + FIND_PATH(MYSQL_INCLUDE_DIR mysql.h + /opt/homebrew/include/mysql + /opt/homebrew/opt/mysql@8.0/include + /usr/local/Cellar/mysql@8.0/*/include/mysql + /opt/homebrew/Cellar/mysql@8.0/*/include/mysql) +ENDIF() SET(MYSQL_NAMES mysqlclient) IF (WIN32) @@ -30,16 +33,20 @@ IF (WIN32) PATHS $ENV{PROGRAMFILES}/MySQL/*/lib $ENV{SYSTEMDRIVE}/MySQL/*/lib PATH_SUFFIXES mysql) -ELSE (WIN32) +ELSEIF (LINUX) FIND_LIBRARY(MYSQL_LIBRARY NAMES ${MYSQL_NAMES} PATHS /usr/lib /usr/local/lib - /opt/homebrew/lib + PATH_SUFFIXES mysql) +ELSEIF (APPLE) + FIND_LIBRARY(MYSQL_LIBRARY + NAMES ${MYSQL_NAMES} + PATHS /opt/homebrew/lib /opt/homebrew/opt/mysql@8.0/lib - /usr/local/Cellar/mysql@8.0/*/lib + /opt/homebrew/Cellar/mysql@8.0/*/lib PATH_SUFFIXES mysql) -ENDIF(WIN32) +ENDIF() IF (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) SET(MYSQL_FOUND TRUE) diff --git a/cmake/pgsql.cmake b/cmake/pgsql.cmake index ae6eb039..65a7d41d 100644 --- a/cmake/pgsql.cmake +++ b/cmake/pgsql.cmake @@ -14,12 +14,12 @@ IF (WIN32) FIND_PATH(PGSQL_INCLUDE_DIR libpq-fe.h $ENV{PROGRAMFILES}/PostgreSQL/*/include $ENV{SYSTEMDRIVE}/PostgreSQL/*/include) -ELSE (WIN32) +ELSE () FIND_PATH(PGSQL_INCLUDE_DIR libpq-fe.h /opt/homebrew/include/postgresql /usr/local/include/postgresql /usr/include/postgresql) -ENDIF(WIN32) +ENDIF() IF (WIN32) SET(PGSQL_NAMES libpq) @@ -27,14 +27,14 @@ IF (WIN32) NAMES ${PGSQL_NAMES} PATHS $ENV{PROGRAMFILES}/PostgreSQL/*/lib $ENV{SYSTEMDRIVE}/PostgreSQL/*/lib) -ELSE (WIN32) +ELSE () SET(PGSQL_NAMES pq) FIND_LIBRARY(PGSQL_LIBRARY NAMES ${PGSQL_NAMES} PATHS /usr/lib /usr/local/lib /opt/homebrew/lib) -ENDIF(WIN32) +ENDIF() IF (PGSQL_INCLUDE_DIR AND PGSQL_LIBRARY) SET(PGSQL_FOUND TRUE) From fbe64b4b7219bbfd2d3620e107b2f180a8a89331 Mon Sep 17 00:00:00 2001 From: Jacyking <791026912@qq.com> Date: Wed, 8 May 2024 23:59:21 +0800 Subject: [PATCH 11/12] add section field update --- cmake/mariadb.cmake | 12 ++++++++++-- cmake/mysql.cmake | 1 - cmake/pgsql.cmake | 16 +++++++++++----- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/cmake/mariadb.cmake b/cmake/mariadb.cmake index 33d15c37..e3e95e11 100644 --- a/cmake/mariadb.cmake +++ b/cmake/mariadb.cmake @@ -14,10 +14,13 @@ IF (WIN32) FIND_PATH(MARIADB_INCLUDE_DIR mysql.h $ENV{PROGRAMFILES}/MariaDB*/include $ENV{SYSTEMDRIVE}/MariaDB*/include) -ELSE () +ELSEIF (LINUX) FIND_PATH(MARIADB_INCLUDE_DIR mysql.h /usr/local/include/mariadb /usr/include/mariadb) +ELSEIF (APPLE) + FIND_PATH(MARIADB_INCLUDE_DIR mysql.h + /opt/homebrew/include/mariadb) ENDIF() SET(MARIADB_NAMES mariadb) @@ -27,12 +30,17 @@ IF (WIN32) PATHS $ENV{PROGRAMFILES}/MariaDB*/lib $ENV{SYSTEMDRIVE}/MariaDB*/lib PATH_SUFFIXES mariadb) -ELSE () +ELSEIF (LINUX) FIND_LIBRARY(MARIADB_LIBRARY NAMES ${MARIADB_NAMES} PATHS /usr/lib /usr/local/lib PATH_SUFFIXES mariadb) +ELSEIF (APPLE) + FIND_LIBRARY(MARIADB_LIBRARY + NAMES ${MARIADB_NAMES} + PATHS /opt/homebrew/lib + PATH_SUFFIXES mariadb) ENDIF() IF (MARIADB_INCLUDE_DIR AND MARIADB_LIBRARY) diff --git a/cmake/mysql.cmake b/cmake/mysql.cmake index ed339841..23778f87 100644 --- a/cmake/mysql.cmake +++ b/cmake/mysql.cmake @@ -22,7 +22,6 @@ ELSEIF (APPLE) FIND_PATH(MYSQL_INCLUDE_DIR mysql.h /opt/homebrew/include/mysql /opt/homebrew/opt/mysql@8.0/include - /usr/local/Cellar/mysql@8.0/*/include/mysql /opt/homebrew/Cellar/mysql@8.0/*/include/mysql) ENDIF() diff --git a/cmake/pgsql.cmake b/cmake/pgsql.cmake index 65a7d41d..7f8846cc 100644 --- a/cmake/pgsql.cmake +++ b/cmake/pgsql.cmake @@ -14,11 +14,13 @@ IF (WIN32) FIND_PATH(PGSQL_INCLUDE_DIR libpq-fe.h $ENV{PROGRAMFILES}/PostgreSQL/*/include $ENV{SYSTEMDRIVE}/PostgreSQL/*/include) -ELSE () +ELSEIF (LINUX) FIND_PATH(PGSQL_INCLUDE_DIR libpq-fe.h - /opt/homebrew/include/postgresql /usr/local/include/postgresql /usr/include/postgresql) +ELSEIF (APPLE) + FIND_PATH(PGSQL_INCLUDE_DIR libpq-fe.h + /opt/homebrew/include/postgresql) ENDIF() IF (WIN32) @@ -27,13 +29,17 @@ IF (WIN32) NAMES ${PGSQL_NAMES} PATHS $ENV{PROGRAMFILES}/PostgreSQL/*/lib $ENV{SYSTEMDRIVE}/PostgreSQL/*/lib) -ELSE () +ELSEIF (LINUX) SET(PGSQL_NAMES pq) FIND_LIBRARY(PGSQL_LIBRARY NAMES ${PGSQL_NAMES} PATHS /usr/lib - /usr/local/lib - /opt/homebrew/lib) + /usr/local/lib) +ELSEIF (APPLE) + SET(PGSQL_NAMES pq) + FIND_LIBRARY(PGSQL_LIBRARY + NAMES ${PGSQL_NAMES} + PATHS /opt/homebrew/lib) ENDIF() IF (PGSQL_INCLUDE_DIR AND PGSQL_LIBRARY) From 24e202e10e398ec3bc88b2fa602c2a1cb00f8f7e Mon Sep 17 00:00:00 2001 From: Jacyking <791026912@qq.com> Date: Thu, 9 May 2024 00:08:40 +0800 Subject: [PATCH 12/12] add section field update --- ormpp/utility.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ormpp/utility.hpp b/ormpp/utility.hpp index edc15421..33e8842d 100644 --- a/ormpp/utility.hpp +++ b/ormpp/utility.hpp @@ -405,9 +405,8 @@ inline std::string generate_update_sql(Args &&...args) { inline bool is_empty(const std::string &t) { return t.empty(); } template -constexpr bool is_char_array_v = - std::is_array_v && - std::is_same_v>>; +constexpr bool is_char_array_v = std::is_array_v + &&std::is_same_v>>; template inline constexpr size_t char_array_size(char (&)[N]) {