更新:1、新增数据记录功能,将过程数据录入数据库;
2、增加输入点迹数据、凝聚后点迹数据记录; Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
This commit is contained in:
Vendored
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
# 第三方库来源说明(ormpp / iguana / SQLite)
|
||||
|
||||
本目录为**按上游原样内置**的第三方源码(保留上游字节,含换行符,不做编码/格式转换),与源码目录下内置的 Eigen 3.3.7 同类处理:仓库自带依赖,离线可配置、可交付。
|
||||
|
||||
| 内容 | 本目录位置 | 上游 | 版本 / 标识 |
|
||||
|---|---|---|---|
|
||||
| ormpp(Modern C++ ORM 头文件库) | `ormpp/*.hpp` | <https://github.com/qicosmos/ormpp> | commit `acf23989f8e40f2c8671eaf209d014164f2d6319`(master,2026-08-29);`ormpp/version.hpp` = 0.2.0 |
|
||||
| iguana(编译期反射,随 ormpp 内置) | `iguana/**` | 随 ormpp 内置;独立仓库 <https://github.com/qicosmos/iguana> | `iguana/version.hpp` = 1.0.5 |
|
||||
| SQLite amalgamation | `thirdparty/sqlite3/{sqlite3.c,sqlite3.h,sqlite3ext.h}` | 随 ormpp 内置;<https://sqlite.org> | SQLite 3.49.1(`sqlite3.h` 的 `SQLITE_VERSION`),公共领域(Public Domain) |
|
||||
| frozen(iguana 的 constexpr 容器依赖,随 ormpp 内置) | `frozen/**` | 随 ormpp 内置;独立仓库 <https://github.com/serge-sans-paille/frozen> | 18 个文件 / 86,136 字节(`iguana/field_reflection.hpp` 需要 `frozen/string.h`、`frozen/unordered_map.h`) |
|
||||
| 许可证 | `LICENSE`、`iguana/LICENSE` | — | 均为 Apache License 2.0;与上游 git blob 逐字节一致(`261eeb9e…` / `8dada3ed…`) |
|
||||
|
||||
## 复现步骤(重新获取/升级时执行)
|
||||
|
||||
```bat
|
||||
git -c core.autocrlf=false clone --depth 1 https://github.com/qicosmos/ormpp.git <tmp-ormpp>
|
||||
git -c core.autocrlf=false clone --depth 1 https://github.com/qicosmos/iguana.git <tmp-iguana>
|
||||
|
||||
:: 仅拷贝以下内容到本目录(其余 tests/example/cfg/docs/lang/shell.c 等均不需要):
|
||||
:: <tmp-ormpp>\ormpp\*.hpp + ormpp\pg_types.h -> third_party\ormpp\ormpp\
|
||||
:: <tmp-ormpp>\iguana\ -> third_party\ormpp\iguana\
|
||||
:: <tmp-ormpp>\frozen\ -> third_party\ormpp\frozen\ (iguana 依赖)
|
||||
:: <tmp-ormpp>\thirdparty\sqlite3\sqlite3.c|sqlite3.h|sqlite3ext.h -> third_party\ormpp\thirdparty\sqlite3\
|
||||
:: <tmp-ormpp>\LICENSE -> third_party\ormpp\LICENSE
|
||||
:: <tmp-iguana>\LICENSE -> third_party\ormpp\iguana\LICENSE
|
||||
```
|
||||
|
||||
注意:本机全局 `core.autocrlf=true` 会把源码转成 CRLF(`sqlite3.c` 由 9,461,193 字节变为 9,728,476 字节),因此克隆时必须显式 `-c core.autocrlf=false`。校验方式:与上游 blob 大小比对(`sqlite3.c` = 9,461,193、`dbng.hpp` = 11,681、`iguana/common.hpp` = 45,850)。
|
||||
|
||||
## 使用方式与约束
|
||||
|
||||
- **只被**可选目标 `rdp_record` 使用(见根目录 `CMakeLists.txt` 的 `RDP_DATA_RECORD` 选项);该目标以 **C++20** 编译(ormpp 的 `cmake/build.cmake` 强制 `CMAKE_CXX_STANDARD 20`),**雷达处理代码仍为 C++17**。
|
||||
- 包含路径按上游约定给全(4 个):`third_party/ormpp`(`iguana/...`)、`third_party/ormpp/iguana`、`third_party/ormpp/ormpp`(`dbng.hpp`、`sqlite.hpp`)、`third_party/ormpp/thirdparty/sqlite3`(`sqlite3.h`)。
|
||||
- 告警处理:`sqlite3.c` 以 `/w` 单独编译;首个包含第三方头的本项目文件(`data_process_class_dll/rdp_record.cpp`)用 `#pragma warning(push, 0)` / `(pop)` 隔离模板告警——这两处是 README 4.5「0 告警」的唯一例外,范围仅限本目录代码。
|
||||
- **升级方式**:整目录替换为新的上游快照,同步更新本文件的 commit / 版本号 / blob 校验值,然后必须重新验证:`-DRDP_DATA_RECORD=ON` 构建通过(0 告警)+ 记录功能写入校验(见 `docs/data_record_debug.md`)。
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
target_sources(frozen-headers INTERFACE
|
||||
"${prefix}/frozen/algorithm.h"
|
||||
"${prefix}/frozen/map.h"
|
||||
"${prefix}/frozen/random.h"
|
||||
"${prefix}/frozen/set.h"
|
||||
"${prefix}/frozen/string.h"
|
||||
"${prefix}/frozen/unordered_map.h"
|
||||
"${prefix}/frozen/unordered_set.h"
|
||||
"${prefix}/frozen/bits/algorithms.h"
|
||||
"${prefix}/frozen/bits/basic_types.h"
|
||||
"${prefix}/frozen/bits/elsa.h"
|
||||
"${prefix}/frozen/bits/pmh.h")
|
||||
Vendored
+203
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_LETITGO_ALGORITHM_H
|
||||
#define FROZEN_LETITGO_ALGORITHM_H
|
||||
|
||||
#include "frozen/bits/basic_types.h"
|
||||
#include "frozen/bits/version.h"
|
||||
#include "frozen/string.h"
|
||||
|
||||
namespace frozen {
|
||||
|
||||
// 'search' implementation if C++17 is not available
|
||||
// https://en.cppreference.com/w/cpp/algorithm/search
|
||||
template <class ForwardIterator, class Searcher>
|
||||
ForwardIterator search(ForwardIterator first, ForwardIterator last,
|
||||
const Searcher &searcher) {
|
||||
return searcher(first, last).first;
|
||||
}
|
||||
|
||||
// text book implementation from
|
||||
// https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
|
||||
|
||||
template <std::size_t size>
|
||||
class knuth_morris_pratt_searcher {
|
||||
bits::carray<std::ptrdiff_t, size> step_;
|
||||
bits::carray<char, size> needle_;
|
||||
|
||||
static constexpr bits::carray<std::ptrdiff_t, size> build_kmp_cache(
|
||||
char const (&needle)[size + 1]) {
|
||||
std::ptrdiff_t cnd = 0;
|
||||
bits::carray<std::ptrdiff_t, size> cache;
|
||||
|
||||
cache.fill(-1);
|
||||
for (std::size_t pos = 1; pos < size; ++pos) {
|
||||
if (needle[pos] == needle[cnd]) {
|
||||
cache[pos] = cache[cnd];
|
||||
cnd += 1;
|
||||
}
|
||||
else {
|
||||
cache[pos] = cnd;
|
||||
cnd = cache[cnd];
|
||||
while (cnd >= 0 && needle[pos] != needle[cnd]) cnd = cache[cnd];
|
||||
cnd += 1;
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr knuth_morris_pratt_searcher(char const (&needle)[size + 1])
|
||||
: step_{build_kmp_cache(needle)}, needle_(needle) {}
|
||||
|
||||
template <class ForwardIterator>
|
||||
constexpr std::pair<ForwardIterator, ForwardIterator> operator()(
|
||||
ForwardIterator first, ForwardIterator last) const {
|
||||
std::size_t i = 0;
|
||||
ForwardIterator iter = first;
|
||||
while (iter != last) {
|
||||
if (needle_[i] == *iter) {
|
||||
if (i == (size - 1))
|
||||
return {iter - i, iter - i + size};
|
||||
++i;
|
||||
++iter;
|
||||
}
|
||||
else {
|
||||
if (step_[i] > -1) {
|
||||
i = step_[i];
|
||||
}
|
||||
else {
|
||||
++iter;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {last, last};
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t N>
|
||||
constexpr knuth_morris_pratt_searcher<N - 1> make_knuth_morris_pratt_searcher(
|
||||
char const (&needle)[N]) {
|
||||
return {needle};
|
||||
}
|
||||
|
||||
// text book implementation from
|
||||
// https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm
|
||||
|
||||
template <std::size_t size>
|
||||
class boyer_moore_searcher {
|
||||
using skip_table_type = bits::carray<std::ptrdiff_t, sizeof(char) << 8>;
|
||||
using suffix_table_type = bits::carray<std::ptrdiff_t, size>;
|
||||
|
||||
skip_table_type skip_table_;
|
||||
suffix_table_type suffix_table_;
|
||||
bits::carray<char, size> needle_;
|
||||
|
||||
constexpr auto build_skip_table(char const (&needle)[size + 1]) {
|
||||
skip_table_type skip_table;
|
||||
|
||||
skip_table.fill(size);
|
||||
for (std::size_t i = 0; i < size - 1; ++i) skip_table[needle[i]] -= i + 1;
|
||||
return skip_table;
|
||||
}
|
||||
|
||||
constexpr bool is_prefix(char const (&needle)[size + 1], std::size_t pos) {
|
||||
std::size_t suffixlen = size - pos;
|
||||
|
||||
for (std::size_t i = 0; i < suffixlen; i++) {
|
||||
if (needle[i] != needle[pos + i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr std::size_t suffix_length(char const (&needle)[size + 1],
|
||||
std::size_t pos) {
|
||||
// increment suffix length slen to the first mismatch or beginning
|
||||
// of the word
|
||||
for (std::size_t slen = 0; slen < pos; slen++)
|
||||
if (needle[pos - slen] != needle[size - 1 - slen])
|
||||
return slen;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
constexpr auto build_suffix_table(char const (&needle)[size + 1]) {
|
||||
suffix_table_type suffix;
|
||||
std::ptrdiff_t last_prefix_index = size - 1;
|
||||
|
||||
// first loop
|
||||
for (std::ptrdiff_t p = size - 1; p >= 0; p--) {
|
||||
if (is_prefix(needle, p + 1))
|
||||
last_prefix_index = p + 1;
|
||||
|
||||
suffix[p] = last_prefix_index + (size - 1 - p);
|
||||
}
|
||||
|
||||
// second loop
|
||||
for (std::size_t p = 0; p < size - 1; p++) {
|
||||
auto slen = suffix_length(needle, p);
|
||||
if (needle[p - slen] != needle[size - 1 - slen])
|
||||
suffix[size - 1 - slen] = size - 1 - p + slen;
|
||||
}
|
||||
return suffix;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr boyer_moore_searcher(char const (&needle)[size + 1])
|
||||
: skip_table_{build_skip_table(needle)},
|
||||
suffix_table_{build_suffix_table(needle)},
|
||||
needle_(needle) {}
|
||||
|
||||
template <class ForwardIterator>
|
||||
constexpr std::pair<ForwardIterator, ForwardIterator> operator()(
|
||||
ForwardIterator first, ForwardIterator last) const {
|
||||
if (size == 0)
|
||||
return {first, first + size};
|
||||
|
||||
ForwardIterator iter = first + size - 1;
|
||||
while (iter < last) {
|
||||
std::ptrdiff_t j = size - 1;
|
||||
while (j > 0 && (*iter == needle_[j])) {
|
||||
--iter;
|
||||
--j;
|
||||
}
|
||||
if (*iter == needle_[0])
|
||||
return {iter, iter + size};
|
||||
|
||||
iter += std::max(skip_table_[*iter], suffix_table_[j]);
|
||||
}
|
||||
return {last, last + size};
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t N>
|
||||
constexpr boyer_moore_searcher<N - 1> make_boyer_moore_searcher(
|
||||
char const (&needle)[N]) {
|
||||
return {needle};
|
||||
}
|
||||
|
||||
} // namespace frozen
|
||||
|
||||
#endif
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_LETITGO_BITS_ALGORITHMS_H
|
||||
#define FROZEN_LETITGO_BITS_ALGORITHMS_H
|
||||
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
|
||||
#include "frozen/bits/basic_types.h"
|
||||
|
||||
namespace frozen {
|
||||
|
||||
namespace bits {
|
||||
|
||||
auto constexpr next_highest_power_of_two(std::size_t v) {
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||
constexpr auto trip_count = std::numeric_limits<decltype(v)>::digits;
|
||||
v--;
|
||||
for (std::size_t i = 1; i < trip_count; i <<= 1) v |= v >> i;
|
||||
v++;
|
||||
return v;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto constexpr log(T v) {
|
||||
std::size_t n = 0;
|
||||
while (v > 1) {
|
||||
n += 1;
|
||||
v >>= 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
constexpr std::size_t bit_weight(std::size_t n) {
|
||||
return (n <= 8 * sizeof(unsigned int)) + (n <= 8 * sizeof(unsigned long)) +
|
||||
(n <= 8 * sizeof(unsigned long long)) + (n <= 128);
|
||||
}
|
||||
|
||||
unsigned int select_uint_least(std::integral_constant<std::size_t, 4>);
|
||||
unsigned long select_uint_least(std::integral_constant<std::size_t, 3>);
|
||||
unsigned long long select_uint_least(std::integral_constant<std::size_t, 2>);
|
||||
template <std::size_t N>
|
||||
unsigned long long select_uint_least(std::integral_constant<std::size_t, N>) {
|
||||
static_assert(N < 2, "unsupported type size");
|
||||
return {};
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
using select_uint_least_t = decltype(select_uint_least(
|
||||
std::integral_constant<std::size_t, bit_weight(N)>()));
|
||||
|
||||
template <typename Iter, typename Compare>
|
||||
constexpr auto min_element(Iter begin, const Iter end, Compare const &compare) {
|
||||
auto result = begin;
|
||||
while (begin != end) {
|
||||
if (compare(*begin, *result)) {
|
||||
result = begin;
|
||||
}
|
||||
++begin;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
constexpr void cswap(T &a, T &b) {
|
||||
auto tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
constexpr void cswap(std::pair<T, U> &a, std::pair<T, U> &b) {
|
||||
cswap(a.first, b.first);
|
||||
cswap(a.second, b.second);
|
||||
}
|
||||
|
||||
template <class... Tys, std::size_t... Is>
|
||||
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b,
|
||||
std::index_sequence<Is...>) {
|
||||
using swallow = int[];
|
||||
(void)swallow{(cswap(std::get<Is>(a), std::get<Is>(b)), 0)...};
|
||||
}
|
||||
|
||||
template <class... Tys>
|
||||
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b) {
|
||||
cswap(a, b, std::make_index_sequence<sizeof...(Tys)>());
|
||||
}
|
||||
|
||||
template <typename Iterator, class Compare>
|
||||
constexpr Iterator partition(Iterator left, Iterator right,
|
||||
Compare const &compare) {
|
||||
auto pivot = left + (right - left) / 2;
|
||||
auto value = *pivot;
|
||||
cswap(*right, *pivot);
|
||||
for (auto it = left; 0 < right - it; ++it) {
|
||||
if (compare(*it, value)) {
|
||||
cswap(*it, *left);
|
||||
left++;
|
||||
}
|
||||
}
|
||||
cswap(*right, *left);
|
||||
return left;
|
||||
}
|
||||
|
||||
template <typename Iterator, class Compare>
|
||||
constexpr void quicksort(Iterator left, Iterator right,
|
||||
Compare const &compare) {
|
||||
while (0 < right - left) {
|
||||
auto new_pivot = bits::partition(left, right, compare);
|
||||
quicksort(left, new_pivot, compare);
|
||||
left = new_pivot + 1;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N, class Compare>
|
||||
constexpr bits::carray<T, N> quicksort(bits::carray<T, N> const &array,
|
||||
Compare const &compare) {
|
||||
bits::carray<T, N> res = array;
|
||||
quicksort(res.begin(), res.end() - 1, compare);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class T, class Compare>
|
||||
struct LowerBound {
|
||||
T const &value_;
|
||||
Compare const &compare_;
|
||||
constexpr LowerBound(T const &value, Compare const &compare)
|
||||
: value_(value), compare_(compare) {}
|
||||
|
||||
template <class ForwardIt>
|
||||
inline constexpr ForwardIt doit_fast(ForwardIt first,
|
||||
std::integral_constant<std::size_t, 0>) {
|
||||
return first;
|
||||
}
|
||||
|
||||
template <class ForwardIt, std::size_t N>
|
||||
inline constexpr ForwardIt doit_fast(ForwardIt first,
|
||||
std::integral_constant<std::size_t, N>) {
|
||||
auto constexpr step = N / 2;
|
||||
static_assert(N / 2 == N - N / 2 - 1, "power of two minus 1");
|
||||
auto it = first + step;
|
||||
auto next_it = compare_(*it, value_) ? it + 1 : first;
|
||||
return doit_fast(next_it, std::integral_constant<std::size_t, N / 2>{});
|
||||
}
|
||||
|
||||
template <class ForwardIt, std::size_t N>
|
||||
inline constexpr ForwardIt doitfirst(ForwardIt first,
|
||||
std::integral_constant<std::size_t, N>,
|
||||
std::integral_constant<bool, true>) {
|
||||
return doit_fast(first, std::integral_constant<std::size_t, N>{});
|
||||
}
|
||||
|
||||
template <class ForwardIt, std::size_t N>
|
||||
inline constexpr ForwardIt doitfirst(ForwardIt first,
|
||||
std::integral_constant<std::size_t, N>,
|
||||
std::integral_constant<bool, false>) {
|
||||
auto constexpr next_power = next_highest_power_of_two(N);
|
||||
auto constexpr next_start = next_power / 2 - 1;
|
||||
auto it = first + next_start;
|
||||
if (compare_(*it, value_)) {
|
||||
auto constexpr next = N - next_start - 1;
|
||||
return doitfirst(
|
||||
it + 1, std::integral_constant<std::size_t, next>{},
|
||||
std::integral_constant<bool, next_highest_power_of_two(next) - 1 ==
|
||||
next>{});
|
||||
}
|
||||
else
|
||||
return doit_fast(first,
|
||||
std::integral_constant<std::size_t, next_start>{});
|
||||
}
|
||||
|
||||
template <class ForwardIt>
|
||||
inline constexpr ForwardIt doitfirst(ForwardIt first,
|
||||
std::integral_constant<std::size_t, 1>,
|
||||
std::integral_constant<bool, false>) {
|
||||
return doit_fast(first, std::integral_constant<std::size_t, 1>{});
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t N, class ForwardIt, class T, class Compare>
|
||||
constexpr ForwardIt lower_bound(ForwardIt first, const T &value,
|
||||
Compare const &compare) {
|
||||
return LowerBound<T, Compare>{value, compare}.doitfirst(
|
||||
first, std::integral_constant<std::size_t, N>{},
|
||||
std::integral_constant<bool, next_highest_power_of_two(N) - 1 == N>{});
|
||||
}
|
||||
|
||||
template <std::size_t N, class Compare, class ForwardIt, class T>
|
||||
constexpr bool binary_search(ForwardIt first, const T &value,
|
||||
Compare const &compare) {
|
||||
ForwardIt where = lower_bound<N>(first, value, compare);
|
||||
return (!(where == first + N) && !(compare(value, *where)));
|
||||
}
|
||||
|
||||
template <class InputIt1, class InputIt2>
|
||||
constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) {
|
||||
for (; first1 != last1; ++first1, ++first2) {
|
||||
if (!(*first1 == *first2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class InputIt1, class InputIt2>
|
||||
constexpr bool lexicographical_compare(InputIt1 first1, InputIt1 last1,
|
||||
InputIt2 first2, InputIt2 last2) {
|
||||
for (; (first1 != last1) && (first2 != last2); ++first1, ++first2) {
|
||||
if (*first1 < *first2)
|
||||
return true;
|
||||
if (*first2 < *first1)
|
||||
return false;
|
||||
}
|
||||
return (first1 == last1) && (first2 != last2);
|
||||
}
|
||||
|
||||
} // namespace bits
|
||||
} // namespace frozen
|
||||
|
||||
#endif
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_LETITGO_BASIC_TYPES_H
|
||||
#define FROZEN_LETITGO_BASIC_TYPES_H
|
||||
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "frozen/bits/exceptions.h"
|
||||
|
||||
namespace frozen {
|
||||
|
||||
namespace bits {
|
||||
|
||||
// used as a fake argument for frozen::make_set and frozen::make_map in the case
|
||||
// of N=0
|
||||
struct ignored_arg {};
|
||||
|
||||
template <class T, std::size_t N>
|
||||
class cvector {
|
||||
T data[N] = {}; // zero-initialization for scalar type T, default-initialized
|
||||
// otherwise
|
||||
std::size_t dsize = 0;
|
||||
|
||||
public:
|
||||
// Container typdefs
|
||||
using value_type = T;
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using pointer = value_type *;
|
||||
using const_pointer = const value_type *;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
// Constructors
|
||||
constexpr cvector(void) = default;
|
||||
constexpr cvector(size_type count, const T &value) : dsize(count) {
|
||||
for (std::size_t i = 0; i < N; ++i) data[i] = value;
|
||||
}
|
||||
|
||||
// Iterators
|
||||
constexpr iterator begin() noexcept { return data; }
|
||||
constexpr iterator end() noexcept { return data + dsize; }
|
||||
|
||||
// Capacity
|
||||
constexpr size_type size() const { return dsize; }
|
||||
|
||||
// Element access
|
||||
constexpr reference operator[](std::size_t index) { return data[index]; }
|
||||
constexpr const_reference operator[](std::size_t index) const {
|
||||
return data[index];
|
||||
}
|
||||
|
||||
constexpr reference back() { return data[dsize - 1]; }
|
||||
constexpr const_reference back() const { return data[dsize - 1]; }
|
||||
|
||||
// Modifiers
|
||||
constexpr void push_back(const T &a) { data[dsize++] = a; }
|
||||
constexpr void push_back(T &&a) { data[dsize++] = std::move(a); }
|
||||
constexpr void pop_back() { --dsize; }
|
||||
|
||||
constexpr void clear() { dsize = 0; }
|
||||
};
|
||||
|
||||
template <class T, std::size_t N>
|
||||
class carray {
|
||||
T data_[N] = {}; // zero-initialization for scalar type T,
|
||||
// default-initialized otherwise
|
||||
|
||||
template <std::size_t M, std::size_t... I>
|
||||
constexpr carray(T const (&init)[M], std::index_sequence<I...>)
|
||||
: data_{init[I]...} {}
|
||||
template <class Iter, std::size_t... I>
|
||||
constexpr carray(Iter iter, std::index_sequence<I...>)
|
||||
: data_{((void)I, *iter++)...} {}
|
||||
|
||||
public:
|
||||
// Container typdefs
|
||||
using value_type = T;
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using pointer = value_type *;
|
||||
using const_pointer = const value_type *;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
// Constructors
|
||||
constexpr carray(void) = default;
|
||||
template <std::size_t M>
|
||||
constexpr carray(T const (&init)[M])
|
||||
: carray(init, std::make_index_sequence<N>()) {
|
||||
static_assert(M >= N, "Cannot initialize a carray with an smaller array");
|
||||
}
|
||||
template <std::size_t M>
|
||||
constexpr carray(std::array<T, M> const &init)
|
||||
: carray(&init[0], std::make_index_sequence<N>()) {
|
||||
static_assert(M >= N, "Cannot initialize a carray with an smaller array");
|
||||
}
|
||||
constexpr carray(std::initializer_list<T> init)
|
||||
: carray(init.begin(), std::make_index_sequence<N>()) {
|
||||
// clang & gcc doesn't recognize init.size() as a constexpr
|
||||
// static_assert(init.size() >= N, "Cannot initialize a carray with an
|
||||
// smaller initializer list");
|
||||
}
|
||||
|
||||
// Iterators
|
||||
constexpr iterator begin() noexcept { return data_; }
|
||||
constexpr const_iterator begin() const noexcept { return data_; }
|
||||
constexpr const_iterator cbegin() const noexcept { return data_; }
|
||||
constexpr iterator end() noexcept { return data_ + N; }
|
||||
constexpr const_iterator end() const noexcept { return data_ + N; }
|
||||
constexpr const_iterator cend() const noexcept { return data_ + N; }
|
||||
|
||||
constexpr reverse_iterator rbegin() noexcept {
|
||||
return reverse_iterator(end());
|
||||
}
|
||||
constexpr const_reverse_iterator rbegin() const noexcept {
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
constexpr const_reverse_iterator crbegin() const noexcept {
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
constexpr reverse_iterator rend() noexcept {
|
||||
return reverse_iterator(begin());
|
||||
}
|
||||
constexpr const_reverse_iterator rend() const noexcept {
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
constexpr const_reverse_iterator crend() const noexcept {
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
// Capacity
|
||||
constexpr size_type size() const { return N; }
|
||||
constexpr size_type max_size() const { return N; }
|
||||
|
||||
// Element access
|
||||
constexpr reference operator[](std::size_t index) { return data_[index]; }
|
||||
constexpr const_reference operator[](std::size_t index) const {
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
constexpr reference at(std::size_t index) {
|
||||
if (index > N)
|
||||
FROZEN_THROW_OR_ABORT(
|
||||
std::out_of_range("Index (" + std::to_string(index) +
|
||||
") out of bound (" + std::to_string(N) + ')'));
|
||||
return data_[index];
|
||||
}
|
||||
constexpr const_reference at(std::size_t index) const {
|
||||
if (index > N)
|
||||
FROZEN_THROW_OR_ABORT(
|
||||
std::out_of_range("Index (" + std::to_string(index) +
|
||||
") out of bound (" + std::to_string(N) + ')'));
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
constexpr reference front() { return data_[0]; }
|
||||
constexpr const_reference front() const { return data_[0]; }
|
||||
|
||||
constexpr reference back() { return data_[N - 1]; }
|
||||
constexpr const_reference back() const { return data_[N - 1]; }
|
||||
|
||||
constexpr value_type *data() noexcept { return data_; }
|
||||
constexpr const value_type *data() const noexcept { return data_; }
|
||||
|
||||
// Modifiers
|
||||
constexpr void fill(const value_type &val) {
|
||||
for (std::size_t i = 0; i < N; ++i) data_[i] = val;
|
||||
}
|
||||
};
|
||||
template <class T>
|
||||
class carray<T, 0> {
|
||||
public:
|
||||
// Container typdefs
|
||||
using value_type = T;
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using pointer = value_type *;
|
||||
using const_pointer = const value_type *;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
// Constructors
|
||||
constexpr carray(void) = default;
|
||||
};
|
||||
|
||||
} // namespace bits
|
||||
|
||||
} // namespace frozen
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_LETITGO_CONSTEXPR_ASSERT_H
|
||||
#define FROZEN_LETITGO_CONSTEXPR_ASSERT_H
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
// FIXME: find a way to implement that correctly for msvc
|
||||
#define constexpr_assert(cond, msg)
|
||||
|
||||
#else
|
||||
|
||||
#define constexpr_assert(cond, msg) assert(cond &&msg);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_LETITGO_DEFINES_H
|
||||
#define FROZEN_LETITGO_DEFINES_H
|
||||
|
||||
#if defined(_MSVC_LANG) && \
|
||||
!(defined(__EDG__) && defined(__clang__)) // TRANSITION, VSO#273681
|
||||
#define FROZEN_LETITGO_IS_MSVC
|
||||
#endif
|
||||
|
||||
// Code taken from
|
||||
// https://stackoverflow.com/questions/43639122/which-values-can-msvc-lang-have
|
||||
#if defined(FROZEN_LETITGO_IS_MSVC)
|
||||
#if _MSVC_LANG > 201402
|
||||
#define FROZEN_LETITGO_HAS_CXX17 1
|
||||
#else /* _MSVC_LANG > 201402 */
|
||||
#define FROZEN_LETITGO_HAS_CXX17 0
|
||||
#endif /* _MSVC_LANG > 201402 */
|
||||
#else /* _MSVC_LANG etc. */
|
||||
#if __cplusplus > 201402
|
||||
#define FROZEN_LETITGO_HAS_CXX17 1
|
||||
#else /* __cplusplus > 201402 */
|
||||
#define FROZEN_LETITGO_HAS_CXX17 0
|
||||
#endif /* __cplusplus > 201402 */
|
||||
#endif /* _MSVC_LANG etc. */
|
||||
// End if taken code
|
||||
|
||||
#if FROZEN_LETITGO_HAS_CXX17 == 1 && defined(FROZEN_LETITGO_IS_MSVC)
|
||||
#define FROZEN_LETITGO_HAS_STRING_VIEW // We assume Visual Studio always has
|
||||
// string_view in C++17
|
||||
#else
|
||||
#if FROZEN_LETITGO_HAS_CXX17 == 1 && __has_include(<string_view>)
|
||||
#define FROZEN_LETITGO_HAS_STRING_VIEW
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cpp_char8_t
|
||||
#define FROZEN_LETITGO_HAS_CHAR8T
|
||||
#endif
|
||||
|
||||
#if __cpp_deduction_guides >= 201703L
|
||||
#define FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
|
||||
#endif
|
||||
|
||||
#if __cpp_lib_constexpr_string >= 201907L
|
||||
#define FROZEN_LETITGO_HAS_CONSTEXPR_STRING
|
||||
#endif
|
||||
|
||||
#endif // FROZEN_LETITGO_DEFINES_H
|
||||
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_LETITGO_ELSA_H
|
||||
#define FROZEN_LETITGO_ELSA_H
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace frozen {
|
||||
|
||||
template <class T = void>
|
||||
struct elsa {
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value,
|
||||
"only supports integral types, specialize for other types");
|
||||
|
||||
constexpr std::size_t operator()(T const &value, std::size_t seed) const {
|
||||
std::size_t key = seed ^ static_cast<std::size_t>(value);
|
||||
key = (~key) + (key << 21); // key = (key << 21) - key - 1;
|
||||
key = key ^ (key >> 24);
|
||||
key = (key + (key << 3)) + (key << 8); // key * 265
|
||||
key = key ^ (key >> 14);
|
||||
key = (key + (key << 2)) + (key << 4); // key * 21
|
||||
key = key ^ (key >> 28);
|
||||
key = key + (key << 31);
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct elsa<void> {
|
||||
template <class T>
|
||||
constexpr std::size_t operator()(T const &value, std::size_t seed) const {
|
||||
return elsa<T>{}(value, seed);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using anna = elsa<T>;
|
||||
} // namespace frozen
|
||||
|
||||
#endif
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
#ifndef FROZEN_LETITGO_BITS_ELSA_STD_H
|
||||
#define FROZEN_LETITGO_BITS_ELSA_STD_H
|
||||
|
||||
#include "elsa.h"
|
||||
#include "hash_string.h"
|
||||
|
||||
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
|
||||
#include <string_view>
|
||||
#endif
|
||||
#include <string>
|
||||
|
||||
namespace frozen {
|
||||
|
||||
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
|
||||
|
||||
template <typename CharT>
|
||||
struct elsa<std::basic_string_view<CharT>> {
|
||||
constexpr std::size_t operator()(
|
||||
const std::basic_string_view<CharT> &value) const {
|
||||
return hash_string(value);
|
||||
}
|
||||
constexpr std::size_t operator()(const std::basic_string_view<CharT> &value,
|
||||
std::size_t seed) const {
|
||||
return hash_string(value, seed);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
template <typename CharT>
|
||||
struct elsa<std::basic_string<CharT>> {
|
||||
constexpr std::size_t operator()(
|
||||
const std::basic_string<CharT> &value) const {
|
||||
return hash_string(value);
|
||||
}
|
||||
constexpr std::size_t operator()(const std::basic_string<CharT> &value,
|
||||
std::size_t seed) const {
|
||||
return hash_string(value, seed);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace frozen
|
||||
|
||||
#endif // FROZEN_LETITGO_BITS_ELSA_STD_H
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_LETITGO_EXCEPTIONS_H
|
||||
#define FROZEN_LETITGO_EXCEPTIONS_H
|
||||
|
||||
#if defined(FROZEN_NO_EXCEPTIONS) || \
|
||||
(defined(_MSC_VER) && !defined(_CPPUNWIND)) || \
|
||||
(!defined(_MSC_VER) && !defined(__cpp_exceptions))
|
||||
|
||||
#include <cstdlib>
|
||||
#define FROZEN_THROW_OR_ABORT(_) std::abort()
|
||||
|
||||
#else
|
||||
|
||||
#include <stdexcept>
|
||||
#define FROZEN_THROW_OR_ABORT(err) throw err
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
#ifndef FROZEN_LETITGO_BITS_HASH_STRING_H
|
||||
#define FROZEN_LETITGO_BITS_HASH_STRING_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace frozen {
|
||||
|
||||
template <typename String>
|
||||
constexpr std::size_t hash_string(const String &value) {
|
||||
std::size_t d = 5381;
|
||||
for (const auto &c : value) d = d * 33 + static_cast<size_t>(c);
|
||||
return d;
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
||||
// With the lowest bits removed, based on experimental setup.
|
||||
template <typename String>
|
||||
constexpr std::size_t hash_string(const String &value, std::size_t seed) {
|
||||
std::size_t d = (0x811c9dc5 ^ seed) * static_cast<size_t>(0x01000193);
|
||||
for (const auto &c : value)
|
||||
d = (d ^ static_cast<size_t>(c)) * static_cast<size_t>(0x01000193);
|
||||
return d >> 8;
|
||||
}
|
||||
|
||||
} // namespace frozen
|
||||
|
||||
#endif // FROZEN_LETITGO_BITS_HASH_STRING_H
|
||||
Vendored
+259
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
// inspired from http://stevehanov.ca/blog/index.php?id=119
|
||||
#ifndef FROZEN_LETITGO_PMH_H
|
||||
#define FROZEN_LETITGO_PMH_H
|
||||
|
||||
#include <array>
|
||||
#include <limits>
|
||||
|
||||
#include "frozen/bits/algorithms.h"
|
||||
#include "frozen/bits/basic_types.h"
|
||||
|
||||
namespace frozen {
|
||||
|
||||
namespace bits {
|
||||
|
||||
// Function object for sorting buckets in decreasing order of size
|
||||
struct bucket_size_compare {
|
||||
template <typename B>
|
||||
bool constexpr operator()(B const &b0, B const &b1) const {
|
||||
return b0.size() > b1.size();
|
||||
}
|
||||
};
|
||||
|
||||
// Step One in pmh routine is to take all items and hash them into buckets,
|
||||
// with some collisions. Then process those buckets further to build a perfect
|
||||
// hash function.
|
||||
// pmh_buckets represents the initial placement into buckets.
|
||||
|
||||
template <size_t M>
|
||||
struct pmh_buckets {
|
||||
// Step 0: Bucket max is 2 * sqrt M
|
||||
// TODO: Come up with justification for this, should it not be O(log M)?
|
||||
static constexpr auto bucket_max = 2 * (1u << (log(M) / 2));
|
||||
|
||||
using bucket_t = cvector<std::size_t, bucket_max>;
|
||||
carray<bucket_t, M> buckets;
|
||||
uint64_t seed;
|
||||
|
||||
// Represents a reference to a bucket. This is used because the buckets
|
||||
// have to be sorted, but buckets are big, making it slower than sorting refs
|
||||
struct bucket_ref {
|
||||
unsigned hash;
|
||||
const bucket_t *ptr;
|
||||
|
||||
// Forward some interface of bucket
|
||||
using value_type = typename bucket_t::value_type;
|
||||
using const_iterator = typename bucket_t::const_iterator;
|
||||
|
||||
constexpr auto size() const { return ptr->size(); }
|
||||
constexpr const auto &operator[](std::size_t idx) const {
|
||||
return (*ptr)[idx];
|
||||
}
|
||||
constexpr auto begin() const { return ptr->begin(); }
|
||||
constexpr auto end() const { return ptr->end(); }
|
||||
};
|
||||
|
||||
// Make a bucket_ref for each bucket
|
||||
template <std::size_t... Is>
|
||||
carray<bucket_ref, M> constexpr make_bucket_refs(
|
||||
std::index_sequence<Is...>) const {
|
||||
return {{bucket_ref{Is, &buckets[Is]}...}};
|
||||
}
|
||||
|
||||
// Makes a bucket_ref for each bucket and sorts them by size
|
||||
carray<bucket_ref, M> constexpr get_sorted_buckets() const {
|
||||
carray<bucket_ref, M> result{
|
||||
this->make_bucket_refs(std::make_index_sequence<M>())};
|
||||
bits::quicksort(result.begin(), result.end() - 1, bucket_size_compare{});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t M, class Item, size_t N, class Hash, class Key, class PRG>
|
||||
pmh_buckets<M> constexpr make_pmh_buckets(const carray<Item, N> &items,
|
||||
Hash const &hash, Key const &key,
|
||||
PRG &prg) {
|
||||
using result_t = pmh_buckets<M>;
|
||||
result_t result{};
|
||||
bool rejected = false;
|
||||
// Continue until all items are placed without exceeding bucket_max
|
||||
while (1) {
|
||||
for (auto &b : result.buckets) {
|
||||
b.clear();
|
||||
}
|
||||
result.seed = prg();
|
||||
rejected = false;
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
auto &bucket =
|
||||
result.buckets[hash(key(items[i]), static_cast<size_t>(result.seed)) %
|
||||
M];
|
||||
if (bucket.size() >= result_t::bucket_max) {
|
||||
rejected = true;
|
||||
break;
|
||||
}
|
||||
bucket.push_back(i);
|
||||
}
|
||||
if (!rejected) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if an item appears in a cvector
|
||||
template <class T, size_t N>
|
||||
constexpr bool all_different_from(cvector<T, N> &data, T &a) {
|
||||
for (std::size_t i = 0; i < data.size(); ++i)
|
||||
if (data[i] == a)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Represents either an index to a data item array, or a seed to be used with
|
||||
// a hasher. Seed must have high bit of 1, value has high bit of zero.
|
||||
struct seed_or_index {
|
||||
using value_type = uint64_t;
|
||||
|
||||
private:
|
||||
static constexpr value_type MINUS_ONE =
|
||||
(std::numeric_limits<value_type>::max)();
|
||||
static constexpr value_type HIGH_BIT = ~(MINUS_ONE >> 1);
|
||||
|
||||
value_type value_ = 0;
|
||||
|
||||
public:
|
||||
constexpr value_type value() const { return value_; }
|
||||
constexpr bool is_seed() const { return value_ & HIGH_BIT; }
|
||||
|
||||
constexpr seed_or_index(bool is_seed, value_type value)
|
||||
: value_(is_seed ? (value | HIGH_BIT) : (value & ~HIGH_BIT)) {}
|
||||
|
||||
constexpr seed_or_index() = default;
|
||||
constexpr seed_or_index(const seed_or_index &) = default;
|
||||
constexpr seed_or_index &operator=(const seed_or_index &) = default;
|
||||
};
|
||||
|
||||
// Represents the perfect hash function created by pmh algorithm
|
||||
template <std::size_t M, class Hasher>
|
||||
struct pmh_tables {
|
||||
uint64_t first_seed_;
|
||||
carray<seed_or_index, M> first_table_;
|
||||
carray<std::size_t, M> second_table_;
|
||||
Hasher hash_;
|
||||
|
||||
template <typename KeyType>
|
||||
constexpr std::size_t lookup(const KeyType &key) const {
|
||||
return lookup(key, hash_);
|
||||
}
|
||||
|
||||
// Looks up a given key, to find its expected index in carray<Item, N>
|
||||
// Always returns a valid index, must use KeyEqual test after to confirm.
|
||||
template <typename KeyType, typename HasherType>
|
||||
constexpr std::size_t lookup(const KeyType &key,
|
||||
const HasherType &hasher) const {
|
||||
auto const d =
|
||||
first_table_[hasher(key, static_cast<size_t>(first_seed_)) % M];
|
||||
if (!d.is_seed()) {
|
||||
return static_cast<std::size_t>(d.value());
|
||||
} // this is narrowing uint64 -> size_t but should be fine
|
||||
else {
|
||||
return second_table_[hasher(key, static_cast<std::size_t>(d.value())) %
|
||||
M];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Make pmh tables for given items, hash function, prg, etc.
|
||||
template <std::size_t M, class Item, std::size_t N, class Hash, class Key,
|
||||
class PRG>
|
||||
pmh_tables<M, Hash> constexpr make_pmh_tables(const carray<Item, N> &items,
|
||||
Hash const &hash, Key const &key,
|
||||
PRG prg) {
|
||||
// Step 1: Place all of the keys into buckets
|
||||
auto step_one = make_pmh_buckets<M>(items, hash, key, prg);
|
||||
|
||||
// Step 2: Sort the buckets to process the ones with the most items first.
|
||||
auto buckets = step_one.get_sorted_buckets();
|
||||
|
||||
// G becomes the first hash table in the resulting pmh function
|
||||
carray<seed_or_index, M> G; // Default constructed to "index 0"
|
||||
|
||||
// H becomes the second hash table in the resulting pmh function
|
||||
constexpr std::size_t UNUSED = (std::numeric_limits<std::size_t>::max)();
|
||||
carray<std::size_t, M> H;
|
||||
H.fill(UNUSED);
|
||||
|
||||
// Step 3: Map the items in buckets into hash tables.
|
||||
for (const auto &bucket : buckets) {
|
||||
auto const bsize = bucket.size();
|
||||
|
||||
if (bsize == 1) {
|
||||
// Store index to the (single) item in G
|
||||
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M);
|
||||
G[bucket.hash] = {false, static_cast<uint64_t>(bucket[0])};
|
||||
}
|
||||
else if (bsize > 1) {
|
||||
// Repeatedly try different H of d until we find a hash function
|
||||
// that places all items in the bucket into free slots
|
||||
seed_or_index d{true, prg()};
|
||||
cvector<std::size_t, decltype(step_one)::bucket_max> bucket_slots;
|
||||
|
||||
while (bucket_slots.size() < bsize) {
|
||||
auto slot = hash(key(items[bucket[bucket_slots.size()]]),
|
||||
static_cast<size_t>(d.value())) %
|
||||
M;
|
||||
|
||||
if (H[slot] != UNUSED || !all_different_from(bucket_slots, slot)) {
|
||||
bucket_slots.clear();
|
||||
d = {true, prg()};
|
||||
continue;
|
||||
}
|
||||
|
||||
bucket_slots.push_back(slot);
|
||||
}
|
||||
|
||||
// Put successful seed in G, and put indices to items in their slots
|
||||
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M);
|
||||
G[bucket.hash] = d;
|
||||
for (std::size_t i = 0; i < bsize; ++i) H[bucket_slots[i]] = bucket[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Any unused entries in the H table have to get changed to zero.
|
||||
// This is because hashing should not fail or return an out-of-bounds entry.
|
||||
// A lookup fails after we apply user-supplied KeyEqual to the query and the
|
||||
// key found by hashing. Sending such queries to zero cannot hurt.
|
||||
for (std::size_t i = 0; i < M; ++i)
|
||||
if (H[i] == UNUSED)
|
||||
H[i] = 0;
|
||||
|
||||
return {step_one.seed, G, H, hash};
|
||||
}
|
||||
|
||||
} // namespace bits
|
||||
|
||||
} // namespace frozen
|
||||
|
||||
#endif
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_LETITGO_VERSION_H
|
||||
#define FROZEN_LETITGO_VERSION_H
|
||||
|
||||
#define FROZEN_MAJOR_VERSION 1
|
||||
#define FROZEN_MINOR_VERSION 1
|
||||
#define FROZEN_PATCH_VERSION 1
|
||||
|
||||
#endif
|
||||
Vendored
+378
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_LETITGO_MAP_H
|
||||
#define FROZEN_LETITGO_MAP_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "frozen/bits/algorithms.h"
|
||||
#include "frozen/bits/basic_types.h"
|
||||
#include "frozen/bits/constexpr_assert.h"
|
||||
#include "frozen/bits/exceptions.h"
|
||||
#include "frozen/bits/version.h"
|
||||
|
||||
namespace frozen {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template <class Comparator>
|
||||
class CompareKey {
|
||||
Comparator const comparator_;
|
||||
|
||||
public:
|
||||
constexpr CompareKey(Comparator const &comparator)
|
||||
: comparator_(comparator) {}
|
||||
|
||||
template <class Key1, class Key2, class Value>
|
||||
constexpr int operator()(std::pair<Key1, Value> const &self,
|
||||
std::pair<Key2, Value> const &other) const {
|
||||
return comparator_(std::get<0>(self), std::get<0>(other));
|
||||
}
|
||||
|
||||
template <class Key1, class Key2, class Value>
|
||||
constexpr int operator()(Key1 const &self_key,
|
||||
std::pair<Key2, Value> const &other) const {
|
||||
return comparator_(self_key, std::get<0>(other));
|
||||
}
|
||||
|
||||
template <class Key1, class Key2, class Value>
|
||||
constexpr int operator()(std::pair<Key1, Value> const &self,
|
||||
Key2 const &other_key) const {
|
||||
return comparator_(std::get<0>(self), other_key);
|
||||
}
|
||||
|
||||
template <class Key1, class Key2>
|
||||
constexpr int operator()(Key1 const &self_key, Key2 const &other_key) const {
|
||||
return comparator_(self_key, other_key);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template <class Key, class Value, std::size_t N, class Compare = std::less<Key>>
|
||||
class map {
|
||||
using container_type = bits::carray<std::pair<Key, Value>, N>;
|
||||
impl::CompareKey<Compare> less_than_;
|
||||
container_type items_;
|
||||
|
||||
public:
|
||||
using key_type = Key;
|
||||
using mapped_type = Value;
|
||||
using value_type = typename container_type::value_type;
|
||||
using size_type = typename container_type::size_type;
|
||||
using difference_type = typename container_type::difference_type;
|
||||
using key_compare = decltype(less_than_);
|
||||
using reference = typename container_type::reference;
|
||||
using const_reference = typename container_type::const_reference;
|
||||
using pointer = typename container_type::pointer;
|
||||
using const_pointer = typename container_type::const_pointer;
|
||||
using iterator = typename container_type::iterator;
|
||||
using const_iterator = typename container_type::const_iterator;
|
||||
using reverse_iterator = typename container_type::reverse_iterator;
|
||||
using const_reverse_iterator =
|
||||
typename container_type::const_reverse_iterator;
|
||||
|
||||
public:
|
||||
/* constructors */
|
||||
constexpr map(container_type items, Compare const &compare)
|
||||
: less_than_{compare}, items_{bits::quicksort(items, less_than_)} {}
|
||||
|
||||
explicit constexpr map(container_type items) : map{items, Compare{}} {}
|
||||
|
||||
constexpr map(std::initializer_list<value_type> items, Compare const &compare)
|
||||
: map{container_type{items}, compare} {
|
||||
constexpr_assert(
|
||||
items.size() == N,
|
||||
"Inconsistent initializer_list size and type size argument");
|
||||
}
|
||||
|
||||
constexpr map(std::initializer_list<value_type> items)
|
||||
: map{items, Compare{}} {}
|
||||
|
||||
/* element access */
|
||||
constexpr Value const &at(Key const &key) const {
|
||||
return at_impl(*this, key);
|
||||
}
|
||||
constexpr Value &at(Key const &key) { return at_impl(*this, key); }
|
||||
|
||||
/* iterators */
|
||||
constexpr iterator begin() { return items_.begin(); }
|
||||
constexpr const_iterator begin() const { return items_.begin(); }
|
||||
constexpr const_iterator cbegin() const { return items_.cbegin(); }
|
||||
constexpr iterator end() { return items_.end(); }
|
||||
constexpr const_iterator end() const { return items_.end(); }
|
||||
constexpr const_iterator cend() const { return items_.cend(); }
|
||||
|
||||
constexpr reverse_iterator rbegin() { return items_.rbegin(); }
|
||||
constexpr const_reverse_iterator rbegin() const { return items_.rbegin(); }
|
||||
constexpr const_reverse_iterator crbegin() const { return items_.crbegin(); }
|
||||
constexpr reverse_iterator rend() { return items_.rend(); }
|
||||
constexpr const_reverse_iterator rend() const { return items_.rend(); }
|
||||
constexpr const_reverse_iterator crend() const { return items_.crend(); }
|
||||
|
||||
/* capacity */
|
||||
constexpr bool empty() const { return !N; }
|
||||
constexpr size_type size() const { return N; }
|
||||
constexpr size_type max_size() const { return N; }
|
||||
|
||||
/* lookup */
|
||||
|
||||
template <class KeyType>
|
||||
constexpr std::size_t count(KeyType const &key) const {
|
||||
return bits::binary_search<N>(items_.begin(), key, less_than_);
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr const_iterator find(KeyType const &key) const {
|
||||
return map::find_impl(*this, key);
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr iterator find(KeyType const &key) {
|
||||
return map::find_impl(*this, key);
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr std::pair<const_iterator, const_iterator> equal_range(
|
||||
KeyType const &key) const {
|
||||
return equal_range_impl(*this, key);
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr std::pair<iterator, iterator> equal_range(KeyType const &key) {
|
||||
return equal_range_impl(*this, key);
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr const_iterator lower_bound(KeyType const &key) const {
|
||||
return lower_bound_impl(*this, key);
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr iterator lower_bound(KeyType const &key) {
|
||||
return lower_bound_impl(*this, key);
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr const_iterator upper_bound(KeyType const &key) const {
|
||||
return upper_bound_impl(*this, key);
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr iterator upper_bound(KeyType const &key) {
|
||||
return upper_bound_impl(*this, key);
|
||||
}
|
||||
|
||||
/* observers */
|
||||
constexpr const key_compare &key_comp() const { return less_than_; }
|
||||
constexpr const key_compare &value_comp() const { return less_than_; }
|
||||
|
||||
private:
|
||||
template <class This, class KeyType>
|
||||
static inline constexpr auto &at_impl(This &&self, KeyType const &key) {
|
||||
auto where = self.lower_bound(key);
|
||||
if (where != self.end())
|
||||
return where->second;
|
||||
else
|
||||
FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key"));
|
||||
}
|
||||
|
||||
template <class This, class KeyType>
|
||||
static inline constexpr auto find_impl(This &&self, KeyType const &key) {
|
||||
auto where = self.lower_bound(key);
|
||||
if ((where != self.end()) && !self.less_than_(key, *where))
|
||||
return where;
|
||||
else
|
||||
return self.end();
|
||||
}
|
||||
|
||||
template <class This, class KeyType>
|
||||
static inline constexpr auto equal_range_impl(This &&self,
|
||||
KeyType const &key) {
|
||||
auto lower = self.lower_bound(key);
|
||||
using lower_t = decltype(lower);
|
||||
if (lower == self.end())
|
||||
return std::pair<lower_t, lower_t>{lower, lower};
|
||||
else
|
||||
return std::pair<lower_t, lower_t>{lower, lower + 1};
|
||||
}
|
||||
|
||||
template <class This, class KeyType>
|
||||
static inline constexpr auto lower_bound_impl(This &&self, KeyType const &key)
|
||||
-> decltype(self.end()) {
|
||||
auto where =
|
||||
bits::lower_bound<N>(self.items_.begin(), key, self.less_than_);
|
||||
if ((where != self.end()) && !self.less_than_(key, *where))
|
||||
return where;
|
||||
else
|
||||
return self.end();
|
||||
}
|
||||
|
||||
template <class This, class KeyType>
|
||||
static inline constexpr auto upper_bound_impl(This &&self, KeyType const &key)
|
||||
-> decltype(self.end()) {
|
||||
auto where =
|
||||
bits::lower_bound<N>(self.items_.begin(), key, self.less_than_);
|
||||
if ((where != self.end()) && !self.less_than_(key, *where))
|
||||
return where + 1;
|
||||
else
|
||||
return self.end();
|
||||
}
|
||||
};
|
||||
|
||||
template <class Key, class Value, class Compare>
|
||||
class map<Key, Value, 0, Compare> {
|
||||
using container_type = bits::carray<std::pair<Key, Value>, 0>;
|
||||
impl::CompareKey<Compare> less_than_;
|
||||
|
||||
public:
|
||||
using key_type = Key;
|
||||
using mapped_type = Value;
|
||||
using value_type = typename container_type::value_type;
|
||||
using size_type = typename container_type::size_type;
|
||||
using difference_type = typename container_type::difference_type;
|
||||
using key_compare = decltype(less_than_);
|
||||
using reference = typename container_type::reference;
|
||||
using const_reference = typename container_type::const_reference;
|
||||
using pointer = typename container_type::pointer;
|
||||
using const_pointer = typename container_type::const_pointer;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using reverse_iterator = pointer;
|
||||
using const_reverse_iterator = const_pointer;
|
||||
|
||||
public:
|
||||
/* constructors */
|
||||
constexpr map(const map &other) = default;
|
||||
constexpr map(std::initializer_list<value_type>, Compare const &compare)
|
||||
: less_than_{compare} {}
|
||||
constexpr map(std::initializer_list<value_type> items)
|
||||
: map{items, Compare{}} {}
|
||||
|
||||
/* element access */
|
||||
template <class KeyType>
|
||||
constexpr mapped_type at(KeyType const &) const {
|
||||
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key"));
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr mapped_type at(KeyType const &) {
|
||||
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key"));
|
||||
}
|
||||
|
||||
/* iterators */
|
||||
constexpr iterator begin() { return nullptr; }
|
||||
constexpr const_iterator begin() const { return nullptr; }
|
||||
constexpr const_iterator cbegin() const { return nullptr; }
|
||||
constexpr iterator end() { return nullptr; }
|
||||
constexpr const_iterator end() const { return nullptr; }
|
||||
constexpr const_iterator cend() const { return nullptr; }
|
||||
|
||||
constexpr reverse_iterator rbegin() { return nullptr; }
|
||||
constexpr const_reverse_iterator rbegin() const { return nullptr; }
|
||||
constexpr const_reverse_iterator crbegin() const { return nullptr; }
|
||||
constexpr reverse_iterator rend() { return nullptr; }
|
||||
constexpr const_reverse_iterator rend() const { return nullptr; }
|
||||
constexpr const_reverse_iterator crend() const { return nullptr; }
|
||||
|
||||
/* capacity */
|
||||
constexpr bool empty() const { return true; }
|
||||
constexpr size_type size() const { return 0; }
|
||||
constexpr size_type max_size() const { return 0; }
|
||||
|
||||
/* lookup */
|
||||
|
||||
template <class KeyType>
|
||||
constexpr std::size_t count(KeyType const &) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr const_iterator find(KeyType const &) const {
|
||||
return end();
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr iterator find(KeyType const &) {
|
||||
return end();
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr std::pair<const_iterator, const_iterator> equal_range(
|
||||
KeyType const &) const {
|
||||
return {end(), end()};
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr std::pair<iterator, iterator> equal_range(KeyType const &) {
|
||||
return {end(), end()};
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr const_iterator lower_bound(KeyType const &) const {
|
||||
return end();
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr iterator lower_bound(KeyType const &) {
|
||||
return end();
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr const_iterator upper_bound(KeyType const &) const {
|
||||
return end();
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr iterator upper_bound(KeyType const &) {
|
||||
return end();
|
||||
}
|
||||
|
||||
/* observers */
|
||||
constexpr key_compare const &key_comp() const { return less_than_; }
|
||||
constexpr key_compare const &value_comp() const { return less_than_; }
|
||||
};
|
||||
|
||||
template <typename T, typename U, typename Compare = std::less<T>>
|
||||
constexpr auto make_map(
|
||||
bits::ignored_arg =
|
||||
{} /* for consistency with the initializer below for N = 0*/) {
|
||||
return map<T, U, 0, Compare>{};
|
||||
}
|
||||
|
||||
template <typename T, typename U, std::size_t N>
|
||||
constexpr auto make_map(std::pair<T, U> const (&items)[N]) {
|
||||
return map<T, U, N>{items};
|
||||
}
|
||||
|
||||
template <typename T, typename U, std::size_t N>
|
||||
constexpr auto make_map(std::array<std::pair<T, U>, N> const &items) {
|
||||
return map<T, U, N>{items};
|
||||
}
|
||||
|
||||
template <typename T, typename U, typename Compare, std::size_t N>
|
||||
constexpr auto make_map(std::pair<T, U> const (&items)[N],
|
||||
Compare const &compare = Compare{}) {
|
||||
return map<T, U, N, Compare>{items, compare};
|
||||
}
|
||||
|
||||
template <typename T, typename U, typename Compare, std::size_t N>
|
||||
constexpr auto make_map(std::array<std::pair<T, U>, N> const &items,
|
||||
Compare const &compare = Compare{}) {
|
||||
return map<T, U, N, Compare>{items, compare};
|
||||
}
|
||||
|
||||
} // namespace frozen
|
||||
|
||||
#endif
|
||||
Vendored
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_LETITGO_RANDOM_H
|
||||
#define FROZEN_LETITGO_RANDOM_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#include "frozen/bits/algorithms.h"
|
||||
#include "frozen/bits/version.h"
|
||||
|
||||
namespace frozen {
|
||||
template <class UIntType, UIntType a, UIntType c, UIntType m>
|
||||
class linear_congruential_engine {
|
||||
static_assert(std::is_unsigned<UIntType>::value,
|
||||
"UIntType must be an unsigned integral type");
|
||||
|
||||
template <class T>
|
||||
static constexpr UIntType modulo(T val, std::integral_constant<UIntType, 0>) {
|
||||
return static_cast<UIntType>(val);
|
||||
}
|
||||
|
||||
template <class T, UIntType M>
|
||||
static constexpr UIntType modulo(T val, std::integral_constant<UIntType, M>) {
|
||||
// the static cast below may end up doing a truncation
|
||||
return static_cast<UIntType>(val % M);
|
||||
}
|
||||
|
||||
public:
|
||||
using result_type = UIntType;
|
||||
static constexpr result_type multiplier = a;
|
||||
static constexpr result_type increment = c;
|
||||
static constexpr result_type modulus = m;
|
||||
static constexpr result_type default_seed = 1u;
|
||||
|
||||
linear_congruential_engine() = default;
|
||||
constexpr linear_congruential_engine(result_type s) { seed(s); }
|
||||
|
||||
void seed(result_type s = default_seed) { state_ = s; }
|
||||
constexpr result_type operator()() {
|
||||
using uint_least_t =
|
||||
bits::select_uint_least_t<bits::log(a) + bits::log(m) + 4>;
|
||||
uint_least_t tmp =
|
||||
static_cast<uint_least_t>(multiplier) * state_ + increment;
|
||||
|
||||
state_ = modulo(tmp, std::integral_constant<UIntType, modulus>());
|
||||
return state_;
|
||||
}
|
||||
// constexpr void discard(unsigned long long n) {
|
||||
// while (n--) operator()();
|
||||
// }
|
||||
// static constexpr result_type min() { return increment == 0u ? 1u : 0u; }
|
||||
// static constexpr result_type max() { return modulus - 1u; }
|
||||
friend constexpr bool operator==(linear_congruential_engine const &self,
|
||||
linear_congruential_engine const &other) {
|
||||
return self.state_ == other.state_;
|
||||
}
|
||||
friend constexpr bool operator!=(linear_congruential_engine const &self,
|
||||
linear_congruential_engine const &other) {
|
||||
return !(self == other);
|
||||
}
|
||||
|
||||
private:
|
||||
result_type state_ = default_seed;
|
||||
};
|
||||
|
||||
using minstd_rand0 =
|
||||
linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647>;
|
||||
using minstd_rand =
|
||||
linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>;
|
||||
|
||||
// This generator is used by default in unordered frozen containers
|
||||
using default_prg_t = minstd_rand;
|
||||
|
||||
} // namespace frozen
|
||||
|
||||
#endif
|
||||
Vendored
+283
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_SET_H
|
||||
#define FROZEN_SET_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "frozen/bits/algorithms.h"
|
||||
#include "frozen/bits/basic_types.h"
|
||||
#include "frozen/bits/constexpr_assert.h"
|
||||
#include "frozen/bits/defines.h"
|
||||
#include "frozen/bits/version.h"
|
||||
|
||||
namespace frozen {
|
||||
|
||||
template <class Key, std::size_t N, class Compare = std::less<Key>>
|
||||
class set {
|
||||
using container_type = bits::carray<Key, N>;
|
||||
Compare less_than_;
|
||||
container_type keys_;
|
||||
|
||||
public:
|
||||
/* container typedefs*/
|
||||
using key_type = Key;
|
||||
using value_type = Key;
|
||||
using size_type = typename container_type::size_type;
|
||||
using difference_type = typename container_type::size_type;
|
||||
using key_compare = Compare;
|
||||
using value_compare = Compare;
|
||||
using reference = typename container_type::const_reference;
|
||||
using const_reference = reference;
|
||||
using pointer = typename container_type::const_pointer;
|
||||
using const_pointer = pointer;
|
||||
using iterator = typename container_type::const_iterator;
|
||||
using reverse_iterator = typename container_type::const_reverse_iterator;
|
||||
using const_iterator = iterator;
|
||||
using const_reverse_iterator = reverse_iterator;
|
||||
|
||||
public:
|
||||
/* constructors */
|
||||
constexpr set(const set &other) = default;
|
||||
|
||||
constexpr set(container_type keys, Compare const &comp)
|
||||
: less_than_{comp}, keys_(bits::quicksort(keys, less_than_)) {}
|
||||
|
||||
explicit constexpr set(container_type keys) : set{keys, Compare{}} {}
|
||||
|
||||
constexpr set(std::initializer_list<Key> keys, Compare const &comp)
|
||||
: set{container_type{keys}, comp} {
|
||||
constexpr_assert(
|
||||
keys.size() == N,
|
||||
"Inconsistent initializer_list size and type size argument");
|
||||
}
|
||||
|
||||
constexpr set(std::initializer_list<Key> keys) : set{keys, Compare{}} {}
|
||||
|
||||
constexpr set &operator=(const set &other) = default;
|
||||
|
||||
/* capacity */
|
||||
constexpr bool empty() const { return !N; }
|
||||
constexpr size_type size() const { return N; }
|
||||
constexpr size_type max_size() const { return N; }
|
||||
|
||||
/* lookup */
|
||||
template <class KeyType>
|
||||
constexpr std::size_t count(KeyType const &key) const {
|
||||
return bits::binary_search<N>(keys_.begin(), key, less_than_);
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr const_iterator find(KeyType const &key) const {
|
||||
const_iterator where = lower_bound(key);
|
||||
if ((where != end()) && !less_than_(key, *where))
|
||||
return where;
|
||||
else
|
||||
return end();
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr std::pair<const_iterator, const_iterator> equal_range(
|
||||
KeyType const &key) const {
|
||||
auto const lower = lower_bound(key);
|
||||
if (lower == end())
|
||||
return {lower, lower};
|
||||
else
|
||||
return {lower, lower + 1};
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr const_iterator lower_bound(KeyType const &key) const {
|
||||
auto const where = bits::lower_bound<N>(keys_.begin(), key, less_than_);
|
||||
if ((where != end()) && !less_than_(key, *where))
|
||||
return where;
|
||||
else
|
||||
return end();
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr const_iterator upper_bound(KeyType const &key) const {
|
||||
auto const where = bits::lower_bound<N>(keys_.begin(), key, less_than_);
|
||||
if ((where != end()) && !less_than_(key, *where))
|
||||
return where + 1;
|
||||
else
|
||||
return end();
|
||||
}
|
||||
|
||||
/* observers */
|
||||
constexpr const key_compare &key_comp() const { return less_than_; }
|
||||
constexpr const key_compare &value_comp() const { return less_than_; }
|
||||
|
||||
/* iterators */
|
||||
constexpr const_iterator begin() const { return keys_.begin(); }
|
||||
constexpr const_iterator cbegin() const { return keys_.cbegin(); }
|
||||
constexpr const_iterator end() const { return keys_.end(); }
|
||||
constexpr const_iterator cend() const { return keys_.cend(); }
|
||||
|
||||
constexpr const_reverse_iterator rbegin() const { return keys_.rbegin(); }
|
||||
constexpr const_reverse_iterator crbegin() const { return keys_.crbegin(); }
|
||||
constexpr const_reverse_iterator rend() const { return keys_.rend(); }
|
||||
constexpr const_reverse_iterator crend() const { return keys_.crend(); }
|
||||
|
||||
/* comparison */
|
||||
constexpr bool operator==(set const &rhs) const {
|
||||
return bits::equal(begin(), end(), rhs.begin());
|
||||
}
|
||||
constexpr bool operator!=(set const &rhs) const { return !(*this == rhs); }
|
||||
constexpr bool operator<(set const &rhs) const {
|
||||
return bits::lexicographical_compare(begin(), end(), rhs.begin(),
|
||||
rhs.end());
|
||||
}
|
||||
constexpr bool operator<=(set const &rhs) const {
|
||||
return (*this < rhs) || (*this == rhs);
|
||||
}
|
||||
constexpr bool operator>(set const &rhs) const {
|
||||
return bits::lexicographical_compare(rhs.begin(), rhs.end(), begin(),
|
||||
end());
|
||||
}
|
||||
constexpr bool operator>=(set const &rhs) const {
|
||||
return (*this > rhs) || (*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Key, class Compare>
|
||||
class set<Key, 0, Compare> {
|
||||
using container_type = bits::carray<Key, 0>; // just for the type definitions
|
||||
Compare less_than_;
|
||||
|
||||
public:
|
||||
/* container typedefs*/
|
||||
using key_type = Key;
|
||||
using value_type = Key;
|
||||
using size_type = typename container_type::size_type;
|
||||
using difference_type = typename container_type::size_type;
|
||||
using key_compare = Compare;
|
||||
using value_compare = Compare;
|
||||
using reference = typename container_type::const_reference;
|
||||
using const_reference = reference;
|
||||
using pointer = typename container_type::const_pointer;
|
||||
using const_pointer = pointer;
|
||||
using iterator = pointer;
|
||||
using reverse_iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using const_reverse_iterator = const_pointer;
|
||||
|
||||
public:
|
||||
/* constructors */
|
||||
constexpr set(const set &other) = default;
|
||||
constexpr set(bits::carray<Key, 0>, Compare const &) {}
|
||||
explicit constexpr set(bits::carray<Key, 0>) {}
|
||||
|
||||
constexpr set(std::initializer_list<Key>, Compare const &comp)
|
||||
: less_than_{comp} {}
|
||||
constexpr set(std::initializer_list<Key> keys) : set{keys, Compare{}} {}
|
||||
|
||||
constexpr set &operator=(const set &other) = default;
|
||||
|
||||
/* capacity */
|
||||
constexpr bool empty() const { return true; }
|
||||
constexpr size_type size() const { return 0; }
|
||||
constexpr size_type max_size() const { return 0; }
|
||||
|
||||
/* lookup */
|
||||
template <class KeyType>
|
||||
constexpr std::size_t count(KeyType const &) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr const_iterator find(KeyType const &) const {
|
||||
return end();
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr std::pair<const_iterator, const_iterator> equal_range(
|
||||
KeyType const &) const {
|
||||
return {end(), end()};
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr const_iterator lower_bound(KeyType const &) const {
|
||||
return end();
|
||||
}
|
||||
|
||||
template <class KeyType>
|
||||
constexpr const_iterator upper_bound(KeyType const &) const {
|
||||
return end();
|
||||
}
|
||||
|
||||
/* observers */
|
||||
constexpr key_compare key_comp() const { return less_than_; }
|
||||
constexpr key_compare value_comp() const { return less_than_; }
|
||||
|
||||
/* iterators */
|
||||
constexpr const_iterator begin() const { return nullptr; }
|
||||
constexpr const_iterator cbegin() const { return nullptr; }
|
||||
constexpr const_iterator end() const { return nullptr; }
|
||||
constexpr const_iterator cend() const { return nullptr; }
|
||||
|
||||
constexpr const_reverse_iterator rbegin() const { return nullptr; }
|
||||
constexpr const_reverse_iterator crbegin() const { return nullptr; }
|
||||
constexpr const_reverse_iterator rend() const { return nullptr; }
|
||||
constexpr const_reverse_iterator crend() const { return nullptr; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr auto make_set(
|
||||
bits::ignored_arg =
|
||||
{} /* for consistency with the initializer below for N = 0*/) {
|
||||
return set<T, 0>{};
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
constexpr auto make_set(const T (&args)[N]) {
|
||||
return set<T, N>(args);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
constexpr auto make_set(std::array<T, N> const &args) {
|
||||
return set<T, N>(args);
|
||||
}
|
||||
|
||||
template <typename T, typename Compare, std::size_t N>
|
||||
constexpr auto make_set(const T (&args)[N],
|
||||
Compare const &compare = Compare{}) {
|
||||
return set<T, N, Compare>(args, compare);
|
||||
}
|
||||
|
||||
template <typename T, typename Compare, std::size_t N>
|
||||
constexpr auto make_set(std::array<T, N> const &args,
|
||||
Compare const &compare = Compare{}) {
|
||||
return set<T, N, Compare>(args, compare);
|
||||
}
|
||||
|
||||
#ifdef FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
|
||||
|
||||
template <class T, class... Args>
|
||||
set(T, Args...) -> set<T, sizeof...(Args) + 1>;
|
||||
|
||||
#endif // FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
|
||||
|
||||
} // namespace frozen
|
||||
|
||||
#endif
|
||||
Vendored
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_LETITGO_STRING_H
|
||||
#define FROZEN_LETITGO_STRING_H
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
#include "frozen/bits/defines.h"
|
||||
#include "frozen/bits/elsa.h"
|
||||
#include "frozen/bits/hash_string.h"
|
||||
#include "frozen/bits/version.h"
|
||||
|
||||
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
|
||||
#include <string_view>
|
||||
#endif
|
||||
|
||||
namespace frozen {
|
||||
|
||||
template <typename _CharT>
|
||||
class basic_string {
|
||||
using chr_t = _CharT;
|
||||
|
||||
chr_t const *data_;
|
||||
std::size_t size_;
|
||||
|
||||
public:
|
||||
template <std::size_t N>
|
||||
constexpr basic_string(chr_t const (&data)[N]) : data_(data), size_(N - 1) {}
|
||||
constexpr basic_string(chr_t const *data, std::size_t size)
|
||||
: data_(data), size_(size) {}
|
||||
|
||||
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
|
||||
constexpr basic_string(std::basic_string_view<chr_t> data)
|
||||
: data_(data.data()), size_(data.size()) {}
|
||||
#endif
|
||||
|
||||
constexpr basic_string(const basic_string &) noexcept = default;
|
||||
constexpr basic_string &operator=(const basic_string &) noexcept = default;
|
||||
|
||||
constexpr std::size_t size() const { return size_; }
|
||||
|
||||
constexpr chr_t operator[](std::size_t i) const { return data_[i]; }
|
||||
|
||||
constexpr bool operator==(basic_string other) const {
|
||||
if (size_ != other.size_)
|
||||
return false;
|
||||
for (std::size_t i = 0; i < size_; ++i)
|
||||
if (data_[i] != other.data_[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool operator<(const basic_string &other) const {
|
||||
unsigned i = 0;
|
||||
while (i < size() && i < other.size()) {
|
||||
if ((*this)[i] < other[i]) {
|
||||
return true;
|
||||
}
|
||||
if ((*this)[i] > other[i]) {
|
||||
return false;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return size() < other.size();
|
||||
}
|
||||
|
||||
constexpr const chr_t *data() const { return data_; }
|
||||
constexpr const chr_t *begin() const { return data(); }
|
||||
constexpr const chr_t *end() const { return data() + size(); }
|
||||
};
|
||||
|
||||
template <typename _CharT>
|
||||
struct elsa<basic_string<_CharT>> {
|
||||
constexpr std::size_t operator()(basic_string<_CharT> value) const {
|
||||
return hash_string(value);
|
||||
}
|
||||
constexpr std::size_t operator()(basic_string<_CharT> value,
|
||||
std::size_t seed) const {
|
||||
return hash_string(value, seed);
|
||||
}
|
||||
};
|
||||
|
||||
using string = basic_string<char>;
|
||||
using wstring = basic_string<wchar_t>;
|
||||
using u16string = basic_string<char16_t>;
|
||||
using u32string = basic_string<char32_t>;
|
||||
|
||||
#ifdef FROZEN_LETITGO_HAS_CHAR8T
|
||||
using u8string = basic_string<char8_t>;
|
||||
#endif
|
||||
|
||||
namespace string_literals {
|
||||
|
||||
constexpr string operator"" _s(const char *data, std::size_t size) {
|
||||
return {data, size};
|
||||
}
|
||||
|
||||
constexpr wstring operator"" _s(const wchar_t *data, std::size_t size) {
|
||||
return {data, size};
|
||||
}
|
||||
|
||||
constexpr u16string operator"" _s(const char16_t *data, std::size_t size) {
|
||||
return {data, size};
|
||||
}
|
||||
|
||||
constexpr u32string operator"" _s(const char32_t *data, std::size_t size) {
|
||||
return {data, size};
|
||||
}
|
||||
|
||||
#ifdef FROZEN_LETITGO_HAS_CHAR8T
|
||||
constexpr u8string operator"" _s(const char8_t *data, std::size_t size) {
|
||||
return {data, size};
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace string_literals
|
||||
|
||||
} // namespace frozen
|
||||
|
||||
namespace std {
|
||||
template <typename _CharT>
|
||||
struct hash<frozen::basic_string<_CharT>> {
|
||||
size_t operator()(frozen::basic_string<_CharT> s) const {
|
||||
return frozen::elsa<frozen::basic_string<_CharT>>{}(s);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
#endif
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_LETITGO_UNORDERED_MAP_H
|
||||
#define FROZEN_LETITGO_UNORDERED_MAP_H
|
||||
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
|
||||
#include "frozen/bits/basic_types.h"
|
||||
#include "frozen/bits/constexpr_assert.h"
|
||||
#include "frozen/bits/elsa.h"
|
||||
#include "frozen/bits/exceptions.h"
|
||||
#include "frozen/bits/pmh.h"
|
||||
#include "frozen/bits/version.h"
|
||||
#include "frozen/random.h"
|
||||
|
||||
namespace frozen {
|
||||
|
||||
namespace bits {
|
||||
|
||||
struct GetKey {
|
||||
template <class KV>
|
||||
constexpr auto const &operator()(KV const &kv) const {
|
||||
return kv.first;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace bits
|
||||
|
||||
template <class Key, class Value, std::size_t N, typename Hash = anna<Key>,
|
||||
class KeyEqual = std::equal_to<Key>>
|
||||
class unordered_map {
|
||||
static constexpr std::size_t storage_size =
|
||||
bits::next_highest_power_of_two(N) *
|
||||
(N < 32 ? 2 : 1); // size adjustment to prevent high collision rate for
|
||||
// small sets
|
||||
using container_type = bits::carray<std::pair<Key, Value>, N>;
|
||||
using tables_type = bits::pmh_tables<storage_size, Hash>;
|
||||
|
||||
KeyEqual const equal_;
|
||||
container_type items_;
|
||||
tables_type tables_;
|
||||
|
||||
public:
|
||||
/* typedefs */
|
||||
using Self = unordered_map<Key, Value, N, Hash, KeyEqual>;
|
||||
using key_type = Key;
|
||||
using mapped_type = Value;
|
||||
using value_type = typename container_type::value_type;
|
||||
using size_type = typename container_type::size_type;
|
||||
using difference_type = typename container_type::difference_type;
|
||||
using hasher = Hash;
|
||||
using key_equal = KeyEqual;
|
||||
using reference = typename container_type::reference;
|
||||
using const_reference = typename container_type::const_reference;
|
||||
using pointer = typename container_type::pointer;
|
||||
using const_pointer = typename container_type::const_pointer;
|
||||
using iterator = typename container_type::iterator;
|
||||
using const_iterator = typename container_type::const_iterator;
|
||||
|
||||
public:
|
||||
/* constructors */
|
||||
unordered_map(unordered_map const &) = default;
|
||||
constexpr unordered_map(container_type items, Hash const &hash,
|
||||
KeyEqual const &equal)
|
||||
: equal_{equal},
|
||||
items_{items},
|
||||
tables_{bits::make_pmh_tables<storage_size>(
|
||||
items_, hash, bits::GetKey{}, default_prg_t{})} {}
|
||||
explicit constexpr unordered_map(container_type items)
|
||||
: unordered_map{items, Hash{}, KeyEqual{}} {}
|
||||
|
||||
constexpr unordered_map(std::initializer_list<value_type> items,
|
||||
Hash const &hash, KeyEqual const &equal)
|
||||
: unordered_map{container_type{items}, hash, equal} {
|
||||
constexpr_assert(
|
||||
items.size() == N,
|
||||
"Inconsistent initializer_list size and type size argument");
|
||||
}
|
||||
|
||||
constexpr unordered_map(std::initializer_list<value_type> items)
|
||||
: unordered_map{items, Hash{}, KeyEqual{}} {}
|
||||
|
||||
/* iterators */
|
||||
constexpr iterator begin() { return items_.begin(); }
|
||||
constexpr iterator end() { return items_.end(); }
|
||||
constexpr const_iterator begin() const { return items_.begin(); }
|
||||
constexpr const_iterator end() const { return items_.end(); }
|
||||
constexpr const_iterator cbegin() const { return items_.cbegin(); }
|
||||
constexpr const_iterator cend() const { return items_.cend(); }
|
||||
|
||||
/* capacity */
|
||||
constexpr bool empty() const { return !N; }
|
||||
constexpr size_type size() const { return N; }
|
||||
constexpr size_type max_size() const { return N; }
|
||||
|
||||
/* lookup */
|
||||
template <class KeyType, class Hasher, class Equal>
|
||||
constexpr std::size_t count(KeyType const &key, Hasher const &hash,
|
||||
Equal const &equal) const {
|
||||
auto const &kv = lookup(key, hash);
|
||||
return equal(kv.first, key);
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr std::size_t count(KeyType const &key) const {
|
||||
return count(key, hash_function(), key_eq());
|
||||
}
|
||||
|
||||
template <class KeyType, class Hasher, class Equal>
|
||||
constexpr Value const &at(KeyType const &key, Hasher const &hash,
|
||||
Equal const &equal) const {
|
||||
return at_impl(*this, key, hash, equal);
|
||||
}
|
||||
template <class KeyType, class Hasher, class Equal>
|
||||
constexpr Value &at(KeyType const &key, Hasher const &hash,
|
||||
Equal const &equal) {
|
||||
return at_impl(*this, key, hash, equal);
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr Value const &at(KeyType const &key) const {
|
||||
return at(key, hash_function(), key_eq());
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr Value &at(KeyType const &key) {
|
||||
return at(key, hash_function(), key_eq());
|
||||
}
|
||||
|
||||
template <class KeyType, class Hasher, class Equal>
|
||||
constexpr const_iterator find(KeyType const &key, Hasher const &hash,
|
||||
Equal const &equal) const {
|
||||
return find_impl(*this, key, hash, equal);
|
||||
}
|
||||
template <class KeyType, class Hasher, class Equal>
|
||||
constexpr iterator find(KeyType const &key, Hasher const &hash,
|
||||
Equal const &equal) {
|
||||
return find_impl(*this, key, hash, equal);
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr const_iterator find(KeyType const &key) const {
|
||||
return find(key, hash_function(), key_eq());
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr iterator find(KeyType const &key) {
|
||||
return find(key, hash_function(), key_eq());
|
||||
}
|
||||
|
||||
template <class KeyType, class Hasher, class Equal>
|
||||
constexpr std::pair<const_iterator, const_iterator> equal_range(
|
||||
KeyType const &key, Hasher const &hash, Equal const &equal) const {
|
||||
return equal_range_impl(*this, key, hash, equal);
|
||||
}
|
||||
template <class KeyType, class Hasher, class Equal>
|
||||
constexpr std::pair<iterator, iterator> equal_range(KeyType const &key,
|
||||
Hasher const &hash,
|
||||
Equal const &equal) {
|
||||
return equal_range_impl(*this, key, hash, equal);
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr std::pair<const_iterator, const_iterator> equal_range(
|
||||
KeyType const &key) const {
|
||||
return equal_range(key, hash_function(), key_eq());
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr std::pair<iterator, iterator> equal_range(KeyType const &key) {
|
||||
return equal_range(key, hash_function(), key_eq());
|
||||
}
|
||||
|
||||
/* bucket interface */
|
||||
constexpr std::size_t bucket_count() const { return storage_size; }
|
||||
constexpr std::size_t max_bucket_count() const { return storage_size; }
|
||||
|
||||
/* observers*/
|
||||
constexpr const hasher &hash_function() const { return tables_.hash_; }
|
||||
constexpr const key_equal &key_eq() const { return equal_; }
|
||||
|
||||
private:
|
||||
template <class This, class KeyType, class Hasher, class Equal>
|
||||
static inline constexpr auto &at_impl(This &&self, KeyType const &key,
|
||||
Hasher const &hash,
|
||||
Equal const &equal) {
|
||||
auto &kv = self.lookup(key, hash);
|
||||
if (equal(kv.first, key))
|
||||
return kv.second;
|
||||
else
|
||||
FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key"));
|
||||
}
|
||||
|
||||
template <class This, class KeyType, class Hasher, class Equal>
|
||||
static inline constexpr auto find_impl(This &&self, KeyType const &key,
|
||||
Hasher const &hash,
|
||||
Equal const &equal) {
|
||||
auto &kv = self.lookup(key, hash);
|
||||
if (equal(kv.first, key))
|
||||
return &kv;
|
||||
else
|
||||
return self.items_.end();
|
||||
}
|
||||
|
||||
template <class This, class KeyType, class Hasher, class Equal>
|
||||
static inline constexpr auto equal_range_impl(This &&self, KeyType const &key,
|
||||
Hasher const &hash,
|
||||
Equal const &equal) {
|
||||
auto &kv = self.lookup(key, hash);
|
||||
using kv_ptr = decltype(&kv);
|
||||
if (equal(kv.first, key))
|
||||
return std::pair<kv_ptr, kv_ptr>{&kv, &kv + 1};
|
||||
else
|
||||
return std::pair<kv_ptr, kv_ptr>{self.items_.end(), self.items_.end()};
|
||||
}
|
||||
|
||||
template <class This, class KeyType, class Hasher>
|
||||
static inline constexpr auto &lookup_impl(This &&self, KeyType const &key,
|
||||
Hasher const &hash) {
|
||||
return self.items_[self.tables_.lookup(key, hash)];
|
||||
}
|
||||
|
||||
template <class KeyType, class Hasher>
|
||||
constexpr auto const &lookup(KeyType const &key, Hasher const &hash) const {
|
||||
return lookup_impl(*this, key, hash);
|
||||
}
|
||||
template <class KeyType, class Hasher>
|
||||
constexpr auto &lookup(KeyType const &key, Hasher const &hash) {
|
||||
return lookup_impl(*this, key, hash);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U, std::size_t N>
|
||||
constexpr auto make_unordered_map(std::pair<T, U> const (&items)[N]) {
|
||||
return unordered_map<T, U, N>{items};
|
||||
}
|
||||
|
||||
template <typename T, typename U, std::size_t N, typename Hasher,
|
||||
typename Equal>
|
||||
constexpr auto make_unordered_map(std::pair<T, U> const (&items)[N],
|
||||
Hasher const &hash = elsa<T>{},
|
||||
Equal const &equal = std::equal_to<T>{}) {
|
||||
return unordered_map<T, U, N, Hasher, Equal>{items, hash, equal};
|
||||
}
|
||||
|
||||
template <typename T, typename U, std::size_t N>
|
||||
constexpr auto make_unordered_map(std::array<std::pair<T, U>, N> const &items) {
|
||||
return unordered_map<T, U, N>{items};
|
||||
}
|
||||
|
||||
template <typename T, typename U, std::size_t N, typename Hasher,
|
||||
typename Equal>
|
||||
constexpr auto make_unordered_map(std::array<std::pair<T, U>, N> const &items,
|
||||
Hasher const &hash = elsa<T>{},
|
||||
Equal const &equal = std::equal_to<T>{}) {
|
||||
return unordered_map<T, U, N, Hasher, Equal>{items, hash, equal};
|
||||
}
|
||||
|
||||
} // namespace frozen
|
||||
|
||||
#endif
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Frozen
|
||||
* Copyright 2016 QuarksLab
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef FROZEN_LETITGO_UNORDERED_SET_H
|
||||
#define FROZEN_LETITGO_UNORDERED_SET_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "frozen/bits/basic_types.h"
|
||||
#include "frozen/bits/constexpr_assert.h"
|
||||
#include "frozen/bits/elsa.h"
|
||||
#include "frozen/bits/pmh.h"
|
||||
#include "frozen/bits/version.h"
|
||||
#include "frozen/random.h"
|
||||
|
||||
namespace frozen {
|
||||
|
||||
namespace bits {
|
||||
|
||||
struct Get {
|
||||
template <class T>
|
||||
constexpr T const &operator()(T const &key) const {
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace bits
|
||||
|
||||
template <class Key, std::size_t N, typename Hash = elsa<Key>,
|
||||
class KeyEqual = std::equal_to<Key>>
|
||||
class unordered_set {
|
||||
static constexpr std::size_t storage_size =
|
||||
bits::next_highest_power_of_two(N) *
|
||||
(N < 32 ? 2 : 1); // size adjustment to prevent high collision rate for
|
||||
// small sets
|
||||
using container_type = bits::carray<Key, N>;
|
||||
using tables_type = bits::pmh_tables<storage_size, Hash>;
|
||||
|
||||
KeyEqual const equal_;
|
||||
container_type keys_;
|
||||
tables_type tables_;
|
||||
|
||||
public:
|
||||
/* typedefs */
|
||||
using key_type = Key;
|
||||
using value_type = Key;
|
||||
using size_type = typename container_type::size_type;
|
||||
using difference_type = typename container_type::difference_type;
|
||||
using hasher = Hash;
|
||||
using key_equal = KeyEqual;
|
||||
using const_reference = typename container_type::const_reference;
|
||||
using reference = const_reference;
|
||||
using const_pointer = typename container_type::const_pointer;
|
||||
using pointer = const_pointer;
|
||||
using const_iterator = const_pointer;
|
||||
using iterator = const_iterator;
|
||||
|
||||
public:
|
||||
/* constructors */
|
||||
unordered_set(unordered_set const &) = default;
|
||||
constexpr unordered_set(container_type keys, Hash const &hash,
|
||||
KeyEqual const &equal)
|
||||
: equal_{equal},
|
||||
keys_{keys},
|
||||
tables_{bits::make_pmh_tables<storage_size>(keys_, hash, bits::Get{},
|
||||
default_prg_t{})} {}
|
||||
explicit constexpr unordered_set(container_type keys)
|
||||
: unordered_set{keys, Hash{}, KeyEqual{}} {}
|
||||
|
||||
constexpr unordered_set(std::initializer_list<Key> keys)
|
||||
: unordered_set{keys, Hash{}, KeyEqual{}} {}
|
||||
|
||||
constexpr unordered_set(std::initializer_list<Key> keys, Hash const &hash,
|
||||
KeyEqual const &equal)
|
||||
: unordered_set{container_type{keys}, hash, equal} {
|
||||
constexpr_assert(
|
||||
keys.size() == N,
|
||||
"Inconsistent initializer_list size and type size argument");
|
||||
}
|
||||
|
||||
/* iterators */
|
||||
constexpr const_iterator begin() const { return keys_.begin(); }
|
||||
constexpr const_iterator end() const { return keys_.end(); }
|
||||
constexpr const_iterator cbegin() const { return keys_.cbegin(); }
|
||||
constexpr const_iterator cend() const { return keys_.cend(); }
|
||||
|
||||
/* capacity */
|
||||
constexpr bool empty() const { return !N; }
|
||||
constexpr size_type size() const { return N; }
|
||||
constexpr size_type max_size() const { return N; }
|
||||
|
||||
/* lookup */
|
||||
template <class KeyType, class Hasher, class Equal>
|
||||
constexpr std::size_t count(KeyType const &key, Hasher const &hash,
|
||||
Equal const &equal) const {
|
||||
auto const k = lookup(key, hash);
|
||||
return equal(k, key);
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr std::size_t count(KeyType const &key) const {
|
||||
return count(key, hash_function(), key_eq());
|
||||
}
|
||||
|
||||
template <class KeyType, class Hasher, class Equal>
|
||||
constexpr const_iterator find(KeyType const &key, Hasher const &hash,
|
||||
Equal const &equal) const {
|
||||
auto const &k = lookup(key, hash);
|
||||
if (equal(k, key))
|
||||
return &k;
|
||||
else
|
||||
return keys_.end();
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr const_iterator find(KeyType const &key) const {
|
||||
return find(key, hash_function(), key_eq());
|
||||
}
|
||||
|
||||
template <class KeyType, class Hasher, class Equal>
|
||||
constexpr std::pair<const_iterator, const_iterator> equal_range(
|
||||
KeyType const &key, Hasher const &hash, Equal const &equal) const {
|
||||
auto const &k = lookup(key, hash);
|
||||
if (equal(k, key))
|
||||
return {&k, &k + 1};
|
||||
else
|
||||
return {keys_.end(), keys_.end()};
|
||||
}
|
||||
template <class KeyType>
|
||||
constexpr std::pair<const_iterator, const_iterator> equal_range(
|
||||
KeyType const &key) const {
|
||||
return equal_range(key, hash_function(), key_eq());
|
||||
}
|
||||
|
||||
/* bucket interface */
|
||||
constexpr std::size_t bucket_count() const { return storage_size; }
|
||||
constexpr std::size_t max_bucket_count() const { return storage_size; }
|
||||
|
||||
/* observers*/
|
||||
constexpr const hasher &hash_function() const { return tables_.hash_; }
|
||||
constexpr const key_equal &key_eq() const { return equal_; }
|
||||
|
||||
private:
|
||||
template <class KeyType, class Hasher>
|
||||
constexpr auto const &lookup(KeyType const &key, Hasher const &hash) const {
|
||||
return keys_[tables_.lookup(key, hash)];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
constexpr auto make_unordered_set(T const (&keys)[N]) {
|
||||
return unordered_set<T, N>{keys};
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N, typename Hasher, typename Equal>
|
||||
constexpr auto make_unordered_set(T const (&keys)[N], Hasher const &hash,
|
||||
Equal const &equal) {
|
||||
return unordered_set<T, N, Hasher, Equal>{keys, hash, equal};
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
constexpr auto make_unordered_set(std::array<T, N> const &keys) {
|
||||
return unordered_set<T, N>{keys};
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N, typename Hasher, typename Equal>
|
||||
constexpr auto make_unordered_set(std::array<T, N> const &keys,
|
||||
Hasher const &hash, Equal const &equal) {
|
||||
return unordered_set<T, N, Hasher, Equal>{keys, hash, equal};
|
||||
}
|
||||
|
||||
#ifdef FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
|
||||
|
||||
template <class T, class... Args>
|
||||
unordered_set(T, Args...) -> unordered_set<T, sizeof...(Args) + 1>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace frozen
|
||||
|
||||
#endif
|
||||
Vendored
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Vendored
+1301
File diff suppressed because it is too large
Load Diff
Vendored
+117
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#if (defined(_MSC_VER) && _MSC_VER >= 1920 && _MSVC_LANG >= 202002L) || \
|
||||
(!defined(_MSC_VER) && defined(__cplusplus) && __cplusplus >= 202002L)
|
||||
#include <bit>
|
||||
#endif
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#if defined __clang__
|
||||
#define IGUANA_INLINE __attribute__((always_inline)) inline
|
||||
#define IGUANA__INLINE_LAMBDA __attribute__((always_inline)) constexpr
|
||||
#elif defined _MSC_VER
|
||||
#define IGUANA_INLINE __forceinline
|
||||
#define IGUANA__INLINE_LAMBDA constexpr
|
||||
#else
|
||||
#define IGUANA_INLINE __attribute__((always_inline)) inline
|
||||
#define IGUANA__INLINE_LAMBDA constexpr __attribute__((always_inline))
|
||||
#endif
|
||||
|
||||
#if __has_cpp_attribute(likely) && __has_cpp_attribute(unlikely) && \
|
||||
__cplusplus >= 202002L
|
||||
#define IGUANA_LIKELY [[likely]]
|
||||
#define IGUANA_UNLIKELY [[unlikely]]
|
||||
#else
|
||||
#define IGUANA_LIKELY
|
||||
#define IGUANA_UNLIKELY
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#if _MSC_VER >= 1920 && _MSVC_LANG >= 202002L
|
||||
IGUANA_INLINE int countr_zero(unsigned long long x) {
|
||||
return std::countr_zero(x);
|
||||
}
|
||||
template <typename T>
|
||||
constexpr inline bool contiguous_iterator = std::contiguous_iterator<T>;
|
||||
|
||||
#else
|
||||
IGUANA_INLINE int countr_zero(unsigned long long n) {
|
||||
// x will never be zero in iguana
|
||||
#if defined(_MSC_VER) && defined(_M_X64)
|
||||
unsigned long c;
|
||||
_BitScanForward64(&c, n);
|
||||
return static_cast<int>(c);
|
||||
#elif defined(_MSC_VER) && defined(_M_IX86)
|
||||
unsigned long c;
|
||||
if (static_cast<uint32_t>(n) != 0) {
|
||||
_BitScanForward(&c, static_cast<uint32_t>(n));
|
||||
return static_cast<int>(c);
|
||||
}
|
||||
else {
|
||||
_BitScanForward(&c, static_cast<uint32_t>(n >> 32));
|
||||
return static_cast<int>(c) + 32;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template <typename T>
|
||||
using remove_cvref_t =
|
||||
typename remove_cv<typename remove_reference<T>::type>::type;
|
||||
}
|
||||
template <typename T>
|
||||
constexpr inline bool contiguous_iterator =
|
||||
std::is_same_v<std::remove_cvref_t<T>,
|
||||
typename std::vector<char>::iterator> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>,
|
||||
typename std::vector<char>::const_iterator> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>, typename std::string::iterator> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>,
|
||||
typename std::string::const_iterator> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>, char *> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>,
|
||||
typename std::string_view::iterator> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>,
|
||||
typename std::string_view::const_iterator>;
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
IGUANA_INLINE int countr_zero(unsigned long long x) {
|
||||
return std::countr_zero(x);
|
||||
}
|
||||
template <typename T>
|
||||
constexpr inline bool contiguous_iterator = std::contiguous_iterator<T>;
|
||||
#else
|
||||
IGUANA_INLINE int countr_zero(unsigned long long x) {
|
||||
// x will never be zero in iguana
|
||||
return __builtin_ctzll(x);
|
||||
}
|
||||
namespace std {
|
||||
template <typename T>
|
||||
using remove_cvref_t =
|
||||
typename remove_cv<typename remove_reference<T>::type>::type;
|
||||
}
|
||||
template <typename T>
|
||||
constexpr inline bool contiguous_iterator =
|
||||
std::is_same_v<std::remove_cvref_t<T>,
|
||||
typename std::vector<char>::iterator> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>,
|
||||
typename std::vector<char>::const_iterator> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>, typename std::string::iterator> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>,
|
||||
typename std::string::const_iterator> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>, char *> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>,
|
||||
typename std::string_view::iterator> ||
|
||||
std::is_same_v<std::remove_cvref_t<T>,
|
||||
typename std::string_view::const_iterator>;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
#include <charconv>
|
||||
|
||||
#include "dragonbox_to_chars.h"
|
||||
#include "fast_float.h"
|
||||
#include "iguana/define.h"
|
||||
#include "itoa.hpp"
|
||||
|
||||
namespace iguana {
|
||||
template <typename T>
|
||||
struct is_char_type
|
||||
: std::disjunction<std::is_same<T, char>, std::is_same<T, wchar_t>,
|
||||
std::is_same<T, char16_t>, std::is_same<T, char32_t>> {};
|
||||
|
||||
inline void *to_chars_float(...) {
|
||||
throw std::runtime_error("not allowed to invoke");
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename T, typename Ret = decltype(to_chars_float(
|
||||
std::declval<T>(), std::declval<char *>()))>
|
||||
using return_of_tochars = std::conditional_t<std::is_same_v<Ret, char *>,
|
||||
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<std::true_type>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
// check_number==true: check if the string [first, last) is a legal number
|
||||
template <bool check_number = true, typename U>
|
||||
std::pair<const char *, std::errc> from_chars(const char *first,
|
||||
const char *last, U &value) {
|
||||
using T = std::decay_t<U>;
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
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};
|
||||
}
|
||||
}
|
||||
|
||||
// not support uint8 for now
|
||||
template <typename T>
|
||||
char *to_chars(char *buffer, T value) noexcept {
|
||||
using U = std::decay_t<T>;
|
||||
if constexpr (std::is_floating_point_v<U>) {
|
||||
if constexpr (has_to_chars_float::value) {
|
||||
return static_cast<char *>(to_chars_float(value, buffer));
|
||||
}
|
||||
else {
|
||||
return jkj::dragonbox::to_chars(value, buffer);
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_signed_v<U> && (sizeof(U) >= 8)) {
|
||||
return xtoa(value, buffer, 10, 1); // int64_t
|
||||
}
|
||||
else if constexpr (std::is_unsigned_v<U> && (sizeof(U) >= 8)) {
|
||||
return xtoa(value, buffer, 10, 0); // uint64_t
|
||||
}
|
||||
else if constexpr (std::is_integral_v<U> && (sizeof(U) > 1)) {
|
||||
return itoa_fwd(value, buffer); // only support more than 2 bytes intergal
|
||||
}
|
||||
else if constexpr (!is_char_type<U>::value) {
|
||||
return itoa_fwd(static_cast<int>(value),
|
||||
buffer); // only support more than 2 bytes intergal
|
||||
}
|
||||
else {
|
||||
static_assert(!sizeof(U), "only support arithmetic type except char type");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace iguana
|
||||
+3044
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,629 @@
|
||||
// Copyright 2020-2022 Junekey Jeon
|
||||
//
|
||||
// The contents of this file may be used under the terms of
|
||||
// the Apache License v2.0 with LLVM Exceptions.
|
||||
//
|
||||
// (See accompanying file LICENSE-Apache or copy at
|
||||
// https://llvm.org/foundation/relicensing/LICENSE.txt)
|
||||
//
|
||||
// Alternatively, the contents of this file may be used under the terms of
|
||||
// the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE-Boost or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, this software
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied.
|
||||
|
||||
#ifndef JKJ_HEADER_DRAGONBOX_TO_CHARS
|
||||
#define JKJ_HEADER_DRAGONBOX_TO_CHARS
|
||||
|
||||
#include "dragonbox.h"
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define JKJ_FORCEINLINE inline __attribute__((always_inline))
|
||||
#elif defined(_MSC_VER)
|
||||
#define JKJ_FORCEINLINE __forceinline
|
||||
#else
|
||||
#define JKJ_FORCEINLINE inline
|
||||
#endif
|
||||
|
||||
namespace jkj::dragonbox {
|
||||
namespace to_chars_detail {
|
||||
template <class Float, class FloatTraits>
|
||||
extern char *to_chars(typename FloatTraits::carrier_uint significand,
|
||||
int exponent, char *buffer) noexcept;
|
||||
|
||||
// These "//"'s are to prevent clang-format to ruin this nice alignment.
|
||||
// Thanks to reddit user u/mcmcc:
|
||||
// https://www.reddit.com/r/cpp/comments/so3wx9/dragonbox_110_is_released_a_fast_floattostring/hw8z26r/?context=3
|
||||
static constexpr char radix_100_table[] = {
|
||||
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', //
|
||||
'0', '5', '0', '6', '0', '7', '0', '8', '0', '9', //
|
||||
'1', '0', '1', '1', '1', '2', '1', '3', '1', '4', //
|
||||
'1', '5', '1', '6', '1', '7', '1', '8', '1', '9', //
|
||||
'2', '0', '2', '1', '2', '2', '2', '3', '2', '4', //
|
||||
'2', '5', '2', '6', '2', '7', '2', '8', '2', '9', //
|
||||
'3', '0', '3', '1', '3', '2', '3', '3', '3', '4', //
|
||||
'3', '5', '3', '6', '3', '7', '3', '8', '3', '9', //
|
||||
'4', '0', '4', '1', '4', '2', '4', '3', '4', '4', //
|
||||
'4', '5', '4', '6', '4', '7', '4', '8', '4', '9', //
|
||||
'5', '0', '5', '1', '5', '2', '5', '3', '5', '4', //
|
||||
'5', '5', '5', '6', '5', '7', '5', '8', '5', '9', //
|
||||
'6', '0', '6', '1', '6', '2', '6', '3', '6', '4', //
|
||||
'6', '5', '6', '6', '6', '7', '6', '8', '6', '9', //
|
||||
'7', '0', '7', '1', '7', '2', '7', '3', '7', '4', //
|
||||
'7', '5', '7', '6', '7', '7', '7', '8', '7', '9', //
|
||||
'8', '0', '8', '1', '8', '2', '8', '3', '8', '4', //
|
||||
'8', '5', '8', '6', '8', '7', '8', '8', '8', '9', //
|
||||
'9', '0', '9', '1', '9', '2', '9', '3', '9', '4', //
|
||||
'9', '5', '9', '6', '9', '7', '9', '8', '9', '9' //
|
||||
};
|
||||
static constexpr char radix_100_head_table[] = {
|
||||
'0', '.', '1', '.', '2', '.', '3', '.', '4', '.', //
|
||||
'5', '.', '6', '.', '7', '.', '8', '.', '9', '.', //
|
||||
'1', '.', '1', '.', '1', '.', '1', '.', '1', '.', //
|
||||
'1', '.', '1', '.', '1', '.', '1', '.', '1', '.', //
|
||||
'2', '.', '2', '.', '2', '.', '2', '.', '2', '.', //
|
||||
'2', '.', '2', '.', '2', '.', '2', '.', '2', '.', //
|
||||
'3', '.', '3', '.', '3', '.', '3', '.', '3', '.', //
|
||||
'3', '.', '3', '.', '3', '.', '3', '.', '3', '.', //
|
||||
'4', '.', '4', '.', '4', '.', '4', '.', '4', '.', //
|
||||
'4', '.', '4', '.', '4', '.', '4', '.', '4', '.', //
|
||||
'5', '.', '5', '.', '5', '.', '5', '.', '5', '.', //
|
||||
'5', '.', '5', '.', '5', '.', '5', '.', '5', '.', //
|
||||
'6', '.', '6', '.', '6', '.', '6', '.', '6', '.', //
|
||||
'6', '.', '6', '.', '6', '.', '6', '.', '6', '.', //
|
||||
'7', '.', '7', '.', '7', '.', '7', '.', '7', '.', //
|
||||
'7', '.', '7', '.', '7', '.', '7', '.', '7', '.', //
|
||||
'8', '.', '8', '.', '8', '.', '8', '.', '8', '.', //
|
||||
'8', '.', '8', '.', '8', '.', '8', '.', '8', '.', //
|
||||
'9', '.', '9', '.', '9', '.', '9', '.', '9', '.', //
|
||||
'9', '.', '9', '.', '9', '.', '9', '.', '9', '.' //
|
||||
};
|
||||
|
||||
// These digit generation routines are inspired by James Anhalt's itoa
|
||||
// algorithm: https://github.com/jeaiii/itoa The main idea is for given n, find
|
||||
// y such that floor(10^k * y / 2^32) = n holds, where k is an appropriate
|
||||
// integer depending on the length of n. For example, if n = 1234567, we set k
|
||||
// = 6. In this case, we have floor(y / 2^32) = 1, floor(10^2 * ((10^0 * y) mod
|
||||
// 2^32) / 2^32) = 23, floor(10^2 * ((10^2 * y) mod 2^32) / 2^32) = 45, and
|
||||
// floor(10^2 * ((10^4 * y) mod 2^32) / 2^32) = 67.
|
||||
// See https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ for more
|
||||
// explanation.
|
||||
|
||||
JKJ_FORCEINLINE static void print_9_digits(std::uint32_t s32, int &exponent,
|
||||
char *&buffer) noexcept {
|
||||
// -- IEEE-754 binary32
|
||||
// Since we do not cut trailing zeros in advance, s32 must be of 6~9 digits
|
||||
// unless the original input was subnormal.
|
||||
// In particular, when it is of 9 digits it shouldn't have any trailing zeros.
|
||||
// -- IEEE-754 binary64
|
||||
// In this case, s32 must be of 7~9 digits unless the input is subnormal,
|
||||
// and it shouldn't have any trailing zeros if it is of 9 digits.
|
||||
if (s32 >= 1'0000'0000) {
|
||||
// 9 digits.
|
||||
// 1441151882 = ceil(2^57 / 1'0000'0000) + 1
|
||||
auto prod = s32 * std::uint64_t(1441151882);
|
||||
prod >>= 25;
|
||||
std::memcpy(buffer, radix_100_head_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 6, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 8, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
|
||||
|
||||
exponent += 8;
|
||||
buffer += 10;
|
||||
}
|
||||
else if (s32 >= 100'0000) {
|
||||
// 7 or 8 digits.
|
||||
// 281474978 = ceil(2^48 / 100'0000) + 1
|
||||
auto prod = s32 * std::uint64_t(281474978);
|
||||
prod >>= 16;
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
// If s32 is of 8 digits, increase the exponent by 7.
|
||||
// Otherwise, increase it by 6.
|
||||
exponent += (6 + unsigned(two_digits >= 10));
|
||||
|
||||
// Write the first digit and the decimal point.
|
||||
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
|
||||
// This third character may be overwritten later but we don't care.
|
||||
buffer[2] = radix_100_table[two_digits * 2 + 1];
|
||||
|
||||
// Remaining 6 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 100'0000)) {
|
||||
// The number of characters actually written is:
|
||||
// 1, if only the first digit is nonzero, which means that either s32 is
|
||||
// of 7 digits or it is of 8 digits but the second digit is zero, or 3,
|
||||
// otherwise.
|
||||
// Note that buffer[2] is never zero if s32 is of 7 digits, because the
|
||||
// input is never zero.
|
||||
buffer +=
|
||||
(1 + (unsigned(two_digits >= 10) & unsigned(buffer[2] > '0')) * 2);
|
||||
}
|
||||
else {
|
||||
// At least one of the remaining 6 digits are nonzero.
|
||||
// After this adjustment, now the first destination becomes buffer + 2.
|
||||
buffer += unsigned(two_digits >= 10);
|
||||
|
||||
// Obtain the next two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
// Remaining 4 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 1'0000)) {
|
||||
buffer += (3 + unsigned(buffer[3] > '0'));
|
||||
}
|
||||
else {
|
||||
// At least one of the remaining 4 digits are nonzero.
|
||||
|
||||
// Obtain the next two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 4, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
// Remaining 2 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 100)) {
|
||||
buffer += (5 + unsigned(buffer[5] > '0'));
|
||||
}
|
||||
else {
|
||||
// Obtain the last two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 6, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
buffer += (7 + unsigned(buffer[7] > '0'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (s32 >= 1'0000) {
|
||||
// 5 or 6 digits.
|
||||
// 429497 = ceil(2^32 / 1'0000)
|
||||
auto prod = s32 * std::uint64_t(429497);
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
|
||||
// If s32 is of 6 digits, increase the exponent by 5.
|
||||
// Otherwise, increase it by 4.
|
||||
exponent += (4 + unsigned(two_digits >= 10));
|
||||
|
||||
// Write the first digit and the decimal point.
|
||||
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
|
||||
// This third character may be overwritten later but we don't care.
|
||||
buffer[2] = radix_100_table[two_digits * 2 + 1];
|
||||
|
||||
// Remaining 4 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 1'0000)) {
|
||||
// The number of characters actually written is 1 or 3, similarly to the
|
||||
// case of 7 or 8 digits.
|
||||
buffer +=
|
||||
(1 + (unsigned(two_digits >= 10) & unsigned(buffer[2] > '0')) * 2);
|
||||
}
|
||||
else {
|
||||
// At least one of the remaining 4 digits are nonzero.
|
||||
// After this adjustment, now the first destination becomes buffer + 2.
|
||||
buffer += unsigned(two_digits >= 10);
|
||||
|
||||
// Obtain the next two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
// Remaining 2 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 100)) {
|
||||
buffer += (3 + unsigned(buffer[3] > '0'));
|
||||
}
|
||||
else {
|
||||
// Obtain the last two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 4, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
buffer += (5 + unsigned(buffer[5] > '0'));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (s32 >= 100) {
|
||||
// 3 or 4 digits.
|
||||
// 42949673 = ceil(2^32 / 100)
|
||||
auto prod = s32 * std::uint64_t(42949673);
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
|
||||
// If s32 is of 4 digits, increase the exponent by 3.
|
||||
// Otherwise, increase it by 2.
|
||||
exponent += (2 + int(two_digits >= 10));
|
||||
|
||||
// Write the first digit and the decimal point.
|
||||
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
|
||||
// This third character may be overwritten later but we don't care.
|
||||
buffer[2] = radix_100_table[two_digits * 2 + 1];
|
||||
|
||||
// Remaining 2 digits are all zero?
|
||||
if (std::uint32_t(prod) <= std::uint32_t((std::uint64_t(1) << 32) / 100)) {
|
||||
// The number of characters actually written is 1 or 3, similarly to the
|
||||
// case of 7 or 8 digits.
|
||||
buffer +=
|
||||
(1 + (unsigned(two_digits >= 10) & unsigned(buffer[2] > '0')) * 2);
|
||||
}
|
||||
else {
|
||||
// At least one of the remaining 2 digits are nonzero.
|
||||
// After this adjustment, now the first destination becomes buffer + 2.
|
||||
buffer += unsigned(two_digits >= 10);
|
||||
|
||||
// Obtain the last two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
buffer += (3 + unsigned(buffer[3] > '0'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 1 or 2 digits.
|
||||
// If s32 is of 2 digits, increase the exponent by 1.
|
||||
exponent += int(s32 >= 10);
|
||||
|
||||
// Write the first digit and the decimal point.
|
||||
std::memcpy(buffer, radix_100_head_table + s32 * 2, 2);
|
||||
// This third character may be overwritten later but we don't care.
|
||||
buffer[2] = radix_100_table[s32 * 2 + 1];
|
||||
|
||||
// The number of characters actually written is 1 or 3, similarly to the
|
||||
// case of 7 or 8 digits.
|
||||
buffer += (1 + (unsigned(s32 >= 10) & unsigned(buffer[2] > '0')) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
inline char *to_chars<float, default_float_traits<float>>(
|
||||
std::uint32_t s32, int exponent, char *buffer) noexcept {
|
||||
// Print significand.
|
||||
print_9_digits(s32, exponent, buffer);
|
||||
|
||||
// Print exponent and return
|
||||
if (exponent < 0) {
|
||||
std::memcpy(buffer, "E-", 2);
|
||||
buffer += 2;
|
||||
exponent = -exponent;
|
||||
}
|
||||
else {
|
||||
buffer[0] = 'E';
|
||||
buffer += 1;
|
||||
}
|
||||
|
||||
if (exponent >= 10) {
|
||||
std::memcpy(buffer, &radix_100_table[exponent * 2], 2);
|
||||
buffer += 2;
|
||||
}
|
||||
else {
|
||||
buffer[0] = char('0' + exponent);
|
||||
buffer += 1;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline char *to_chars<double, default_float_traits<double>>(
|
||||
std::uint64_t const significand, int exponent, char *buffer) noexcept {
|
||||
// Print significand by decomposing it into a 9-digit block and a 8-digit
|
||||
// block.
|
||||
std::uint32_t first_block, second_block;
|
||||
bool no_second_block;
|
||||
|
||||
if (significand >= 1'0000'0000) {
|
||||
first_block = std::uint32_t(significand / 1'0000'0000);
|
||||
second_block = std::uint32_t(significand) - first_block * 1'0000'0000;
|
||||
exponent += 8;
|
||||
no_second_block = (second_block == 0);
|
||||
}
|
||||
else {
|
||||
first_block = std::uint32_t(significand);
|
||||
no_second_block = true;
|
||||
}
|
||||
|
||||
if (no_second_block) {
|
||||
print_9_digits(first_block, exponent, buffer);
|
||||
}
|
||||
else {
|
||||
// We proceed similarly to print_9_digits(), but since we do not need to
|
||||
// remove trailing zeros, the procedure is a bit simpler.
|
||||
if (first_block >= 1'0000'0000) {
|
||||
// The input is of 17 digits, thus there should be no trailing zero at
|
||||
// all. The first block is of 9 digits. 1441151882 = ceil(2^57 /
|
||||
// 1'0000'0000) + 1
|
||||
auto prod = first_block * std::uint64_t(1441151882);
|
||||
prod >>= 25;
|
||||
std::memcpy(buffer, radix_100_head_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 6, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 8, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
// The second block is of 8 digits.
|
||||
// 281474978 = ceil(2^48 / 100'0000) + 1
|
||||
prod = second_block * std::uint64_t(281474978);
|
||||
prod >>= 16;
|
||||
prod += 1;
|
||||
std::memcpy(buffer + 10, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 12, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 14, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 16, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
exponent += 8;
|
||||
buffer += 18;
|
||||
}
|
||||
else {
|
||||
if (first_block >= 100'0000) {
|
||||
// 7 or 8 digits.
|
||||
// 281474978 = ceil(2^48 / 100'0000) + 1
|
||||
auto prod = first_block * std::uint64_t(281474978);
|
||||
prod >>= 16;
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
|
||||
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
|
||||
buffer[2] = radix_100_table[two_digits * 2 + 1];
|
||||
|
||||
exponent += (6 + unsigned(two_digits >= 10));
|
||||
buffer += unsigned(two_digits >= 10);
|
||||
|
||||
// Print remaining 6 digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 6, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
buffer += 8;
|
||||
}
|
||||
else if (first_block >= 1'0000) {
|
||||
// 5 or 6 digits.
|
||||
// 429497 = ceil(2^32 / 1'0000)
|
||||
auto prod = first_block * std::uint64_t(429497);
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
|
||||
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
|
||||
buffer[2] = radix_100_table[two_digits * 2 + 1];
|
||||
|
||||
exponent += (4 + unsigned(two_digits >= 10));
|
||||
buffer += unsigned(two_digits >= 10);
|
||||
|
||||
// Print remaining 4 digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
buffer += 6;
|
||||
}
|
||||
else if (first_block >= 100) {
|
||||
// 3 or 4 digits.
|
||||
// 42949673 = ceil(2^32 / 100)
|
||||
auto prod = first_block * std::uint64_t(42949673);
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
|
||||
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
|
||||
buffer[2] = radix_100_table[two_digits * 2 + 1];
|
||||
|
||||
exponent += (2 + unsigned(two_digits >= 10));
|
||||
buffer += unsigned(two_digits >= 10);
|
||||
|
||||
// Print remaining 2 digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
|
||||
2);
|
||||
|
||||
buffer += 4;
|
||||
}
|
||||
else {
|
||||
// 1 or 2 digits.
|
||||
std::memcpy(buffer, radix_100_head_table + first_block * 2, 2);
|
||||
buffer[2] = radix_100_table[first_block * 2 + 1];
|
||||
|
||||
exponent += unsigned(first_block >= 10);
|
||||
buffer += (2 + unsigned(first_block >= 10));
|
||||
}
|
||||
|
||||
// Next, print the second block.
|
||||
// The second block is of 8 digits, but we may have trailing zeros.
|
||||
// 281474978 = ceil(2^48 / 100'0000) + 1
|
||||
auto prod = second_block * std::uint64_t(281474978);
|
||||
prod >>= 16;
|
||||
prod += 1;
|
||||
auto two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
// Remaining 6 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 100'0000)) {
|
||||
buffer += (1 + unsigned(buffer[1] > '0'));
|
||||
}
|
||||
else {
|
||||
// Obtain the next two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
// Remaining 4 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 1'0000)) {
|
||||
buffer += (3 + unsigned(buffer[3] > '0'));
|
||||
}
|
||||
else {
|
||||
// Obtain the next two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 4, radix_100_table + two_digits * 2, 2);
|
||||
|
||||
// Remaining 2 digits are all zero?
|
||||
if (std::uint32_t(prod) <=
|
||||
std::uint32_t((std::uint64_t(1) << 32) / 100)) {
|
||||
buffer += (5 + unsigned(buffer[5] > '0'));
|
||||
}
|
||||
else {
|
||||
// Obtain the last two digits.
|
||||
prod = std::uint32_t(prod) * std::uint64_t(100);
|
||||
two_digits = std::uint32_t(prod >> 32);
|
||||
std::memcpy(buffer + 6, radix_100_table + two_digits * 2, 2);
|
||||
buffer += (7 + unsigned(buffer[7] > '0'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print exponent and return
|
||||
if (exponent < 0) {
|
||||
std::memcpy(buffer, "E-", 2);
|
||||
buffer += 2;
|
||||
exponent = -exponent;
|
||||
}
|
||||
else {
|
||||
buffer[0] = 'E';
|
||||
buffer += 1;
|
||||
}
|
||||
|
||||
if (exponent >= 100) {
|
||||
// d1 = exponent / 10; d2 = exponent % 10;
|
||||
// 6554 = ceil(2^16 / 10)
|
||||
auto prod = std::uint32_t(exponent) * std::uint32_t(6554);
|
||||
auto d1 = prod >> 16;
|
||||
prod = std::uint16_t(prod) * std::uint32_t(5); // * 10
|
||||
auto d2 = prod >> 15; // >> 16
|
||||
std::memcpy(buffer, &radix_100_table[d1 * 2], 2);
|
||||
buffer[2] = char('0' + d2);
|
||||
buffer += 3;
|
||||
}
|
||||
else if (exponent >= 10) {
|
||||
std::memcpy(buffer, &radix_100_table[exponent * 2], 2);
|
||||
buffer += 2;
|
||||
}
|
||||
else {
|
||||
buffer[0] = char('0' + exponent);
|
||||
buffer += 1;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Avoid needless ABI overhead incurred by tag dispatch.
|
||||
template <class PolicyHolder, class Float, class FloatTraits>
|
||||
char *to_chars_n_impl(float_bits<Float, FloatTraits> br,
|
||||
char *buffer) noexcept {
|
||||
auto const exponent_bits = br.extract_exponent_bits();
|
||||
auto const s = br.remove_exponent_bits(exponent_bits);
|
||||
|
||||
if (br.is_finite(exponent_bits)) {
|
||||
if (s.is_negative()) {
|
||||
*buffer = '-';
|
||||
++buffer;
|
||||
}
|
||||
if (br.is_nonzero()) {
|
||||
auto result = to_decimal<Float, FloatTraits>(
|
||||
s, exponent_bits, policy::sign::ignore, policy::trailing_zero::ignore,
|
||||
typename PolicyHolder::decimal_to_binary_rounding_policy{},
|
||||
typename PolicyHolder::binary_to_decimal_rounding_policy{},
|
||||
typename PolicyHolder::cache_policy{});
|
||||
return to_chars_detail::to_chars<Float, FloatTraits>(
|
||||
result.significand, result.exponent, buffer);
|
||||
}
|
||||
else {
|
||||
std::memcpy(buffer, "0E0", 3);
|
||||
return buffer + 3;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (s.has_all_zero_significand_bits()) {
|
||||
if (s.is_negative()) {
|
||||
*buffer = '-';
|
||||
++buffer;
|
||||
}
|
||||
std::memcpy(buffer, "Infinity", 8);
|
||||
return buffer + 8;
|
||||
}
|
||||
else {
|
||||
std::memcpy(buffer, "NaN", 3);
|
||||
return buffer + 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace to_chars_detail
|
||||
|
||||
// Returns the next-to-end position
|
||||
template <class Float, class FloatTraits = default_float_traits<Float>,
|
||||
class... Policies>
|
||||
char *to_chars_n(Float x, char *buffer, Policies... policies) noexcept {
|
||||
using namespace jkj::dragonbox::detail::policy_impl;
|
||||
using policy_holder = decltype(make_policy_holder(
|
||||
base_default_pair_list<
|
||||
base_default_pair<decimal_to_binary_rounding::base,
|
||||
decimal_to_binary_rounding::nearest_to_even>,
|
||||
base_default_pair<binary_to_decimal_rounding::base,
|
||||
binary_to_decimal_rounding::to_even>,
|
||||
base_default_pair<cache::base, cache::full>>{},
|
||||
policies...));
|
||||
|
||||
return to_chars_detail::to_chars_n_impl<policy_holder>(
|
||||
float_bits<Float, FloatTraits>(x), buffer);
|
||||
}
|
||||
|
||||
// Null-terminate and bypass the return value of fp_to_chars_n
|
||||
template <class Float, class FloatTraits = default_float_traits<Float>,
|
||||
class... Policies>
|
||||
char *to_chars(Float x, char *buffer, Policies... policies) noexcept {
|
||||
auto ptr = to_chars_n<Float, FloatTraits>(x, buffer, policies...);
|
||||
*ptr = '\0';
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Maximum required buffer size (excluding null-terminator)
|
||||
template <class FloatFormat>
|
||||
inline constexpr std::size_t max_output_string_length =
|
||||
std::is_same_v<FloatFormat, ieee754_binary32>
|
||||
?
|
||||
// sign(1) + significand(9) + decimal_point(1) + exp_marker(1) +
|
||||
// exp_sign(1) + exp(2)
|
||||
(1 + 9 + 1 + 1 + 1 + 2)
|
||||
:
|
||||
// format == ieee754_format::binary64
|
||||
// sign(1) + significand(17) + decimal_point(1) + exp_marker(1) +
|
||||
// exp_sign(1) + exp(3)
|
||||
(1 + 17 + 1 + 1 + 1 + 3);
|
||||
} // namespace jkj::dragonbox
|
||||
|
||||
#endif
|
||||
+3196
File diff suppressed because it is too large
Load Diff
+321
@@ -0,0 +1,321 @@
|
||||
//=== itoa.h - Fast integer to ascii conversion --*- C++ -*-//
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
// Copyright (c) 2016 Arturo Martin-de-Nicolas
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef DEC_ITOA_IMPL_H
|
||||
#define DEC_ITOA_IMPL_H
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring> // memcpy
|
||||
#include <type_traits>
|
||||
|
||||
namespace dec_ {
|
||||
|
||||
// Using a lookup table to convert binary numbers from 0 to 99
|
||||
// into ascii characters as described by Andrei Alexandrescu in
|
||||
// https://www.facebook.com/notes/facebook-engineering/
|
||||
// three-optimization-tips-for-c/10151361643253920/
|
||||
|
||||
template <typename T, size_t N, typename Gen, size_t... Is>
|
||||
constexpr auto generate_array(Gen&& item, std::index_sequence<Is...>) {
|
||||
return std::array<T, N>{{item(Is)...}};
|
||||
}
|
||||
|
||||
const std::array<char, 200> digits = generate_array<char, 200>(
|
||||
[](size_t i) {
|
||||
return char('0' + ((i % 2) ? ((i / 2) % 10) : ((i / 2) / 10)));
|
||||
},
|
||||
std::make_index_sequence<200>{});
|
||||
|
||||
// extern const std::array<char,200> digits;
|
||||
static inline uint16_t const& dd(uint8_t u) {
|
||||
return reinterpret_cast<uint16_t const*>(digits.data())[u];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static constexpr T pow10(size_t x) {
|
||||
return x ? 10 * pow10<T>(x - 1) : 1;
|
||||
}
|
||||
|
||||
// Division by a power of 10 is implemented using a multiplicative inverse.
|
||||
// This strength reduction is also done by optimizing compilers, but
|
||||
// presently the fastest results are produced by using the values
|
||||
// for the multiplication and the shift as given by the algorithm
|
||||
// described by Agner Fog in "Optimizing Subroutines in Assembly Language"
|
||||
//
|
||||
// http://www.agner.org/optimize/optimizing_assembly.pdf
|
||||
//
|
||||
// "Integer division by a constant (all processors)
|
||||
// A floating point number can be divided by a constant by multiplying
|
||||
// with the reciprocal. If we want to do the same with integers, we have
|
||||
// to scale the reciprocal by 2n and then shift the product to the right
|
||||
// by n. There are various algorithms for finding a suitable value of n
|
||||
// and compensating for rounding errors. The algorithm described below
|
||||
// was invented by Terje Mathisen, Norway, and not published elsewhere."
|
||||
|
||||
// using uint128_t = unsigned __int128;
|
||||
|
||||
template <typename UInt, bool A, UInt M, unsigned S>
|
||||
struct MulInv {
|
||||
using type = UInt;
|
||||
static constexpr bool a{A};
|
||||
static constexpr UInt m{M};
|
||||
static constexpr unsigned s{S};
|
||||
};
|
||||
template <int, int, class...>
|
||||
struct UT;
|
||||
template <int N, class T, class... Ts>
|
||||
struct UT<N, N, T, Ts...> {
|
||||
using U = T;
|
||||
};
|
||||
template <int N, int M, class T, class... Ts>
|
||||
struct UT<N, M, T, Ts...> {
|
||||
using U = typename UT<N, 2 * M, Ts...>::U;
|
||||
};
|
||||
template <int N>
|
||||
using MI = typename UT<N, 1, MulInv<uint8_t, 0, 205U, 11>,
|
||||
MulInv<uint16_t, 1, 41943U, 22>,
|
||||
MulInv<uint32_t, 0, 3518437209U, 45>,
|
||||
MulInv<uint64_t, 0, 12379400392853802749U, 90>>::U;
|
||||
template <int N>
|
||||
using U = typename MI<N>::type;
|
||||
|
||||
// struct QR holds the result of dividing an unsigned N-byte variable
|
||||
// by 10^N resulting in
|
||||
template <size_t N>
|
||||
struct QR {
|
||||
U<N> q; // quotient with fewer than 2*N decimal digits
|
||||
U<N / 2> r; // remainder with at most N decimal digits
|
||||
};
|
||||
template <size_t N>
|
||||
QR<N> static inline split(U<N> u) {
|
||||
constexpr MI<N> mi{};
|
||||
U<N> q = (mi.m * (U<2 * N>(u) + mi.a)) >> mi.s;
|
||||
return {q, U<N / 2>(u - q * pow10<U<N / 2>>(N))};
|
||||
}
|
||||
|
||||
enum Direction { Fwd, Rev };
|
||||
|
||||
template <Direction D>
|
||||
struct convert {
|
||||
//===----------------------------------------------------------===//
|
||||
// output the digits in either a forward or reverse direction
|
||||
// use memcpy so compiler handles alignment on target architecture.
|
||||
// Typically generates one store to memory with an optimizing
|
||||
// compiler for target architecture that supports unaligned access.
|
||||
//===----------------------------------------------------------===//
|
||||
|
||||
template <typename T>
|
||||
static inline char* out(char* p, T&& obj) {
|
||||
if (D == Rev)
|
||||
p -= sizeof(T);
|
||||
memcpy(p, reinterpret_cast<const void*>(&obj), sizeof(T));
|
||||
if (D == Fwd)
|
||||
p += sizeof(T);
|
||||
return p;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------===//
|
||||
// head: find most significant digit, skip leading zeros
|
||||
//===----------------------------------------------------------===//
|
||||
|
||||
// "x" contains quotient and remainder after division by 10^N
|
||||
// quotient is less than 10^N
|
||||
template <size_t N>
|
||||
static inline char* head(char* p, QR<N> x) {
|
||||
return (D == Fwd ? (tail(head(p, U<N / 2>(x.q)), x.r))
|
||||
: (head(tail(p, x.r), U<N / 2>(x.q))));
|
||||
}
|
||||
|
||||
// "u" is less than 10^2*N
|
||||
template <typename UInt, size_t N = sizeof(UInt)>
|
||||
static inline char* head(char* p, UInt u) {
|
||||
return (u < pow10<U<N>>(N) ? (head(p, U<N / 2>(u)))
|
||||
: (head<N>(p, split<N>(u))));
|
||||
}
|
||||
|
||||
// recursion base case, selected when "u" is one byte
|
||||
static inline char* head(char* p, U<1> u) {
|
||||
return (u < 10 ? (out<char>(p, '0' + u)) : (out(p, dd(u))));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------===//
|
||||
// tail: produce all digits including leading zeros
|
||||
//===----------------------------------------------------------===//
|
||||
|
||||
// recursive step, "u" is less than 10^2*N
|
||||
template <typename UInt, size_t N = sizeof(UInt)>
|
||||
static inline char* tail(char* p, UInt u) {
|
||||
QR<N> x = split<N>(u);
|
||||
return (D == Fwd ? (tail(tail(p, U<N / 2>(x.q)), x.r))
|
||||
: (tail(tail(p, x.r), U<N / 2>(x.q))));
|
||||
}
|
||||
|
||||
// recursion base case, selected when "u" is one byte
|
||||
static inline char* tail(char* p, U<1> u) { return out(p, dd(u)); }
|
||||
|
||||
//===----------------------------------------------------------===//
|
||||
// large values are >= 10^2*N
|
||||
// where x contains quotient and remainder after division by 10^N
|
||||
//===----------------------------------------------------------===//
|
||||
|
||||
template <size_t N>
|
||||
static inline char* large(char* p, QR<N> x) {
|
||||
QR<N> y = split<N>(x.q);
|
||||
return (D == Fwd ? (tail(tail(head(p, U<N / 2>(y.q)), y.r), x.r))
|
||||
: (head(tail(tail(p, x.r), y.r), U<N / 2>(y.q))));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------===//
|
||||
// handle values of "u" that might be >= 10^2*N
|
||||
// where N is the size of "u" in bytes
|
||||
//===----------------------------------------------------------===//
|
||||
|
||||
template <typename UInt, size_t N = sizeof(UInt)>
|
||||
static inline char* itoa(char* p, UInt u) {
|
||||
if (u < pow10<U<N>>(N))
|
||||
return head(p, U<N / 2>(u));
|
||||
QR<N> x = split<N>(u);
|
||||
return (u < pow10<U<N>>(2 * N) ? (head<N>(p, x)) : (large<N>(p, x)));
|
||||
}
|
||||
|
||||
// selected when "u" is one byte
|
||||
static inline char* itoa(char* p, U<1> u) {
|
||||
if (u < 10)
|
||||
return out<char>(p, '0' + u);
|
||||
if (u < 100)
|
||||
return out(p, dd(u));
|
||||
return (D == Fwd ? (out(out<char>(p, '0' + u / 100), dd(u % 100)))
|
||||
: (out<char>(out(p, dd(u % 100)), '0' + u / 100)));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------===//
|
||||
// handle unsigned and signed integral operands
|
||||
//===----------------------------------------------------------===//
|
||||
|
||||
// itoa: handle unsigned integral operands (selected by SFINAE)
|
||||
template <typename U, std::enable_if_t<!std::is_signed<U>::value &&
|
||||
std::is_integral<U>::value>* = nullptr>
|
||||
static inline char* itoa(U u, char* p) {
|
||||
return convert<D>::template itoa<>(p, u);
|
||||
}
|
||||
|
||||
// itoa: handle signed integral operands (selected by SFINAE)
|
||||
template <typename I, size_t N = sizeof(I),
|
||||
std::enable_if_t<std::is_signed<I>::value &&
|
||||
std::is_integral<I>::value>* = nullptr>
|
||||
static inline char* itoa(I i, char* p) {
|
||||
// Need "mask" to be filled with a copy of the sign bit.
|
||||
// If "i" is a negative value, then the result of "operator >>"
|
||||
// is implementation-defined, though usually it is an arithmetic
|
||||
// right shift that replicates the sign bit.
|
||||
// Use a conditional expression to be portable,
|
||||
// a good optimizing compiler generates an arithmetic right shift
|
||||
// and avoids the conditional branch.
|
||||
U<N> mask = i < 0 ? ~U<N>(0) : 0;
|
||||
// Now get the absolute value of "i" and cast to unsigned type U<N>.
|
||||
// Cannot use std::abs() because the result is undefined
|
||||
// in 2's complement systems for the most-negative value.
|
||||
// Want to avoid conditional branch for performance reasons since
|
||||
// CPU branch prediction will be ineffective when negative values
|
||||
// occur randomly.
|
||||
// Let "u" be "i" cast to unsigned type U<N>.
|
||||
// Subtract "u" from 2*u if "i" is positive or 0 if "i" is negative.
|
||||
// This yields the absolute value with the desired type without
|
||||
// using a conditional branch and without invoking undefined or
|
||||
// implementation defined behavior:
|
||||
U<N> u = ((2 * U<N>(i)) & ~mask) - U<N>(i);
|
||||
// Unconditionally store a minus sign when producing digits
|
||||
// in a forward direction and increment the pointer only if
|
||||
// the value is in fact negative.
|
||||
// This avoids a conditional branch and is safe because we will
|
||||
// always produce at least one digit and it will overwrite the
|
||||
// minus sign when the value is not negative.
|
||||
if (D == Fwd) {
|
||||
*p = '-';
|
||||
p += (mask & 1);
|
||||
}
|
||||
p = convert<D>::template itoa<>(p, u);
|
||||
if (D == Rev && mask)
|
||||
*--p = '-';
|
||||
return p;
|
||||
}
|
||||
};
|
||||
} // namespace dec_
|
||||
|
||||
// Programming interface: itoa_fwd, itoa_rev
|
||||
template <typename I>
|
||||
char* itoa_fwd(I i, char* p) {
|
||||
return dec_::convert<dec_::Fwd>::itoa(i, p);
|
||||
}
|
||||
|
||||
inline char* xtoa(long long sval, char* str, int radix, int signedp) {
|
||||
unsigned long long uval;
|
||||
unsigned int uradix = radix;
|
||||
char* sp = str;
|
||||
char* sp2;
|
||||
char* sp3;
|
||||
|
||||
/* If signed, store sign at start of buffer for negative base-10 values */
|
||||
if (signedp && (10 == uradix) && (0 > sval)) {
|
||||
*sp++ = '-';
|
||||
uval = -sval;
|
||||
}
|
||||
else {
|
||||
uval = sval;
|
||||
}
|
||||
sp2 = sp;
|
||||
|
||||
do {
|
||||
unsigned int rem = uval % uradix;
|
||||
uval /= uradix;
|
||||
if (10 > rem) {
|
||||
*sp++ = '0' + (char)rem;
|
||||
}
|
||||
else {
|
||||
*sp++ = 'A' + (char)rem - 10;
|
||||
}
|
||||
} while (0 < uval);
|
||||
|
||||
/* Mark end of string */
|
||||
sp3 = sp;
|
||||
*sp-- = 0;
|
||||
|
||||
/* Reverse string contents (excluding sign) in place */
|
||||
while (sp2 < sp) {
|
||||
char tmp = *sp2;
|
||||
*sp2++ = *sp;
|
||||
*sp-- = tmp;
|
||||
}
|
||||
|
||||
return sp3;
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
char* itoa_rev(I i, char* p) {
|
||||
return dec_::convert<dec_::Rev>::itoa(i, p);
|
||||
}
|
||||
|
||||
#endif // DEC_ITOA_IMPL_H
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace iguana {
|
||||
|
||||
struct sint32_t {
|
||||
using value_type = int32_t;
|
||||
int32_t val;
|
||||
bool operator==(const sint32_t& other) const { return val == other.val; }
|
||||
};
|
||||
|
||||
inline bool operator==(sint32_t value1, int32_t value2) {
|
||||
return value1.val == value2;
|
||||
}
|
||||
|
||||
// for key in std::map
|
||||
inline bool operator<(const sint32_t& lhs, const sint32_t& rhs) {
|
||||
return lhs.val < rhs.val;
|
||||
}
|
||||
|
||||
struct sint64_t {
|
||||
using value_type = int64_t;
|
||||
int64_t val;
|
||||
bool operator==(const sint64_t& other) const { return val == other.val; }
|
||||
};
|
||||
|
||||
inline bool operator==(sint64_t value1, int64_t value2) {
|
||||
return value1.val == value2;
|
||||
}
|
||||
|
||||
inline bool operator<(const sint64_t& lhs, const sint64_t& rhs) {
|
||||
return lhs.val < rhs.val;
|
||||
}
|
||||
|
||||
struct fixed32_t {
|
||||
using value_type = uint32_t;
|
||||
uint32_t val;
|
||||
bool operator==(const fixed32_t& other) const { return val == other.val; }
|
||||
};
|
||||
|
||||
inline bool operator==(fixed32_t value1, uint32_t value2) {
|
||||
return value1.val == value2;
|
||||
}
|
||||
|
||||
inline bool operator<(const fixed32_t& lhs, const fixed32_t& rhs) {
|
||||
return lhs.val < rhs.val;
|
||||
}
|
||||
|
||||
struct fixed64_t {
|
||||
using value_type = uint64_t;
|
||||
uint64_t val;
|
||||
bool operator==(const fixed64_t& other) const { return val == other.val; }
|
||||
};
|
||||
|
||||
inline bool operator==(fixed64_t value1, uint64_t value2) {
|
||||
return value1.val == value2;
|
||||
}
|
||||
|
||||
inline bool operator<(const fixed64_t& lhs, const fixed64_t& rhs) {
|
||||
return lhs.val < rhs.val;
|
||||
}
|
||||
|
||||
struct sfixed32_t {
|
||||
using value_type = int32_t;
|
||||
int32_t val;
|
||||
bool operator==(const sfixed32_t& other) const { return val == other.val; }
|
||||
};
|
||||
|
||||
inline bool operator==(sfixed32_t value1, int32_t value2) {
|
||||
return value1.val == value2;
|
||||
}
|
||||
|
||||
inline bool operator<(const sfixed32_t& lhs, const sfixed32_t& rhs) {
|
||||
return lhs.val < rhs.val;
|
||||
}
|
||||
|
||||
struct sfixed64_t {
|
||||
using value_type = int64_t;
|
||||
int64_t val;
|
||||
bool operator==(const sfixed64_t& other) const { return val == other.val; }
|
||||
};
|
||||
|
||||
inline bool operator==(sfixed64_t value1, int64_t value2) {
|
||||
return value1.val == value2;
|
||||
}
|
||||
|
||||
inline bool operator<(const sfixed64_t& lhs, const sfixed64_t& rhs) {
|
||||
return lhs.val < rhs.val;
|
||||
}
|
||||
|
||||
template <typename Type, typename T = std::decay_t<Type>>
|
||||
constexpr bool is_pb_type_v =
|
||||
std::is_same_v<sint32_t, T> || std::is_same_v<sint64_t, T> ||
|
||||
std::is_same_v<fixed32_t, T> || std::is_same_v<fixed64_t, T> ||
|
||||
std::is_same_v<sfixed32_t, T> || std::is_same_v<sfixed64_t, T>;
|
||||
|
||||
} // namespace iguana
|
||||
|
||||
// for key in std::unordered_map
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<iguana::sint32_t> {
|
||||
size_t operator()(const iguana::sint32_t& x) const noexcept {
|
||||
return std::hash<int32_t>()(x.val);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<iguana::sint64_t> {
|
||||
size_t operator()(const iguana::sint64_t& x) const noexcept {
|
||||
return std::hash<int64_t>()(x.val);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<iguana::fixed32_t> {
|
||||
size_t operator()(const iguana::fixed32_t& x) const noexcept {
|
||||
return std::hash<uint32_t>()(x.val);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<iguana::fixed64_t> {
|
||||
size_t operator()(const iguana::fixed64_t& x) const noexcept {
|
||||
return std::hash<uint64_t>()(x.val);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<iguana::sfixed32_t> {
|
||||
size_t operator()(const iguana::sfixed32_t& x) const noexcept {
|
||||
return std::hash<int32_t>()(x.val);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<iguana::sfixed64_t> {
|
||||
size_t operator()(const iguana::sfixed64_t& x) const noexcept {
|
||||
return std::hash<int64_t>()(x.val);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace iguana::detail {
|
||||
|
||||
#if __cpp_lib_string_resize_and_overwrite >= 202110L
|
||||
template <typename ch>
|
||||
inline void resize(std::basic_string<ch> &str, std::size_t sz) {
|
||||
str.resize_and_overwrite(sz, [sz](ch *, std::size_t) {
|
||||
return sz;
|
||||
});
|
||||
}
|
||||
#elif (defined(_MSC_VER) && _MSC_VER <= 1920)
|
||||
// old msvc don't support visit private, discard it.
|
||||
|
||||
#else
|
||||
|
||||
template <typename Function, Function func_ptr>
|
||||
class string_thief {
|
||||
public:
|
||||
friend void string_set_length_hacker(std::string &self, std::size_t sz) {
|
||||
#if defined(_MSVC_STL_VERSION)
|
||||
(self.*func_ptr)._Myval2._Mysize = sz;
|
||||
#else
|
||||
#if defined(_LIBCPP_VERSION)
|
||||
(self.*func_ptr)(sz);
|
||||
#else
|
||||
#if (_GLIBCXX_USE_CXX11_ABI == 0) && defined(__GLIBCXX__)
|
||||
(self.*func_ptr)()->_M_set_length_and_sharable(sz);
|
||||
#else
|
||||
#if defined(__GLIBCXX__)
|
||||
(self.*func_ptr)(sz);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(__GLIBCXX__) // libstdc++
|
||||
#if (_GLIBCXX_USE_CXX11_ABI == 0)
|
||||
template class string_thief<decltype(&std::string::_M_rep),
|
||||
&std::string::_M_rep>;
|
||||
#else
|
||||
template class string_thief<decltype(&std::string::_M_set_length),
|
||||
&std::string::_M_set_length>;
|
||||
#endif
|
||||
#elif defined(_LIBCPP_VERSION)
|
||||
template class string_thief<decltype(&std::string::__set_size),
|
||||
&std::string::__set_size>;
|
||||
#elif defined(_MSVC_STL_VERSION)
|
||||
template class string_thief<decltype(&std::string::_Mypair),
|
||||
&std::string::_Mypair>;
|
||||
#endif
|
||||
|
||||
void string_set_length_hacker(std::string &, std::size_t);
|
||||
|
||||
template <typename ch>
|
||||
inline void resize(std::basic_string<ch> &raw_str, std::size_t sz) {
|
||||
std::string &str = *reinterpret_cast<std::string *>(&raw_str);
|
||||
#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || \
|
||||
defined(_MSVC_STL_VERSION)
|
||||
if (sz > str.capacity()) {
|
||||
str.reserve(sz);
|
||||
}
|
||||
string_set_length_hacker(str, sz);
|
||||
str[sz] = '\0';
|
||||
#else
|
||||
raw_str.resize(sz);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if (defined(_MSC_VER) && _MSC_VER <= 1920)
|
||||
#else
|
||||
void vector_set_length_hacker(std::vector<char> &self, std::size_t sz);
|
||||
|
||||
template <typename Function, Function func_ptr>
|
||||
class vector_thief {
|
||||
public:
|
||||
friend void vector_set_length_hacker(std::vector<char> &self,
|
||||
std::size_t sz) {
|
||||
#if defined(_MSVC_STL_VERSION)
|
||||
(self.*func_ptr)._Myval2._Mylast = self.data() + sz;
|
||||
#else
|
||||
#if defined(_LIBCPP_VERSION)
|
||||
#if _LIBCPP_VERSION < 14000
|
||||
((*(std::__vector_base<char, std::allocator<char> > *)(&self)).*func_ptr) =
|
||||
self.data() + sz;
|
||||
#else
|
||||
(self.*func_ptr) = self.data() + sz;
|
||||
#endif
|
||||
#else
|
||||
#if defined(__GLIBCXX__)
|
||||
((*(std::_Vector_base<char, std::allocator<char> > *)(&self)).*func_ptr)
|
||||
._M_finish = self.data() + sz;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(__GLIBCXX__) // libstdc++
|
||||
template class vector_thief<decltype(&std::vector<char>::_M_impl),
|
||||
&std::vector<char>::_M_impl>;
|
||||
#elif defined(_LIBCPP_VERSION)
|
||||
template class vector_thief<decltype(&std::vector<char>::__end_),
|
||||
&std::vector<char>::__end_>;
|
||||
#elif defined(_MSVC_STL_VERSION)
|
||||
template class vector_thief<decltype(&std::vector<char>::_Mypair),
|
||||
&std::vector<char>::_Mypair>;
|
||||
#endif
|
||||
|
||||
template <typename ch>
|
||||
inline void resize(std::vector<ch> &raw_vec, std::size_t sz) {
|
||||
#if defined(__GLIBCXX__) || \
|
||||
(defined(_LIBCPP_VERSION) && defined(_LIBCPP_HAS_NO_ASAN)) || \
|
||||
defined(_MSVC_STL_VERSION)
|
||||
std::vector<char> &vec = *reinterpret_cast<std::vector<char> *>(&raw_vec);
|
||||
vec.reserve(sz);
|
||||
vector_set_length_hacker(vec, sz);
|
||||
#else
|
||||
raw_vec.resize(sz);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}; // namespace iguana::detail
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#if __has_include(<memory_resource>)
|
||||
#include <memory_resource>
|
||||
#endif
|
||||
|
||||
namespace iguana {
|
||||
#ifdef IGUANA_ENABLE_PMR
|
||||
#if __has_include(<memory_resource>)
|
||||
inline char iguana_buf[2048];
|
||||
inline std::pmr::monotonic_buffer_resource iguana_resource(iguana_buf, 2048);
|
||||
using string_stream = std::pmr::string;
|
||||
#endif
|
||||
#else
|
||||
using string_stream = std::string;
|
||||
#endif
|
||||
} // namespace iguana
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
//
|
||||
// Created by QY on 2017-01-05.
|
||||
//
|
||||
|
||||
#ifndef SERIALIZE_TRAITS_HPP
|
||||
#define SERIALIZE_TRAITS_HPP
|
||||
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#if __cplusplus > 201703L
|
||||
#if __has_include(<span>)
|
||||
#include <span>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "iguana/define.h"
|
||||
|
||||
namespace iguana {
|
||||
|
||||
template <class T>
|
||||
struct is_signed_intergral_like
|
||||
: std::integral_constant<bool, (std::is_integral<T>::value) &&
|
||||
std::is_signed<T>::value> {};
|
||||
|
||||
template <class T>
|
||||
struct is_unsigned_intergral_like
|
||||
: std::integral_constant<bool, (std::is_integral<T>::value) &&
|
||||
std::is_unsigned<T>::value> {};
|
||||
|
||||
template <template <typename...> class U, typename T>
|
||||
struct is_template_instant_of : std::false_type {};
|
||||
|
||||
template <template <typename...> class U, typename... args>
|
||||
struct is_template_instant_of<U, U<args...>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_stdstring : is_template_instant_of<std::basic_string, T> {};
|
||||
|
||||
template <typename T>
|
||||
struct is_tuple : is_template_instant_of<std::tuple, T> {};
|
||||
|
||||
#if __cplusplus > 201703L
|
||||
#if __has_include(<span>)
|
||||
template <typename>
|
||||
struct is_span : std::false_type {};
|
||||
|
||||
template <typename T, size_t Extent>
|
||||
struct is_span<std::span<T, Extent>> : std::true_type {};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
struct is_sequence_container
|
||||
: std::integral_constant<
|
||||
bool, is_template_instant_of<std::deque, T>::value ||
|
||||
is_template_instant_of<std::list, T>::value ||
|
||||
is_template_instant_of<std::vector, T>::value ||
|
||||
is_template_instant_of<std::queue, T>::value> {};
|
||||
|
||||
template <class T>
|
||||
struct is_associat_container
|
||||
: std::integral_constant<
|
||||
bool, is_template_instant_of<std::map, T>::value ||
|
||||
is_template_instant_of<std::unordered_map, T>::value> {};
|
||||
|
||||
template <class T>
|
||||
struct is_emplace_back_able
|
||||
: std::integral_constant<
|
||||
bool, is_template_instant_of<std::deque, T>::value ||
|
||||
is_template_instant_of<std::list, T>::value ||
|
||||
is_template_instant_of<std::vector, T>::value> {};
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
struct has_type;
|
||||
|
||||
template <typename T, typename... Us>
|
||||
struct has_type<T, std::tuple<Us...>>
|
||||
: std::disjunction<std::is_same<T, Us>...> {};
|
||||
|
||||
template <class T>
|
||||
struct member_tratis {};
|
||||
|
||||
template <class T, class Owner>
|
||||
struct member_tratis<T Owner::*> {
|
||||
using owner_type = Owner;
|
||||
using value_type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_int64_v =
|
||||
std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>;
|
||||
|
||||
template <typename T>
|
||||
struct is_variant : std::false_type {};
|
||||
|
||||
template <typename... T>
|
||||
struct is_variant<std::variant<T...>> : std::true_type {};
|
||||
|
||||
template <class T>
|
||||
struct member_traits {
|
||||
using value_type = T;
|
||||
};
|
||||
|
||||
template <class T, class Owner>
|
||||
struct member_traits<T Owner::*> {
|
||||
using owner_type = Owner;
|
||||
using value_type = T;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using member_value_type_t = typename member_traits<T>::value_type;
|
||||
|
||||
template <typename T, std::size_t I, typename = void>
|
||||
struct variant_type_at {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t I>
|
||||
struct variant_type_at<T, I, std::enable_if_t<is_variant<T>::value>> {
|
||||
using type = std::variant_alternative_t<I, T>;
|
||||
};
|
||||
template <typename T, std::size_t I>
|
||||
using variant_type_at_t =
|
||||
typename variant_type_at<typename member_traits<T>::value_type, I>::type;
|
||||
|
||||
/// concepts
|
||||
template <typename Type>
|
||||
constexpr bool is_char_v =
|
||||
std::is_same_v<Type, signed char> || std::is_same_v<Type, char> ||
|
||||
std::is_same_v<Type, unsigned char> || std::is_same_v<Type, wchar_t> ||
|
||||
std::is_same_v<Type, char16_t> || std::is_same_v<Type, char32_t>
|
||||
#ifdef __cpp_lib_char8_t
|
||||
|| std::is_same_v<Type, char8_t>
|
||||
#endif
|
||||
;
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
template <typename Type>
|
||||
concept is_container_v = requires(Type container) {
|
||||
typename std::remove_cvref_t<Type>::value_type;
|
||||
container.size();
|
||||
container.begin();
|
||||
container.end();
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
concept is_resizable_char_container_v = requires(Type container) {
|
||||
typename std::remove_cvref_t<Type>::value_type;
|
||||
container.size();
|
||||
container.begin();
|
||||
container.end();
|
||||
requires is_char_v<typename std::remove_cvref_t<Type>::value_type>;
|
||||
container.resize(std::size_t{});
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept char_writer = requires(T t) {
|
||||
t.write((const char *)nullptr, std::size_t{});
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept char_reader = requires(T t) {
|
||||
t.read((char *)nullptr, std::size_t{});
|
||||
};
|
||||
#else
|
||||
template <typename T, typename = void>
|
||||
struct container_impl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct container_impl<T,
|
||||
std::void_t<typename std::remove_cvref_t<T>::value_type,
|
||||
decltype(std::declval<T>().size()),
|
||||
decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end())>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_container_v = container_impl<T>::value;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct resizable_char_container_impl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct resizable_char_container_impl<
|
||||
T, std::void_t<typename std::remove_cvref_t<T>::value_type,
|
||||
decltype(std::declval<T>().size()),
|
||||
decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end()),
|
||||
std::enable_if_t<
|
||||
is_char_v<typename std::remove_cvref_t<T>::value_type>>,
|
||||
decltype(std::declval<T>().resize(std::size_t{}))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_resizable_char_container_v =
|
||||
resizable_char_container_impl<T>::value;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct char_writer_impl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct char_writer_impl<T, std::void_t<decltype(std::declval<T>().write(
|
||||
(const char *)nullptr, std::size_t{}))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool char_writer = char_writer_impl<T>::value;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct char_reader_impl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct char_reader_impl<T, std::void_t<decltype(std::declval<T>().read(
|
||||
(char *)nullptr, std::size_t{}))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool char_reader = char_reader_impl<T>::value;
|
||||
#endif
|
||||
|
||||
} // namespace iguana
|
||||
#endif // SERIALIZE_TRAITS_HPP
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "iguana/define.h"
|
||||
|
||||
namespace iguana {
|
||||
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/reader.h
|
||||
template <typename Ch = char, typename It>
|
||||
inline unsigned parse_unicode_hex4(It &&it) {
|
||||
unsigned codepoint = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Ch c = *it;
|
||||
codepoint <<= 4;
|
||||
codepoint += static_cast<unsigned>(c);
|
||||
if (c >= '0' && c <= '9')
|
||||
codepoint -= '0';
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
codepoint -= 'A' - 10;
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
codepoint -= 'a' - 10;
|
||||
else {
|
||||
throw std::runtime_error("Invalid Unicode Escape Hex");
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return codepoint;
|
||||
}
|
||||
|
||||
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/encodings.h
|
||||
template <typename Ch = char, typename OutputStream>
|
||||
inline void encode_utf8(OutputStream &os, unsigned codepoint) {
|
||||
if (codepoint > 0x10FFFF)
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::runtime_error("Invalid unicode codepoint:" +
|
||||
std::to_string(codepoint));
|
||||
}
|
||||
if (codepoint <= 0x7F)
|
||||
os.push_back(static_cast<Ch>(codepoint & 0xFF));
|
||||
else if (codepoint <= 0x7FF) {
|
||||
os.push_back(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
|
||||
os.push_back(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) {
|
||||
os.push_back(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
|
||||
os.push_back(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.push_back(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
else {
|
||||
os.push_back(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
|
||||
os.push_back(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
|
||||
os.push_back(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.push_back(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/encodings.h
|
||||
static inline unsigned char GetRange(unsigned char c) {
|
||||
static const unsigned char type[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0x10, 0x10, 0x10,
|
||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
|
||||
0x40, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 10, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3,
|
||||
11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 8, 8, 8,
|
||||
};
|
||||
return type[c];
|
||||
}
|
||||
|
||||
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/encodings.h
|
||||
template <typename Ch = char, typename It>
|
||||
inline bool decode_utf8(It &&it, unsigned &codepoint) {
|
||||
auto c = *(it++);
|
||||
bool result = true;
|
||||
auto copy = [&]() IGUANA__INLINE_LAMBDA {
|
||||
c = *(it++);
|
||||
codepoint = (codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu);
|
||||
};
|
||||
auto trans = [&](unsigned mask) IGUANA__INLINE_LAMBDA {
|
||||
result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0);
|
||||
};
|
||||
auto tail = [&]() IGUANA__INLINE_LAMBDA {
|
||||
copy();
|
||||
trans(0x70);
|
||||
};
|
||||
if (!(c & 0x80)) {
|
||||
codepoint = static_cast<unsigned char>(c);
|
||||
return true;
|
||||
}
|
||||
unsigned char type = GetRange(static_cast<unsigned char>(c));
|
||||
if (type >= 32) {
|
||||
codepoint = 0;
|
||||
}
|
||||
else {
|
||||
codepoint = (0xFFu >> type) & static_cast<unsigned char>(c);
|
||||
}
|
||||
switch (type) {
|
||||
case 2:
|
||||
tail();
|
||||
return result;
|
||||
case 3:
|
||||
tail();
|
||||
tail();
|
||||
return result;
|
||||
case 4:
|
||||
copy();
|
||||
trans(0x50);
|
||||
tail();
|
||||
return result;
|
||||
case 5:
|
||||
copy();
|
||||
trans(0x10);
|
||||
tail();
|
||||
tail();
|
||||
return result;
|
||||
case 6:
|
||||
tail();
|
||||
tail();
|
||||
tail();
|
||||
return result;
|
||||
case 10:
|
||||
copy();
|
||||
trans(0x20);
|
||||
tail();
|
||||
return result;
|
||||
case 11:
|
||||
copy();
|
||||
trans(0x60);
|
||||
tail();
|
||||
tail();
|
||||
return result;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace iguana
|
||||
Vendored
+197
@@ -0,0 +1,197 @@
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
|
||||
namespace iguana {
|
||||
using base = detail::base;
|
||||
|
||||
constexpr inline uint8_t ENABLE_JSON = 0x01;
|
||||
constexpr inline uint8_t ENABLE_YAML = 0x02;
|
||||
constexpr inline uint8_t ENABLE_XML = 0x04;
|
||||
constexpr inline uint8_t ENABLE_PB = 0x08;
|
||||
constexpr inline uint8_t ENABLE_ALL = 0x0F;
|
||||
|
||||
template <typename T, uint8_t ENABLE_FLAG = ENABLE_PB>
|
||||
struct base_impl : public base {
|
||||
base_impl() { [[maybe_unused]] static bool r = register_type<T>(); }
|
||||
void to_pb(std::string& str) const override {
|
||||
if constexpr ((ENABLE_FLAG & ENABLE_PB) != 0) {
|
||||
to_pb_adl((iguana_adl_t*)nullptr, *(static_cast<T const*>(this)), str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Protobuf Disabled");
|
||||
}
|
||||
}
|
||||
|
||||
void from_pb(std::string_view str) override {
|
||||
if constexpr ((ENABLE_FLAG & ENABLE_PB) != 0) {
|
||||
from_pb_adl((iguana_adl_t*)nullptr, *(static_cast<T*>(this)), str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Protobuf Disabled");
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(std::string& str) const override {
|
||||
if constexpr ((ENABLE_FLAG & ENABLE_JSON) != 0) {
|
||||
to_json_adl((iguana_adl_t*)nullptr, *(static_cast<T const*>(this)), str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Json Disabled");
|
||||
}
|
||||
}
|
||||
|
||||
void from_json(std::string_view str) override {
|
||||
if constexpr ((ENABLE_FLAG & ENABLE_JSON) != 0) {
|
||||
from_json_adl((iguana_adl_t*)nullptr, *(static_cast<T*>(this)), str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Json Disabled");
|
||||
}
|
||||
}
|
||||
|
||||
void to_xml(std::string& str) const override {
|
||||
if constexpr ((ENABLE_FLAG & ENABLE_XML) != 0) {
|
||||
to_xml_adl((iguana_adl_t*)nullptr, *(static_cast<T const*>(this)), str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Xml Disabled");
|
||||
}
|
||||
}
|
||||
|
||||
void from_xml(std::string_view str) override {
|
||||
if constexpr ((ENABLE_FLAG & ENABLE_XML) != 0) {
|
||||
from_xml_adl((iguana_adl_t*)nullptr, *(static_cast<T*>(this)), str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Xml Disabled");
|
||||
}
|
||||
}
|
||||
|
||||
void to_yaml(std::string& str) const override {
|
||||
if constexpr ((ENABLE_FLAG & ENABLE_YAML) != 0) {
|
||||
to_yaml_adl((iguana_adl_t*)nullptr, *(static_cast<T const*>(this)), str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Yaml Disabled");
|
||||
}
|
||||
}
|
||||
|
||||
void from_yaml(std::string_view str) override {
|
||||
if constexpr ((ENABLE_FLAG & ENABLE_YAML) != 0) {
|
||||
from_yaml_adl((iguana_adl_t*)nullptr, *(static_cast<T*>(this)), str);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Yaml Disabled");
|
||||
}
|
||||
}
|
||||
|
||||
iguana::detail::field_info get_field_info(
|
||||
std::string_view name) const override {
|
||||
static auto map =
|
||||
detail::get_members(ylt::reflection::internal::wrapper<T>::value);
|
||||
iguana::detail::field_info info{};
|
||||
for (auto const& [no, field] : map) {
|
||||
if (info.offset > 0) {
|
||||
break;
|
||||
}
|
||||
std::visit(
|
||||
[&](auto const& val) {
|
||||
if (val.field_name == name) {
|
||||
info.offset = val.offset;
|
||||
using value_type =
|
||||
typename std::remove_reference_t<decltype(val)>::value_type;
|
||||
#if defined(__clang__) || defined(_MSC_VER) || \
|
||||
(defined(__GNUC__) && __GNUC__ > 8)
|
||||
info.type_name = type_string<value_type>();
|
||||
#endif
|
||||
}
|
||||
},
|
||||
field);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
std::vector<std::string_view> get_fields_name() const override {
|
||||
static auto map =
|
||||
detail::get_members(ylt::reflection::internal::wrapper<T>::value);
|
||||
|
||||
std::vector<std::string_view> vec;
|
||||
|
||||
for (auto const& [no, val] : map) {
|
||||
std::visit(
|
||||
[&](auto const& field) {
|
||||
std::string_view const current_name{field.field_name.data(),
|
||||
field.field_name.size()};
|
||||
if (vec.empty() || (vec.back() != current_name)) {
|
||||
vec.push_back(current_name);
|
||||
}
|
||||
},
|
||||
val);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::any get_field_any(std::string_view name) const override {
|
||||
static auto map =
|
||||
detail::get_members(ylt::reflection::internal::wrapper<T>::value);
|
||||
std::any result;
|
||||
|
||||
for (auto const& [no, field] : map) {
|
||||
if (result.has_value()) {
|
||||
break;
|
||||
}
|
||||
std::visit(
|
||||
[&](auto const& val) {
|
||||
if (val.field_name == name) {
|
||||
using value_type =
|
||||
typename std::remove_reference_t<decltype(val)>::value_type;
|
||||
auto ptr =
|
||||
reinterpret_cast<const char*>(static_cast<const T*>(this)) +
|
||||
val.offset;
|
||||
result = *((value_type*)ptr);
|
||||
}
|
||||
},
|
||||
field);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual ~base_impl() {}
|
||||
|
||||
mutable size_t cache_size = 0;
|
||||
|
||||
protected:
|
||||
void* object_ptr() override { return static_cast<T*>(this); }
|
||||
const void* object_ptr() const override {
|
||||
return static_cast<const T*>(this);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void dummy() {
|
||||
// make sure init T before main, and can register_type before main.
|
||||
[[maybe_unused]] static auto t =
|
||||
ylt::reflection::internal::wrapper<T>::value;
|
||||
}
|
||||
};
|
||||
|
||||
IGUANA_INLINE std::shared_ptr<base> create_instance(std::string_view name) {
|
||||
auto it = iguana::detail::g_pb_map.find(name);
|
||||
if (it == iguana::detail::g_pb_map.end()) {
|
||||
throw std::invalid_argument(std::string(name) +
|
||||
"not inheried from iguana::base_impl");
|
||||
}
|
||||
return it->second();
|
||||
}
|
||||
} // namespace iguana
|
||||
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
namespace ylt::reflection::reflect26 {
|
||||
template <>
|
||||
constexpr inline bool skip_base_v<iguana::detail::base> = true;
|
||||
|
||||
template <typename T, uint8_t ENABLE_FLAG>
|
||||
constexpr inline bool skip_base_v<iguana::base_impl<T, ENABLE_FLAG>> = true;
|
||||
} // namespace ylt::reflection::reflect26
|
||||
#endif
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <system_error>
|
||||
|
||||
namespace iguana {
|
||||
|
||||
class iguana_category : public std::error_category {
|
||||
public:
|
||||
virtual const char *name() const noexcept override {
|
||||
return "iguana::category";
|
||||
}
|
||||
virtual std::string message(int err_val) const override {
|
||||
for (auto &pair : err_map_) {
|
||||
if (pair.second == err_val) {
|
||||
return pair.first;
|
||||
}
|
||||
}
|
||||
|
||||
return "unrecognized error";
|
||||
}
|
||||
|
||||
int add_message(const std::string &msg) {
|
||||
if (auto it = err_map_.find(msg); it != err_map_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
else {
|
||||
err_++;
|
||||
err_map_.emplace(msg, err_);
|
||||
return err_;
|
||||
}
|
||||
}
|
||||
|
||||
int err_ = 0;
|
||||
std::map<std::string, int> err_map_;
|
||||
};
|
||||
|
||||
inline iguana::iguana_category &category() {
|
||||
static iguana::iguana_category instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
inline std::error_code make_error_code(const std::string data = "") {
|
||||
assert(!data.empty());
|
||||
|
||||
auto err = iguana::category().add_message(data);
|
||||
return std::error_code(err, iguana::category());
|
||||
}
|
||||
|
||||
// dom parse error code
|
||||
enum class dom_errc {
|
||||
ok = 0,
|
||||
wrong_type,
|
||||
};
|
||||
|
||||
class iguana_dom_category : public std::error_category {
|
||||
public:
|
||||
virtual const char *name() const noexcept override {
|
||||
return "iguana::dom_category";
|
||||
}
|
||||
virtual std::string message(int err_val) const override {
|
||||
switch (static_cast<dom_errc>(err_val)) {
|
||||
case dom_errc::ok:
|
||||
return "ok";
|
||||
case dom_errc::wrong_type: {
|
||||
auto it = detail_msg_map_.find(dom_errc::wrong_type);
|
||||
if (it != detail_msg_map_.end()) {
|
||||
return std::string("wrong type, ")
|
||||
.append("real type is ")
|
||||
.append(it->second);
|
||||
}
|
||||
else {
|
||||
return "wrong type";
|
||||
}
|
||||
}
|
||||
default:
|
||||
return "(unrecognized error)";
|
||||
}
|
||||
}
|
||||
|
||||
// private:
|
||||
std::map<iguana::dom_errc, std::string> detail_msg_map_;
|
||||
};
|
||||
|
||||
inline iguana::iguana_dom_category &dom_category() {
|
||||
static iguana::iguana_dom_category instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
inline std::error_code make_error_code(iguana::dom_errc err,
|
||||
const std::string &error_msg) {
|
||||
auto &instance = iguana::dom_category();
|
||||
if (!error_msg.empty()) {
|
||||
instance.detail_msg_map_.emplace(err, error_msg);
|
||||
}
|
||||
|
||||
return std::error_code((int)err, instance);
|
||||
}
|
||||
} // namespace iguana
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
|
||||
#include "frozen/string.h"
|
||||
#include "frozen/unordered_map.h"
|
||||
|
||||
namespace iguana {
|
||||
|
||||
#if defined(__clang__) || defined(_MSC_VER) || \
|
||||
(defined(__GNUC__) && __GNUC__ > 8)
|
||||
|
||||
template <typename T>
|
||||
constexpr std::string_view get_raw_name() {
|
||||
#ifdef _MSC_VER
|
||||
return __FUNCSIG__;
|
||||
#else
|
||||
return __PRETTY_FUNCTION__;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <auto T>
|
||||
constexpr std::string_view get_raw_name() {
|
||||
#ifdef _MSC_VER
|
||||
return __FUNCSIG__;
|
||||
#else
|
||||
return __PRETTY_FUNCTION__;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr std::string_view type_string() {
|
||||
constexpr std::string_view sample = get_raw_name<int>();
|
||||
constexpr size_t pos = sample.find("int");
|
||||
constexpr std::string_view str = get_raw_name<T>();
|
||||
constexpr auto next1 = str.rfind(sample[pos + 3]);
|
||||
#if defined(_MSC_VER)
|
||||
constexpr std::size_t npos = str.find_first_of(" ", pos);
|
||||
if (npos != std::string_view::npos)
|
||||
return str.substr(npos + 1, next1 - npos - 1);
|
||||
else
|
||||
return str.substr(pos, next1 - pos);
|
||||
#else
|
||||
return str.substr(pos, next1 - pos);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <auto T>
|
||||
inline constexpr std::string_view enum_string() {
|
||||
constexpr std::string_view sample = get_raw_name<int>();
|
||||
constexpr size_t pos = sample.find("int");
|
||||
constexpr std::string_view str = get_raw_name<T>();
|
||||
constexpr auto next1 = str.rfind(sample[pos + 3]);
|
||||
#if defined(__clang__) || defined(_MSC_VER)
|
||||
constexpr auto s1 = str.substr(pos, next1 - pos);
|
||||
#else
|
||||
constexpr auto s1 = str.substr(pos + 5, next1 - pos - 5);
|
||||
#endif
|
||||
return s1;
|
||||
}
|
||||
|
||||
template <auto field>
|
||||
inline constexpr std::string_view field_string() {
|
||||
constexpr std::string_view raw_name = enum_string<field>();
|
||||
return raw_name.substr(raw_name.rfind(":") + 1);
|
||||
}
|
||||
|
||||
#if defined(__clang__) && (__clang_major__ >= 17)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wenum-constexpr-conversion"
|
||||
#endif
|
||||
|
||||
template <typename E, E V>
|
||||
constexpr std::string_view get_raw_name_with_v() {
|
||||
#ifdef _MSC_VER
|
||||
return __FUNCSIG__;
|
||||
#else
|
||||
return __PRETTY_FUNCTION__;
|
||||
#endif
|
||||
}
|
||||
|
||||
// True means the V is a valid enum value, and the second of the result is the
|
||||
// name
|
||||
template <typename E, E V>
|
||||
constexpr std::pair<bool, std::string_view> try_get_enum_name() {
|
||||
constexpr std::string_view sample_raw_name = get_raw_name_with_v<int, 5>();
|
||||
constexpr size_t pos = sample_raw_name.find("5");
|
||||
constexpr std::string_view enum_raw_name = get_raw_name_with_v<E, V>();
|
||||
constexpr auto enum_end = enum_raw_name.rfind(&sample_raw_name[pos + 1]);
|
||||
#ifdef _MSC_VER
|
||||
constexpr auto enum_begin = enum_raw_name.rfind(',', enum_end) + 1;
|
||||
#else
|
||||
constexpr auto enum_begin = enum_raw_name.rfind(' ', enum_end) + 1;
|
||||
#endif
|
||||
constexpr std::string_view res =
|
||||
enum_raw_name.substr(enum_begin, enum_end - enum_begin);
|
||||
|
||||
constexpr size_t pos_brackets = res.find(')');
|
||||
|
||||
size_t pos_colon = res.find("::");
|
||||
return {pos_brackets == std::string_view::npos,
|
||||
res.substr(pos_colon == std::string_view::npos ? 0 : pos_colon + 2)};
|
||||
}
|
||||
|
||||
// Enumerate the numbers in a integer sequence to see if they are legal enum
|
||||
// value
|
||||
template <typename E, std::int64_t... Is>
|
||||
constexpr inline auto get_enum_arr(
|
||||
const std::integer_sequence<std::int64_t, Is...> &) {
|
||||
constexpr std::size_t N = sizeof...(Is);
|
||||
std::array<std::string_view, N> enum_names = {};
|
||||
std::array<E, N> enum_values = {};
|
||||
std::size_t num = 0;
|
||||
(([&]() {
|
||||
constexpr auto res = try_get_enum_name<E, static_cast<E>(Is)>();
|
||||
if constexpr (res.first) {
|
||||
// the Is is a valid enum value
|
||||
enum_names[num] = res.second;
|
||||
enum_values[num] = static_cast<E>(Is);
|
||||
++num;
|
||||
}
|
||||
})(),
|
||||
...);
|
||||
return std::make_tuple(num, enum_values, enum_names);
|
||||
}
|
||||
|
||||
template <std::size_t N, const std::array<int, N> &arr, size_t... Is>
|
||||
constexpr auto array_to_seq(const std::index_sequence<Is...> &) {
|
||||
return std::integer_sequence<std::int64_t, arr[Is]...>();
|
||||
}
|
||||
|
||||
// convert array to map
|
||||
template <typename E, size_t N, size_t... Is>
|
||||
constexpr inline auto get_enum_to_str_map(
|
||||
const std::array<std::string_view, N> &enum_names,
|
||||
const std::array<E, N> &enum_values, const std::index_sequence<Is...> &) {
|
||||
return frozen::unordered_map<E, frozen::string, sizeof...(Is)>{
|
||||
{enum_values[Is], enum_names[Is]}...};
|
||||
}
|
||||
|
||||
template <typename E, size_t N, size_t... Is>
|
||||
constexpr inline auto get_str_to_enum_map(
|
||||
const std::array<std::string_view, N> &enum_names,
|
||||
const std::array<E, N> &enum_values, const std::index_sequence<Is...> &) {
|
||||
return frozen::unordered_map<frozen::string, E, sizeof...(Is)>{
|
||||
{enum_names[Is], enum_values[Is]}...};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// the default generic enum_value
|
||||
// if the user has not defined a specialization template, this will be called
|
||||
template <typename T>
|
||||
struct enum_value {
|
||||
constexpr static std::array<int, 0> value = {};
|
||||
};
|
||||
|
||||
template <bool str_to_enum, typename E>
|
||||
constexpr inline auto get_enum_map() {
|
||||
#if defined(__clang__) || defined(_MSC_VER) || \
|
||||
(defined(__GNUC__) && __GNUC__ > 8)
|
||||
constexpr auto &arr = enum_value<E>::value;
|
||||
constexpr auto arr_size = arr.size();
|
||||
if constexpr (arr_size > 0) {
|
||||
// the user has defined a specialization template
|
||||
constexpr auto arr_seq =
|
||||
array_to_seq<arr_size, arr>(std::make_index_sequence<arr_size>());
|
||||
constexpr auto t = get_enum_arr<E>(arr_seq);
|
||||
if constexpr (str_to_enum) {
|
||||
return get_str_to_enum_map<E, arr_size>(
|
||||
std::get<2>(t), std::get<1>(t),
|
||||
std::make_index_sequence<std::get<0>(t)>{});
|
||||
}
|
||||
else {
|
||||
return get_enum_to_str_map<E, arr_size>(
|
||||
std::get<2>(t), std::get<1>(t),
|
||||
std::make_index_sequence<std::get<0>(t)>{});
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__clang__) && (__clang_major__ >= 17)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
} // namespace iguana
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "iguana/dynamic.hpp"
|
||||
#include "iguana/json_reader.hpp"
|
||||
#include "iguana/json_writer.hpp"
|
||||
#include "iguana/pb_reader.hpp"
|
||||
#include "iguana/pb_writer.hpp"
|
||||
#include "iguana/xml_reader.hpp"
|
||||
#include "iguana/xml_writer.hpp"
|
||||
#include "iguana/yaml_reader.hpp"
|
||||
#include "iguana/yaml_writer.hpp"
|
||||
+989
@@ -0,0 +1,989 @@
|
||||
#pragma once
|
||||
#include "detail/utf.hpp"
|
||||
#include "error_code.h"
|
||||
#include "json_util.hpp"
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
#include "ylt/reflection/reflect26_dispatch.hpp"
|
||||
#endif
|
||||
namespace iguana {
|
||||
|
||||
template <typename T, typename It,
|
||||
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void from_json(T &value, It &&it, It &&end);
|
||||
|
||||
template <typename T, typename View,
|
||||
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void from_json(T &value, const View &view);
|
||||
|
||||
template <typename T, typename View,
|
||||
std::enable_if_t<non_ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void from_json(T &value, const View &view);
|
||||
|
||||
namespace detail {
|
||||
template <typename U, typename It, std::enable_if_t<optional_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<tuple_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
|
||||
|
||||
template <typename U, typename It,
|
||||
std::enable_if_t<sequence_container_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<variant_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
|
||||
|
||||
template <typename U, typename It,
|
||||
std::enable_if_t<ylt_refletable_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
|
||||
from_json(value, it, end);
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<string_v<U>, int> = 0>
|
||||
IGUANA_INLINE void parse_escape(U &value, It &&it, It &&end) {
|
||||
if (it == end)
|
||||
IGUANA_UNLIKELY { throw std::runtime_error(R"(Expected ")"); }
|
||||
if (*it == 'u') {
|
||||
++it;
|
||||
if (std::distance(it, end) <= 4)
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::runtime_error(R"(Expected 4 hexadecimal digits)");
|
||||
}
|
||||
auto code_point = parse_unicode_hex4(it);
|
||||
encode_utf8(value, code_point);
|
||||
}
|
||||
else if (*it == 'n') {
|
||||
++it;
|
||||
value.push_back('\n');
|
||||
}
|
||||
else if (*it == 't') {
|
||||
++it;
|
||||
value.push_back('\t');
|
||||
}
|
||||
else if (*it == 'r') {
|
||||
++it;
|
||||
value.push_back('\r');
|
||||
}
|
||||
else if (*it == 'b') {
|
||||
++it;
|
||||
value.push_back('\b');
|
||||
}
|
||||
else if (*it == 'f') {
|
||||
++it;
|
||||
value.push_back('\f');
|
||||
}
|
||||
else {
|
||||
value.push_back(*it); // add the escaped character
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<num_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
|
||||
skip_ws(it, end);
|
||||
if (it != end && *it == 'n') {
|
||||
++it;
|
||||
match<'u', 'l', 'l'>(it, end);
|
||||
return;
|
||||
}
|
||||
if constexpr (contiguous_iterator<std::decay_t<It>>) {
|
||||
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<false>(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);
|
||||
}
|
||||
else {
|
||||
char buffer[256];
|
||||
size_t i{};
|
||||
while (it != end && is_numeric(*it)) {
|
||||
if (i > 254)
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Number is too long"); }
|
||||
buffer[i] = *it++;
|
||||
++i;
|
||||
}
|
||||
detail::from_chars(buffer, buffer + i, value);
|
||||
}
|
||||
}
|
||||
|
||||
// template <typename U, typename It, std::enable_if_t<is_pb_type_v<U>, int> =
|
||||
// 0> IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
|
||||
// from_json_impl(value.val, it, end);
|
||||
// }
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<numeric_str_v<U>, int> = 0>
|
||||
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)) {
|
||||
++it;
|
||||
}
|
||||
value.value() =
|
||||
std::string_view(&*start, static_cast<size_t>(std::distance(start, it)));
|
||||
}
|
||||
|
||||
template <bool skip = false, typename U, typename It,
|
||||
std::enable_if_t<char_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
|
||||
if constexpr (!skip) {
|
||||
skip_ws(it, end);
|
||||
match<'"'>(it, end);
|
||||
}
|
||||
if (it == end)
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Unxpected end of buffer"); }
|
||||
if (*it == '\\')
|
||||
IGUANA_UNLIKELY {
|
||||
if (++it == end)
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Unxpected end of buffer"); }
|
||||
else if (*it == 'n') {
|
||||
value = '\n';
|
||||
}
|
||||
else if (*it == 't') {
|
||||
value = '\t';
|
||||
}
|
||||
else if (*it == 'r') {
|
||||
value = '\r';
|
||||
}
|
||||
else if (*it == 'b') {
|
||||
value = '\b';
|
||||
}
|
||||
else if (*it == 'f') {
|
||||
value = '\f';
|
||||
}
|
||||
else
|
||||
IGUANA_UNLIKELY { value = *it; }
|
||||
}
|
||||
else {
|
||||
value = *it;
|
||||
}
|
||||
++it;
|
||||
if constexpr (!skip) {
|
||||
match<'"'>(it, end);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<bool_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &&value, It &&it, It &&end) {
|
||||
skip_ws(it, end);
|
||||
|
||||
if (it < end)
|
||||
IGUANA_LIKELY {
|
||||
switch (*it) {
|
||||
case 't':
|
||||
++it;
|
||||
match<'r', 'u', 'e'>(it, end);
|
||||
value = true;
|
||||
break;
|
||||
case 'f':
|
||||
++it;
|
||||
match<'a', 'l', 's', 'e'>(it, end);
|
||||
value = false;
|
||||
break;
|
||||
case 'n':
|
||||
++it;
|
||||
match<'u', 'l', 'l'>(it, end);
|
||||
break;
|
||||
IGUANA_UNLIKELY default
|
||||
: throw std::runtime_error("Expected true or false");
|
||||
}
|
||||
}
|
||||
else
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Expected true or false"); }
|
||||
}
|
||||
|
||||
template <bool skip = false, typename U, typename It,
|
||||
std::enable_if_t<string_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
|
||||
if constexpr (!skip) {
|
||||
skip_ws(it, end);
|
||||
// handle JSON null: leave value empty and skip "null"
|
||||
if (it != end && *it == 'n') {
|
||||
++it;
|
||||
match<'u', 'l', 'l'>(it, end);
|
||||
value.clear();
|
||||
return;
|
||||
}
|
||||
match<'"'>(it, end);
|
||||
}
|
||||
value.clear();
|
||||
if constexpr (contiguous_iterator<std::decay_t<It>>) {
|
||||
auto start = it;
|
||||
while (it < end) {
|
||||
skip_till_escape_or_qoute(it, end);
|
||||
if (*it == '"') {
|
||||
value.append(&*start, static_cast<size_t>(std::distance(start, it)));
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Must be an escape
|
||||
value.append(&*start, static_cast<size_t>(std::distance(start, it)));
|
||||
++it; // skip first escape
|
||||
parse_escape(value, it, end);
|
||||
start = it;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
while (it != end) {
|
||||
switch (*it) {
|
||||
IGUANA_UNLIKELY case '\\':
|
||||
++it;
|
||||
parse_escape(value, it, end);
|
||||
break;
|
||||
// IGUANA_UNLIKELY case ']' : return;
|
||||
IGUANA_UNLIKELY case '"':
|
||||
++it;
|
||||
return;
|
||||
IGUANA_LIKELY default : value.push_back(*it);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool skip = false, typename U, typename It,
|
||||
std::enable_if_t<string_view_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
|
||||
static_assert(contiguous_iterator<std::decay_t<It>>, "must be contiguous");
|
||||
if constexpr (!skip) {
|
||||
skip_ws(it, end);
|
||||
match<'"'>(it, end);
|
||||
}
|
||||
using T = std::decay_t<U>;
|
||||
auto start = it;
|
||||
while (it != end) {
|
||||
skip_till_qoute(it, end);
|
||||
if (*(it - 1) != '\\') {
|
||||
value = T(&*start, static_cast<size_t>(std::distance(start, it)));
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
throw std::runtime_error("Expected \"");
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<enum_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
|
||||
static constexpr auto str_to_enum = get_enum_map<true, std::decay_t<U>>();
|
||||
if constexpr (bool_v<decltype(str_to_enum)>) {
|
||||
// not defined a specialization template
|
||||
using T = std::underlying_type_t<std::decay_t<U>>;
|
||||
from_json_impl(reinterpret_cast<T &>(value), it, end);
|
||||
}
|
||||
else {
|
||||
std::string_view enum_names;
|
||||
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; }
|
||||
else {
|
||||
throw std::runtime_error(std::string(enum_names) +
|
||||
" missing corresponding value in enum_value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr size_t get_array_size(const T &t) {
|
||||
#if __cplusplus > 201703L
|
||||
#if __has_include(<span>)
|
||||
if constexpr (is_span<T>::value)
|
||||
return t.size();
|
||||
else
|
||||
#endif
|
||||
#endif
|
||||
return sizeof(T) / sizeof(decltype(std::declval<T>()[0]));
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<fixed_array_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
|
||||
using T = std::remove_reference_t<U>;
|
||||
size_t n = get_array_size(value);
|
||||
skip_ws(it, end);
|
||||
|
||||
if constexpr (std::is_same_v<char, std::remove_reference_t<
|
||||
decltype(std::declval<T>()[0])>>) {
|
||||
if (*it == '"') {
|
||||
match<'"'>(it, end);
|
||||
auto value_it = std::begin(value);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (*it != '"')
|
||||
IGUANA_LIKELY { from_json_impl<true>(*value_it++, it, end); }
|
||||
}
|
||||
match<'"'>(it, end);
|
||||
return;
|
||||
}
|
||||
}
|
||||
match<'['>(it, end);
|
||||
skip_ws(it, end);
|
||||
if (it == end)
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Unexpected end"); }
|
||||
|
||||
if (*it == ']')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
auto value_it = std::begin(value);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
from_json_impl(*value_it++, it, end);
|
||||
skip_ws(it, end);
|
||||
if (it == end) {
|
||||
throw std::runtime_error("Unexpected end");
|
||||
}
|
||||
if (*it == ',')
|
||||
IGUANA_LIKELY {
|
||||
++it;
|
||||
skip_ws(it, end);
|
||||
}
|
||||
else if (*it == ']') {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
else
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Expected ]"); }
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It,
|
||||
std::enable_if_t<sequence_container_v<U>, int>>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
|
||||
value.clear();
|
||||
skip_ws(it, end);
|
||||
|
||||
match<'['>(it, end);
|
||||
skip_ws(it, end);
|
||||
for (size_t i = 0; it != end; ++i) {
|
||||
if (*it == ']')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
if (i > 0)
|
||||
IGUANA_LIKELY { match<','>(it, end); }
|
||||
|
||||
from_json_impl(value.emplace_back(), it, end);
|
||||
skip_ws(it, end);
|
||||
}
|
||||
throw std::runtime_error("Expected ]");
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
IGUANA_INLINE auto get_key(It &&it, It &&end) {
|
||||
if constexpr (contiguous_iterator<std::decay_t<It>>) {
|
||||
// skip white space and escape characters and find the string
|
||||
skip_ws(it, end);
|
||||
match<'"'>(it, end);
|
||||
auto start = it;
|
||||
skip_till_escape_or_qoute(it, end);
|
||||
if (*it == '\\')
|
||||
IGUANA_UNLIKELY {
|
||||
// we dont' optimize this currently because it would increase binary
|
||||
// size significantly with the complexity of generating escaped
|
||||
// compile time versions of keys
|
||||
it = start;
|
||||
static thread_local std::string static_key{};
|
||||
detail::from_json_impl<true>(static_key, it, end);
|
||||
return std::string_view(static_key);
|
||||
}
|
||||
else
|
||||
IGUANA_LIKELY {
|
||||
auto key = std::string_view{
|
||||
&*start, static_cast<size_t>(std::distance(start, it))};
|
||||
++it;
|
||||
if (key[0] == '@')
|
||||
IGUANA_UNLIKELY { return key.substr(1); }
|
||||
return key;
|
||||
}
|
||||
}
|
||||
else {
|
||||
static thread_local std::string static_key{};
|
||||
detail::from_json_impl(static_key, it, end);
|
||||
return std::string_view(static_key);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It,
|
||||
std::enable_if_t<map_container_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
|
||||
using T = std::remove_reference_t<U>;
|
||||
using key_type = typename T::key_type;
|
||||
skip_ws(it, end);
|
||||
|
||||
match<'{'>(it, end);
|
||||
skip_ws(it, end);
|
||||
bool first = true;
|
||||
while (it != end) {
|
||||
if (*it == '}')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
else if (first)
|
||||
IGUANA_UNLIKELY { first = false; }
|
||||
else
|
||||
IGUANA_LIKELY { match<','>(it, end); }
|
||||
|
||||
std::string_view key = get_key(it, end);
|
||||
|
||||
skip_ws(it, end);
|
||||
match<':'>(it, end);
|
||||
|
||||
if constexpr (string_v<key_type> || string_view_v<key_type>) {
|
||||
from_json_impl(value[key_type(key)], it, end);
|
||||
}
|
||||
else {
|
||||
static thread_local key_type key_value{};
|
||||
from_json_impl(key_value, key.begin(), key.end());
|
||||
from_json_impl(value[key_value], it, end);
|
||||
}
|
||||
skip_ws(it, end);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<tuple_v<U>, int>>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
|
||||
skip_ws(it, end);
|
||||
match<'['>(it, end);
|
||||
skip_ws(it, end);
|
||||
|
||||
foreach_tuple(
|
||||
[&](auto &v, auto i) IGUANA__INLINE_LAMBDA {
|
||||
constexpr auto I = decltype(i)::value;
|
||||
if (it == end || *it == ']') {
|
||||
return;
|
||||
}
|
||||
if constexpr (I != 0) {
|
||||
match<','>(it, end);
|
||||
skip_ws(it, end);
|
||||
}
|
||||
from_json_impl(v, it, end);
|
||||
skip_ws(it, end);
|
||||
},
|
||||
value);
|
||||
|
||||
match<']'>(it, end);
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<optional_v<U>, int>>
|
||||
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
|
||||
skip_ws(it, end);
|
||||
using T = std::remove_reference_t<U>;
|
||||
if (it == end)
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Unexexpected eof"); }
|
||||
if (*it == 'n') {
|
||||
++it;
|
||||
match<'u', 'l', 'l'>(it, end);
|
||||
if constexpr (!std::is_pointer_v<T>) {
|
||||
value.reset();
|
||||
if (it < end && *it == '"') {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
using value_type = typename T::value_type;
|
||||
value_type t{};
|
||||
if constexpr (string_v<value_type> || string_view_v<value_type>) {
|
||||
if (it < end && *it == '"')
|
||||
IGUANA_LIKELY { ++it; }
|
||||
from_json_impl<true>(t, it, end);
|
||||
}
|
||||
else {
|
||||
from_json_impl(t, it, end);
|
||||
}
|
||||
value = std::move(t);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int>>
|
||||
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"); }
|
||||
if (*it == 'n') {
|
||||
++it;
|
||||
match<'u', 'l', 'l'>(it, end);
|
||||
}
|
||||
else {
|
||||
using value_type = typename std::remove_reference_t<U>::element_type;
|
||||
if constexpr (unique_ptr_v<U>) {
|
||||
value = std::make_unique<value_type>();
|
||||
}
|
||||
else {
|
||||
value = std::make_shared<value_type>();
|
||||
}
|
||||
from_json_impl(*value, it, end);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
IGUANA_INLINE void skip_object_value(It &&it, It &&end) {
|
||||
skip_ws(it, end);
|
||||
while (it != end) {
|
||||
switch (*it) {
|
||||
case '{':
|
||||
skip_until_closed<'{', '}'>(it, end);
|
||||
break;
|
||||
case '[':
|
||||
skip_until_closed<'[', ']'>(it, end);
|
||||
break;
|
||||
case '"':
|
||||
skip_string(it, end);
|
||||
break;
|
||||
case '/':
|
||||
skip_comment(it, end);
|
||||
continue;
|
||||
case ',':
|
||||
case '}':
|
||||
case ']':
|
||||
break;
|
||||
default: {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename value_type, typename U, typename It>
|
||||
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 <typename U, typename It, size_t... Idx>
|
||||
IGUANA_INLINE void from_json_variant(U &value, It &it, It &end,
|
||||
std::index_sequence<Idx...>) {
|
||||
static_assert(!has_duplicate_type_v<std::remove_reference_t<U>>,
|
||||
"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<Idx, std::remove_reference_t<U>>>(
|
||||
value, it, end, temp_it, temp_end),
|
||||
true)),
|
||||
...);
|
||||
it = temp_it;
|
||||
end = temp_end;
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<variant_v<U>, int>>
|
||||
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<std::remove_reference_t<U>>>{});
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
template <typename T, typename It, std::enable_if_t<ylt_refletable_v<T>, int>>
|
||||
IGUANA_INLINE void from_json(T &value, It &&it, It &&end) {
|
||||
skip_ws(it, end);
|
||||
match<'{'>(it, end);
|
||||
skip_ws(it, end);
|
||||
if (*it == '}')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
|
||||
while (it != end) {
|
||||
std::string_view key = detail::get_key(it, end);
|
||||
skip_ws(it, end);
|
||||
match<':'>(it, end);
|
||||
bool found = ylt::reflection::reflect26::dispatch_by_name(
|
||||
value, key, [&](auto &field) IGUANA__INLINE_LAMBDA {
|
||||
using namespace detail;
|
||||
from_json_impl(field, it, end);
|
||||
});
|
||||
if (!found)
|
||||
IGUANA_UNLIKELY {
|
||||
#ifdef THROW_UNKNOWN_KEY
|
||||
throw std::runtime_error("Unknown key: " + std::string(key));
|
||||
#else
|
||||
detail::skip_object_value(it, end);
|
||||
#endif
|
||||
}
|
||||
skip_ws(it, end);
|
||||
if (*it == '}')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
else
|
||||
IGUANA_LIKELY { match<','>(it, end); }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef YLT_USE_CXX26_REFLECTION
|
||||
template <typename T, typename It, std::enable_if_t<ylt_refletable_v<T>, int>>
|
||||
IGUANA_INLINE void from_json(T &value, It &&it, It &&end) {
|
||||
skip_ws(it, end);
|
||||
match<'{'>(it, end);
|
||||
|
||||
skip_ws(it, end);
|
||||
if (*it == '}')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
constexpr auto Count = ylt::reflection::members_count_v<U>;
|
||||
if constexpr (Count > 0) {
|
||||
std::string_view key = detail::get_key(it, end);
|
||||
#ifdef SEQUENTIAL_PARSE
|
||||
bool parse_done = false;
|
||||
ylt::reflection::for_each(
|
||||
value, [&](auto &field, auto name, auto index) IGUANA__INLINE_LAMBDA {
|
||||
if (parse_done || (key != name))
|
||||
IGUANA_UNLIKELY { return; }
|
||||
skip_ws(it, end);
|
||||
match<':'>(it, end);
|
||||
{
|
||||
using namespace detail;
|
||||
from_json_impl(field, it, end);
|
||||
}
|
||||
|
||||
skip_ws(it, end);
|
||||
if (*it == '}')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
parse_done = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
IGUANA_LIKELY { match<','>(it, end); }
|
||||
key = detail::get_key(it, end);
|
||||
});
|
||||
if (parse_done) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
while (it != end) {
|
||||
static auto frozen_map = ylt::reflection::get_variant_map<U>();
|
||||
if (frozen_map.size() > 0) {
|
||||
const auto &member_it = frozen_map.find(key);
|
||||
skip_ws(it, end);
|
||||
match<':'>(it, end);
|
||||
if (member_it != frozen_map.end())
|
||||
IGUANA_LIKELY {
|
||||
std::visit(
|
||||
[&](auto offset) IGUANA__INLINE_LAMBDA {
|
||||
using namespace detail;
|
||||
using value_type = typename decltype(offset)::type;
|
||||
auto member_ptr =
|
||||
(value_type *)((char *)(&value) + offset.value);
|
||||
from_json_impl(*member_ptr, it, end);
|
||||
},
|
||||
member_it->second);
|
||||
}
|
||||
else
|
||||
IGUANA_UNLIKELY {
|
||||
#ifdef THROW_UNKNOWN_KEY
|
||||
throw std::runtime_error("Unknown key: " + std::string(key));
|
||||
#else
|
||||
detail::skip_object_value(it, end);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
skip_ws(it, end);
|
||||
if (*it == '}')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
else
|
||||
IGUANA_LIKELY { match<','>(it, end); }
|
||||
key = detail::get_key(it, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !YLT_USE_CXX26_REFLECTION
|
||||
|
||||
template <typename T, typename It,
|
||||
std::enable_if_t<non_ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void from_json(T &value, It &&it, It &&end) {
|
||||
using namespace detail;
|
||||
from_json_impl(value, it, end);
|
||||
}
|
||||
|
||||
template <typename T, typename It>
|
||||
IGUANA_INLINE void from_json(T &value, It &&it, It &&end,
|
||||
std::error_code &ec) noexcept {
|
||||
try {
|
||||
from_json(value, it, end);
|
||||
ec = {};
|
||||
} catch (std::runtime_error &e) {
|
||||
ec = iguana::make_error_code(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename View, std::enable_if_t<ylt_refletable_v<T>, int>>
|
||||
IGUANA_INLINE void from_json(T &value, const View &view) {
|
||||
from_json(value, std::begin(view), std::end(view));
|
||||
}
|
||||
|
||||
template <typename T, typename View,
|
||||
std::enable_if_t<non_ylt_refletable_v<T>, int>>
|
||||
IGUANA_INLINE void from_json(T &value, const View &view) {
|
||||
from_json(value, std::begin(view), std::end(view));
|
||||
}
|
||||
|
||||
template <typename T, typename View,
|
||||
std::enable_if_t<json_view_v<View>, int> = 0>
|
||||
IGUANA_INLINE void from_json(T &value, const View &view,
|
||||
std::error_code &ec) noexcept {
|
||||
try {
|
||||
from_json(value, view);
|
||||
ec = {};
|
||||
} catch (std::runtime_error &e) {
|
||||
ec = iguana::make_error_code(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Byte,
|
||||
std::enable_if_t<json_byte_v<Byte>, int> = 0>
|
||||
IGUANA_INLINE void from_json(T &value, const Byte *data, size_t size) {
|
||||
std::string_view buffer(data, size);
|
||||
from_json(value, buffer);
|
||||
}
|
||||
|
||||
template <typename T, typename Byte,
|
||||
std::enable_if_t<json_byte_v<Byte>, int> = 0>
|
||||
IGUANA_INLINE void from_json(T &value, const Byte *data, size_t size,
|
||||
std::error_code &ec) noexcept {
|
||||
try {
|
||||
from_json(value, data, size);
|
||||
ec = {};
|
||||
} catch (std::runtime_error &e) {
|
||||
ec = iguana::make_error_code(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Is_view = false, typename It>
|
||||
void parse(jvalue &result, It &&it, It &&end, bool parse_as_double = false);
|
||||
|
||||
template <bool Is_view = false, typename It>
|
||||
inline void parse(jarray &result, It &&it, It &&end,
|
||||
bool parse_as_double = false) {
|
||||
skip_ws(it, end);
|
||||
match<'['>(it, end);
|
||||
if (*it == ']')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
while (true) {
|
||||
if (it == end) {
|
||||
break;
|
||||
}
|
||||
result.emplace_back();
|
||||
|
||||
parse<Is_view>(result.back(), it, end, parse_as_double);
|
||||
|
||||
if (*it == ']')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
|
||||
match<','>(it, end);
|
||||
}
|
||||
throw std::runtime_error("Expected ]");
|
||||
}
|
||||
|
||||
template <bool Is_view = false, typename It>
|
||||
inline void parse(jobject &result, It &&it, It &&end,
|
||||
bool parse_as_double = false) {
|
||||
skip_ws(it, end);
|
||||
match<'{'>(it, end);
|
||||
if (*it == '}')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
|
||||
skip_ws(it, end);
|
||||
|
||||
while (true) {
|
||||
if (it == end)
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Expected }"); }
|
||||
std::string key;
|
||||
using namespace detail;
|
||||
from_json_impl(key, it, end);
|
||||
|
||||
auto emplaced = result.try_emplace(key);
|
||||
if (!emplaced.second)
|
||||
throw std::runtime_error("duplicated key " + key);
|
||||
|
||||
match<':'>(it, end);
|
||||
|
||||
parse<Is_view>(emplaced.first->second, it, end, parse_as_double);
|
||||
|
||||
if (*it == '}')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
|
||||
match<','>(it, end);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Is_view, typename It>
|
||||
inline void parse(jvalue &result, It &&it, It &&end, bool parse_as_double) {
|
||||
using namespace detail;
|
||||
skip_ws(it, end);
|
||||
switch (*it) {
|
||||
case 'n':
|
||||
++it;
|
||||
match<'u', 'l', 'l'>(it, end);
|
||||
result.template emplace<std::nullptr_t>();
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
case 't':
|
||||
from_json_impl(result.template emplace<bool>(), it, end);
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '-': {
|
||||
double d{};
|
||||
from_json_impl(d, it, end);
|
||||
if (!parse_as_double && (static_cast<int>(d) == d))
|
||||
result.emplace<int>(d);
|
||||
else
|
||||
result.emplace<double>(d);
|
||||
break;
|
||||
}
|
||||
case '"':
|
||||
if constexpr (Is_view) {
|
||||
result.template emplace<std::string_view>();
|
||||
from_json_impl(std::get<std::string_view>(result), it, end);
|
||||
}
|
||||
else {
|
||||
result.template emplace<std::string>();
|
||||
from_json_impl(std::get<std::string>(result), it, end);
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
result.template emplace<jarray>();
|
||||
parse<Is_view>(std::get<jarray>(result), it, end, parse_as_double);
|
||||
break;
|
||||
case '{': {
|
||||
result.template emplace<jobject>();
|
||||
parse<Is_view>(std::get<jobject>(result), it, end, parse_as_double);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("parse failed");
|
||||
}
|
||||
|
||||
skip_ws(it, end);
|
||||
}
|
||||
|
||||
// set Is_view == true, parse str as std::string_view
|
||||
// set parse_as_double == true, parse the number as double in any case
|
||||
template <bool Is_view = false, typename It>
|
||||
inline void parse(jvalue &result, It &&it, It &&end, std::error_code &ec,
|
||||
bool parse_as_double = false) {
|
||||
try {
|
||||
parse<Is_view>(result, it, end, parse_as_double);
|
||||
ec = {};
|
||||
} catch (const std::runtime_error &e) {
|
||||
result.template emplace<std::nullptr_t>();
|
||||
ec = iguana::make_error_code(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Is_view = false, typename T, typename View,
|
||||
std::enable_if_t<json_view_v<View>, int> = 0>
|
||||
inline void parse(T &result, const View &view, bool parse_as_double = false) {
|
||||
parse<Is_view>(result, std::begin(view), std::end(view), parse_as_double);
|
||||
}
|
||||
|
||||
template <bool Is_view = false, typename T, typename View,
|
||||
std::enable_if_t<json_view_v<View>, int> = 0>
|
||||
inline void parse(T &result, const View &view, std::error_code &ec,
|
||||
bool parse_as_double = false) noexcept {
|
||||
try {
|
||||
parse<Is_view>(result, view, parse_as_double);
|
||||
ec = {};
|
||||
} catch (std::runtime_error &e) {
|
||||
ec = iguana::make_error_code(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
IGUANA_INLINE std::string json_file_content(const std::string &filename) {
|
||||
std::error_code ec;
|
||||
uint64_t size = std::filesystem::file_size(filename, ec);
|
||||
if (ec) {
|
||||
throw std::runtime_error("file size error " + ec.message());
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
throw std::runtime_error("empty file");
|
||||
}
|
||||
|
||||
std::string content;
|
||||
content.resize(size);
|
||||
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
file.read(content.data(), content.size());
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void from_json_file(T &value, const std::string &filename) {
|
||||
std::string content = json_file_content(filename);
|
||||
from_json(value, content.begin(), content.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void from_json_file(T &value, const std::string &filename,
|
||||
std::error_code &ec) noexcept {
|
||||
try {
|
||||
from_json_file(value, filename);
|
||||
ec = {};
|
||||
} catch (std::runtime_error &e) {
|
||||
ec = iguana::make_error_code(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void from_json_adl(iguana_adl_t *p, T &t,
|
||||
std::string_view pb_str) {
|
||||
iguana::from_json(t, pb_str);
|
||||
}
|
||||
|
||||
} // namespace iguana
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
// Glaze Library
|
||||
// For the license information refer to glaze.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
#include "detail/string_stream.hpp"
|
||||
#include "util.hpp"
|
||||
#include "value.hpp"
|
||||
|
||||
namespace iguana {
|
||||
|
||||
class numeric_str {
|
||||
public:
|
||||
std::string_view &value() { return val_; }
|
||||
std::string_view value() const { return val_; }
|
||||
template <typename T>
|
||||
T convert() {
|
||||
static_assert(num_v<T>, "T must be numeric type");
|
||||
if (val_.empty())
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
|
||||
T res;
|
||||
detail::from_chars(val_.data(), val_.data() + val_.size(), res);
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string_view val_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool numeric_str_v =
|
||||
std::is_same_v<numeric_str, std::remove_cvref_t<T>>;
|
||||
|
||||
template <>
|
||||
constexpr inline bool reflect26_excluded_v<numeric_str> = true;
|
||||
|
||||
template <typename It>
|
||||
IGUANA_INLINE void skip_comment(It &&it, It &&end) {
|
||||
++it;
|
||||
if (it == end)
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::runtime_error("Unexpected end, expected comment");
|
||||
}
|
||||
else if (*it == '/') {
|
||||
while (++it != end && *it != '\n')
|
||||
;
|
||||
}
|
||||
else if (*it == '*') {
|
||||
while (++it != end) {
|
||||
if (*it == '*')
|
||||
IGUANA_UNLIKELY {
|
||||
if (++it == end)
|
||||
IGUANA_UNLIKELY { break; }
|
||||
else if (*it == '/')
|
||||
IGUANA_LIKELY {
|
||||
++it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
IGUANA_UNLIKELY throw std::runtime_error("Expected / or * after /");
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
IGUANA_INLINE void skip_ws(It &&it, It &&end) {
|
||||
while (it != end) {
|
||||
// assuming ascii
|
||||
if (static_cast<uint8_t>(*it) < 33) {
|
||||
++it;
|
||||
}
|
||||
else if (*it == '/') {
|
||||
skip_comment(it, end);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
IGUANA_INLINE void skip_ws_no_comments(It &&it, It &&end) {
|
||||
while (it != end) {
|
||||
// assuming ascii
|
||||
if (static_cast<uint8_t>(*it) < 33)
|
||||
IGUANA_LIKELY { ++it; }
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline constexpr auto has_escape = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
|
||||
return has_zero(
|
||||
chunk ^
|
||||
0b0101110001011100010111000101110001011100010111000101110001011100);
|
||||
};
|
||||
|
||||
template <typename It>
|
||||
IGUANA_INLINE void skip_till_escape_or_qoute(It &&it, It &&end) {
|
||||
static_assert(contiguous_iterator<std::decay_t<decltype(it)>>);
|
||||
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<const uint64_t *>(&*it);
|
||||
uint64_t test = has_qoute(chunk) | has_escape(chunk);
|
||||
if (test != 0) {
|
||||
it += (countr_zero(test) >> 3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tail end of buffer. Should be rare we even get here
|
||||
while (it < end) {
|
||||
switch (*it) {
|
||||
case '\\':
|
||||
case '"':
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
throw std::runtime_error("Expected \"");
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
IGUANA_INLINE void skip_till_qoute(It &&it, It &&end) {
|
||||
static_assert(contiguous_iterator<std::decay_t<decltype(it)>>);
|
||||
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<const uint64_t *>(&*it);
|
||||
uint64_t test = has_qoute(chunk);
|
||||
if (test != 0) {
|
||||
it += (countr_zero(test) >> 3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tail end of buffer. Should be rare we even get here
|
||||
while (it < end) {
|
||||
if (*it == '"')
|
||||
return;
|
||||
++it;
|
||||
}
|
||||
throw std::runtime_error("Expected \"");
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
IGUANA_INLINE void skip_string(It &&it, It &&end) noexcept {
|
||||
++it;
|
||||
while (it < end) {
|
||||
if (*it == '"') {
|
||||
++it;
|
||||
break;
|
||||
}
|
||||
else if (*it == '\\' && ++it == end)
|
||||
IGUANA_UNLIKELY
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
template <char open, char close, typename It>
|
||||
IGUANA_INLINE void skip_until_closed(It &&it, It &&end) {
|
||||
++it;
|
||||
size_t open_count = 1;
|
||||
size_t close_count = 0;
|
||||
while (it < end && open_count > close_count) {
|
||||
switch (*it) {
|
||||
case '/':
|
||||
skip_comment(it, end);
|
||||
break;
|
||||
case '"':
|
||||
skip_string(it, end);
|
||||
break;
|
||||
case open:
|
||||
++open_count;
|
||||
++it;
|
||||
break;
|
||||
case close:
|
||||
++close_count;
|
||||
++it;
|
||||
break;
|
||||
default:
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IGUANA_INLINE bool is_numeric(char c) noexcept {
|
||||
static constexpr int is_num[256] = {
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, // 2
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 3
|
||||
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5
|
||||
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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<bool>(is_num[static_cast<unsigned char>(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<bool>(can_follow_num[static_cast<unsigned char>(c)]);
|
||||
}
|
||||
|
||||
} // namespace iguana
|
||||
+320
@@ -0,0 +1,320 @@
|
||||
//
|
||||
// Created by Qiyu on 17-6-5.
|
||||
//
|
||||
|
||||
#ifndef SERIALIZE_JSON_HPP
|
||||
#define SERIALIZE_JSON_HPP
|
||||
#include "json_util.hpp"
|
||||
|
||||
namespace iguana {
|
||||
|
||||
template <bool Is_writing_escape = true, typename Stream, typename T,
|
||||
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_json(T &&t, Stream &s);
|
||||
namespace detail {
|
||||
template <bool Is_writing_escape = true, typename Stream, typename T,
|
||||
std::enable_if_t<optional_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, const T &val);
|
||||
|
||||
template <bool Is_writing_escape = true, typename Stream, typename T,
|
||||
std::enable_if_t<fixed_array_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, const T &t);
|
||||
|
||||
template <bool Is_writing_escape = true, typename Stream, typename T,
|
||||
std::enable_if_t<sequence_container_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v);
|
||||
|
||||
template <bool Is_writing_escape = true, typename Stream, typename T,
|
||||
std::enable_if_t<pair_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v);
|
||||
|
||||
template <bool Is_writing_escape = true, typename Stream, typename T,
|
||||
std::enable_if_t<smart_ptr_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v);
|
||||
|
||||
template <bool Is_writing_escape = true, typename Stream, typename T,
|
||||
std::enable_if_t<map_container_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, const T &o);
|
||||
|
||||
template <bool Is_writing_escape = true, typename Stream, typename T,
|
||||
std::enable_if_t<tuple_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_json_impl(Stream &s, T &&t);
|
||||
|
||||
template <bool Is_writing_escape = true, typename Stream, typename T,
|
||||
std::enable_if_t<variant_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_json_impl(Stream &s, T &&t);
|
||||
|
||||
template <typename Stream, typename InputIt, typename T, typename F>
|
||||
IGUANA_INLINE void join(Stream &ss, InputIt first, InputIt last, const T &delim,
|
||||
const F &f) {
|
||||
if (first == last)
|
||||
return;
|
||||
f(*first++);
|
||||
while (first != last) {
|
||||
ss.push_back(delim);
|
||||
f(*first++);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, std::nullptr_t) {
|
||||
ss.append("null");
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, bool b) {
|
||||
ss.append(b ? "true" : "false");
|
||||
};
|
||||
|
||||
template <bool Is_writing_escape, typename Stream>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, char value) {
|
||||
ss.append("\"");
|
||||
ss.push_back(value);
|
||||
ss.append("\"");
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<num_v<T>, int> = 0>
|
||||
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);
|
||||
}
|
||||
|
||||
// template <bool Is_writing_escape, typename Stream, typename T,
|
||||
// std::enable_if_t<is_pb_type_v<T>, int> = 0>
|
||||
// IGUANA_INLINE void to_json_impl(Stream &ss, T value) {
|
||||
// to_json_impl<Is_writing_escape>(ss, value.val);
|
||||
// }
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<numeric_str_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, T v) {
|
||||
ss.append(v.value().data(), v.value().size());
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<string_container_v<T>, int> = 0>
|
||||
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);
|
||||
}
|
||||
else {
|
||||
ss.append(t.data(), t.size());
|
||||
}
|
||||
ss.push_back('"');
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<num_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_key(Stream &ss, const T &t) {
|
||||
ss.push_back('"');
|
||||
to_json_impl<Is_writing_escape>(ss, t);
|
||||
ss.push_back('"');
|
||||
}
|
||||
|
||||
// template <bool Is_writing_escape, typename Stream, typename T,
|
||||
// std::enable_if_t<is_pb_type_v<T>, int> = 0>
|
||||
// IGUANA_INLINE void render_key(Stream &ss, const T &t) {
|
||||
// render_key<Is_writing_escape>(ss, t.val);
|
||||
// }
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<string_container_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_key(Stream &ss, T &&t) {
|
||||
to_json_impl<Is_writing_escape>(ss, std::forward<T>(t));
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, T &&t) {
|
||||
to_json(std::forward<T>(t), ss);
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<enum_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, T val) {
|
||||
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
|
||||
if constexpr (bool_v<decltype(enum_to_str)>) {
|
||||
to_json_impl<Is_writing_escape>(
|
||||
ss, static_cast<std::underlying_type_t<T>>(val));
|
||||
}
|
||||
else {
|
||||
auto it = enum_to_str.find(val);
|
||||
if (it != enum_to_str.end())
|
||||
IGUANA_LIKELY {
|
||||
auto str = it->second;
|
||||
to_json_impl<Is_writing_escape>(
|
||||
ss, std::string_view(str.data(), str.size()));
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error(
|
||||
std::to_string(static_cast<std::underlying_type_t<T>>(val)) +
|
||||
" is a missing value in enum_value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<optional_v<T>, int>>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, const T &val) {
|
||||
if (!val) {
|
||||
ss.append("null");
|
||||
}
|
||||
else {
|
||||
to_json_impl<Is_writing_escape>(ss, *val);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T>
|
||||
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 {
|
||||
to_json_impl<Is_writing_escape>(ss, jsv);
|
||||
});
|
||||
ss.push_back(']');
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<fixed_array_v<T>, int>>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, const T &t) {
|
||||
if constexpr (std::is_same_v<char, std::remove_reference_t<
|
||||
decltype(std::declval<T>()[0])>>) {
|
||||
constexpr size_t n = sizeof(T) / sizeof(decltype(std::declval<T>()[0]));
|
||||
ss.push_back('"');
|
||||
auto get_length = [&t](int n) constexpr {
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (t[i] == '\0')
|
||||
return i;
|
||||
}
|
||||
return n;
|
||||
};
|
||||
size_t len = get_length(n);
|
||||
ss.append(std::data(t), len);
|
||||
ss.push_back('"');
|
||||
}
|
||||
else {
|
||||
render_array<Is_writing_escape>(ss, t);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<map_container_v<T>, int>>
|
||||
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<Is_writing_escape>(ss, jsv.first);
|
||||
ss.push_back(':');
|
||||
to_json_impl<Is_writing_escape>(ss, jsv.second);
|
||||
});
|
||||
ss.push_back('}');
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<sequence_container_v<T>, int>>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) {
|
||||
constexpr char pre = pair_v<typename T::value_type> ? '{' : '[';
|
||||
ss.push_back(pre);
|
||||
join(ss, v.cbegin(), v.cend(), ',',
|
||||
[&ss](const auto &jsv) IGUANA__INLINE_LAMBDA {
|
||||
to_json_impl<Is_writing_escape>(ss, jsv);
|
||||
});
|
||||
constexpr char post = pair_v<typename T::value_type> ? '}' : ']';
|
||||
ss.push_back(post);
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<pair_v<T>, int>>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) {
|
||||
render_key<Is_writing_escape>(ss, v.first);
|
||||
ss.push_back(':');
|
||||
to_json_impl<Is_writing_escape>(ss, v.second);
|
||||
}
|
||||
|
||||
constexpr auto write_json_key = [](auto &s, auto name) IGUANA__INLINE_LAMBDA {
|
||||
s.push_back('"');
|
||||
// will be replaced by string_view later
|
||||
s.append(name.data(), name.size());
|
||||
s.push_back('"');
|
||||
};
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<smart_ptr_v<T>, int>>
|
||||
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) {
|
||||
if (v) {
|
||||
to_json_impl<Is_writing_escape>(ss, *v);
|
||||
}
|
||||
else {
|
||||
ss.append("null");
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<tuple_v<T>, int>>
|
||||
IGUANA_INLINE void to_json_impl(Stream &s, T &&t) {
|
||||
using U = typename std::decay_t<T>;
|
||||
s.push_back('[');
|
||||
constexpr size_t size = std::tuple_size_v<U>;
|
||||
foreach_tuple(
|
||||
[&s, size](auto &v, auto i) IGUANA__INLINE_LAMBDA {
|
||||
to_json_impl<Is_writing_escape>(s, v);
|
||||
|
||||
if (i != size - 1)
|
||||
IGUANA_LIKELY { s.push_back(','); }
|
||||
},
|
||||
std::forward<T>(t));
|
||||
s.push_back(']');
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<variant_v<T>, int>>
|
||||
IGUANA_INLINE void to_json_impl(Stream &s, T &&t) {
|
||||
static_assert(!has_duplicate_type_v<std::remove_reference_t<T>>,
|
||||
"don't allow same type in std::variant");
|
||||
std::visit(
|
||||
[&s](auto value) {
|
||||
to_json_impl<Is_writing_escape>(s, value);
|
||||
},
|
||||
t);
|
||||
}
|
||||
} // namespace detail
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<ylt_refletable_v<T>, int>>
|
||||
IGUANA_INLINE void to_json(T &&t, Stream &s) {
|
||||
using namespace detail;
|
||||
s.push_back('{');
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
constexpr auto Count = ylt::reflection::members_count_v<U>;
|
||||
if constexpr (Count > 0) {
|
||||
ylt::reflection::for_each(t, [&](auto &field, auto name, auto index) {
|
||||
static_assert(
|
||||
!std::is_pointer_v<std::remove_reference_t<decltype(field)>>,
|
||||
"don't use raw pointer, please use smart pointer");
|
||||
write_json_key(s, name);
|
||||
s.push_back(':');
|
||||
to_json_impl<Is_writing_escape>(s, field);
|
||||
if (index < Count - 1)
|
||||
IGUANA_LIKELY { s.push_back(','); }
|
||||
});
|
||||
}
|
||||
|
||||
s.push_back('}');
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape = true, typename Stream, typename T,
|
||||
std::enable_if_t<non_ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_json(T &&t, Stream &s) {
|
||||
using namespace detail;
|
||||
to_json_impl<Is_writing_escape>(s, t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void to_json_adl(iguana_adl_t *p, T &t, std::string &pb_str) {
|
||||
to_json(t, pb_str);
|
||||
}
|
||||
|
||||
} // namespace iguana
|
||||
#endif // SERIALIZE_JSON_HPP
|
||||
+555
@@ -0,0 +1,555 @@
|
||||
#pragma once
|
||||
#include "detail/string_resize.hpp"
|
||||
#include "pb_util.hpp"
|
||||
|
||||
namespace iguana {
|
||||
template <typename T>
|
||||
IGUANA_INLINE void from_pb(T& t, std::string_view pb_str);
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str,
|
||||
uint32_t field_no = 0);
|
||||
|
||||
inline void skip_unknown_field(std::string_view& pb_str, WireType wire_type,
|
||||
uint32_t field_number = 0);
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void decode_pair_value(T& val, std::string_view& pb_str) {
|
||||
auto key = detail::decode_key(pb_str);
|
||||
pb_str = pb_str.substr(key.tag_size);
|
||||
WireType wire_type = key.wire_type;
|
||||
if (wire_type != detail::get_wire_type<std::remove_reference_t<T>>()) {
|
||||
return;
|
||||
}
|
||||
from_pb_impl(val, pb_str);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str,
|
||||
uint32_t field_no) {
|
||||
size_t pos = 0;
|
||||
if constexpr (ylt_refletable_v<T>) {
|
||||
const size_t size = detail::decode_length_delimited(
|
||||
pb_str, "Invalid message value: too few bytes.");
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
from_pb(val, pb_str.substr(0, size));
|
||||
pb_str = pb_str.substr(size);
|
||||
}
|
||||
else if constexpr (is_sequence_container<T>::value) {
|
||||
using item_type = typename T::value_type;
|
||||
if constexpr (is_lenprefix_v<item_type>) {
|
||||
// item_type non-packed
|
||||
while (!pb_str.empty()) {
|
||||
item_type item{};
|
||||
from_pb_impl(item, pb_str);
|
||||
val.push_back(std::move(item));
|
||||
if (pb_str.empty()) {
|
||||
break;
|
||||
}
|
||||
auto key = detail::decode_key(pb_str);
|
||||
if (key.field_number != field_no ||
|
||||
key.wire_type != detail::get_wire_type<item_type>()) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
pb_str = pb_str.substr(key.tag_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// item_type packed
|
||||
const size_t size = detail::decode_length_delimited(
|
||||
pb_str, "Invalid packed repeated value: too few bytes.");
|
||||
std::string_view packed = pb_str.substr(0, size);
|
||||
pb_str = pb_str.substr(size);
|
||||
if constexpr (is_fixed_v<item_type>) {
|
||||
if (size % sizeof(item_type) != 0)
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::invalid_argument(
|
||||
"Invalid packed fixed repeated value: bad length.");
|
||||
}
|
||||
size_t num = size / sizeof(item_type);
|
||||
size_t old_size = val.size();
|
||||
detail::resize(val, old_size + num);
|
||||
std::memcpy(val.data() + old_size, packed.data(), size);
|
||||
}
|
||||
else {
|
||||
while (!packed.empty()) {
|
||||
item_type item;
|
||||
from_pb_impl(item, packed);
|
||||
val.push_back(std::move(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_map_container<T>::value) {
|
||||
using key_type = typename T::key_type;
|
||||
using mapped_type = typename T::mapped_type;
|
||||
while (!pb_str.empty()) {
|
||||
const size_t size = detail::decode_length_delimited(
|
||||
pb_str, "Invalid map entry value: too few bytes.");
|
||||
std::string_view entry = pb_str.substr(0, size);
|
||||
pb_str = pb_str.substr(size);
|
||||
|
||||
key_type map_key{};
|
||||
mapped_type mapped{};
|
||||
while (!entry.empty()) {
|
||||
auto entry_key = detail::decode_key(entry);
|
||||
WireType entry_wire_type = entry_key.wire_type;
|
||||
uint32_t entry_field_number = entry_key.field_number;
|
||||
entry = entry.substr(entry_key.tag_size);
|
||||
|
||||
if (entry_field_number == 1) {
|
||||
if (entry_wire_type == detail::get_wire_type<key_type>()) {
|
||||
from_pb_impl(map_key, entry, 1);
|
||||
}
|
||||
else {
|
||||
skip_unknown_field(entry, entry_wire_type, entry_field_number);
|
||||
}
|
||||
}
|
||||
else if (entry_field_number == 2) {
|
||||
if (entry_wire_type == detail::get_wire_type<mapped_type>()) {
|
||||
from_pb_impl(mapped, entry, 2);
|
||||
}
|
||||
else {
|
||||
skip_unknown_field(entry, entry_wire_type, entry_field_number);
|
||||
}
|
||||
}
|
||||
else {
|
||||
skip_unknown_field(entry, entry_wire_type, entry_field_number);
|
||||
}
|
||||
}
|
||||
val.insert_or_assign(std::move(map_key), std::move(mapped));
|
||||
|
||||
if (pb_str.empty()) {
|
||||
break;
|
||||
}
|
||||
auto key = detail::decode_key(pb_str);
|
||||
if (key.field_number != field_no ||
|
||||
key.wire_type != WireType::LengthDelimeted) {
|
||||
break;
|
||||
}
|
||||
pb_str = pb_str.substr(key.tag_size);
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_integral_v<T>) {
|
||||
val = static_cast<T>(detail::decode_varint(pb_str, pos));
|
||||
pb_str = pb_str.substr(pos);
|
||||
}
|
||||
else if constexpr (detail::is_signed_varint_v<T>) {
|
||||
constexpr size_t len = sizeof(typename T::value_type);
|
||||
uint64_t temp = detail::decode_varint(pb_str, pos);
|
||||
if constexpr (len == 8) {
|
||||
val.val = detail::decode_zigzag(temp);
|
||||
}
|
||||
else {
|
||||
val.val = detail::decode_zigzag(static_cast<uint32_t>(temp));
|
||||
}
|
||||
pb_str = pb_str.substr(pos);
|
||||
}
|
||||
else if constexpr (detail::is_fixed_v<T>) {
|
||||
constexpr size_t size = sizeof(typename T::value_type);
|
||||
if (pb_str.size() < size)
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::invalid_argument("Invalid fixed int value: too few bytes.");
|
||||
}
|
||||
memcpy(&(val.val), pb_str.data(), size);
|
||||
pb_str = pb_str.substr(size);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
|
||||
constexpr size_t size = sizeof(T);
|
||||
if (pb_str.size() < size)
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::invalid_argument("Invalid fixed int value: too few bytes.");
|
||||
}
|
||||
memcpy(&(val), pb_str.data(), size);
|
||||
pb_str = pb_str.substr(size);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::string> ||
|
||||
std::is_same_v<T, std::string_view>) {
|
||||
const size_t size = detail::decode_length_delimited(
|
||||
pb_str, "Invalid string value: too few bytes.");
|
||||
if constexpr (std::is_same_v<T, std::string_view>) {
|
||||
val = std::string_view(pb_str.data(), size);
|
||||
}
|
||||
else {
|
||||
detail::resize(val, size);
|
||||
memcpy(val.data(), pb_str.data(), size);
|
||||
}
|
||||
pb_str = pb_str.substr(size);
|
||||
}
|
||||
else if constexpr (std::is_enum_v<T>) {
|
||||
using U = std::underlying_type_t<T>;
|
||||
U value{};
|
||||
from_pb_impl(value, pb_str);
|
||||
val = static_cast<T>(value);
|
||||
}
|
||||
else if constexpr (optional_v<T>) {
|
||||
if (!val.has_value()) {
|
||||
val.emplace();
|
||||
}
|
||||
from_pb_impl(*val, pb_str, field_no);
|
||||
}
|
||||
else {
|
||||
static_assert(!sizeof(T), "err");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Field>
|
||||
IGUANA_INLINE void parse_oneof(T& t, const Field& f, std::string_view& pb_str) {
|
||||
using item_type = typename std::decay_t<Field>::sub_type;
|
||||
from_pb_impl(t.template emplace<item_type>(), pb_str, f.field_no);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_unpacked_repeated_type() {
|
||||
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
|
||||
if constexpr (is_sequence_container<value_type>::value) {
|
||||
using item_type = typename value_type::value_type;
|
||||
return !is_lenprefix_v<item_type>;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_unpacked_repeated_wire_type(WireType wire_type) {
|
||||
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
|
||||
if constexpr (is_unpacked_repeated_type<value_type>()) {
|
||||
using item_type = typename value_type::value_type;
|
||||
return wire_type == detail::get_wire_type<item_type>();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void from_pb_unpacked_repeated_item(T& val,
|
||||
std::string_view& pb_str) {
|
||||
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
|
||||
using item_type = typename value_type::value_type;
|
||||
item_type item{};
|
||||
from_pb_impl(item, pb_str);
|
||||
val.push_back(std::move(item));
|
||||
}
|
||||
|
||||
template <bool TimestampSchema, typename T>
|
||||
IGUANA_INLINE auto from_pb_well_known_value(const T* current,
|
||||
std::string_view& pb_str,
|
||||
uint32_t field_no) {
|
||||
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
|
||||
if constexpr (TimestampSchema) {
|
||||
iguana::pb_timestamp wire_value{};
|
||||
if (current != nullptr) {
|
||||
wire_value = iguana::pb_timestamp{*current};
|
||||
}
|
||||
from_pb_impl(wire_value, pb_str, field_no);
|
||||
return static_cast<value_type>(wire_value);
|
||||
}
|
||||
else {
|
||||
iguana::pb_duration wire_value{};
|
||||
if (current != nullptr) {
|
||||
wire_value = iguana::pb_duration{*current};
|
||||
}
|
||||
from_pb_impl(wire_value, pb_str, field_no);
|
||||
return static_cast<value_type>(wire_value);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool TimestampSchema, typename T>
|
||||
IGUANA_INLINE void from_pb_well_known_impl(T& val, std::string_view& pb_str,
|
||||
uint32_t field_no) {
|
||||
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
|
||||
if constexpr (optional_v<value_type>) {
|
||||
using item_type = typename value_type::value_type;
|
||||
const item_type* current = val.has_value() ? &*val : nullptr;
|
||||
val = from_pb_well_known_value<TimestampSchema, item_type>(current, pb_str,
|
||||
field_no);
|
||||
}
|
||||
else if constexpr (is_sequence_container<value_type>::value) {
|
||||
using item_type = typename value_type::value_type;
|
||||
val.push_back(from_pb_well_known_value<TimestampSchema, item_type>(
|
||||
nullptr, pb_str, field_no));
|
||||
}
|
||||
else {
|
||||
val = from_pb_well_known_value<TimestampSchema, value_type>(&val, pb_str,
|
||||
field_no);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Wire, typename T>
|
||||
IGUANA_INLINE void from_pb_schema_impl(T& val, std::string_view& pb_str,
|
||||
uint32_t field_no) {
|
||||
using value_type = ylt::reflection::remove_cvref_t<T>;
|
||||
using wire_type = ylt::reflection::remove_cvref_t<Wire>;
|
||||
if constexpr (std::is_same_v<value_type, wire_type>) {
|
||||
from_pb_impl(val, pb_str, field_no);
|
||||
}
|
||||
else if constexpr (is_sequence_container<value_type>::value) {
|
||||
using wire_item_type = typename wire_type::value_type;
|
||||
if constexpr (is_lenprefix_v<wire_item_type>) {
|
||||
wire_type wire_value{};
|
||||
from_pb_impl(wire_value, pb_str, field_no);
|
||||
assign_pb_wire_value(val, std::move(wire_value));
|
||||
}
|
||||
else {
|
||||
const size_t size = decode_length_delimited(
|
||||
pb_str, "Invalid packed repeated value: too few bytes.");
|
||||
std::string_view packed = pb_str.substr(0, size);
|
||||
pb_str = pb_str.substr(size);
|
||||
while (!packed.empty()) {
|
||||
wire_item_type wire_item{};
|
||||
from_pb_impl(wire_item, packed);
|
||||
typename value_type::value_type item{};
|
||||
assign_pb_wire_value(item, std::move(wire_item));
|
||||
val.push_back(std::move(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
wire_type wire_value{};
|
||||
from_pb_impl(wire_value, pb_str, field_no);
|
||||
assign_pb_wire_value(val, std::move(wire_value));
|
||||
}
|
||||
}
|
||||
|
||||
// pb_str must already point past the field tag varint.
|
||||
inline void skip_unknown_field(std::string_view& pb_str, WireType wire_type,
|
||||
uint32_t field_number) {
|
||||
switch (wire_type) {
|
||||
case WireType::Varint: {
|
||||
if (pb_str.empty())
|
||||
throw std::invalid_argument("unknown Varint field: too few bytes");
|
||||
size_t skip_pos = 0;
|
||||
decode_varint(pb_str, skip_pos);
|
||||
pb_str = pb_str.substr(skip_pos);
|
||||
break;
|
||||
}
|
||||
case WireType::Fixed64:
|
||||
if (pb_str.size() < 8)
|
||||
throw std::invalid_argument("unknown Fixed64 field: too few bytes");
|
||||
pb_str = pb_str.substr(8);
|
||||
break;
|
||||
case WireType::LengthDelimeted: {
|
||||
const size_t len = decode_length_delimited(
|
||||
pb_str, "unknown LengthDelimited field: too few bytes");
|
||||
pb_str = pb_str.substr(len);
|
||||
break;
|
||||
}
|
||||
case WireType::Fixed32:
|
||||
if (pb_str.size() < 4)
|
||||
throw std::invalid_argument("unknown Fixed32 field: too few bytes");
|
||||
pb_str = pb_str.substr(4);
|
||||
break;
|
||||
case WireType::StartGroup: {
|
||||
pb_recursion_guard recursion_guard;
|
||||
while (true) {
|
||||
if (pb_str.empty())
|
||||
throw std::invalid_argument("unknown group field: missing end tag");
|
||||
auto key = detail::decode_key(pb_str, true);
|
||||
pb_str = pb_str.substr(key.tag_size);
|
||||
WireType child_wire_type = key.wire_type;
|
||||
uint32_t child_field_number = key.field_number;
|
||||
if (child_wire_type == WireType::EndGroup) {
|
||||
if (child_field_number != field_number) {
|
||||
throw std::invalid_argument(
|
||||
"unknown group field: mismatched end tag");
|
||||
}
|
||||
return;
|
||||
}
|
||||
skip_unknown_field(pb_str, child_wire_type, child_field_number);
|
||||
}
|
||||
}
|
||||
case WireType::EndGroup:
|
||||
throw std::runtime_error("unexpected end group in unknown field");
|
||||
default:
|
||||
throw std::runtime_error("unknown wire type in unknown field");
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void from_pb(T& t, std::string_view pb_str) {
|
||||
if (pb_str.empty())
|
||||
IGUANA_UNLIKELY { return; }
|
||||
detail::pb_recursion_guard recursion_guard;
|
||||
auto key = detail::decode_key(pb_str);
|
||||
size_t pos = key.tag_size;
|
||||
WireType wire_type = key.wire_type;
|
||||
uint32_t field_number = key.field_number;
|
||||
#ifdef SEQUENTIAL_PARSE
|
||||
static auto tp = detail::get_pb_members_tuple(t);
|
||||
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tp)>>;
|
||||
bool parse_done = false;
|
||||
auto ptr = &t;
|
||||
detail::for_each_n(
|
||||
[&, ptr](auto i) IGUANA__INLINE_LAMBDA {
|
||||
auto val = std::get<decltype(i)::value>(tp);
|
||||
using sub_type = typename std::decay_t<decltype(val)>::sub_type;
|
||||
using value_type = typename std::decay_t<decltype(val)>::value_type;
|
||||
using wire_value_type =
|
||||
typename std::decay_t<decltype(val)>::wire_value_type;
|
||||
using wire_sub_type =
|
||||
typename std::decay_t<decltype(val)>::wire_sub_type;
|
||||
constexpr bool is_variant_v = variant_v<value_type>;
|
||||
// sub_type is the element type when value_type is the variant type;
|
||||
// otherwise, they are the same.
|
||||
if (parse_done || field_number != val.field_no) {
|
||||
return;
|
||||
}
|
||||
pb_str = pb_str.substr(pos);
|
||||
if constexpr (std::decay_t<decltype(val)>::timestamp_schema ||
|
||||
std::decay_t<decltype(val)>::duration_schema) {
|
||||
if (wire_type != WireType::LengthDelimeted) {
|
||||
throw std::runtime_error("unmatched wire_type");
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<sub_type, std::monostate>) {
|
||||
throw std::runtime_error("oneof monostate has no wire type");
|
||||
}
|
||||
else if constexpr (detail::is_unpacked_repeated_type<
|
||||
wire_value_type>()) {
|
||||
if (wire_type != WireType::LengthDelimeted &&
|
||||
!detail::is_unpacked_repeated_wire_type<wire_value_type>(
|
||||
wire_type)) {
|
||||
throw std::runtime_error("unmatched wire_type");
|
||||
}
|
||||
}
|
||||
else if (wire_type != detail::get_wire_type<wire_sub_type>())
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("unmatched wire_type"); }
|
||||
|
||||
auto member_ptr = (value_type*)((char*)(ptr) + val.offset);
|
||||
if constexpr (std::decay_t<decltype(val)>::timestamp_schema ||
|
||||
std::decay_t<decltype(val)>::duration_schema) {
|
||||
detail::from_pb_well_known_impl<
|
||||
std::decay_t<decltype(val)>::timestamp_schema>(
|
||||
*member_ptr, pb_str, val.field_no);
|
||||
}
|
||||
else if constexpr (detail::is_unpacked_repeated_type<
|
||||
wire_value_type>()) {
|
||||
if (wire_type == WireType::LengthDelimeted) {
|
||||
detail::from_pb_schema_impl<wire_value_type>(*member_ptr, pb_str,
|
||||
val.field_no);
|
||||
}
|
||||
else {
|
||||
typename wire_value_type::value_type wire_item{};
|
||||
detail::from_pb_impl(wire_item, pb_str);
|
||||
typename value_type::value_type item{};
|
||||
detail::assign_pb_wire_value(item, std::move(wire_item));
|
||||
member_ptr->push_back(std::move(item));
|
||||
}
|
||||
}
|
||||
else if constexpr (is_variant_v) {
|
||||
detail::parse_oneof(*member_ptr, val, pb_str);
|
||||
}
|
||||
else {
|
||||
detail::from_pb_schema_impl<wire_value_type>(*member_ptr, pb_str,
|
||||
val.field_no);
|
||||
}
|
||||
if (pb_str.empty()) {
|
||||
parse_done = true;
|
||||
return;
|
||||
}
|
||||
key = detail::decode_key(pb_str);
|
||||
pos = key.tag_size;
|
||||
wire_type = key.wire_type;
|
||||
field_number = key.field_number;
|
||||
},
|
||||
std::make_index_sequence<SIZE>{});
|
||||
if (parse_done)
|
||||
IGUANA_LIKELY { return; }
|
||||
#endif
|
||||
static auto map = detail::get_members(t);
|
||||
while (true) {
|
||||
const char* field_start = pb_str.data();
|
||||
pb_str = pb_str.substr(pos);
|
||||
auto it = map.find(field_number);
|
||||
if (it == map.end()) {
|
||||
// Unknown field: skip according to wire type (proto3 forward compat)
|
||||
detail::skip_unknown_field(pb_str, wire_type, field_number);
|
||||
detail::append_pb_unknown_field(
|
||||
t, field_start, static_cast<size_t>(pb_str.data() - field_start));
|
||||
}
|
||||
else {
|
||||
auto& member = it->second;
|
||||
std::visit(
|
||||
[&t, &pb_str, wire_type](auto& val) {
|
||||
using sub_type = typename std::decay_t<decltype(val)>::sub_type;
|
||||
using value_type = typename std::decay_t<decltype(val)>::value_type;
|
||||
using wire_value_type =
|
||||
typename std::decay_t<decltype(val)>::wire_value_type;
|
||||
using wire_sub_type =
|
||||
typename std::decay_t<decltype(val)>::wire_sub_type;
|
||||
if constexpr (std::decay_t<decltype(val)>::timestamp_schema ||
|
||||
std::decay_t<decltype(val)>::duration_schema) {
|
||||
if (wire_type != WireType::LengthDelimeted) {
|
||||
throw std::runtime_error("unmatched wire_type");
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<sub_type, std::monostate>) {
|
||||
throw std::runtime_error("oneof monostate has no wire type");
|
||||
}
|
||||
else if constexpr (detail::is_unpacked_repeated_type<
|
||||
wire_value_type>()) {
|
||||
if (wire_type != WireType::LengthDelimeted &&
|
||||
!detail::is_unpacked_repeated_wire_type<wire_value_type>(
|
||||
wire_type)) {
|
||||
throw std::runtime_error("unmatched wire_type");
|
||||
}
|
||||
}
|
||||
else if (wire_type != detail::get_wire_type<wire_sub_type>()) {
|
||||
throw std::runtime_error("unmatched wire_type");
|
||||
}
|
||||
if constexpr (std::decay_t<decltype(val)>::timestamp_schema ||
|
||||
std::decay_t<decltype(val)>::duration_schema) {
|
||||
detail::from_pb_well_known_impl<
|
||||
std::decay_t<decltype(val)>::timestamp_schema>(
|
||||
val.value(t), pb_str, val.field_no);
|
||||
}
|
||||
else if constexpr (detail::is_unpacked_repeated_type<
|
||||
wire_value_type>()) {
|
||||
if (wire_type == WireType::LengthDelimeted) {
|
||||
detail::from_pb_schema_impl<wire_value_type>(
|
||||
val.value(t), pb_str, val.field_no);
|
||||
}
|
||||
else {
|
||||
typename wire_value_type::value_type wire_item{};
|
||||
detail::from_pb_impl(wire_item, pb_str);
|
||||
typename value_type::value_type item{};
|
||||
detail::assign_pb_wire_value(item, std::move(wire_item));
|
||||
val.value(t).push_back(std::move(item));
|
||||
}
|
||||
}
|
||||
else if constexpr (variant_v<value_type>) {
|
||||
detail::parse_oneof(val.value(t), val, pb_str);
|
||||
}
|
||||
else {
|
||||
detail::from_pb_schema_impl<wire_value_type>(val.value(t), pb_str,
|
||||
val.field_no);
|
||||
}
|
||||
},
|
||||
member);
|
||||
}
|
||||
if (!pb_str.empty())
|
||||
IGUANA_LIKELY {
|
||||
key = detail::decode_key(pb_str);
|
||||
pos = key.tag_size;
|
||||
wire_type = key.wire_type;
|
||||
field_number = key.field_number;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void from_pb_adl(iguana_adl_t* p, T& t, std::string_view pb_str) {
|
||||
iguana::from_pb(t, pb_str);
|
||||
}
|
||||
} // namespace iguana
|
||||
Vendored
+826
@@ -0,0 +1,826 @@
|
||||
#pragma once
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "common.hpp"
|
||||
#include "detail/pb_type.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
namespace iguana {
|
||||
|
||||
enum class WireType : uint32_t {
|
||||
Varint = 0,
|
||||
Fixed64 = 1,
|
||||
LengthDelimeted = 2,
|
||||
StartGroup = 3,
|
||||
EndGroup = 4,
|
||||
Fixed32 = 5,
|
||||
Unknown
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool inherits_from_base_v = std::is_base_of_v<detail::base, T>;
|
||||
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
constexpr bool is_fixed_v =
|
||||
std::is_same_v<T, fixed32_t> || std::is_same_v<T, fixed64_t> ||
|
||||
std::is_same_v<T, sfixed32_t> || std::is_same_v<T, sfixed64_t>;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_signed_varint_v =
|
||||
std::is_same_v<T, sint32_t> || std::is_same_v<T, sint64_t>;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline WireType get_wire_type() {
|
||||
if constexpr (std::is_integral_v<T> || is_signed_varint_v<T> ||
|
||||
std::is_enum_v<T> || std::is_same_v<T, bool>) {
|
||||
return WireType::Varint;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, fixed32_t> ||
|
||||
std::is_same_v<T, sfixed32_t> ||
|
||||
std::is_same_v<T, float>) {
|
||||
return WireType::Fixed32;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, fixed64_t> ||
|
||||
std::is_same_v<T, sfixed64_t> ||
|
||||
std::is_same_v<T, double>) {
|
||||
return WireType::Fixed64;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::string> ||
|
||||
std::is_same_v<T, std::string_view> ||
|
||||
ylt_refletable_v<T> || is_sequence_container<T>::value ||
|
||||
is_map_container<T>::value) {
|
||||
return WireType::LengthDelimeted;
|
||||
}
|
||||
else if constexpr (optional_v<T>) {
|
||||
return get_wire_type<typename T::value_type>();
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("unknown type");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_lenprefix_v = (get_wire_type<T>() ==
|
||||
WireType::LengthDelimeted);
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool pb_well_known_chrono_v =
|
||||
std::is_same_v<std::remove_const_t<std::remove_reference_t<T>>,
|
||||
std::chrono::system_clock::time_point> ||
|
||||
std::is_same_v<std::remove_const_t<std::remove_reference_t<T>>,
|
||||
std::chrono::nanoseconds>;
|
||||
|
||||
template <bool TimestampSchema, typename T>
|
||||
IGUANA_INLINE auto make_pb_well_known_value(const T& value) {
|
||||
if constexpr (TimestampSchema) {
|
||||
return iguana::pb_timestamp{value};
|
||||
}
|
||||
else {
|
||||
return iguana::pb_duration{value};
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] IGUANA_INLINE uint32_t encode_zigzag(int32_t v) {
|
||||
return (static_cast<uint32_t>(v) << 1U) ^
|
||||
static_cast<uint32_t>(
|
||||
-static_cast<int32_t>(static_cast<uint32_t>(v) >> 31U));
|
||||
}
|
||||
[[nodiscard]] IGUANA_INLINE uint64_t encode_zigzag(int64_t v) {
|
||||
return (static_cast<uint64_t>(v) << 1U) ^
|
||||
static_cast<uint64_t>(
|
||||
-static_cast<int64_t>(static_cast<uint64_t>(v) >> 63U));
|
||||
}
|
||||
|
||||
[[nodiscard]] IGUANA_INLINE int64_t decode_zigzag(uint64_t u) {
|
||||
return static_cast<int64_t>((u >> 1U)) ^
|
||||
static_cast<uint64_t>(-static_cast<int64_t>(u & 1U));
|
||||
}
|
||||
[[nodiscard]] IGUANA_INLINE int64_t decode_zigzag(uint32_t u) {
|
||||
return static_cast<int64_t>((u >> 1U)) ^
|
||||
static_cast<uint64_t>(-static_cast<int64_t>(u & 1U));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
IGUANA_INLINE uint64_t decode_varint(T& data, size_t& pos) {
|
||||
if (data.empty())
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::invalid_argument("Invalid varint value: too few bytes.");
|
||||
}
|
||||
|
||||
const auto* begin = reinterpret_cast<const unsigned char*>(data.data());
|
||||
uint64_t val = 0;
|
||||
const size_t size = data.size();
|
||||
const size_t limit = size < 10 ? size : 10;
|
||||
for (size_t i = 0; i < limit; ++i) {
|
||||
const uint64_t byte = begin[i];
|
||||
if (i == 9 && (byte & 0xfeU) != 0)
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::invalid_argument("Invalid varint value: too many bytes.");
|
||||
}
|
||||
val |= (byte & 0x7fU) << (7 * i);
|
||||
if ((byte & 0x80U) == 0) {
|
||||
pos = i + 1;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
if (size >= 10)
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::invalid_argument("Invalid varint value: too many bytes.");
|
||||
}
|
||||
throw std::invalid_argument("Invalid varint value: too few bytes.");
|
||||
}
|
||||
|
||||
struct pb_key {
|
||||
uint32_t field_number;
|
||||
WireType wire_type;
|
||||
size_t tag_size;
|
||||
};
|
||||
|
||||
IGUANA_INLINE pb_key decode_key(std::string_view data,
|
||||
bool allow_end_group = false) {
|
||||
size_t pos = 0;
|
||||
const uint64_t raw_key = decode_varint(data, pos);
|
||||
if (raw_key > std::numeric_limits<uint32_t>::max())
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::invalid_argument("Invalid protobuf tag: too large.");
|
||||
}
|
||||
const uint32_t key = static_cast<uint32_t>(raw_key);
|
||||
const uint32_t wire = key & 0b0111U;
|
||||
const uint32_t field_number = key >> 3U;
|
||||
if (field_number == 0)
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::invalid_argument(
|
||||
"Invalid protobuf tag: field number must be positive.");
|
||||
}
|
||||
if (wire > static_cast<uint32_t>(WireType::Fixed32))
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::invalid_argument("Invalid protobuf tag: unknown wire type.");
|
||||
}
|
||||
auto wire_type = static_cast<WireType>(wire);
|
||||
if (wire_type == WireType::EndGroup && !allow_end_group)
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::invalid_argument("unexpected end group in protobuf stream");
|
||||
}
|
||||
return {field_number, wire_type, pos};
|
||||
}
|
||||
|
||||
IGUANA_INLINE size_t decode_length_delimited(std::string_view& pb_str,
|
||||
const char* error) {
|
||||
size_t pos = 0;
|
||||
const uint64_t size = decode_varint(pb_str, pos);
|
||||
pb_str = pb_str.substr(pos);
|
||||
if (size > pb_str.size())
|
||||
IGUANA_UNLIKELY { throw std::invalid_argument(error); }
|
||||
if (size > std::numeric_limits<size_t>::max())
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::invalid_argument("Invalid length-delimited value: too large.");
|
||||
}
|
||||
return static_cast<size_t>(size);
|
||||
}
|
||||
|
||||
inline constexpr size_t max_pb_recursion_depth = 100;
|
||||
inline thread_local size_t pb_recursion_depth = 0;
|
||||
|
||||
struct pb_recursion_guard {
|
||||
pb_recursion_guard() {
|
||||
if (pb_recursion_depth >= max_pb_recursion_depth)
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::runtime_error("protobuf message recursion limit exceeded");
|
||||
}
|
||||
++pb_recursion_depth;
|
||||
}
|
||||
|
||||
~pb_recursion_guard() { --pb_recursion_depth; }
|
||||
};
|
||||
|
||||
// value == 0 ? 1 : floor(log2(value)) / 7 + 1
|
||||
constexpr size_t variant_uint32_size_constexpr(uint32_t value) {
|
||||
if (value == 0) {
|
||||
return 1;
|
||||
}
|
||||
int log = 0;
|
||||
while (value >>= 1) ++log;
|
||||
return log / 7 + 1;
|
||||
}
|
||||
|
||||
template <uint64_t v, typename Writer, size_t... I>
|
||||
IGUANA_INLINE void append_varint_u32(Writer& writer,
|
||||
std::index_sequence<I...>) {
|
||||
uint8_t temp = 0;
|
||||
((temp = static_cast<uint8_t>((v >> (7 * I)) | 0x80),
|
||||
writer.write((const char*)&temp, 1)),
|
||||
...);
|
||||
}
|
||||
|
||||
template <uint32_t v, typename Writer>
|
||||
IGUANA_INLINE void serialize_varint_u32(Writer& writer) {
|
||||
constexpr auto size = variant_uint32_size_constexpr(v);
|
||||
append_varint_u32<v>(writer, std::make_index_sequence<size - 1>{});
|
||||
uint8_t temp = static_cast<uint8_t>(v >> (7 * (size - 1)));
|
||||
writer.write((const char*)&temp, 1);
|
||||
}
|
||||
|
||||
template <typename Writer>
|
||||
IGUANA_INLINE void serialize_varint(uint64_t v, Writer& writer) {
|
||||
uint8_t temp = static_cast<uint8_t>(v);
|
||||
if (v < 0x80) {
|
||||
writer.write((const char*)&temp, 1);
|
||||
return;
|
||||
}
|
||||
temp = static_cast<uint8_t>(v | 0x80);
|
||||
writer.write((const char*)&temp, 1);
|
||||
v >>= 7;
|
||||
if (v < 0x80) {
|
||||
temp = static_cast<uint8_t>(v);
|
||||
writer.write((const char*)&temp, 1);
|
||||
return;
|
||||
}
|
||||
do {
|
||||
temp = static_cast<uint8_t>(v | 0x80);
|
||||
writer.write((const char*)&temp, 1);
|
||||
v >>= 7;
|
||||
} while (v >= 0x80);
|
||||
temp = static_cast<uint8_t>(v);
|
||||
writer.write((const char*)&temp, 1);
|
||||
}
|
||||
|
||||
IGUANA_INLINE uint32_t log2_floor_uint32(uint32_t n) {
|
||||
#if defined(__GNUC__)
|
||||
return 31 ^ static_cast<uint32_t>(__builtin_clz(n));
|
||||
#else
|
||||
unsigned long where;
|
||||
_BitScanReverse(&where, n);
|
||||
return where;
|
||||
#endif
|
||||
}
|
||||
|
||||
IGUANA_INLINE size_t variant_uint32_size(uint32_t value) {
|
||||
// This computes value == 0 ? 1 : floor(log2(value)) / 7 + 1
|
||||
// Use an explicit multiplication to implement the divide of
|
||||
// a number in the 1..31 range.
|
||||
// Explicit OR 0x1 to avoid calling Bits::Log2FloorNonZero(0), which is
|
||||
// undefined.
|
||||
uint32_t log2value = log2_floor_uint32(value | 0x1);
|
||||
return static_cast<size_t>((log2value * 9 + 73) / 64);
|
||||
}
|
||||
|
||||
IGUANA_INLINE int Log2FloorNonZero_Portable(uint32_t n) {
|
||||
if (n == 0)
|
||||
return -1;
|
||||
int log = 0;
|
||||
uint32_t value = n;
|
||||
for (int i = 4; i >= 0; --i) {
|
||||
int shift = (1 << i);
|
||||
uint32_t x = value >> shift;
|
||||
if (x != 0) {
|
||||
value = x;
|
||||
log += shift;
|
||||
}
|
||||
}
|
||||
assert(value == 1);
|
||||
return log;
|
||||
}
|
||||
|
||||
IGUANA_INLINE uint32_t Log2FloorNonZero(uint32_t n) {
|
||||
#if defined(__GNUC__)
|
||||
return 31 ^ static_cast<uint32_t>(__builtin_clz(n));
|
||||
#elif defined(_MSC_VER)
|
||||
unsigned long where;
|
||||
_BitScanReverse(&where, n);
|
||||
return where;
|
||||
#else
|
||||
return Log2FloorNonZero_Portable(n);
|
||||
#endif
|
||||
}
|
||||
|
||||
IGUANA_INLINE int Log2FloorNonZero64_Portable(uint64_t n) {
|
||||
const uint32_t topbits = static_cast<uint32_t>(n >> 32);
|
||||
if (topbits == 0) {
|
||||
// Top bits are zero, so scan in bottom bits
|
||||
return static_cast<int>(Log2FloorNonZero(static_cast<uint32_t>(n)));
|
||||
}
|
||||
else {
|
||||
return 32 + static_cast<int>(Log2FloorNonZero(topbits));
|
||||
}
|
||||
}
|
||||
|
||||
IGUANA_INLINE uint32_t log2_floor_uint64(uint64_t n) {
|
||||
#if defined(__GNUC__)
|
||||
return 63 ^ static_cast<uint32_t>(__builtin_clzll(n));
|
||||
#elif defined(_MSC_VER) && defined(_M_X64)
|
||||
unsigned long where;
|
||||
_BitScanReverse64(&where, n);
|
||||
return where;
|
||||
#else
|
||||
return Log2FloorNonZero64_Portable(n);
|
||||
#endif
|
||||
}
|
||||
|
||||
IGUANA_INLINE size_t variant_uint64_size(uint64_t value) {
|
||||
// This computes value == 0 ? 1 : floor(log2(value)) / 7 + 1
|
||||
// Use an explicit multiplication to implement the divide of
|
||||
// a number in the 1..63 range.
|
||||
// Explicit OR 0x1 to avoid calling Bits::Log2FloorNonZero(0), which is
|
||||
// undefined.
|
||||
uint32_t log2value = log2_floor_uint64(value | 0x1);
|
||||
return static_cast<size_t>((log2value * 9 + 73) / 64);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
constexpr IGUANA_INLINE size_t variant_intergal_size(U value) {
|
||||
using T = std::remove_reference_t<U>;
|
||||
if constexpr (sizeof(T) == 8) {
|
||||
return variant_uint64_size(static_cast<uint64_t>(value));
|
||||
}
|
||||
else if constexpr (sizeof(T) <= 4) {
|
||||
if constexpr (std::is_signed_v<T>) {
|
||||
if (value < 0) {
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
return variant_uint32_size(static_cast<uint32_t>(value));
|
||||
}
|
||||
else {
|
||||
static_assert(!sizeof(T), "intergal in not supported");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F, size_t... I>
|
||||
IGUANA_INLINE constexpr void for_each_n(F&& f, std::index_sequence<I...>) {
|
||||
(std::forward<F>(f)(std::integral_constant<size_t, I>{}), ...);
|
||||
}
|
||||
|
||||
template <size_t key_size, bool omit_default_val, typename Type>
|
||||
IGUANA_INLINE size_t str_numeric_size(Type&& t) {
|
||||
using T = std::remove_const_t<std::remove_reference_t<Type>>;
|
||||
if constexpr (std::is_same_v<T, std::string> ||
|
||||
std::is_same_v<T, std::string_view>) {
|
||||
// string
|
||||
if constexpr (omit_default_val) {
|
||||
if (t.size() == 0)
|
||||
IGUANA_UNLIKELY { return 0; }
|
||||
}
|
||||
if constexpr (key_size == 0) {
|
||||
return t.size();
|
||||
}
|
||||
else {
|
||||
return key_size + variant_uint32_size(static_cast<uint32_t>(t.size())) +
|
||||
t.size();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// numeric
|
||||
if constexpr (omit_default_val) {
|
||||
if constexpr (is_fixed_v<T> || is_signed_varint_v<T>) {
|
||||
if (t.val == 0)
|
||||
IGUANA_UNLIKELY { return 0; }
|
||||
}
|
||||
else {
|
||||
if (t == static_cast<T>(0))
|
||||
IGUANA_UNLIKELY { return 0; }
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_integral_v<T>) {
|
||||
if constexpr (std::is_same_v<bool, T>) {
|
||||
return 1 + key_size;
|
||||
}
|
||||
else {
|
||||
return key_size + variant_intergal_size(t);
|
||||
}
|
||||
}
|
||||
else if constexpr (detail::is_signed_varint_v<T>) {
|
||||
return key_size + variant_intergal_size(encode_zigzag(t.val));
|
||||
}
|
||||
else if constexpr (detail::is_fixed_v<T>) {
|
||||
return key_size + sizeof(typename T::value_type);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
|
||||
return key_size + sizeof(T);
|
||||
}
|
||||
else if constexpr (std::is_enum_v<T>) {
|
||||
using U = std::underlying_type_t<T>;
|
||||
return key_size + variant_intergal_size(static_cast<U>(t));
|
||||
}
|
||||
else {
|
||||
static_assert(!sizeof(T), "err");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t key_size, bool omit_default_val = true, typename Type,
|
||||
typename Arr>
|
||||
IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr);
|
||||
|
||||
template <bool TimestampSchema, size_t key_size, bool omit_default_val = true,
|
||||
typename Type, typename Arr>
|
||||
IGUANA_INLINE size_t pb_well_known_key_value_size(Type&& t, Arr& size_arr) {
|
||||
using T = std::remove_const_t<std::remove_reference_t<Type>>;
|
||||
if constexpr (optional_v<T>) {
|
||||
if (!t.has_value()) {
|
||||
return 0;
|
||||
}
|
||||
return pb_well_known_key_value_size<TimestampSchema, key_size,
|
||||
omit_default_val>(*t, size_arr);
|
||||
}
|
||||
else if constexpr (is_sequence_container<T>::value) {
|
||||
size_t len = 0;
|
||||
for (auto& item : t) {
|
||||
len += pb_well_known_key_value_size<TimestampSchema, key_size, false>(
|
||||
item, size_arr);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
else {
|
||||
auto wire_value = make_pb_well_known_value<TimestampSchema>(t);
|
||||
return pb_key_value_size<key_size, omit_default_val>(wire_value, size_arr);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Wire, typename T>
|
||||
IGUANA_INLINE auto make_pb_wire_scalar(const T& value) {
|
||||
using W = ylt::reflection::remove_cvref_t<Wire>;
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
if constexpr (std::is_same_v<W, U>) {
|
||||
return value;
|
||||
}
|
||||
else if constexpr (is_pb_type_v<W>) {
|
||||
return W{static_cast<typename W::value_type>(value)};
|
||||
}
|
||||
else {
|
||||
return W{value};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Wire>
|
||||
IGUANA_INLINE void assign_pb_wire_value(T& value, Wire&& wire) {
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
using W = ylt::reflection::remove_cvref_t<Wire>;
|
||||
if constexpr (std::is_same_v<U, W>) {
|
||||
value = std::forward<Wire>(wire);
|
||||
}
|
||||
else if constexpr (optional_v<U>) {
|
||||
if (wire.has_value()) {
|
||||
typename U::value_type item{};
|
||||
assign_pb_wire_value(item, *wire);
|
||||
value = std::move(item);
|
||||
}
|
||||
else {
|
||||
value.reset();
|
||||
}
|
||||
}
|
||||
else if constexpr (is_sequence_container<U>::value) {
|
||||
value.clear();
|
||||
for (auto& item : wire) {
|
||||
typename U::value_type converted{};
|
||||
assign_pb_wire_value(converted, item);
|
||||
value.push_back(std::move(converted));
|
||||
}
|
||||
}
|
||||
else if constexpr (is_pb_type_v<W>) {
|
||||
value = wire.val;
|
||||
}
|
||||
else {
|
||||
value = static_cast<U>(wire);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Wire, size_t key_size, bool omit_default_val = true,
|
||||
typename Type, typename Arr>
|
||||
IGUANA_INLINE size_t pb_schema_key_value_size(Type&& t, Arr& size_arr);
|
||||
|
||||
template <typename Variant, typename T, size_t I>
|
||||
constexpr inline size_t get_variant_index() {
|
||||
if constexpr (I == 0) {
|
||||
static_assert(std::is_same_v<std::variant_alternative_t<0, Variant>, T>,
|
||||
"Type T is not found in Variant");
|
||||
return 0;
|
||||
}
|
||||
else if constexpr (std::is_same_v<std::variant_alternative_t<I, Variant>,
|
||||
T>) {
|
||||
return I;
|
||||
}
|
||||
else {
|
||||
return get_variant_index<Variant, T, I - 1>();
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t field_no, typename Type, typename Arr>
|
||||
IGUANA_INLINE size_t pb_oneof_size(Type&& t, Arr& size_arr) {
|
||||
using T = std::decay_t<Type>;
|
||||
int len = 0;
|
||||
std::visit(
|
||||
[&len, &size_arr](auto&& value) IGUANA__INLINE_LAMBDA {
|
||||
using raw_value_type = decltype(value);
|
||||
using value_type =
|
||||
std::remove_const_t<std::remove_reference_t<decltype(value)>>;
|
||||
constexpr auto offset =
|
||||
get_variant_index<T, value_type, std::variant_size_v<T> - 1>();
|
||||
constexpr uint32_t key =
|
||||
((field_no + offset) << 3) |
|
||||
static_cast<uint32_t>(get_wire_type<value_type>());
|
||||
len = pb_key_value_size<variant_uint32_size_constexpr(key), false>(
|
||||
std::forward<raw_value_type>(value), size_arr);
|
||||
},
|
||||
std::forward<Type>(t));
|
||||
return len;
|
||||
}
|
||||
|
||||
// returns size = key_size + optional(len_size) + len
|
||||
// when key_size == 0, return len
|
||||
template <size_t key_size, bool omit_default_val, typename Type, typename Arr>
|
||||
IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr) {
|
||||
using T = std::remove_const_t<std::remove_reference_t<Type>>;
|
||||
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
|
||||
size_t len = 0;
|
||||
static auto tuple = get_pb_members_tuple(std::forward<Type>(t));
|
||||
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;
|
||||
size_t pre_index = -1;
|
||||
if constexpr (!inherits_from_base_v<T> && key_size != 0) {
|
||||
pre_index = size_arr.size();
|
||||
size_arr.push_back(0); // placeholder
|
||||
}
|
||||
for_each_n(
|
||||
[&len, &t, &size_arr](auto i) IGUANA__INLINE_LAMBDA {
|
||||
using field_type =
|
||||
std::tuple_element_t<decltype(i)::value,
|
||||
std::decay_t<decltype(tuple)>>;
|
||||
auto value = std::get<decltype(i)::value>(tuple);
|
||||
using U = typename field_type::value_type;
|
||||
using wire_type = typename field_type::wire_value_type;
|
||||
using sub_type = typename field_type::sub_type;
|
||||
auto& val = value.value(t);
|
||||
if constexpr (field_type::timestamp_schema ||
|
||||
field_type::duration_schema) {
|
||||
constexpr uint32_t sub_key =
|
||||
(value.field_no << 3) |
|
||||
static_cast<uint32_t>(WireType::LengthDelimeted);
|
||||
constexpr auto sub_keysize = variant_uint32_size_constexpr(sub_key);
|
||||
len += pb_well_known_key_value_size<field_type::timestamp_schema,
|
||||
sub_keysize>(val, size_arr);
|
||||
}
|
||||
else if constexpr (field_type::optional_schema) {
|
||||
constexpr uint32_t sub_key =
|
||||
(value.field_no << 3) |
|
||||
static_cast<uint32_t>(get_wire_type<wire_type>());
|
||||
constexpr auto sub_keysize = variant_uint32_size_constexpr(sub_key);
|
||||
len += pb_schema_key_value_size<wire_type, sub_keysize, false>(
|
||||
val, size_arr);
|
||||
}
|
||||
else if constexpr (variant_v<U>) {
|
||||
constexpr auto offset =
|
||||
get_variant_index<U, sub_type, std::variant_size_v<U> - 1>();
|
||||
if (val.index() == offset) {
|
||||
if constexpr (!std::is_same_v<sub_type, std::monostate>) {
|
||||
constexpr uint32_t sub_key =
|
||||
(value.field_no << 3) |
|
||||
static_cast<uint32_t>(get_wire_type<sub_type>());
|
||||
constexpr auto sub_keysize =
|
||||
variant_uint32_size_constexpr(sub_key);
|
||||
len += pb_key_value_size<sub_keysize, false>(
|
||||
std::get<offset>(val), size_arr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
constexpr uint32_t sub_key =
|
||||
(value.field_no << 3) |
|
||||
static_cast<uint32_t>(get_wire_type<wire_type>());
|
||||
constexpr auto sub_keysize = variant_uint32_size_constexpr(sub_key);
|
||||
len +=
|
||||
pb_schema_key_value_size<wire_type, sub_keysize>(val, size_arr);
|
||||
}
|
||||
},
|
||||
std::make_index_sequence<SIZE>{});
|
||||
len += pb_unknown_fields_size(t);
|
||||
if constexpr (inherits_from_base_v<T>) {
|
||||
t.cache_size = len;
|
||||
}
|
||||
else if constexpr (key_size != 0) {
|
||||
size_arr[pre_index] = len;
|
||||
}
|
||||
if constexpr (key_size == 0) {
|
||||
// for top level
|
||||
return len;
|
||||
}
|
||||
else {
|
||||
if (len == 0) {
|
||||
// equals key_size + variant_uint32_size(len)
|
||||
return key_size + 1;
|
||||
}
|
||||
else {
|
||||
return key_size + variant_uint32_size(static_cast<uint32_t>(len)) + len;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_sequence_container<T>::value) {
|
||||
using item_type = typename T::value_type;
|
||||
size_t len = 0;
|
||||
if constexpr (is_lenprefix_v<item_type>) {
|
||||
for (auto& item : t) {
|
||||
len += pb_key_value_size<key_size, false>(item, size_arr);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
else {
|
||||
for (auto& item : t) {
|
||||
// here 0 to get pakced size, and item must be numeric
|
||||
len += str_numeric_size<0, false>(item);
|
||||
}
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return key_size + variant_uint32_size(static_cast<uint32_t>(len)) + len;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_map_container<T>::value) {
|
||||
size_t len = 0;
|
||||
for (auto& [k, v] : t) {
|
||||
// the key_size of k and v is constant 1
|
||||
auto kv_len = pb_key_value_size<1, false>(k, size_arr) +
|
||||
pb_key_value_size<1, false>(v, size_arr);
|
||||
len += key_size + variant_uint32_size(static_cast<uint32_t>(kv_len)) +
|
||||
kv_len;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
else if constexpr (optional_v<T>) {
|
||||
if (!t.has_value()) {
|
||||
return 0;
|
||||
}
|
||||
return pb_key_value_size<key_size, omit_default_val>(*t, size_arr);
|
||||
}
|
||||
else {
|
||||
return str_numeric_size<key_size, omit_default_val>(t);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Wire, size_t key_size, bool omit_default_val, typename Type,
|
||||
typename Arr>
|
||||
IGUANA_INLINE size_t pb_schema_key_value_size(Type&& t, Arr& size_arr) {
|
||||
using T = ylt::reflection::remove_cvref_t<Type>;
|
||||
using W = ylt::reflection::remove_cvref_t<Wire>;
|
||||
if constexpr (std::is_same_v<T, W>) {
|
||||
return pb_key_value_size<key_size, omit_default_val>(std::forward<Type>(t),
|
||||
size_arr);
|
||||
}
|
||||
else if constexpr (optional_v<T>) {
|
||||
if (!t.has_value()) {
|
||||
return 0;
|
||||
}
|
||||
return pb_schema_key_value_size<typename W::value_type, key_size,
|
||||
omit_default_val>(*t, size_arr);
|
||||
}
|
||||
else if constexpr (is_sequence_container<T>::value) {
|
||||
using wire_item_type = typename W::value_type;
|
||||
size_t len = 0;
|
||||
if constexpr (is_lenprefix_v<wire_item_type>) {
|
||||
for (auto& item : t) {
|
||||
len += pb_schema_key_value_size<wire_item_type, key_size, false>(
|
||||
item, size_arr);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
else {
|
||||
for (auto& item : t) {
|
||||
auto wire_item = make_pb_wire_scalar<wire_item_type>(item);
|
||||
len += str_numeric_size<0, false>(wire_item);
|
||||
}
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
return key_size + variant_uint32_size(static_cast<uint32_t>(len)) + len;
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto wire_value = make_pb_wire_scalar<W>(t);
|
||||
return pb_key_value_size<key_size, omit_default_val>(wire_value, size_arr);
|
||||
}
|
||||
}
|
||||
|
||||
// return the payload size
|
||||
template <bool skip_next = true, typename Type>
|
||||
IGUANA_INLINE size_t pb_value_size(Type&& t, uint32_t*& sz_ptr) {
|
||||
using T = std::remove_const_t<std::remove_reference_t<Type>>;
|
||||
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
|
||||
if constexpr (inherits_from_base_v<T>) {
|
||||
return t.cache_size;
|
||||
}
|
||||
else {
|
||||
// *sz_ptr is secure and logically guaranteed
|
||||
if constexpr (skip_next) {
|
||||
return *(sz_ptr++);
|
||||
}
|
||||
else {
|
||||
return *sz_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_sequence_container<T>::value) {
|
||||
using item_type = typename T::value_type;
|
||||
size_t len = 0;
|
||||
if constexpr (!is_lenprefix_v<item_type>) {
|
||||
for (auto& item : t) {
|
||||
len += str_numeric_size<0, false>(item);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
else {
|
||||
static_assert(!sizeof(item_type), "the size of this type is meaningless");
|
||||
}
|
||||
}
|
||||
else if constexpr (is_map_container<T>::value) {
|
||||
static_assert(!sizeof(T), "the size of this type is meaningless");
|
||||
}
|
||||
else if constexpr (optional_v<T>) {
|
||||
if (!t.has_value()) {
|
||||
return 0;
|
||||
}
|
||||
return pb_value_size(*t, sz_ptr);
|
||||
}
|
||||
else {
|
||||
return str_numeric_size<0, false>(t);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, size_t... I>
|
||||
inline auto build_pb_members_impl(
|
||||
const std::array<size_t, sizeof...(I)>& offset_arr,
|
||||
std::index_sequence<I...>) {
|
||||
constexpr auto names = ylt::reflection::get_member_names<T>();
|
||||
constexpr auto numbers = get_pb_field_numbers((T*)nullptr);
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
static constexpr auto members =
|
||||
ylt::reflection::reflect26::data_members_array<T>();
|
||||
return std::tuple_cat(
|
||||
build_pb_fields_impl<T, numbers[I] - 1,
|
||||
ylt::reflection::reflect26::meta_type_t<members[I]>>(
|
||||
offset_arr[I], names[I])...);
|
||||
#else
|
||||
using Tuple = decltype(ylt::reflection::object_to_tuple(std::declval<T>()));
|
||||
return std::tuple_cat(
|
||||
build_pb_fields_impl<T, numbers[I] - 1, std::tuple_element_t<I, Tuple>>(
|
||||
offset_arr[I], names[I])...);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline auto build_pb_members() {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
constexpr size_t N = ylt::reflection::members_count_v<T>;
|
||||
static const auto& offset_arr =
|
||||
ylt::reflection::internal::get_member_offset_arr<T>();
|
||||
#else
|
||||
using Tuple = decltype(ylt::reflection::object_to_tuple(std::declval<T>()));
|
||||
constexpr size_t N = std::tuple_size_v<Tuple>;
|
||||
static auto& offset_arr = ylt::reflection::internal::get_member_offset_arr(
|
||||
ylt::reflection::internal::wrapper<T>::value);
|
||||
#endif
|
||||
|
||||
auto res =
|
||||
build_pb_members_impl<T>(offset_arr, std::make_index_sequence<N>{});
|
||||
using ResultTuple = decltype(res);
|
||||
validate_pb_members_tuple<ResultTuple>();
|
||||
return res;
|
||||
}
|
||||
|
||||
#define YLT_REFL_PB(STRUCT, ...) \
|
||||
IGUANA_PB_YLT_REFL_FWD( \
|
||||
STRUCT, WRAP_ARGS(IGUANA_PB_FIELD_NAME, unused, ##__VA_ARGS__)) \
|
||||
inline constexpr auto get_pb_field_numbers(STRUCT*) { \
|
||||
return std::array<size_t, YLT_ARG_COUNT(__VA_ARGS__)>{ \
|
||||
WRAP_ARGS(IGUANA_PB_FIELD_NO, unused, ##__VA_ARGS__)}; \
|
||||
} \
|
||||
inline auto get_members_impl(STRUCT*) { \
|
||||
return iguana::detail::build_pb_members<STRUCT>(); \
|
||||
}
|
||||
|
||||
#define IGUANA_PB_GET_FIELD_(field, no) field
|
||||
#define IGUANA_PB_GET_FIELD(pair) IGUANA_PB_GET_FIELD_ pair
|
||||
|
||||
#define IGUANA_PB_GET_NO_(field, no) no
|
||||
#define IGUANA_PB_GET_NO(pair) IGUANA_PB_GET_NO_ pair
|
||||
|
||||
#define IGUANA_PB_FIELD_NAME(unused, pair) IGUANA_PB_GET_FIELD(pair)
|
||||
#define IGUANA_PB_FIELD_NO(unused, pair) (size_t) IGUANA_PB_GET_NO(pair)
|
||||
|
||||
#define IGUANA_PB_YLT_REFL_FWD(STRUCT, ...) YLT_REFL(STRUCT, __VA_ARGS__)
|
||||
|
||||
} // namespace detail
|
||||
} // namespace iguana
|
||||
+814
@@ -0,0 +1,814 @@
|
||||
#pragma once
|
||||
#include "detail/string_resize.hpp"
|
||||
#include "pb_util.hpp"
|
||||
|
||||
namespace iguana {
|
||||
namespace detail {
|
||||
|
||||
template <uint32_t key, typename V, typename Writer>
|
||||
IGUANA_INLINE void encode_varint_field(V val, Writer& writer) {
|
||||
static_assert(std::is_integral_v<V>, "must be integral");
|
||||
if constexpr (key != 0) {
|
||||
serialize_varint_u32<key>(writer);
|
||||
}
|
||||
serialize_varint(val, writer);
|
||||
}
|
||||
|
||||
template <uint32_t key, typename V, typename Writer>
|
||||
IGUANA_INLINE void encode_fixed_field(V val, Writer& writer) {
|
||||
if constexpr (key != 0) {
|
||||
serialize_varint_u32<key>(writer);
|
||||
}
|
||||
constexpr size_t size = sizeof(V);
|
||||
// TODO: check Stream continuous
|
||||
writer.write((const char*)&val, size);
|
||||
}
|
||||
|
||||
template <uint32_t key, bool omit_default_val = true, typename Type,
|
||||
typename Writer>
|
||||
IGUANA_INLINE void to_pb_impl(Type&& t, uint32_t*& sz_ptr, Writer& writer);
|
||||
|
||||
template <uint32_t key, bool omit_default_val, typename T, typename Writer>
|
||||
IGUANA_INLINE void encode_numeric_field(T t, Writer& writer);
|
||||
|
||||
template <bool TimestampSchema, uint32_t key, bool omit_default_val = true,
|
||||
typename Type, typename Writer>
|
||||
IGUANA_INLINE void to_pb_well_known_impl(Type&& t, uint32_t*& sz_ptr,
|
||||
Writer& writer) {
|
||||
using T = std::remove_const_t<std::remove_reference_t<Type>>;
|
||||
if constexpr (optional_v<T>) {
|
||||
if (!t.has_value()) {
|
||||
return;
|
||||
}
|
||||
to_pb_well_known_impl<TimestampSchema, key, omit_default_val>(*t, sz_ptr,
|
||||
writer);
|
||||
}
|
||||
else if constexpr (is_sequence_container<T>::value) {
|
||||
for (auto& item : t) {
|
||||
to_pb_well_known_impl<TimestampSchema, key, false>(item, sz_ptr, writer);
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto wire_value = make_pb_well_known_value<TimestampSchema>(t);
|
||||
to_pb_impl<key, omit_default_val>(wire_value, sz_ptr, writer);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Wire, uint32_t key, bool omit_default_val = true,
|
||||
typename Type, typename Writer>
|
||||
IGUANA_INLINE void to_pb_schema_impl(Type&& t, uint32_t*& sz_ptr,
|
||||
Writer& writer) {
|
||||
using T = ylt::reflection::remove_cvref_t<Type>;
|
||||
using W = ylt::reflection::remove_cvref_t<Wire>;
|
||||
if constexpr (std::is_same_v<T, W>) {
|
||||
to_pb_impl<key, omit_default_val>(std::forward<Type>(t), sz_ptr, writer);
|
||||
}
|
||||
else if constexpr (optional_v<T>) {
|
||||
if (!t.has_value()) {
|
||||
return;
|
||||
}
|
||||
to_pb_schema_impl<typename W::value_type, key, omit_default_val>(*t, sz_ptr,
|
||||
writer);
|
||||
}
|
||||
else if constexpr (is_sequence_container<T>::value) {
|
||||
using wire_item_type = typename W::value_type;
|
||||
if constexpr (is_lenprefix_v<wire_item_type>) {
|
||||
for (auto& item : t) {
|
||||
to_pb_schema_impl<wire_item_type, key, false>(item, sz_ptr, writer);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (t.empty())
|
||||
IGUANA_UNLIKELY { return; }
|
||||
serialize_varint_u32<key>(writer);
|
||||
size_t len = 0;
|
||||
for (auto& item : t) {
|
||||
auto wire_item = make_pb_wire_scalar<wire_item_type>(item);
|
||||
len += str_numeric_size<0, false>(wire_item);
|
||||
}
|
||||
serialize_varint(len, writer);
|
||||
for (auto& item : t) {
|
||||
auto wire_item = make_pb_wire_scalar<wire_item_type>(item);
|
||||
encode_numeric_field<0, false>(wire_item, writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto wire_value = make_pb_wire_scalar<W>(t);
|
||||
to_pb_impl<key, omit_default_val>(wire_value, sz_ptr, writer);
|
||||
}
|
||||
}
|
||||
|
||||
template <uint32_t key, typename V, typename Writer>
|
||||
IGUANA_INLINE void encode_pair_value(V&& val, size_t size, uint32_t*& sz_ptr,
|
||||
Writer& writer) {
|
||||
if (size == 0)
|
||||
IGUANA_UNLIKELY {
|
||||
// map keys can't be omitted even if values are empty
|
||||
// TODO: repeated ?
|
||||
serialize_varint_u32<key>(writer);
|
||||
serialize_varint(0, writer);
|
||||
}
|
||||
else {
|
||||
to_pb_impl<key, false>(val, sz_ptr, writer);
|
||||
}
|
||||
}
|
||||
|
||||
template <uint32_t key, bool omit_default_val, typename T, typename Writer>
|
||||
IGUANA_INLINE void encode_numeric_field(T t, Writer& writer) {
|
||||
if constexpr (omit_default_val) {
|
||||
if constexpr (is_fixed_v<T> || is_signed_varint_v<T>) {
|
||||
if (t.val == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (t == static_cast<T>(0))
|
||||
IGUANA_UNLIKELY { return; }
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_integral_v<T>) {
|
||||
detail::encode_varint_field<key>(t, writer);
|
||||
}
|
||||
else if constexpr (detail::is_signed_varint_v<T>) {
|
||||
detail::encode_varint_field<key>(encode_zigzag(t.val), writer);
|
||||
}
|
||||
else if constexpr (detail::is_fixed_v<T>) {
|
||||
detail::encode_fixed_field<key>(t.val, writer);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
|
||||
detail::encode_fixed_field<key>(t, writer);
|
||||
}
|
||||
else if constexpr (std::is_enum_v<T>) {
|
||||
using U = std::underlying_type_t<T>;
|
||||
detail::encode_varint_field<key>(static_cast<U>(t), writer);
|
||||
}
|
||||
else {
|
||||
static_assert(!sizeof(T), "unsupported type");
|
||||
}
|
||||
}
|
||||
|
||||
template <uint32_t field_no, typename Type, typename Writer>
|
||||
IGUANA_INLINE void to_pb_oneof(Type&& t, uint32_t*& sz_ptr, Writer& writer) {
|
||||
using T = std::decay_t<Type>;
|
||||
std::visit(
|
||||
[&sz_ptr, &writer](auto&& value) IGUANA__INLINE_LAMBDA {
|
||||
using raw_value_type = decltype(value);
|
||||
using value_type =
|
||||
std::remove_const_t<std::remove_reference_t<decltype(value)>>;
|
||||
constexpr auto offset =
|
||||
get_variant_index<T, value_type, std::variant_size_v<T> - 1>();
|
||||
constexpr uint32_t key =
|
||||
((field_no + offset) << 3) |
|
||||
static_cast<uint32_t>(get_wire_type<value_type>());
|
||||
to_pb_impl<key, false>(std::forward<raw_value_type>(value), sz_ptr,
|
||||
writer);
|
||||
},
|
||||
std::forward<Type>(t));
|
||||
}
|
||||
|
||||
// omit_default_val = true indicates to omit the default value in searlization
|
||||
template <uint32_t key, bool omit_default_val, typename Type, typename Writer>
|
||||
IGUANA_INLINE void to_pb_impl(Type&& t, uint32_t*& sz_ptr, Writer& writer) {
|
||||
using T = std::remove_const_t<std::remove_reference_t<Type>>;
|
||||
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
|
||||
// can't be omitted even if values are empty
|
||||
if constexpr (key != 0) {
|
||||
auto len = pb_value_size(t, sz_ptr);
|
||||
serialize_varint_u32<key>(writer);
|
||||
serialize_varint(len, writer);
|
||||
if (len == 0)
|
||||
IGUANA_UNLIKELY { return; }
|
||||
}
|
||||
static auto tuple = get_pb_members_tuple(std::forward<Type>(t));
|
||||
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;
|
||||
for_each_n(
|
||||
[&t, &sz_ptr, &writer](auto i) IGUANA__INLINE_LAMBDA {
|
||||
using field_type =
|
||||
std::tuple_element_t<decltype(i)::value,
|
||||
std::decay_t<decltype(tuple)>>;
|
||||
auto value = std::get<decltype(i)::value>(tuple);
|
||||
auto& val = value.value(t);
|
||||
|
||||
using U = typename field_type::value_type;
|
||||
using wire_type = typename field_type::wire_value_type;
|
||||
using sub_type = typename field_type::sub_type;
|
||||
if constexpr (field_type::timestamp_schema ||
|
||||
field_type::duration_schema) {
|
||||
constexpr uint32_t sub_key =
|
||||
(value.field_no << 3) |
|
||||
static_cast<uint32_t>(WireType::LengthDelimeted);
|
||||
to_pb_well_known_impl<field_type::timestamp_schema, sub_key>(
|
||||
val, sz_ptr, writer);
|
||||
}
|
||||
else if constexpr (field_type::optional_schema) {
|
||||
constexpr uint32_t sub_key =
|
||||
(value.field_no << 3) |
|
||||
static_cast<uint32_t>(get_wire_type<wire_type>());
|
||||
to_pb_schema_impl<wire_type, sub_key, false>(val, sz_ptr, writer);
|
||||
}
|
||||
else if constexpr (variant_v<U>) {
|
||||
constexpr auto offset =
|
||||
get_variant_index<U, sub_type, std::variant_size_v<U> - 1>();
|
||||
if (val.index() == offset) {
|
||||
if constexpr (!std::is_same_v<sub_type, std::monostate>) {
|
||||
constexpr uint32_t sub_key =
|
||||
(value.field_no << 3) |
|
||||
static_cast<uint32_t>(get_wire_type<sub_type>());
|
||||
to_pb_impl<sub_key, false>(std::get<offset>(val), sz_ptr,
|
||||
writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
constexpr uint32_t sub_key =
|
||||
(value.field_no << 3) |
|
||||
static_cast<uint32_t>(get_wire_type<wire_type>());
|
||||
to_pb_schema_impl<wire_type, sub_key>(val, sz_ptr, writer);
|
||||
}
|
||||
},
|
||||
std::make_index_sequence<SIZE>{});
|
||||
write_pb_unknown_fields(t, writer);
|
||||
}
|
||||
else if constexpr (is_sequence_container<T>::value) {
|
||||
// TODO support std::array
|
||||
// repeated values can't be omitted even if values are empty
|
||||
using item_type = typename T::value_type;
|
||||
if constexpr (is_lenprefix_v<item_type>) {
|
||||
// non-packed
|
||||
for (auto& item : t) {
|
||||
to_pb_impl<key, false>(item, sz_ptr, writer);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (t.empty())
|
||||
IGUANA_UNLIKELY { return; }
|
||||
serialize_varint_u32<key>(writer);
|
||||
serialize_varint(pb_value_size(t, sz_ptr), writer);
|
||||
for (auto& item : t) {
|
||||
encode_numeric_field<false, 0>(item, writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_map_container<T>::value) {
|
||||
using first_type = typename T::key_type;
|
||||
using second_type = typename T::mapped_type;
|
||||
constexpr uint32_t key1 =
|
||||
(1 << 3) | static_cast<uint32_t>(get_wire_type<first_type>());
|
||||
constexpr auto key1_size = variant_uint32_size_constexpr(key1);
|
||||
constexpr uint32_t key2 =
|
||||
(2 << 3) | static_cast<uint32_t>(get_wire_type<second_type>());
|
||||
constexpr auto key2_size = variant_uint32_size_constexpr(key2);
|
||||
|
||||
for (auto& [k, v] : t) {
|
||||
serialize_varint_u32<key>(writer);
|
||||
// k must be string or numeric
|
||||
auto k_val_len = str_numeric_size<0, false>(k);
|
||||
auto v_val_len = pb_value_size<false>(v, sz_ptr);
|
||||
auto pair_len = key1_size + key2_size + k_val_len + v_val_len;
|
||||
if constexpr (is_lenprefix_v<first_type>) {
|
||||
pair_len += variant_uint32_size(k_val_len);
|
||||
}
|
||||
if constexpr (is_lenprefix_v<second_type>) {
|
||||
pair_len += variant_uint32_size(v_val_len);
|
||||
}
|
||||
serialize_varint(pair_len, writer);
|
||||
// map k and v can't be omitted even if values are empty
|
||||
encode_pair_value<key1>(k, k_val_len, sz_ptr, writer);
|
||||
encode_pair_value<key2>(v, v_val_len, sz_ptr, writer);
|
||||
}
|
||||
}
|
||||
else if constexpr (optional_v<T>) {
|
||||
if (!t.has_value()) {
|
||||
return;
|
||||
}
|
||||
to_pb_impl<key, omit_default_val>(*t, sz_ptr, writer);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::string> ||
|
||||
std::is_same_v<T, std::string_view>) {
|
||||
if constexpr (omit_default_val) {
|
||||
if (t.size() == 0)
|
||||
IGUANA_UNLIKELY { return; }
|
||||
}
|
||||
serialize_varint_u32<key>(writer);
|
||||
serialize_varint(t.size(), writer);
|
||||
writer.write(t.data(), t.size());
|
||||
}
|
||||
else {
|
||||
encode_numeric_field<key, omit_default_val>(t, writer);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__clang__) || defined(_MSC_VER) || \
|
||||
(defined(__GNUC__) && __GNUC__ > 8)
|
||||
template <typename T>
|
||||
IGUANA_INLINE constexpr std::string_view get_type_string() {
|
||||
if constexpr (std::is_integral_v<T>) {
|
||||
if constexpr (std::is_same_v<T, bool>) {
|
||||
return "bool";
|
||||
}
|
||||
else if constexpr (sizeof(T) <= 4) {
|
||||
if constexpr (std::is_unsigned_v<T>) {
|
||||
return "uint32";
|
||||
}
|
||||
else {
|
||||
return "int32";
|
||||
}
|
||||
}
|
||||
else {
|
||||
if constexpr (std::is_unsigned_v<T>) {
|
||||
return "uint64";
|
||||
}
|
||||
else {
|
||||
return "int64";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, iguana::sint32_t>) {
|
||||
return "sint32";
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, iguana::sint64_t>) {
|
||||
return "sint64";
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, iguana::fixed32_t>) {
|
||||
return "fixed32";
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, iguana::fixed64_t>) {
|
||||
return "fixed64";
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, iguana::sfixed32_t>) {
|
||||
return "sfixed32";
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, iguana::sfixed64_t>) {
|
||||
return "sfixed64";
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::string> ||
|
||||
std::is_same_v<T, std::string_view>) {
|
||||
return "string";
|
||||
}
|
||||
else if constexpr (std::is_floating_point_v<T>) {
|
||||
return type_string<T>();
|
||||
}
|
||||
else {
|
||||
constexpr auto str_type_name = type_string<T>();
|
||||
constexpr size_t pos = str_type_name.rfind("::");
|
||||
if constexpr (pos != std::string_view::npos) {
|
||||
constexpr size_t pos = str_type_name.rfind("::") + 2;
|
||||
if constexpr (detail::is_signed_varint_v<T> || detail::is_fixed_v<T>) {
|
||||
return str_type_name.substr(pos, str_type_name.size() - pos - 2);
|
||||
}
|
||||
else {
|
||||
return str_type_name.substr(pos);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return str_type_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Stream>
|
||||
IGUANA_INLINE void numeric_to_proto(Stream& out, std::string_view field_name,
|
||||
uint32_t field_no) {
|
||||
constexpr auto name = get_type_string<T>();
|
||||
out.append(name).append(" ");
|
||||
out.append(field_name)
|
||||
.append(" = ")
|
||||
.append(std::to_string(field_no))
|
||||
.append(";\n");
|
||||
}
|
||||
|
||||
template <size_t space_count = 2, typename Stream>
|
||||
IGUANA_INLINE void build_proto_field(Stream& out, std::string_view str_type,
|
||||
std::string_view field_name,
|
||||
uint32_t field_no) {
|
||||
for (size_t i = 0; i < space_count; i++) {
|
||||
out.append(" ");
|
||||
}
|
||||
|
||||
if (!str_type.empty()) {
|
||||
out.append(str_type);
|
||||
}
|
||||
|
||||
out.append(" ")
|
||||
.append(field_name)
|
||||
.append(" = ")
|
||||
.append(std::to_string(field_no))
|
||||
.append(";\n");
|
||||
}
|
||||
|
||||
template <typename T, typename Map>
|
||||
IGUANA_INLINE void build_sub_proto(Map& map, std::string_view str_type,
|
||||
std::string& sub_str);
|
||||
|
||||
template <typename Type, typename Stream>
|
||||
IGUANA_INLINE void to_proto_impl(
|
||||
Stream& out, std::unordered_map<std::string_view, std::string>& map,
|
||||
std::string_view field_name = "", uint32_t field_no = 0) {
|
||||
std::string sub_str;
|
||||
using T = std::remove_const_t<std::remove_reference_t<Type>>;
|
||||
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
|
||||
constexpr auto name = type_string<T>();
|
||||
out.append("message ").append(name).append(" {\n");
|
||||
static T t;
|
||||
static auto tuple = get_pb_members_tuple(t);
|
||||
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;
|
||||
|
||||
for_each_n(
|
||||
[&out, &sub_str, &map](auto i) mutable {
|
||||
using field_type =
|
||||
std::tuple_element_t<decltype(i)::value,
|
||||
std::decay_t<decltype(tuple)>>;
|
||||
auto value = std::get<decltype(i)::value>(tuple);
|
||||
|
||||
using U = typename field_type::value_type;
|
||||
using WireU = typename field_type::wire_value_type;
|
||||
using sub_type = typename field_type::sub_type;
|
||||
if constexpr (field_type::bytes_schema) {
|
||||
if constexpr (is_sequence_container<U>::value) {
|
||||
build_proto_field(
|
||||
out, "repeated bytes",
|
||||
{value.field_name.data(), value.field_name.size()},
|
||||
value.field_no);
|
||||
}
|
||||
else if constexpr (optional_v<U>) {
|
||||
out.append(" optional");
|
||||
build_proto_field(
|
||||
out, "bytes",
|
||||
{value.field_name.data(), value.field_name.size()},
|
||||
value.field_no);
|
||||
}
|
||||
else {
|
||||
build_proto_field(
|
||||
out, "bytes ",
|
||||
{value.field_name.data(), value.field_name.size()},
|
||||
value.field_no);
|
||||
}
|
||||
}
|
||||
else if constexpr (field_type::timestamp_schema) {
|
||||
if constexpr (is_sequence_container<U>::value) {
|
||||
build_proto_field(
|
||||
out, "repeated google.protobuf.Timestamp",
|
||||
{value.field_name.data(), value.field_name.size()},
|
||||
value.field_no);
|
||||
}
|
||||
else {
|
||||
build_proto_field(
|
||||
out, "google.protobuf.Timestamp",
|
||||
{value.field_name.data(), value.field_name.size()},
|
||||
value.field_no);
|
||||
}
|
||||
}
|
||||
else if constexpr (field_type::duration_schema) {
|
||||
if constexpr (is_sequence_container<U>::value) {
|
||||
build_proto_field(
|
||||
out, "repeated google.protobuf.Duration",
|
||||
{value.field_name.data(), value.field_name.size()},
|
||||
value.field_no);
|
||||
}
|
||||
else {
|
||||
build_proto_field(
|
||||
out, "google.protobuf.Duration",
|
||||
{value.field_name.data(), value.field_name.size()},
|
||||
value.field_no);
|
||||
}
|
||||
}
|
||||
else if constexpr (field_type::optional_schema) {
|
||||
static_assert(optional_v<U>,
|
||||
"pb_optional member must be std::optional<T>");
|
||||
out.append(" optional");
|
||||
to_proto_impl<typename WireU::value_type>(
|
||||
out, map, {value.field_name.data(), value.field_name.size()},
|
||||
value.field_no);
|
||||
}
|
||||
else if constexpr (ylt_refletable_v<U>) {
|
||||
constexpr auto str_type = get_type_string<U>();
|
||||
build_proto_field(
|
||||
out, str_type,
|
||||
{value.field_name.data(), value.field_name.size()},
|
||||
value.field_no);
|
||||
|
||||
build_sub_proto<U>(map, str_type, sub_str);
|
||||
}
|
||||
else if constexpr (variant_v<U>) {
|
||||
constexpr size_t var_size = std::variant_size_v<U>;
|
||||
constexpr size_t first_case = pb_variant_first_case_index<U>();
|
||||
|
||||
constexpr auto offset =
|
||||
get_variant_index<U, sub_type, var_size - 1>();
|
||||
|
||||
if (offset == first_case) {
|
||||
out.append(" oneof ");
|
||||
out.append(value.field_name.data(), value.field_name.size())
|
||||
.append(" {\n");
|
||||
}
|
||||
|
||||
if constexpr (!std::is_same_v<sub_type, std::monostate>) {
|
||||
constexpr auto str_type = get_type_string<sub_type>();
|
||||
std::string field_name = " one_of_";
|
||||
field_name.append(str_type);
|
||||
|
||||
out.append(" ");
|
||||
build_proto_field(out, str_type, field_name, value.field_no);
|
||||
|
||||
if constexpr (ylt_refletable_v<sub_type>) {
|
||||
build_sub_proto<sub_type>(map, str_type, sub_str);
|
||||
}
|
||||
}
|
||||
|
||||
if (offset == var_size - 1) {
|
||||
out.append(" }\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
to_proto_impl<WireU>(
|
||||
out, map, {value.field_name.data(), value.field_name.size()},
|
||||
value.field_no);
|
||||
}
|
||||
},
|
||||
std::make_index_sequence<SIZE>{});
|
||||
out.append("}\r\n\r\n");
|
||||
}
|
||||
else if constexpr (is_sequence_container<T>::value) {
|
||||
out.append(" repeated");
|
||||
using item_type = typename T::value_type;
|
||||
|
||||
if constexpr (is_lenprefix_v<item_type>) {
|
||||
// non-packed
|
||||
if constexpr (ylt_refletable_v<item_type>) {
|
||||
constexpr auto str_type = get_type_string<item_type>();
|
||||
build_proto_field(out, str_type, field_name, field_no);
|
||||
|
||||
build_sub_proto<item_type>(map, str_type, sub_str);
|
||||
}
|
||||
else {
|
||||
to_proto_impl<item_type>(out, map, field_name, field_no);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.append(" ");
|
||||
numeric_to_proto<item_type>(out, field_name, field_no);
|
||||
}
|
||||
}
|
||||
else if constexpr (is_map_container<T>::value) {
|
||||
out.append(" map<");
|
||||
using first_type = typename T::key_type;
|
||||
using second_type = typename T::mapped_type;
|
||||
|
||||
constexpr auto str_first = get_type_string<first_type>();
|
||||
constexpr auto str_second = get_type_string<second_type>();
|
||||
out.append(str_first).append(", ").append(str_second).append(">");
|
||||
|
||||
build_proto_field<1>(out, "", field_name, field_no);
|
||||
|
||||
if constexpr (ylt_refletable_v<second_type>) {
|
||||
constexpr auto str_type = get_type_string<second_type>();
|
||||
build_sub_proto<second_type>(map, str_type, sub_str);
|
||||
}
|
||||
}
|
||||
else if constexpr (optional_v<T>) {
|
||||
to_proto_impl<typename T::value_type>(
|
||||
out, map, {field_name.data(), field_name.size()}, field_no);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::string> ||
|
||||
std::is_same_v<T, std::string_view>) {
|
||||
build_proto_field(out, "string ", field_name, field_no);
|
||||
}
|
||||
else if constexpr (enum_v<T>) {
|
||||
constexpr auto str_type = get_type_string<T>();
|
||||
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
|
||||
if constexpr (bool_v<decltype(enum_to_str)>) {
|
||||
build_proto_field(out, "int32", field_name, field_no);
|
||||
}
|
||||
else {
|
||||
static_assert(enum_to_str.size() > 0, "empty enum not allowed");
|
||||
static_assert((int)(enum_to_str.begin()->first) == 0,
|
||||
"the first enum value must be zero in proto3");
|
||||
build_proto_field(out, str_type, field_name, field_no);
|
||||
if (map.find(str_type) == map.end()) {
|
||||
sub_str.append("enum ").append(str_type).append(" {\n");
|
||||
for (auto& [k, field_name] : enum_to_str) {
|
||||
std::string_view name{field_name.data(), field_name.size()};
|
||||
size_t pos = name.rfind("::");
|
||||
if (pos != std::string_view::npos) {
|
||||
name = name.substr(pos + 2);
|
||||
}
|
||||
sub_str.append(" ")
|
||||
.append(name)
|
||||
.append(" = ")
|
||||
.append(std::to_string(static_cast<std::underlying_type_t<T>>(k)))
|
||||
.append(";\n");
|
||||
}
|
||||
sub_str.append("}\r\n\r\n");
|
||||
map.emplace(str_type, std::move(sub_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.append(" ");
|
||||
numeric_to_proto<Type>(out, field_name, field_no);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Map>
|
||||
IGUANA_INLINE void build_sub_proto(Map& map, std::string_view str_type,
|
||||
std::string& sub_str) {
|
||||
if (map.find(str_type) == map.end()) {
|
||||
to_proto_impl<T>(sub_str, map);
|
||||
map.emplace(str_type, std::move(sub_str));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool proto_needs_timestamp_import();
|
||||
|
||||
template <typename T>
|
||||
constexpr bool proto_needs_duration_import();
|
||||
|
||||
template <typename T>
|
||||
constexpr bool proto_value_needs_timestamp_import();
|
||||
|
||||
template <typename T>
|
||||
constexpr bool proto_value_needs_duration_import();
|
||||
|
||||
template <typename Variant, size_t... I>
|
||||
constexpr bool proto_variant_needs_timestamp_import(std::index_sequence<I...>) {
|
||||
return (proto_value_needs_timestamp_import<
|
||||
std::variant_alternative_t<I, Variant>>() ||
|
||||
...);
|
||||
}
|
||||
|
||||
template <typename Variant, size_t... I>
|
||||
constexpr bool proto_variant_needs_duration_import(std::index_sequence<I...>) {
|
||||
return (proto_value_needs_duration_import<
|
||||
std::variant_alternative_t<I, Variant>>() ||
|
||||
...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool proto_value_needs_timestamp_import() {
|
||||
using U = std::remove_cvref_t<T>;
|
||||
if constexpr (std::is_same_v<U, std::monostate> ||
|
||||
pb_well_known_chrono_v<U>) {
|
||||
return false;
|
||||
}
|
||||
else if constexpr (optional_v<U>) {
|
||||
return proto_value_needs_timestamp_import<typename U::value_type>();
|
||||
}
|
||||
else if constexpr (is_sequence_container<U>::value) {
|
||||
return proto_value_needs_timestamp_import<typename U::value_type>();
|
||||
}
|
||||
else if constexpr (is_map_container<U>::value) {
|
||||
return proto_value_needs_timestamp_import<typename U::mapped_type>();
|
||||
}
|
||||
else if constexpr (variant_v<U>) {
|
||||
return proto_variant_needs_timestamp_import<U>(
|
||||
std::make_index_sequence<std::variant_size_v<U>>{});
|
||||
}
|
||||
else if constexpr (ylt_refletable_v<U> || is_custom_reflection_v<U>) {
|
||||
return proto_needs_timestamp_import<U>();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool proto_value_needs_duration_import() {
|
||||
using U = std::remove_cvref_t<T>;
|
||||
if constexpr (std::is_same_v<U, std::monostate> ||
|
||||
pb_well_known_chrono_v<U>) {
|
||||
return false;
|
||||
}
|
||||
else if constexpr (optional_v<U>) {
|
||||
return proto_value_needs_duration_import<typename U::value_type>();
|
||||
}
|
||||
else if constexpr (is_sequence_container<U>::value) {
|
||||
return proto_value_needs_duration_import<typename U::value_type>();
|
||||
}
|
||||
else if constexpr (is_map_container<U>::value) {
|
||||
return proto_value_needs_duration_import<typename U::mapped_type>();
|
||||
}
|
||||
else if constexpr (variant_v<U>) {
|
||||
return proto_variant_needs_duration_import<U>(
|
||||
std::make_index_sequence<std::variant_size_v<U>>{});
|
||||
}
|
||||
else if constexpr (ylt_refletable_v<U> || is_custom_reflection_v<U>) {
|
||||
return proto_needs_duration_import<U>();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tuple, size_t... I>
|
||||
constexpr bool proto_tuple_needs_timestamp_import(std::index_sequence<I...>) {
|
||||
return ((std::tuple_element_t<I, Tuple>::timestamp_schema ||
|
||||
proto_value_needs_timestamp_import<
|
||||
typename std::tuple_element_t<I, Tuple>::value_type>()) ||
|
||||
...);
|
||||
}
|
||||
|
||||
template <typename Tuple, size_t... I>
|
||||
constexpr bool proto_tuple_needs_duration_import(std::index_sequence<I...>) {
|
||||
return ((std::tuple_element_t<I, Tuple>::duration_schema ||
|
||||
proto_value_needs_duration_import<
|
||||
typename std::tuple_element_t<I, Tuple>::value_type>()) ||
|
||||
...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool proto_needs_timestamp_import() {
|
||||
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
|
||||
using Tuple =
|
||||
std::decay_t<decltype(get_pb_members_tuple(std::declval<T&>()))>;
|
||||
return proto_tuple_needs_timestamp_import<Tuple>(
|
||||
std::make_index_sequence<std::tuple_size_v<Tuple>>{});
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool proto_needs_duration_import() {
|
||||
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
|
||||
using Tuple =
|
||||
std::decay_t<decltype(get_pb_members_tuple(std::declval<T&>()))>;
|
||||
return proto_tuple_needs_duration_import<Tuple>(
|
||||
std::make_index_sequence<std::tuple_size_v<Tuple>>{});
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} // namespace detail
|
||||
|
||||
template <
|
||||
typename T, typename Stream,
|
||||
std::enable_if_t<ylt_refletable_v<T> || detail::is_custom_reflection_v<T>,
|
||||
int> = 0>
|
||||
IGUANA_INLINE void to_pb(T const& t, Stream& out) {
|
||||
std::vector<uint32_t> size_arr;
|
||||
auto byte_len = detail::pb_key_value_size<0>(t, size_arr);
|
||||
auto sz_ptr = size_arr.empty() ? nullptr : &size_arr[0];
|
||||
|
||||
if constexpr (is_resizable_char_container_v<Stream>) {
|
||||
detail::resize(out, byte_len);
|
||||
memory_writer writer{out.data()};
|
||||
detail::to_pb_impl<0>(t, sz_ptr, writer);
|
||||
}
|
||||
else if constexpr (char_writer<Stream>) {
|
||||
detail::to_pb_impl<0>(t, sz_ptr, out);
|
||||
}
|
||||
else {
|
||||
static_assert(!sizeof(Stream), "Invalid stream type");
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__clang__) || defined(_MSC_VER) || \
|
||||
(defined(__GNUC__) && __GNUC__ > 8)
|
||||
template <typename T, bool gen_header = true, typename Stream>
|
||||
IGUANA_INLINE void to_proto(Stream& out, std::string_view ns = "") {
|
||||
if (gen_header) {
|
||||
constexpr std::string_view crlf = "\r\n\r\n";
|
||||
out.append(R"(syntax = "proto3";)").append(crlf);
|
||||
if constexpr (detail::proto_needs_timestamp_import<T>()) {
|
||||
out.append(R"(import "google/protobuf/timestamp.proto";)").append(crlf);
|
||||
}
|
||||
if constexpr (detail::proto_needs_duration_import<T>()) {
|
||||
out.append(R"(import "google/protobuf/duration.proto";)").append(crlf);
|
||||
}
|
||||
if (!ns.empty()) {
|
||||
out.append("package ").append(ns).append(";").append(crlf);
|
||||
}
|
||||
|
||||
out.append(R"(option optimize_for = SPEED;)").append(crlf);
|
||||
out.append(R"(option cc_enable_arenas = true;)").append(crlf);
|
||||
}
|
||||
|
||||
std::unordered_map<std::string_view, std::string> map;
|
||||
detail::to_proto_impl<T>(out, map);
|
||||
for (auto& [k, s] : map) {
|
||||
out.append(s);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, bool gen_header = true, typename Stream>
|
||||
IGUANA_INLINE void to_proto_file(Stream& stream, std::string_view ns = "") {
|
||||
if (!stream.is_open()) {
|
||||
return;
|
||||
}
|
||||
std::string out;
|
||||
to_proto<T, gen_header>(out, ns);
|
||||
stream.write(out.data(), out.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename Stream>
|
||||
IGUANA_INLINE void to_pb_adl(iguana_adl_t* p, T const& t, Stream& out) {
|
||||
to_pb(t, out);
|
||||
}
|
||||
|
||||
} // namespace iguana
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
#pragma once
|
||||
|
||||
// https://github.com/stephenberry/glaze/blob/main/include/glaze/json/prettify.hpp
|
||||
|
||||
namespace iguana {
|
||||
namespace detail {
|
||||
enum class general_state : uint32_t {
|
||||
NORMAL,
|
||||
ESCAPED,
|
||||
STRING,
|
||||
BEFORE_ASTERISK,
|
||||
COMMENT,
|
||||
BEFORE_FSLASH
|
||||
};
|
||||
|
||||
template <typename Out, typename NL>
|
||||
inline void prettify_normal_state(const char c, Out &out, uint32_t &indent,
|
||||
NL nl, general_state &state) noexcept {
|
||||
switch (c) {
|
||||
case ',':
|
||||
out += c;
|
||||
nl();
|
||||
break;
|
||||
case '[':
|
||||
out += c;
|
||||
++indent;
|
||||
nl();
|
||||
break;
|
||||
case ']':
|
||||
--indent;
|
||||
nl();
|
||||
out += c;
|
||||
break;
|
||||
case '{':
|
||||
out += c;
|
||||
++indent;
|
||||
nl();
|
||||
break;
|
||||
case '}':
|
||||
--indent;
|
||||
nl();
|
||||
out += c;
|
||||
break;
|
||||
case '\\':
|
||||
out += c;
|
||||
state = general_state::ESCAPED;
|
||||
break;
|
||||
case '\"':
|
||||
out += c;
|
||||
state = general_state::STRING;
|
||||
break;
|
||||
case '/':
|
||||
out += " /";
|
||||
state = general_state::BEFORE_ASTERISK;
|
||||
break;
|
||||
case ':':
|
||||
out += ": ";
|
||||
break;
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
break;
|
||||
default:
|
||||
out += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void prettify_other_states(const char c, general_state &state) noexcept {
|
||||
switch (state) {
|
||||
case general_state::ESCAPED:
|
||||
state = general_state::NORMAL;
|
||||
break;
|
||||
case general_state::STRING:
|
||||
if (c == '"') {
|
||||
state = general_state::NORMAL;
|
||||
}
|
||||
break;
|
||||
case general_state::BEFORE_ASTERISK:
|
||||
state = general_state::COMMENT;
|
||||
break;
|
||||
case general_state::COMMENT:
|
||||
if (c == '*') {
|
||||
state = general_state::BEFORE_FSLASH;
|
||||
}
|
||||
break;
|
||||
case general_state::BEFORE_FSLASH:
|
||||
if (c == '/') {
|
||||
state = general_state::NORMAL;
|
||||
}
|
||||
else {
|
||||
state = general_state::COMMENT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/// <summary>
|
||||
/// pretty print a JSON string
|
||||
/// </summary>
|
||||
template <typename In, typename Out>
|
||||
inline void prettify(const In &in, Out &out, const bool tabs = false,
|
||||
const uint32_t indent_size = 3) noexcept {
|
||||
out.reserve(in.size());
|
||||
uint32_t indent{};
|
||||
|
||||
auto nl = [&]() {
|
||||
out += "\n";
|
||||
|
||||
for (uint32_t i = 0; i < indent * (tabs ? 1 : indent_size); i++) {
|
||||
out += tabs ? "\t" : " ";
|
||||
}
|
||||
};
|
||||
|
||||
using namespace detail;
|
||||
general_state state{general_state::NORMAL};
|
||||
|
||||
for (auto c : in) {
|
||||
if (state == general_state::NORMAL) {
|
||||
prettify_normal_state(c, out, indent, nl, state);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
out += c;
|
||||
prettify_other_states(c, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// allocating version of prettify
|
||||
/// </summary>
|
||||
template <typename In>
|
||||
inline std::string prettify(const In &in, const bool tabs = false,
|
||||
const uint32_t indent_size = 3) noexcept {
|
||||
std::string out{};
|
||||
prettify(in, out, tabs, indent_size);
|
||||
return out;
|
||||
}
|
||||
} // namespace iguana
|
||||
Vendored
+389
@@ -0,0 +1,389 @@
|
||||
#pragma once
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <forward_list>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#include "define.h"
|
||||
#include "detail/charconv.h"
|
||||
#include "detail/pb_type.hpp"
|
||||
#include "detail/traits.hpp"
|
||||
#include "detail/utf.hpp"
|
||||
#include "error_code.h"
|
||||
#include "field_reflection.hpp"
|
||||
#include "ylt/reflection/member_value.hpp"
|
||||
#include "ylt/reflection/user_reflect_macro.hpp"
|
||||
|
||||
namespace iguana {
|
||||
template <typename T>
|
||||
inline constexpr bool char_v = std::is_same_v<std::decay_t<T>, char> ||
|
||||
std::is_same_v<std::decay_t<T>, char16_t> ||
|
||||
std::is_same_v<std::decay_t<T>, char32_t> ||
|
||||
std::is_same_v<std::decay_t<T>, wchar_t>;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool bool_v =
|
||||
std::is_same_v<std::decay_t<T>, bool> ||
|
||||
std::is_same_v<std::decay_t<T>, std::vector<bool>::reference>;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool int_v = std::is_integral_v<std::decay_t<T>> &&
|
||||
!char_v<std::decay_t<T>> && !bool_v<T>;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool float_v = std::is_floating_point_v<std::decay_t<T>>;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool num_v = float_v<T> || int_v<T>;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool enum_v = std::is_enum_v<std::decay_t<T>>;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool optional_v = ylt::reflection::optional<T>;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool pair_v = ylt::reflection::pair<T>;
|
||||
|
||||
template <class, class = void>
|
||||
struct is_container : std::false_type {};
|
||||
|
||||
template <class T>
|
||||
struct is_container<
|
||||
T, std::void_t<decltype(std::declval<T>().size(), std::declval<T>().begin(),
|
||||
std::declval<T>().end())>> : std::true_type {};
|
||||
|
||||
template <class, class = void>
|
||||
struct is_map_container : std::false_type {};
|
||||
|
||||
template <class T>
|
||||
struct is_map_container<
|
||||
T, std::void_t<decltype(std::declval<typename T::mapped_type>())>>
|
||||
: is_container<T> {};
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool container_v = is_container<std::remove_cvref_t<T>>::value;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool map_container_v =
|
||||
is_map_container<std::remove_cvref_t<T>>::value;
|
||||
|
||||
template <class T>
|
||||
constexpr inline bool c_array_v = std::is_array_v<std::remove_cvref_t<T>>&&
|
||||
std::extent_v<std::remove_cvref_t<T>> > 0;
|
||||
|
||||
template <typename Type, typename = void>
|
||||
struct is_array : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_array<
|
||||
T, std::void_t<decltype(std::declval<T>().size()),
|
||||
typename std::enable_if_t<(std::tuple_size<T>::value != 0)>>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool array_v = is_array<std::remove_cvref_t<T>>::value;
|
||||
|
||||
template <typename Type>
|
||||
constexpr inline bool fixed_array_v = c_array_v<Type> ||
|
||||
#if __cplusplus > 201703L
|
||||
#if __has_include(<span>)
|
||||
is_span<Type>::value ||
|
||||
#endif
|
||||
#endif
|
||||
array_v<Type>;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool string_view_v =
|
||||
is_template_instant_of<std::basic_string_view,
|
||||
std::remove_cvref_t<T>>::value;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool string_v =
|
||||
is_template_instant_of<std::basic_string, std::remove_cvref_t<T>>::value;
|
||||
|
||||
// TODO: type must be char
|
||||
template <typename T>
|
||||
constexpr inline bool json_view_v = container_v<T>;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool json_byte_v =
|
||||
std::is_same_v<char, std::remove_cvref_t<T>> ||
|
||||
std::is_same_v<unsigned char, std::remove_cvref_t<T>> ||
|
||||
std::is_same_v<std::byte, std::remove_cvref_t<T>>;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool sequence_container_v =
|
||||
is_sequence_container<std::remove_cvref_t<T>>::value;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool tuple_v = is_tuple<std::remove_cvref_t<T>>::value;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool string_container_v = string_v<T> || string_view_v<T>;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool unique_ptr_v =
|
||||
is_template_instant_of<std::unique_ptr, std::remove_cvref_t<T>>::value;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool shared_ptr_v =
|
||||
is_template_instant_of<std::shared_ptr, std::remove_cvref_t<T>>::value;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool smart_ptr_v = shared_ptr_v<T> || unique_ptr_v<T>;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool variant_v = is_variant<std::remove_cvref_t<T>>::value;
|
||||
|
||||
template <size_t Idx, typename T>
|
||||
using variant_element_t = std::remove_reference_t<decltype(std::get<Idx>(
|
||||
std::declval<std::remove_reference_t<T>>()))>;
|
||||
|
||||
template <typename F, typename Tuple, size_t... Is>
|
||||
constexpr void foreach_tuple(F&& f, Tuple& tp, std::index_sequence<Is...>) {
|
||||
(void(f(std::get<Is>(tp), std::integral_constant<size_t, Is>{})), ...);
|
||||
}
|
||||
|
||||
template <typename F, typename Tuple>
|
||||
constexpr void foreach_tuple(F&& f, Tuple& tp) {
|
||||
foreach_tuple(std::forward<F>(f), tp,
|
||||
std::make_index_sequence<std::tuple_size_v<Tuple>>{});
|
||||
}
|
||||
|
||||
template <typename F, typename T>
|
||||
inline constexpr auto for_each_tuple(F&& f, T&& tup) {
|
||||
std::apply(
|
||||
[&f](auto&&... args) {
|
||||
(f(args), ...);
|
||||
},
|
||||
std::forward<T>(tup));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
constexpr inline bool reflect26_excluded_v = false;
|
||||
|
||||
template <class T>
|
||||
constexpr inline bool ylt_refletable_v =
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
(std::is_class_v<ylt::reflection::remove_cvref_t<T>> ||
|
||||
std::is_aggregate_v<ylt::reflection::remove_cvref_t<T>>) &&
|
||||
!string_container_v<T> && !container_v<T> && !fixed_array_v<T> &&
|
||||
!tuple_v<T> && !optional_v<T> && !variant_v<T> && !smart_ptr_v<T> &&
|
||||
!reflect26_excluded_v<ylt::reflection::remove_cvref_t<T>> &&
|
||||
!ylt::reflection::is_custom_refl_v<T> && !is_pb_type_v<T>;
|
||||
#else
|
||||
(ylt::reflection::is_ylt_refl_v<T> ||
|
||||
std::is_aggregate_v<ylt::reflection::remove_cvref_t<T>>) &&
|
||||
!fixed_array_v<T> && !ylt::reflection::is_custom_refl_v<T> &&
|
||||
!is_pb_type_v<T>;
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
constexpr inline bool non_ylt_refletable_v = !ylt_refletable_v<T>;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool plain_v =
|
||||
string_container_v<T> || num_v<T> || char_v<T> || bool_v<T> || enum_v<T>;
|
||||
|
||||
template <typename T>
|
||||
struct underline_type {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct underline_type<std::unique_ptr<T>> {
|
||||
using type = typename underline_type<T>::type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct underline_type<std::shared_ptr<T>> {
|
||||
using type = typename underline_type<T>::type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct underline_type<std::optional<T>> {
|
||||
using type = typename underline_type<T>::type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using underline_type_t = typename underline_type<std::remove_cvref_t<T>>::type;
|
||||
|
||||
struct memory_writer {
|
||||
char* buffer;
|
||||
void write(const char* data, std::size_t len) {
|
||||
memcpy(buffer, data, len);
|
||||
buffer += len;
|
||||
}
|
||||
};
|
||||
|
||||
template <char... C, typename It>
|
||||
IGUANA_INLINE void match(It&& it, It&& end) {
|
||||
const auto n = static_cast<size_t>(std::distance(it, end));
|
||||
if (n < sizeof...(C))
|
||||
IGUANA_UNLIKELY {
|
||||
// TODO: compile time generate this message, currently borken with
|
||||
// MSVC
|
||||
static constexpr auto error = "Unexpected end of buffer. Expected:";
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
if (((... || (*it++ != C))))
|
||||
IGUANA_UNLIKELY {
|
||||
// TODO: compile time generate this message, currently borken with
|
||||
// MSVC
|
||||
static constexpr char b[] = {C..., '\0'};
|
||||
throw std::runtime_error(std::string("Expected these: ").append(b));
|
||||
}
|
||||
}
|
||||
|
||||
inline constexpr auto has_zero = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
|
||||
return (((chunk - 0x0101010101010101) & ~chunk) & 0x8080808080808080);
|
||||
};
|
||||
|
||||
inline constexpr auto has_qoute = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
|
||||
return has_zero(
|
||||
chunk ^
|
||||
0b0010001000100010001000100010001000100010001000100010001000100010);
|
||||
};
|
||||
|
||||
template <bool is_xml_serialization = false, typename Stream, typename Ch>
|
||||
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 <typename Ch, typename SizeType, typename Stream>
|
||||
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] = {
|
||||
#define Z16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E
|
||||
// F
|
||||
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't',
|
||||
'n', 'u', 'f', 'r', 'u', 'u', // 00
|
||||
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u',
|
||||
'u', 'u', 'u', 'u', 'u', 'u', // 10
|
||||
0, 0, '"', 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, // 20
|
||||
Z16, Z16, // 30~4F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, '\\', 0, 0, 0, // 50
|
||||
Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF
|
||||
#undef Z16
|
||||
};
|
||||
auto end = it;
|
||||
std::advance(end, length);
|
||||
while (it < end) {
|
||||
if (static_cast<unsigned>(*it) >= 0x80)
|
||||
IGUANA_UNLIKELY { write_unicode_to_string(it, ss); }
|
||||
else if (escape[static_cast<unsigned char>(*it)])
|
||||
IGUANA_UNLIKELY {
|
||||
ss.push_back('\\');
|
||||
ss.push_back(escape[static_cast<unsigned char>(*it)]);
|
||||
|
||||
if (escape[static_cast<unsigned char>(*it)] == 'u') {
|
||||
// escape other control characters
|
||||
ss.push_back('0');
|
||||
ss.push_back('0');
|
||||
ss.push_back(hexDigits[static_cast<unsigned char>(*it) >> 4]);
|
||||
ss.push_back(hexDigits[static_cast<unsigned char>(*it) & 0xF]);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
else {
|
||||
ss.push_back(*(it++));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
IGUANA_INLINE constexpr bool has_duplicate(const std::array<T, N>& arr) {
|
||||
for (size_t i = 0; i < arr.size(); i++) {
|
||||
for (size_t 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 <typename... Types>
|
||||
IGUANA_INLINE constexpr bool has_duplicate_type() {
|
||||
std::array<std::string_view, sizeof...(Types)> arr{
|
||||
iguana::type_string<Types>()...};
|
||||
return has_duplicate(arr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct has_duplicate_type_in_variant : std::false_type {};
|
||||
|
||||
template <typename... Us>
|
||||
struct has_duplicate_type_in_variant<std::variant<Us...>> {
|
||||
inline constexpr static bool value = has_duplicate_type<Us...>();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool has_duplicate_type_v =
|
||||
has_duplicate_type_in_variant<T>::value;
|
||||
#else
|
||||
template <typename T>
|
||||
constexpr inline bool has_duplicate_type_v = false;
|
||||
#endif
|
||||
|
||||
} // namespace iguana
|
||||
Vendored
+203
@@ -0,0 +1,203 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "error_code.h"
|
||||
|
||||
namespace iguana {
|
||||
|
||||
#define GCC_COMPILER (defined(__GNUC__) && !defined(__clang__))
|
||||
|
||||
#ifdef GCC_COMPILER
|
||||
#include <map>
|
||||
template <class Key, class T, typename... Args>
|
||||
using json_map = std::map<Key, T, Args...>;
|
||||
#else
|
||||
template <class Key, class T, typename... Args>
|
||||
using json_map = std::unordered_map<Key, T, Args...>;
|
||||
#endif
|
||||
|
||||
enum dom_parse_error { ok, wrong_type };
|
||||
|
||||
template <typename CharT>
|
||||
struct basic_json_value
|
||||
: std::variant<std::monostate, std::nullptr_t, bool, double, int,
|
||||
std::basic_string<CharT>,
|
||||
std::vector<basic_json_value<CharT>>,
|
||||
json_map<std::basic_string<CharT>, basic_json_value<CharT>>,
|
||||
std::basic_string_view<CharT>> {
|
||||
using string_type = std::basic_string<CharT>;
|
||||
using string_view_type = std::basic_string_view<CharT>;
|
||||
using array_type = std::vector<basic_json_value<CharT>>;
|
||||
using object_type = json_map<string_type, basic_json_value<CharT>>;
|
||||
|
||||
using base_type =
|
||||
std::variant<std::monostate, std::nullptr_t, bool, double, int,
|
||||
string_type, array_type, object_type, string_view_type>;
|
||||
|
||||
using base_type::base_type;
|
||||
|
||||
inline const static std::unordered_map<size_t, std::string> type_map_ = {
|
||||
{0, "undefined type"}, {1, "null type"}, {2, "bool type"},
|
||||
{3, "double type"}, {4, "int type"}, {5, "string type"},
|
||||
{6, "array type"}, {7, "object type"}, {8, "string_view type"}};
|
||||
|
||||
basic_json_value() : base_type(std::in_place_type<std::monostate>) {}
|
||||
|
||||
basic_json_value(CharT const *value)
|
||||
: base_type(std::in_place_type<string_type>, value) {}
|
||||
|
||||
base_type &base() { return *this; }
|
||||
base_type const &base() const { return *this; }
|
||||
|
||||
bool is_undefined() const {
|
||||
return std::holds_alternative<std::monostate>(*this);
|
||||
}
|
||||
bool is_null() const { return std::holds_alternative<std::nullptr_t>(*this); }
|
||||
bool is_bool() const { return std::holds_alternative<bool>(*this); }
|
||||
bool is_double() const { return std::holds_alternative<double>(*this); }
|
||||
bool is_int() const { return std::holds_alternative<int>(*this); }
|
||||
bool is_number() const { return is_double() || is_int(); }
|
||||
bool is_string() const { return std::holds_alternative<string_type>(*this); }
|
||||
bool is_array() const { return std::holds_alternative<array_type>(*this); }
|
||||
bool is_object() const { return std::holds_alternative<object_type>(*this); }
|
||||
bool is_string_view() const {
|
||||
return std::holds_alternative<string_view_type>(*this);
|
||||
}
|
||||
|
||||
// if type is not match, will throw exception, if pass std::error_code, won't
|
||||
// throw exception
|
||||
template <typename T>
|
||||
T get() const {
|
||||
try {
|
||||
return std::get<T>(*this);
|
||||
} catch (std::exception &e) {
|
||||
auto it = type_map_.find(this->index());
|
||||
if (it == type_map_.end()) {
|
||||
throw std::invalid_argument("undefined type");
|
||||
}
|
||||
else {
|
||||
throw std::invalid_argument(it->second);
|
||||
}
|
||||
} catch (...) {
|
||||
throw std::invalid_argument("unknown exception");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get(std::error_code &ec) const {
|
||||
try {
|
||||
return get<T>();
|
||||
} catch (std::exception &e) {
|
||||
ec = iguana::make_error_code(iguana::dom_errc::wrong_type, e.what());
|
||||
return T{};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::error_code get_to(T &v) const {
|
||||
std::error_code ec;
|
||||
v = get<T>(ec);
|
||||
return ec;
|
||||
}
|
||||
template <typename T>
|
||||
T at(const std::string &key) {
|
||||
const auto &map = get<object_type>();
|
||||
auto it = map.find(key);
|
||||
if (it == map.end()) {
|
||||
throw std::invalid_argument("the key is unknown");
|
||||
}
|
||||
return it->second.template get<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T at(const std::string &key, std::error_code &ec) {
|
||||
const auto &map = get<object_type>(ec);
|
||||
if (ec) {
|
||||
return T{};
|
||||
}
|
||||
|
||||
auto it = map.find(key);
|
||||
if (it == map.end()) {
|
||||
ec = std::make_error_code(std::errc::invalid_argument);
|
||||
return T{};
|
||||
}
|
||||
return it->second.template get<T>(ec);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T at(size_t idx) {
|
||||
const auto &arr = get<array_type>();
|
||||
if (idx >= arr.size()) {
|
||||
throw std::out_of_range("idx is out of range");
|
||||
}
|
||||
return arr[idx].template get<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T at(size_t idx, std::error_code &ec) {
|
||||
const auto &arr = get<array_type>(ec);
|
||||
if (ec) {
|
||||
return T{};
|
||||
}
|
||||
|
||||
if (idx >= arr.size()) {
|
||||
ec = std::make_error_code(std::errc::result_out_of_range);
|
||||
return T{};
|
||||
}
|
||||
|
||||
return arr[idx].template get<T>(ec);
|
||||
}
|
||||
|
||||
object_type to_object() const { return get<object_type>(); }
|
||||
object_type to_object(std::error_code &ec) const {
|
||||
return get<object_type>(ec);
|
||||
}
|
||||
|
||||
array_type to_array() const { return get<array_type>(); }
|
||||
array_type to_array(std::error_code &ec) const { return get<array_type>(ec); }
|
||||
|
||||
double to_double() const { return get<double>(); }
|
||||
double to_double(std::error_code &ec) const { return get<double>(ec); }
|
||||
|
||||
int to_int() const { return get<int>(); }
|
||||
int to_int(std::error_code &ec) const { return get<int>(ec); }
|
||||
|
||||
bool to_bool() const { return get<bool>(); }
|
||||
bool to_bool(std::error_code &ec) const { return get<bool>(ec); }
|
||||
|
||||
string_type to_string() const { return get<string_type>(); }
|
||||
string_type to_string(std::error_code &ec) const {
|
||||
return get<string_type>(ec);
|
||||
}
|
||||
|
||||
string_view_type to_string_view() const { return get<string_view_type>(); }
|
||||
string_view_type to_string_view(std::error_code &ec) const {
|
||||
return get<string_view_type>(ec);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename CharT>
|
||||
using basic_jarray = typename basic_json_value<CharT>::array_type;
|
||||
|
||||
template <typename CharT>
|
||||
using basic_jobject = typename basic_json_value<CharT>::object_type;
|
||||
|
||||
template <typename CharT>
|
||||
using basic_jpair = typename basic_jobject<CharT>::value_type;
|
||||
|
||||
using jvalue = basic_json_value<char>;
|
||||
using jarray = basic_jarray<char>;
|
||||
using jobject = basic_jobject<char>;
|
||||
using jpair = basic_jpair<char>;
|
||||
|
||||
template <typename CharT>
|
||||
void swap(basic_json_value<CharT> &lhs, basic_json_value<CharT> &rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
} // namespace iguana
|
||||
Vendored
+8
@@ -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 100005 // 1.0.5
|
||||
+577
@@ -0,0 +1,577 @@
|
||||
#pragma once
|
||||
#include <charconv>
|
||||
#include <vector>
|
||||
|
||||
#include "detail/charconv.h"
|
||||
#include "detail/utf.hpp"
|
||||
#include "xml_util.hpp"
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
#include "ylt/reflection/reflect26_dispatch.hpp"
|
||||
#endif
|
||||
|
||||
namespace iguana {
|
||||
namespace detail {
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<optional_v<U>, int> = 0>
|
||||
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
|
||||
std::string_view name);
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int> = 0>
|
||||
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
|
||||
std::string_view name);
|
||||
|
||||
template <typename U, typename It,
|
||||
std::enable_if_t<ylt_refletable_v<U>, int> = 0>
|
||||
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
|
||||
std::string_view name);
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<attr_v<U>, int> = 0>
|
||||
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
|
||||
std::string_view name);
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<plain_v<U>, int> = 0>
|
||||
IGUANA_INLINE void xml_parse_value(U &&value, It &&begin, It &&end) {
|
||||
using T = std::decay_t<U>;
|
||||
if constexpr (string_container_v<T>) {
|
||||
if constexpr (string_view_v<T>) {
|
||||
value = T(&*begin, static_cast<size_t>(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<size_t>(std::distance(pre, begin))));
|
||||
parse_escape_xml(value, begin, end);
|
||||
pre = begin;
|
||||
}
|
||||
value.append(T(&*pre, static_cast<size_t>(std::distance(pre, begin))));
|
||||
}
|
||||
}
|
||||
else if constexpr (num_v<T>) {
|
||||
auto size = std::distance(begin, end);
|
||||
const auto start = &*begin;
|
||||
detail::from_chars(start, start + size, value);
|
||||
}
|
||||
else if constexpr (char_v<T>) {
|
||||
if (static_cast<size_t>(std::distance(begin, end)) != 1)
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Expected one character"); }
|
||||
value = *begin;
|
||||
}
|
||||
else if constexpr (bool_v<T>) {
|
||||
auto bool_v = std::string_view(
|
||||
&*begin, static_cast<size_t>(std::distance(begin, end)));
|
||||
if (bool_v == "true") {
|
||||
value = true;
|
||||
}
|
||||
else if (bool_v == "false") {
|
||||
value = false;
|
||||
}
|
||||
else
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Expected true or false"); }
|
||||
}
|
||||
else if constexpr (enum_v<T>) {
|
||||
static constexpr auto str_to_enum = get_enum_map<true, T>();
|
||||
if constexpr (bool_v<decltype(str_to_enum)>) {
|
||||
// not defined a specialization template
|
||||
xml_parse_value(reinterpret_cast<std::underlying_type_t<T> &>(value),
|
||||
begin, end);
|
||||
}
|
||||
else {
|
||||
auto enum_names = std::string_view(
|
||||
&*begin, static_cast<size_t>(std::distance(begin, end)));
|
||||
auto it = str_to_enum.find(enum_names);
|
||||
if (it != str_to_enum.end())
|
||||
IGUANA_LIKELY { value = it->second; }
|
||||
else {
|
||||
throw std::runtime_error(std::string(enum_names) +
|
||||
" missing corresponding value in enum_value");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<is_pb_type_v<U>, int> = 0>
|
||||
IGUANA_INLINE void xml_parse_value(U &&value, It &&begin, It &&end) {
|
||||
xml_parse_value(value.val, begin, end);
|
||||
}
|
||||
|
||||
template <typename U, typename It,
|
||||
std::enable_if_t<map_container_v<U>, int> = 0>
|
||||
IGUANA_INLINE void parse_attr(U &&value, It &&it, It &&end) {
|
||||
using key_type = typename std::remove_cvref_t<U>::key_type;
|
||||
using value_type = typename std::remove_cvref_t<U>::mapped_type;
|
||||
while (it != end) {
|
||||
skip_sapces_and_newline(it, end);
|
||||
if (*it == '>' || *it == '/')
|
||||
IGUANA_UNLIKELY { return; }
|
||||
auto key_begin = it;
|
||||
auto key_end = skip_pass<'='>(it, end);
|
||||
key_type key;
|
||||
xml_parse_value(key, key_begin, key_end);
|
||||
|
||||
skip_sapces_and_newline(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;
|
||||
xml_parse_value(v, value_begin, value_end);
|
||||
value.emplace(std::move(key), std::move(v));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<plain_v<U>, int> = 0>
|
||||
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
|
||||
std::string_view name) {
|
||||
skip_till<'>'>(it, end);
|
||||
++it;
|
||||
skip_sapces_and_newline(it, end);
|
||||
auto value_begin = it;
|
||||
auto value_end = skip_pass<'<'>(it, end);
|
||||
xml_parse_value(value, value_begin, value_end);
|
||||
match_close_tag(it, end, name);
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<is_pb_type_v<U>, int> = 0>
|
||||
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
|
||||
std::string_view name) {
|
||||
xml_parse_item(value.val, it, end, name);
|
||||
}
|
||||
|
||||
template <typename U, typename It,
|
||||
std::enable_if_t<sequence_container_v<U>, int> = 0>
|
||||
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
|
||||
std::string_view name) {
|
||||
xml_parse_item(value.emplace_back(), it, end, name);
|
||||
skip_sapces_and_newline(it, end);
|
||||
while (it != end) {
|
||||
match<'<'>(it, end);
|
||||
if (*it == '?' || *it == '!')
|
||||
IGUANA_UNLIKELY {
|
||||
--it;
|
||||
return;
|
||||
}
|
||||
auto start = it;
|
||||
skip_till_greater_or_space(it, end);
|
||||
std::string_view key = std::string_view{
|
||||
&*start, static_cast<size_t>(std::distance(start, it))};
|
||||
if (key != name)
|
||||
IGUANA_UNLIKELY {
|
||||
it = start - 1;
|
||||
return;
|
||||
}
|
||||
xml_parse_item(value.emplace_back(), it, end, name);
|
||||
skip_sapces_and_newline(it, end);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It,
|
||||
std::enable_if_t<map_container_v<U>, int> = 0>
|
||||
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
|
||||
std::string_view name) {
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<variant_v<U>, int> = 0>
|
||||
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
|
||||
std::string_view name) {
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<optional_v<U>, int>>
|
||||
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
|
||||
std::string_view name) {
|
||||
using value_type = typename std::remove_reference_t<U>::value_type;
|
||||
skip_till<'>'>(it, end);
|
||||
if (*(it - 1) == '/')
|
||||
IGUANA_LIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (plain_v<value_type>) {
|
||||
// The following code is for option not to be emplaced
|
||||
// when parse "...> <..."
|
||||
skip_till<'>'>(it, end);
|
||||
++it;
|
||||
skip_sapces_and_newline(it, end);
|
||||
auto value_begin = it;
|
||||
auto value_end = skip_pass<'<'>(it, end);
|
||||
if (value_begin == value_end) {
|
||||
match_close_tag(it, end, name);
|
||||
return;
|
||||
}
|
||||
xml_parse_value(value.emplace(), value_begin, value_end);
|
||||
match_close_tag(it, end, name);
|
||||
}
|
||||
else {
|
||||
xml_parse_item(value.emplace(), it, end, name);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int>>
|
||||
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
|
||||
std::string_view name) {
|
||||
if constexpr (unique_ptr_v<U>) {
|
||||
value = std::make_unique<typename std::remove_cvref_t<U>::element_type>();
|
||||
}
|
||||
else {
|
||||
value = std::make_shared<typename std::remove_cvref_t<U>::element_type>();
|
||||
}
|
||||
|
||||
xml_parse_item(*value, it, end, name);
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<attr_v<U>, int>>
|
||||
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
|
||||
std::string_view name) {
|
||||
parse_attr(value.attr(), it, end);
|
||||
xml_parse_item(value.value(), it, end, name);
|
||||
}
|
||||
|
||||
// /> or skip <?>、 <!> and <tag></tag> until the </name>
|
||||
// loose inspection here
|
||||
template <typename It>
|
||||
IGUANA_INLINE void skip_object_value(It &&it, It &&end, std::string_view name) {
|
||||
skip_till<'>'>(it, end);
|
||||
++it;
|
||||
if (*(it - 2) == '/') {
|
||||
// .../>
|
||||
return;
|
||||
}
|
||||
// </name>
|
||||
size_t size = name.size();
|
||||
while (it != end) {
|
||||
skip_till<'>'>(it, end);
|
||||
if (*(it - size - 2) == '<' && *(it - size - 1) == '/' &&
|
||||
(std::string_view{&*(it - size), size} == name)) {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
skip_sapces_and_newline(it, end);
|
||||
}
|
||||
throw std::runtime_error("unclosed tag: " + std::string(name));
|
||||
}
|
||||
|
||||
// skip <?...?>
|
||||
template <typename It>
|
||||
IGUANA_INLINE void skip_instructions(It &&it, It &&end) {
|
||||
while (*(it - 1) != '?') {
|
||||
++it;
|
||||
skip_till<'>'>(it, end);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
IGUANA_INLINE void skip_cdata(It &&it, It &&end) {
|
||||
++it;
|
||||
skip_till<']'>(it, end);
|
||||
++it;
|
||||
match<']', '>'>(it, end);
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
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 <size_t cdata_idx, typename T, typename It,
|
||||
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
|
||||
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 {
|
||||
// reach the close tag
|
||||
return true;
|
||||
}
|
||||
else if (*it == '?')
|
||||
IGUANA_UNLIKELY {
|
||||
skip_instructions(it, end);
|
||||
continue;
|
||||
}
|
||||
else if (*it == '!')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
if (*it == '[') {
|
||||
// <![
|
||||
if constexpr (cdata_idx == ylt::reflection::members_count_v<T>) {
|
||||
skip_cdata(it, end);
|
||||
}
|
||||
else {
|
||||
// if parse cdata
|
||||
++it;
|
||||
match<'C', 'D', 'A', 'T', 'A', '['>(it, end);
|
||||
skip_sapces_and_newline(it, end);
|
||||
auto &cdata_value = ylt::reflection::get<cdata_idx>(value);
|
||||
using VT = typename std::decay_t<decltype(cdata_value)>::value_type;
|
||||
auto vb = it;
|
||||
auto ve = skip_pass<']'>(it, end);
|
||||
if constexpr (string_view_v<VT>) {
|
||||
cdata_value.value() =
|
||||
VT(&*vb, static_cast<size_t>(std::distance(vb, ve)));
|
||||
}
|
||||
else {
|
||||
cdata_value.value().append(
|
||||
&*vb, static_cast<size_t>(std::distance(vb, ve)));
|
||||
}
|
||||
match<']', '>'>(it, end);
|
||||
}
|
||||
}
|
||||
else if (*it == '-') {
|
||||
// <!-- -->
|
||||
skip_comment(it, end);
|
||||
}
|
||||
else {
|
||||
// <!D... >
|
||||
skip_till<'>'>(it, end);
|
||||
++it;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
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 {
|
||||
// <!D... >
|
||||
skip_till<'>'>(it, end);
|
||||
++it;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IGUANA_INLINE bool contains_xml_key(const std::vector<std::string_view> &keys,
|
||||
std::string_view key) {
|
||||
for (auto item : keys) {
|
||||
if (item == key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void check_required(
|
||||
const std::vector<std::string_view> &parsed_keys) {
|
||||
if constexpr (iguana::has_iguana_required_arr_v<T>) {
|
||||
constexpr auto required_arr =
|
||||
iguana::iguana_required_struct<T>::requied_arr();
|
||||
for (auto &item : required_arr) {
|
||||
if (!contains_xml_key(parsed_keys, item)) {
|
||||
std::string err = "required filed ";
|
||||
err.append(item).append(" not found!");
|
||||
throw std::invalid_argument(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
if constexpr (iguana::detail::xml_required_count<T>() > 0) {
|
||||
constexpr auto required_arr = iguana::detail::xml_required_names<T>();
|
||||
for (auto &item : required_arr) {
|
||||
if (!contains_xml_key(parsed_keys, item)) {
|
||||
std::string err = "required filed ";
|
||||
err.append(item).append(" not found!");
|
||||
throw std::invalid_argument(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename It, std::enable_if_t<ylt_refletable_v<T>, int>>
|
||||
IGUANA_INLINE void xml_parse_item(T &value, It &&it, It &&end,
|
||||
std::string_view name) {
|
||||
using U = std::decay_t<T>;
|
||||
constexpr auto cdata_idx = get_type_index<is_cdata_t, U>();
|
||||
skip_till<'>'>(it, end);
|
||||
++it;
|
||||
if (skip_till_close_tag<cdata_idx>(value, it, end)) {
|
||||
match_close_tag(it, end, name);
|
||||
return;
|
||||
}
|
||||
auto start = it;
|
||||
skip_till_greater_or_space(it, end);
|
||||
std::string_view key =
|
||||
std::string_view{&*start, static_cast<size_t>(std::distance(start, it))};
|
||||
|
||||
[[maybe_unused]] std::vector<std::string_view> parsed_keys;
|
||||
bool parse_done = false;
|
||||
// sequential parse
|
||||
ylt::reflection::for_each(value, [&](auto &field, auto st_key, auto index) {
|
||||
#if defined(_MSC_VER) && _MSVC_LANG < 202002L
|
||||
constexpr auto cdata_idx = get_type_index<is_cdata_t, U>();
|
||||
#endif
|
||||
using item_type = std::remove_reference_t<decltype(field)>;
|
||||
if constexpr (cdata_v<item_type>) {
|
||||
return;
|
||||
}
|
||||
if (parse_done || key != st_key)
|
||||
IGUANA_UNLIKELY { return; }
|
||||
if constexpr (!cdata_v<item_type>) {
|
||||
xml_parse_item(field, it, end, key);
|
||||
if constexpr (iguana::has_xml_required_fields_v<U>) {
|
||||
parsed_keys.push_back(key);
|
||||
}
|
||||
}
|
||||
if (skip_till_close_tag<cdata_idx>(value, it, end))
|
||||
IGUANA_UNLIKELY {
|
||||
match_close_tag(it, end, name);
|
||||
parse_done = true;
|
||||
return;
|
||||
}
|
||||
start = it;
|
||||
skip_till_greater_or_space(it, end);
|
||||
key = std::string_view{&*start,
|
||||
static_cast<size_t>(std::distance(start, it))};
|
||||
});
|
||||
if (parse_done)
|
||||
IGUANA_UNLIKELY {
|
||||
check_required<U>(parsed_keys);
|
||||
return;
|
||||
}
|
||||
// map parse
|
||||
while (true) {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
bool found = ylt::reflection::reflect26::dispatch_by_name(
|
||||
value, key, [&](auto &field) IGUANA__INLINE_LAMBDA {
|
||||
if constexpr (!cdata_v<std::remove_reference_t<decltype(field)>>) {
|
||||
xml_parse_item(field, it, end, key);
|
||||
if constexpr (iguana::has_xml_required_fields_v<U>) {
|
||||
parsed_keys.push_back(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!found)
|
||||
IGUANA_UNLIKELY {
|
||||
#ifdef THROW_UNKNOWN_KEY
|
||||
throw std::runtime_error("Unknown key: " + std::string(key));
|
||||
#else
|
||||
skip_object_value(it, end, key);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
static auto frozen_map = ylt::reflection::get_variant_map<U>();
|
||||
const auto &member_it = frozen_map.find(key);
|
||||
if (member_it != frozen_map.end())
|
||||
IGUANA_LIKELY {
|
||||
std::visit(
|
||||
[&](auto offset) IGUANA__INLINE_LAMBDA {
|
||||
using value_type = typename decltype(offset)::type;
|
||||
if constexpr (!cdata_v<value_type>) {
|
||||
auto member_ptr =
|
||||
(value_type *)((char *)(&value) + offset.value);
|
||||
xml_parse_item(*member_ptr, it, end, key);
|
||||
if constexpr (iguana::has_xml_required_fields_v<U>) {
|
||||
parsed_keys.push_back(key);
|
||||
}
|
||||
}
|
||||
},
|
||||
member_it->second);
|
||||
}
|
||||
else
|
||||
IGUANA_UNLIKELY {
|
||||
#ifdef THROW_UNKNOWN_KEY
|
||||
throw std::runtime_error("Unknown key: " + std::string(key));
|
||||
#else
|
||||
skip_object_value(it, end, key);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (skip_till_close_tag<cdata_idx>(value, it, end)) {
|
||||
match_close_tag(it, end, name);
|
||||
check_required<U>(parsed_keys);
|
||||
return;
|
||||
}
|
||||
start = it;
|
||||
skip_till_greater_or_space(it, end);
|
||||
key = std::string_view{&*start,
|
||||
static_cast<size_t>(std::distance(start, it))};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename It, typename U, std::enable_if_t<attr_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_xml(U &value, It &&it, It &&end) {
|
||||
detail::skip_till_first_key(it, end);
|
||||
auto start = it;
|
||||
skip_till_greater_or_space(it, end);
|
||||
std::string_view key =
|
||||
std::string_view{&*start, static_cast<size_t>(std::distance(start, it))};
|
||||
detail::parse_attr(value.attr(), it, end);
|
||||
detail::xml_parse_item(value.value(), it, end, key);
|
||||
}
|
||||
|
||||
template <typename It, typename U,
|
||||
std::enable_if_t<ylt_refletable_v<U>, int> = 0>
|
||||
IGUANA_INLINE void from_xml(U &value, It &&it, It &&end) {
|
||||
detail::skip_till_first_key(it, end);
|
||||
auto start = it;
|
||||
skip_till_greater_or_space(it, end);
|
||||
std::string_view key =
|
||||
std::string_view{&*start, static_cast<size_t>(std::distance(start, it))};
|
||||
detail::xml_parse_item(value, it, end, key);
|
||||
}
|
||||
|
||||
template <typename U, typename View,
|
||||
std::enable_if_t<string_container_v<View>, int> = 0>
|
||||
IGUANA_INLINE void from_xml(U &value, const View &view) {
|
||||
from_xml(value, std::begin(view), std::end(view));
|
||||
}
|
||||
|
||||
template <typename Num, std::enable_if_t<num_v<Num>, int> = 0>
|
||||
IGUANA_INLINE Num get_number(std::string_view str) {
|
||||
Num num;
|
||||
detail::xml_parse_value(num, str.begin(), str.end());
|
||||
return num;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void from_xml_adl(iguana_adl_t *p, T &t,
|
||||
std::string_view pb_str) {
|
||||
iguana::from_xml(t, pb_str);
|
||||
}
|
||||
|
||||
} // namespace iguana
|
||||
+465
@@ -0,0 +1,465 @@
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
#include "detail/pb_type.hpp"
|
||||
#include "util.hpp"
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
#include <meta>
|
||||
|
||||
#include "ylt/reflection/reflect26_core.hpp"
|
||||
#endif
|
||||
|
||||
namespace iguana {
|
||||
struct xml_required {};
|
||||
|
||||
template <typename T>
|
||||
struct iguana_required_struct;
|
||||
#define REQUIRED_IMPL(STRUCT_NAME, N, ...) \
|
||||
template <> \
|
||||
struct iguana::iguana_required_struct<STRUCT_NAME> { \
|
||||
inline static constexpr auto requied_arr() { \
|
||||
std::array<std::string_view, N> arr_required = { \
|
||||
WRAP_ARGS(CONCAT_NAME, 0, ##__VA_ARGS__)}; \
|
||||
return arr_required; \
|
||||
} \
|
||||
};
|
||||
|
||||
#define REQUIRED(STRUCT_NAME, ...) \
|
||||
REQUIRED_IMPL(STRUCT_NAME, YLT_ARG_COUNT(__VA_ARGS__), __VA_ARGS__)
|
||||
|
||||
template <class T, class = void>
|
||||
struct has_iguana_required_arr : std::false_type {};
|
||||
|
||||
template <class T>
|
||||
struct has_iguana_required_arr<
|
||||
T, std::void_t<decltype(iguana_required_struct<T>::requied_arr())>>
|
||||
: std::true_type {};
|
||||
|
||||
template <class T>
|
||||
constexpr bool has_iguana_required_arr_v = has_iguana_required_arr<T>::value;
|
||||
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
struct is_xml_required_annotation : std::false_type {};
|
||||
|
||||
template <>
|
||||
struct is_xml_required_annotation<iguana::xml_required> : std::true_type {};
|
||||
|
||||
template <std::meta::info Member>
|
||||
consteval bool has_xml_required() {
|
||||
return ylt::reflection::reflect26::has_annotation<
|
||||
Member, is_xml_required_annotation>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
consteval size_t xml_required_count() {
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
static constexpr auto members =
|
||||
ylt::reflection::reflect26::data_members_array<U>();
|
||||
size_t count = 0;
|
||||
template for (constexpr auto member : members) {
|
||||
if constexpr (has_xml_required<member>()) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
consteval auto xml_required_names() {
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
static constexpr auto members =
|
||||
ylt::reflection::reflect26::data_members_array<U>();
|
||||
constexpr auto member_names = ylt::reflection::get_member_names<U>();
|
||||
std::array<std::string_view, xml_required_count<U>()> names{};
|
||||
size_t index = 0;
|
||||
size_t member_index = 0;
|
||||
template for (constexpr auto member : members) {
|
||||
if constexpr (has_xml_required<member>()) {
|
||||
names[index++] = member_names[member_index];
|
||||
}
|
||||
++member_index;
|
||||
}
|
||||
return names;
|
||||
}
|
||||
} // namespace detail
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
constexpr bool has_xml_required_fields_v =
|
||||
has_iguana_required_arr_v<T>
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
|| detail::xml_required_count<T>() > 0
|
||||
#endif
|
||||
;
|
||||
|
||||
template <typename T,
|
||||
typename map_type = std::unordered_map<std::string, std::string>>
|
||||
class xml_attr_t {
|
||||
public:
|
||||
T &value() { return val_; }
|
||||
map_type &attr() { return attr_; }
|
||||
const T &value() const { return val_; }
|
||||
const map_type &attr() const { return attr_; }
|
||||
using value_type = std::remove_cvref_t<T>;
|
||||
|
||||
private:
|
||||
T val_;
|
||||
map_type attr_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using xml_attr_view_t =
|
||||
xml_attr_t<T, std::map<std::string_view, std::string_view>>;
|
||||
|
||||
template <typename T = std::string_view,
|
||||
std::enable_if_t<string_container_v<T>, int> = 0>
|
||||
class xml_cdata_t {
|
||||
public:
|
||||
T &value() { return val_; }
|
||||
const T &value() const { return val_; }
|
||||
using value_type = std::remove_cvref_t<T>;
|
||||
|
||||
private:
|
||||
T val_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool is_attr_t_v = false;
|
||||
|
||||
template <typename T, typename map_type>
|
||||
constexpr inline bool is_attr_t_v<xml_attr_t<T, map_type>> = true;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool attr_v = is_attr_t_v<std::remove_cvref_t<T>>;
|
||||
|
||||
template <typename>
|
||||
struct is_cdata_t : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_cdata_t<xml_cdata_t<T>> : std::true_type {};
|
||||
|
||||
template <typename T, typename map_type>
|
||||
constexpr inline bool reflect26_excluded_v<xml_attr_t<T, map_type>> = true;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool reflect26_excluded_v<xml_cdata_t<T>> = true;
|
||||
|
||||
template <std::size_t index, template <typename...> typename Condition,
|
||||
typename Tuple>
|
||||
constexpr int element_index_helper() {
|
||||
if constexpr (index == std::tuple_size_v<Tuple>) {
|
||||
return index;
|
||||
}
|
||||
else {
|
||||
using item_type =
|
||||
ylt::reflection::remove_cvref_t<std::tuple_element_t<index, Tuple>>;
|
||||
|
||||
return Condition<item_type>::value
|
||||
? index
|
||||
: element_index_helper<index + 1, Condition, Tuple>();
|
||||
}
|
||||
}
|
||||
|
||||
template <template <typename...> typename Condition, typename T>
|
||||
constexpr int tuple_element_index() {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
return static_cast<int>(
|
||||
ylt::reflection::reflect26::member_index_if<Condition, T>());
|
||||
#else
|
||||
using Tuple = decltype(ylt::reflection::object_to_tuple(std::declval<T>()));
|
||||
return element_index_helper<0, Condition, Tuple>();
|
||||
#endif
|
||||
}
|
||||
|
||||
template <template <typename...> typename Condition, typename T>
|
||||
constexpr size_t get_type_index() {
|
||||
return tuple_element_index<Condition, T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool cdata_v = is_cdata_t<std::remove_cvref_t<T>>::value;
|
||||
|
||||
inline constexpr auto has_greater = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
|
||||
return has_zero(
|
||||
chunk ^
|
||||
0b0011111000111110001111100011111000111110001111100011111000111110);
|
||||
};
|
||||
|
||||
inline constexpr auto has_space = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
|
||||
return has_zero(
|
||||
chunk ^
|
||||
0b0010000000100000001000000010000000100000001000000010000000100000);
|
||||
};
|
||||
|
||||
inline constexpr auto has_smaller = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
|
||||
return has_zero(
|
||||
chunk ^
|
||||
0b0011110000111100001111000011110000111100001111000011110000111100);
|
||||
};
|
||||
|
||||
inline constexpr auto has_square_bracket =
|
||||
[](uint64_t chunk) IGUANA__INLINE_LAMBDA {
|
||||
return has_zero(
|
||||
chunk ^
|
||||
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 <typename It>
|
||||
IGUANA_INLINE void skip_sapces_and_newline(It &&it, It &&end) {
|
||||
while (it != end && (static_cast<uint8_t>(*it) < 33)) {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
IGUANA_INLINE void match_close_tag(It &&it, It &&end, std::string_view key) {
|
||||
if (it == end || (*it++) != '/')
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::runtime_error("unclosed tag: " + std::string(key));
|
||||
}
|
||||
size_t size = key.size();
|
||||
if (static_cast<size_t>(std::distance(it, end)) <= size ||
|
||||
std::string_view{&*it, size} != key)
|
||||
IGUANA_UNLIKELY {
|
||||
throw std::runtime_error("unclosed tag: " + std::string(key));
|
||||
}
|
||||
it += size;
|
||||
match<'>'>(it, end);
|
||||
|
||||
// skip_till<'>'>(it, end); // not check
|
||||
// ++it;
|
||||
}
|
||||
|
||||
// returns true if the specified character 'c' is found, false otherwise.
|
||||
template <char c, typename It>
|
||||
IGUANA_INLINE bool advance_until_character(It &&it, It &&end) {
|
||||
static_assert(contiguous_iterator<std::decay_t<It>>);
|
||||
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<const uint64_t *>(&*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 <char c, typename It>
|
||||
IGUANA_INLINE void skip_till(It &&it, It &&end) {
|
||||
static_assert(contiguous_iterator<std::decay_t<It>>);
|
||||
|
||||
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<const uint64_t *>(&*it);
|
||||
uint64_t test;
|
||||
if constexpr (c == '>')
|
||||
test = has_greater(chunk);
|
||||
else if constexpr (c == '<')
|
||||
test = has_smaller(chunk);
|
||||
else if constexpr (c == '"')
|
||||
test = has_qoute(chunk);
|
||||
else if constexpr (c == ' ')
|
||||
test = has_space(chunk);
|
||||
else if constexpr (c == ']')
|
||||
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) {
|
||||
it += (countr_zero(test) >> 3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tail end of buffer. Should be rare we even get here
|
||||
while (it < end) {
|
||||
if (*it == c)
|
||||
return;
|
||||
++it;
|
||||
}
|
||||
static constexpr char b[] = {c, '\0'};
|
||||
std::string error = std::string("Expected: ").append(b);
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
|
||||
// skip_till<'>', '<'>(it, end);
|
||||
template <typename It>
|
||||
IGUANA_INLINE void skip_till_greater_or_space(It &&it, It &&end) {
|
||||
static_assert(contiguous_iterator<std::decay_t<It>>);
|
||||
|
||||
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<const uint64_t *>(&*it);
|
||||
uint64_t test = has_greater(chunk) | has_space(chunk);
|
||||
if (test != 0) {
|
||||
it += (countr_zero(test) >> 3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tail end of buffer. Should be rare we even get here
|
||||
while (it < end) {
|
||||
switch (*it) {
|
||||
case '>':
|
||||
case ' ':
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
throw std::runtime_error("Expected > or space");
|
||||
}
|
||||
|
||||
template <char c, typename It>
|
||||
IGUANA_INLINE auto skip_pass(It &&it, It &&end) {
|
||||
skip_till<c>(it, end);
|
||||
auto res = it++ - 1;
|
||||
while (*res == ' ') {
|
||||
--res;
|
||||
}
|
||||
return res + 1;
|
||||
}
|
||||
|
||||
template <char... C, typename It>
|
||||
IGUANA_INLINE bool is_match(It &&it, const It &end) {
|
||||
const auto n = static_cast<size_t>(std::distance(it, end));
|
||||
if ((n < sizeof...(C)) || (... || (*it++ != C))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<string_v<U>, 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<unsigned char>(*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<unsigned char>(*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
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
#pragma once
|
||||
|
||||
#include "detail/charconv.h"
|
||||
#include "xml_util.hpp"
|
||||
|
||||
namespace iguana {
|
||||
|
||||
#ifdef XML_ATTR_USE_APOS
|
||||
#define XML_ATTR_DELIMITER '\''
|
||||
#else
|
||||
#define XML_ATTR_DELIMITER '\"'
|
||||
#endif
|
||||
|
||||
// TODO: improve by precaculate size
|
||||
template <bool escape_quote_apos, typename Ch, typename SizeType,
|
||||
typename Stream>
|
||||
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<unsigned>(*it) >= 0x80)
|
||||
IGUANA_UNLIKELY {
|
||||
write_unicode_to_string<true>(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 <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<sequence_container_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
|
||||
std::string_view name);
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<map_container_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
|
||||
std::string_view name);
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_xml_value(Stream &ss, T &&t, std::string_view name);
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream>
|
||||
IGUANA_INLINE void render_tail(Stream &ss, std::string_view str) {
|
||||
if constexpr (pretty) {
|
||||
ss.append(spaces, '\t');
|
||||
}
|
||||
ss.push_back('<');
|
||||
ss.push_back('/');
|
||||
ss.append(str.data(), str.size());
|
||||
ss.push_back('>');
|
||||
if constexpr (pretty) {
|
||||
ss.push_back('\n');
|
||||
}
|
||||
}
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream>
|
||||
IGUANA_INLINE void render_head(Stream &ss, std::string_view str) {
|
||||
if constexpr (pretty) {
|
||||
ss.append(spaces, '\t');
|
||||
}
|
||||
ss.push_back('<');
|
||||
ss.append(str.data(), str.size());
|
||||
ss.push_back('>');
|
||||
}
|
||||
|
||||
template <bool escape_quote_apos = false, typename Stream, typename T,
|
||||
std::enable_if_t<plain_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_value(Stream &ss, const T &value) {
|
||||
if constexpr (string_container_v<T>) {
|
||||
render_string_with_escape_xml<escape_quote_apos>(value.data(), value.size(),
|
||||
ss);
|
||||
}
|
||||
else if constexpr (num_v<T>) {
|
||||
char temp[65];
|
||||
auto p = detail::to_chars(temp, value);
|
||||
ss.append(temp, p - temp);
|
||||
}
|
||||
else if constexpr (char_v<T>) {
|
||||
ss.push_back(value);
|
||||
}
|
||||
else if constexpr (bool_v<T>) {
|
||||
ss.append(value ? "true" : "false");
|
||||
}
|
||||
else if constexpr (enum_v<T>) {
|
||||
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
|
||||
if constexpr (bool_v<decltype(enum_to_str)>) {
|
||||
render_value(ss, static_cast<std::underlying_type_t<T>>(value));
|
||||
}
|
||||
else {
|
||||
auto it = enum_to_str.find(value);
|
||||
if (it != enum_to_str.end())
|
||||
IGUANA_LIKELY {
|
||||
auto str = it->second;
|
||||
render_value(ss, std::string_view(str.data(), str.size()));
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error(
|
||||
std::to_string(static_cast<std::underlying_type_t<T>>(value)) +
|
||||
" is a missing value in enum_value");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
static_assert(!sizeof(T), "type is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
template <bool escape_quote_apos = false, typename Stream, typename T,
|
||||
std::enable_if_t<is_pb_type_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_value(Stream &ss, const T &value) {
|
||||
render_value<escape_quote_apos>(ss, value);
|
||||
}
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<map_container_v<T>, int> = 0>
|
||||
inline void render_xml_attr(Stream &ss, const T &value, std::string_view name) {
|
||||
if constexpr (pretty) {
|
||||
ss.append(spaces, '\t');
|
||||
}
|
||||
ss.push_back('<');
|
||||
ss.append(name.data(), name.size());
|
||||
for (auto [k, v] : value) {
|
||||
ss.push_back(' ');
|
||||
render_value(ss, k);
|
||||
ss.push_back('=');
|
||||
ss.push_back(XML_ATTR_DELIMITER);
|
||||
render_value<true>(ss, v);
|
||||
ss.push_back(XML_ATTR_DELIMITER);
|
||||
}
|
||||
ss.push_back('>');
|
||||
}
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<plain_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
|
||||
std::string_view name) {
|
||||
render_value(ss, value);
|
||||
render_tail<pretty, 0>(ss, name);
|
||||
}
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<is_pb_type_v<std::decay_t<T>>, int> = 0>
|
||||
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
|
||||
std::string_view name) {
|
||||
render_xml_value<pretty, spaces>(ss, value.val, name);
|
||||
}
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<variant_v<std::decay_t<T>>, int> = 0>
|
||||
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
|
||||
std::string_view name) {
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<optional_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
|
||||
std::string_view name) {
|
||||
if (value) {
|
||||
render_xml_value<pretty, spaces>(ss, *value, name);
|
||||
}
|
||||
else {
|
||||
render_tail<pretty, 0>(ss, name);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<smart_ptr_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
|
||||
std::string_view name) {
|
||||
if (value) {
|
||||
render_xml_value<pretty, spaces>(ss, *value, name);
|
||||
}
|
||||
else {
|
||||
render_tail<pretty, 0>(ss, name);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<attr_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
|
||||
std::string_view name) {
|
||||
render_xml_attr<pretty, spaces>(ss, value.attr(), name);
|
||||
render_xml_value<pretty, spaces>(ss, value.value(), name);
|
||||
}
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<sequence_container_v<T>, int>>
|
||||
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
|
||||
std::string_view name) {
|
||||
using value_type =
|
||||
underline_type_t<typename std::remove_cvref_t<T>::value_type>;
|
||||
for (const auto &v : value) {
|
||||
if constexpr (attr_v<value_type>) {
|
||||
render_xml_value<pretty, spaces>(ss, v, name);
|
||||
}
|
||||
else {
|
||||
render_head<pretty, spaces>(ss, name);
|
||||
render_xml_value<pretty, spaces>(ss, v, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<map_container_v<T>, int>>
|
||||
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
|
||||
std::string_view name) {
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<variant_v<T>, int>>
|
||||
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
|
||||
std::string_view name) {
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
|
||||
template <bool pretty, size_t spaces, typename Stream, typename T,
|
||||
std::enable_if_t<ylt_refletable_v<T>, int>>
|
||||
IGUANA_INLINE void render_xml_value(Stream &ss, T &&t, std::string_view name) {
|
||||
if constexpr (pretty) {
|
||||
ss.push_back('\n');
|
||||
}
|
||||
ylt::reflection::for_each(t, [&](auto &field, auto tag_name, auto index) {
|
||||
using value_type = underline_type_t<decltype(field)>;
|
||||
static_assert(!std::is_pointer_v<value_type>,
|
||||
"don't use raw pointer, please use smart pointer");
|
||||
if constexpr (sequence_container_v<value_type>) {
|
||||
render_xml_value<pretty, spaces + 1>(ss, field, tag_name);
|
||||
}
|
||||
else if constexpr (attr_v<value_type>) {
|
||||
render_xml_value<pretty, spaces + 1>(ss, field, tag_name);
|
||||
}
|
||||
else if constexpr (cdata_v<value_type>) {
|
||||
if constexpr (pretty) {
|
||||
ss.append(spaces + 1, '\t');
|
||||
ss.append("<![CDATA[").append(field.value()).append("]]>\n");
|
||||
}
|
||||
else {
|
||||
ss.append("<![CDATA[").append(field.value()).append("]]>");
|
||||
}
|
||||
}
|
||||
else {
|
||||
render_head<pretty, spaces + 1>(ss, tag_name);
|
||||
render_xml_value<pretty, spaces + 1>(ss, field, tag_name);
|
||||
}
|
||||
});
|
||||
render_tail<pretty, spaces>(ss, name);
|
||||
}
|
||||
|
||||
template <bool pretty = false, typename Stream, typename T,
|
||||
std::enable_if_t<attr_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_xml(T &&t, Stream &s) {
|
||||
using value_type = typename std::decay_t<T>::value_type;
|
||||
static_assert(ylt_refletable_v<value_type>, "value_type must be refletable");
|
||||
constexpr std::string_view root_name =
|
||||
ylt::reflection::get_struct_name<value_type>();
|
||||
render_xml_value<pretty, 0>(s, std::forward<T>(t), root_name);
|
||||
}
|
||||
|
||||
template <bool pretty = false, typename Stream, typename T,
|
||||
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_xml(T &&t, Stream &s) {
|
||||
constexpr std::string_view root_name =
|
||||
ylt::reflection::get_struct_name<ylt::reflection::remove_cvref_t<T>>();
|
||||
render_head<pretty, 0>(s, root_name);
|
||||
render_xml_value<pretty, 0>(s, std::forward<T>(t), root_name);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void to_xml_adl(iguana_adl_t *p, T &t, std::string &pb_str) {
|
||||
to_xml(t, pb_str);
|
||||
}
|
||||
|
||||
} // namespace iguana
|
||||
+679
@@ -0,0 +1,679 @@
|
||||
#pragma once
|
||||
|
||||
#include <charconv>
|
||||
|
||||
#include "detail/charconv.h"
|
||||
#include "detail/utf.hpp"
|
||||
#include "yaml_util.hpp"
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
#include "ylt/reflection/reflect26_dispatch.hpp"
|
||||
#endif
|
||||
|
||||
namespace iguana {
|
||||
|
||||
template <typename T, typename It,
|
||||
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end,
|
||||
size_t min_spaces = 0);
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename U, typename It,
|
||||
std::enable_if_t<yaml_not_support_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
|
||||
size_t min_spaces) {
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
|
||||
template <typename U, class It,
|
||||
std::enable_if_t<yaml_not_support_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_value(U &value, It &&value_begin,
|
||||
It &&value_end) {
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<string_v<U>, int> = 0>
|
||||
IGUANA_INLINE void parse_escape_str(U &value, It &&it, It &&end) {
|
||||
auto start = it;
|
||||
value.clear();
|
||||
while (it != end) {
|
||||
if (*(it++) == '\\' && it != end)
|
||||
IGUANA_UNLIKELY {
|
||||
if (*it == 'u') {
|
||||
value.append(&*start,
|
||||
static_cast<size_t>(std::distance(start, it) - 1));
|
||||
++it;
|
||||
if (std::distance(it, end) < 4)
|
||||
IGUANA_UNLIKELY
|
||||
throw std::runtime_error(R"(Expected 4 hexadecimal digits)");
|
||||
auto code_point = parse_unicode_hex4(it);
|
||||
encode_utf8(value, code_point);
|
||||
start = it;
|
||||
}
|
||||
else if (*it == 'n') {
|
||||
value.append(&*start,
|
||||
static_cast<size_t>(std::distance(start, it) - 1));
|
||||
start = ++it;
|
||||
value.push_back('\n');
|
||||
}
|
||||
else if (*it == 't') {
|
||||
value.append(&*start,
|
||||
static_cast<size_t>(std::distance(start, it) - 1));
|
||||
start = ++it;
|
||||
value.push_back('\t');
|
||||
}
|
||||
else if (*it == 'r') {
|
||||
value.append(&*start,
|
||||
static_cast<size_t>(std::distance(start, it) - 1));
|
||||
start = ++it;
|
||||
value.push_back('\r');
|
||||
}
|
||||
else if (*it == 'b') {
|
||||
value.append(&*start,
|
||||
static_cast<size_t>(std::distance(start, it) - 1));
|
||||
start = ++it;
|
||||
value.push_back('\b');
|
||||
}
|
||||
else if (*it == 'f') {
|
||||
value.append(&*start,
|
||||
static_cast<size_t>(std::distance(start, it) - 1));
|
||||
start = ++it;
|
||||
value.push_back('\f');
|
||||
}
|
||||
}
|
||||
}
|
||||
value.append(&*start, static_cast<size_t>(std::distance(start, end)));
|
||||
}
|
||||
|
||||
// use '-' here to simply represent '>-'
|
||||
template <char block_type, typename U, typename It,
|
||||
std::enable_if_t<string_v<U>, int> = 0>
|
||||
IGUANA_INLINE void parse_block_str(U &value, It &&it, It &&end,
|
||||
size_t min_spaces) {
|
||||
auto spaces = skip_space_and_lines<false>(it, end, min_spaces);
|
||||
if (spaces < min_spaces)
|
||||
IGUANA_UNLIKELY {
|
||||
// back to the end of the previous line
|
||||
if constexpr (block_type == '|' || block_type == '>') {
|
||||
value.push_back('\n');
|
||||
}
|
||||
it -= spaces + 1;
|
||||
return;
|
||||
}
|
||||
while (it != end) {
|
||||
auto start = it;
|
||||
auto value_end = skip_till_newline(it, end);
|
||||
value.append(&*start, static_cast<size_t>(std::distance(start, value_end)));
|
||||
spaces = skip_space_and_lines<false>(it, end, min_spaces);
|
||||
if ((spaces < min_spaces) || (it == end))
|
||||
IGUANA_UNLIKELY {
|
||||
if constexpr (block_type == '|' || block_type == '>') {
|
||||
value.push_back('\n');
|
||||
}
|
||||
it -= spaces + 1;
|
||||
return;
|
||||
}
|
||||
if constexpr (block_type == '|') {
|
||||
value.push_back('\n');
|
||||
}
|
||||
else if constexpr (block_type == '-' || block_type == '>') {
|
||||
value.push_back(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It,
|
||||
std::enable_if_t<map_container_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
|
||||
size_t min_spaces);
|
||||
|
||||
template <typename U, class It, std::enable_if_t<num_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_value(U &value, It &&value_begin,
|
||||
It &&value_end) {
|
||||
if (value_begin == value_end)
|
||||
IGUANA_UNLIKELY { return; }
|
||||
auto size = std::distance(value_begin, value_end);
|
||||
const auto start = &*value_begin;
|
||||
detail::from_chars(start, start + size, value);
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<is_pb_type_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_value(U &value, It &&value_begin,
|
||||
It &&value_end) {
|
||||
yaml_parse_value(value.val, value_begin, value_end);
|
||||
}
|
||||
|
||||
// string_view should be used for string with ' " ?
|
||||
template <typename U, typename It,
|
||||
std::enable_if_t<string_container_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_value(U &value, It value_begin, It value_end) {
|
||||
using T = std::decay_t<U>;
|
||||
auto end = value_end;
|
||||
|
||||
auto handle_quoted = [&](char quote_char, const char *error_msg) {
|
||||
// Check minimum length: at least 2 characters (opening and closing quote)
|
||||
if (std::distance(value_begin, value_end) < 2)
|
||||
IGUANA_UNLIKELY { throw std::runtime_error(error_msg); }
|
||||
++value_begin;
|
||||
--value_end;
|
||||
if (*value_end != quote_char)
|
||||
IGUANA_UNLIKELY {
|
||||
// Search for closing quote (may be followed by comment)
|
||||
auto it = value_begin;
|
||||
while (it != value_end && *it != quote_char) {
|
||||
++it;
|
||||
}
|
||||
if (it == value_end)
|
||||
IGUANA_UNLIKELY { throw std::runtime_error(error_msg); }
|
||||
end = it++;
|
||||
// skip spaces
|
||||
if (!skip_space_till_end(it, value_end)) {
|
||||
// Should be comment
|
||||
if (*it != '#')
|
||||
IGUANA_UNLIKELY { throw std::runtime_error(error_msg); }
|
||||
}
|
||||
}
|
||||
else {
|
||||
end = value_end;
|
||||
}
|
||||
};
|
||||
|
||||
if (*value_begin == '"') {
|
||||
handle_quoted('"', R"(Expected closing ")");
|
||||
if constexpr (string_v<T>) {
|
||||
parse_escape_str(value, value_begin, end);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (*value_begin == '\'') {
|
||||
handle_quoted('\'', R"(Expected closing ')");
|
||||
}
|
||||
value =
|
||||
T(&*value_begin, static_cast<size_t>(std::distance(value_begin, end)));
|
||||
if ((value == "~") || (value == "null")) {
|
||||
value = T{};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<char_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_value(U &value, It &&value_begin,
|
||||
It &&value_end) {
|
||||
if (static_cast<size_t>(std::distance(value_begin, value_end)) != 1)
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Expected one character"); }
|
||||
value = *value_begin;
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<enum_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_value(U &value, It &&value_begin,
|
||||
It &&value_end) {
|
||||
static constexpr auto str_to_enum = get_enum_map<true, std::decay_t<U>>();
|
||||
if constexpr (bool_v<decltype(str_to_enum)>) {
|
||||
// not defined a specialization template
|
||||
using T = std::underlying_type_t<std::decay_t<U>>;
|
||||
yaml_parse_value(reinterpret_cast<T &>(value), value_begin, value_end);
|
||||
}
|
||||
else {
|
||||
auto enum_names = std::string_view(
|
||||
&*value_begin,
|
||||
static_cast<size_t>(std::distance(value_begin, value_end)));
|
||||
auto it = str_to_enum.find(enum_names);
|
||||
if (it != str_to_enum.end())
|
||||
IGUANA_LIKELY { value = it->second; }
|
||||
else {
|
||||
throw std::runtime_error(std::string(enum_names) +
|
||||
" missing corresponding value in enum_value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<bool_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_value(U &&value, It &&value_begin,
|
||||
It &&value_end) {
|
||||
auto bool_v = std::string_view(
|
||||
&*value_begin,
|
||||
static_cast<size_t>(std::distance(value_begin, value_end)));
|
||||
if (bool_v == "true") {
|
||||
value = true;
|
||||
}
|
||||
else if (bool_v == "false") {
|
||||
value = false;
|
||||
}
|
||||
else
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Expected true or false"); }
|
||||
}
|
||||
|
||||
template <typename U, typename It,
|
||||
std::enable_if_t<ylt_refletable_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
|
||||
size_t min_spaces) {
|
||||
from_yaml(value, it, end, min_spaces);
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<plain_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
|
||||
size_t min_spaces) {
|
||||
using T = std::decay_t<U>;
|
||||
if constexpr (string_v<T>) {
|
||||
if (skip_space_till_end(it, end))
|
||||
IGUANA_UNLIKELY { return; }
|
||||
if (*it == '|') {
|
||||
++it;
|
||||
parse_block_str<'|'>(value, it, end, min_spaces);
|
||||
}
|
||||
else if (*it == '>') {
|
||||
++it;
|
||||
if (*it == '-') {
|
||||
++it;
|
||||
parse_block_str<'-'>(value, it, end, min_spaces);
|
||||
}
|
||||
else {
|
||||
parse_block_str<'>'>(value, it, end, min_spaces);
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto indent = skip_space_and_lines<false>(it, end, min_spaces);
|
||||
// in case of a string field it is valid for the field to have no content
|
||||
if (indent < min_spaces) {
|
||||
value = "";
|
||||
}
|
||||
else {
|
||||
auto start = it;
|
||||
auto value_end = skip_till_newline(it, end);
|
||||
yaml_parse_value(value, start, value_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
skip_space_and_lines(it, end, min_spaces);
|
||||
auto start = it;
|
||||
auto value_end = skip_till_newline(it, end);
|
||||
yaml_parse_value(value, start, value_end);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<is_pb_type_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
|
||||
size_t min_spaces) {
|
||||
yaml_parse_item(value.val, it, end, min_spaces);
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<optional_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
|
||||
size_t min_spaces);
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
|
||||
size_t min_spaces);
|
||||
|
||||
// minspaces : The minimum indentation
|
||||
template <typename U, typename It,
|
||||
std::enable_if_t<sequence_container_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
|
||||
size_t min_spaces) {
|
||||
value.clear();
|
||||
auto spaces = skip_space_and_lines(it, end, min_spaces);
|
||||
using value_type = typename std::remove_cv_t<U>::value_type;
|
||||
if (*it == '[') {
|
||||
++it;
|
||||
skip_space_and_lines(it, end, min_spaces);
|
||||
while (it != end) {
|
||||
if (*it == ']')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
if constexpr (plain_v<value_type>) {
|
||||
auto start = it;
|
||||
auto value_end = yaml_skip_till<',', ']'>(it, end);
|
||||
yaml_parse_value(value.emplace_back(), start, value_end);
|
||||
if (*(it - 1) == ']')
|
||||
IGUANA_UNLIKELY { return; }
|
||||
}
|
||||
else {
|
||||
yaml_parse_item(value.emplace_back(), it, end, spaces + 1);
|
||||
skip_space_and_lines(it, end, min_spaces);
|
||||
if (*it == ',')
|
||||
IGUANA_LIKELY { ++it; }
|
||||
}
|
||||
skip_space_and_lines(it, end, min_spaces);
|
||||
}
|
||||
}
|
||||
else if (*it == '-') {
|
||||
while (it != end) {
|
||||
auto subspaces = skip_space_and_lines<false>(it, end, spaces);
|
||||
if (subspaces < spaces)
|
||||
IGUANA_UNLIKELY {
|
||||
it -= subspaces + 1;
|
||||
return;
|
||||
}
|
||||
if (it == end)
|
||||
IGUANA_UNLIKELY
|
||||
return;
|
||||
match<'-'>(it, end);
|
||||
// space + 1 because of the skipped '-'
|
||||
subspaces = skip_space_and_lines(it, end, spaces + 1);
|
||||
if constexpr (string_v<value_type>) {
|
||||
// except string_v because of scalar blocks
|
||||
yaml_parse_item(value.emplace_back(), it, end, spaces + 1);
|
||||
}
|
||||
else if constexpr (plain_v<value_type>) {
|
||||
auto start = it;
|
||||
auto value_end = skip_till_newline(it, end);
|
||||
yaml_parse_value(value.emplace_back(), start, value_end);
|
||||
}
|
||||
else {
|
||||
yaml_parse_item(value.emplace_back(), it, end, subspaces);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Expected ']' or '-'"); }
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<tuple_v<U>, int> = 0>
|
||||
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
|
||||
size_t min_spaces) {
|
||||
auto spaces = skip_space_and_lines(it, end, min_spaces);
|
||||
if (*it == '[') {
|
||||
++it;
|
||||
skip_space_and_lines(it, end, min_spaces);
|
||||
for_each_tuple(
|
||||
[&](auto &v) IGUANA__INLINE_LAMBDA {
|
||||
using value_type = std::decay_t<decltype(v)>;
|
||||
skip_space_and_lines(it, end, min_spaces);
|
||||
if constexpr (plain_v<value_type>) {
|
||||
auto start = it;
|
||||
auto value_end = yaml_skip_till<',', ']'>(it, end);
|
||||
yaml_parse_value(v, start, value_end);
|
||||
}
|
||||
else {
|
||||
yaml_parse_item(v, it, end, spaces + 1);
|
||||
skip_space_and_lines(it, end, min_spaces);
|
||||
++it; // skip ,
|
||||
}
|
||||
},
|
||||
value);
|
||||
}
|
||||
else if (*it == '-') {
|
||||
for_each_tuple(
|
||||
[&](auto &v) IGUANA__INLINE_LAMBDA {
|
||||
// we don't need to determine when it will end
|
||||
// because the user decides how many items there are
|
||||
// the following is similar to sequence_t
|
||||
using value_type = std::decay_t<decltype(v)>;
|
||||
skip_space_and_lines(it, end, spaces);
|
||||
match<'-'>(it, end);
|
||||
[[maybe_unused]] auto subspaces =
|
||||
skip_space_and_lines(it, end, spaces + 1);
|
||||
if constexpr (string_v<value_type>) {
|
||||
yaml_parse_item(v, it, end, spaces + 1);
|
||||
}
|
||||
else if constexpr (plain_v<value_type>) {
|
||||
auto start = it;
|
||||
auto value_end = skip_till_newline(it, end);
|
||||
yaml_parse_value(v, start, value_end);
|
||||
}
|
||||
else {
|
||||
yaml_parse_item(v, it, end, subspaces);
|
||||
}
|
||||
},
|
||||
value);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Expected ']' or '-'");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<map_container_v<U>, int>>
|
||||
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
|
||||
size_t min_spaces) {
|
||||
using T = std::remove_reference_t<U>;
|
||||
using key_type = typename T::key_type;
|
||||
using value_type = typename T::mapped_type;
|
||||
[[maybe_unused]] auto spaces = skip_space_and_lines(it, end, min_spaces);
|
||||
if (*it == '{') {
|
||||
++it;
|
||||
[[maybe_unused]] auto subspaces = skip_space_and_lines(it, end, min_spaces);
|
||||
while (it != end) {
|
||||
if (*it == '}')
|
||||
IGUANA_UNLIKELY {
|
||||
++it;
|
||||
return;
|
||||
}
|
||||
auto start = it;
|
||||
auto value_end = yaml_skip_till<':'>(it, end);
|
||||
key_type key;
|
||||
yaml_parse_value(key, start, value_end);
|
||||
subspaces = skip_space_and_lines(it, end, min_spaces);
|
||||
if constexpr (plain_v<value_type>) {
|
||||
start = it;
|
||||
value_end = yaml_skip_till<',', '}'>(it, end);
|
||||
yaml_parse_value(value[key], start, value_end);
|
||||
if (*(it - 1) == '}')
|
||||
IGUANA_UNLIKELY { return; }
|
||||
}
|
||||
else {
|
||||
yaml_parse_item(value[key], it, end, min_spaces);
|
||||
subspaces = skip_space_and_lines(it, end, min_spaces);
|
||||
if (*it == ',')
|
||||
IGUANA_LIKELY { ++it; }
|
||||
}
|
||||
subspaces = skip_space_and_lines(it, end, min_spaces);
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto subspaces = skip_space_and_lines<false>(it, end, min_spaces);
|
||||
while (it != end) {
|
||||
if (subspaces < min_spaces)
|
||||
IGUANA_UNLIKELY {
|
||||
it -= subspaces + 1;
|
||||
return;
|
||||
}
|
||||
auto start = it;
|
||||
auto value_end = yaml_skip_till<':'>(it, end);
|
||||
key_type key;
|
||||
yaml_parse_value(key, start, value_end);
|
||||
subspaces = skip_space_and_lines(it, end, min_spaces);
|
||||
if constexpr (plain_v<value_type> && !string_v<value_type>) {
|
||||
start = it;
|
||||
value_end = skip_till_newline(it, end);
|
||||
yaml_parse_value(value[key], start, value_end);
|
||||
}
|
||||
else {
|
||||
yaml_parse_item(value[key], it, end, spaces + 1);
|
||||
}
|
||||
subspaces = skip_space_and_lines<false>(it, end, min_spaces);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int>>
|
||||
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
|
||||
size_t min_spaces) {
|
||||
using T = std::remove_reference_t<U>;
|
||||
if constexpr (unique_ptr_v<T>) {
|
||||
value = std::make_unique<typename T::element_type>();
|
||||
}
|
||||
else {
|
||||
value = std::make_shared<typename T::element_type>();
|
||||
}
|
||||
static_assert(!string_v<typename T::element_type>,
|
||||
"smart_ptr<string> is not allowed");
|
||||
yaml_parse_item(*value, it, end, min_spaces);
|
||||
}
|
||||
|
||||
template <typename U, typename It, std::enable_if_t<optional_v<U>, int>>
|
||||
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
|
||||
size_t min_spaces) {
|
||||
using T = std::remove_reference_t<U>;
|
||||
using value_type = typename T::value_type;
|
||||
auto spaces = skip_space_and_lines<false>(it, end, min_spaces);
|
||||
if (it == end)
|
||||
IGUANA_UNLIKELY { return; }
|
||||
if (spaces < min_spaces)
|
||||
IGUANA_UNLIKELY {
|
||||
it -= spaces + 1;
|
||||
return;
|
||||
}
|
||||
auto start = it;
|
||||
std::decay_t<It> value_end = end;
|
||||
if (*it == 'n') {
|
||||
value_end = skip_till_newline(it, end);
|
||||
auto opt_v = std::string_view(
|
||||
&*start, static_cast<size_t>(std::distance(start, value_end)));
|
||||
if (opt_v == "null") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if constexpr (string_v<value_type> || !plain_v<value_type>) {
|
||||
it = start;
|
||||
yaml_parse_item(value.emplace(), it, end, min_spaces);
|
||||
}
|
||||
else {
|
||||
if (value_end == end)
|
||||
IGUANA_LIKELY {
|
||||
// if value_end isn't touched
|
||||
value_end = skip_till_newline(it, end);
|
||||
}
|
||||
yaml_parse_value(value.emplace(), start, value_end);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
IGUANA_INLINE void skip_object_value(It &&it, It &&end, size_t min_spaces) {
|
||||
size_t subspace = min_spaces;
|
||||
while (it != end) {
|
||||
while (it != end && *it != '\n') {
|
||||
++it;
|
||||
}
|
||||
// here : it == '\n'
|
||||
subspace = 0;
|
||||
if (it != end)
|
||||
IGUANA_LIKELY { ++it; }
|
||||
while (it != end && *(it++) == ' ') {
|
||||
++subspace;
|
||||
}
|
||||
if (subspace < min_spaces) {
|
||||
it -= subspace + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
template <typename T, typename It, std::enable_if_t<ylt_refletable_v<T>, int>>
|
||||
IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end, size_t min_spaces) {
|
||||
auto spaces = skip_space_and_lines(it, end, min_spaces);
|
||||
while (it != end) {
|
||||
auto start = it;
|
||||
auto keyend = yaml_skip_till<':'>(it, end);
|
||||
std::string_view key = std::string_view{
|
||||
&*start, static_cast<size_t>(std::distance(start, keyend))};
|
||||
|
||||
bool found = ylt::reflection::reflect26::dispatch_by_name(
|
||||
value, key, [&](auto &field) IGUANA__INLINE_LAMBDA {
|
||||
detail::yaml_parse_item(field, it, end, spaces + 1);
|
||||
});
|
||||
if (!found)
|
||||
IGUANA_UNLIKELY {
|
||||
#ifdef THROW_UNKNOWN_KEY
|
||||
throw std::runtime_error("Unknown key: " + std::string(key));
|
||||
#else
|
||||
detail::skip_object_value(it, end, spaces + 1);
|
||||
#endif
|
||||
}
|
||||
auto subspaces = skip_space_and_lines<false>(it, end, min_spaces);
|
||||
if (subspaces < min_spaces)
|
||||
IGUANA_UNLIKELY {
|
||||
it -= subspaces + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
template <typename T, typename It, std::enable_if_t<ylt_refletable_v<T>, int>>
|
||||
IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end, size_t min_spaces) {
|
||||
auto spaces = skip_space_and_lines(it, end, min_spaces);
|
||||
while (it != end) {
|
||||
auto start = it;
|
||||
auto keyend = yaml_skip_till<':'>(it, end);
|
||||
std::string_view key = std::string_view{
|
||||
&*start, static_cast<size_t>(std::distance(start, keyend))};
|
||||
static auto frozen_map = ylt::reflection::get_variant_map<T>();
|
||||
|
||||
if constexpr (frozen_map.size() > 0) {
|
||||
const auto &member_it = frozen_map.find(key);
|
||||
if (member_it != frozen_map.end())
|
||||
IGUANA_LIKELY {
|
||||
std::visit(
|
||||
[&](auto offset) IGUANA__INLINE_LAMBDA {
|
||||
using value_type = typename decltype(offset)::type;
|
||||
auto member_ptr =
|
||||
(value_type *)((char *)(&value) + offset.value);
|
||||
detail::yaml_parse_item(*member_ptr, it, end, spaces + 1);
|
||||
},
|
||||
member_it->second);
|
||||
}
|
||||
else
|
||||
IGUANA_UNLIKELY {
|
||||
#ifdef THROW_UNKNOWN_KEY
|
||||
throw std::runtime_error("Unknown key: " + std::string(key));
|
||||
#else
|
||||
detail::skip_object_value(it, end, spaces + 1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
auto subspaces = skip_space_and_lines<false>(it, end, min_spaces);
|
||||
if (subspaces < min_spaces)
|
||||
IGUANA_UNLIKELY {
|
||||
it -= subspaces + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !YLT_USE_CXX26_REFLECTION
|
||||
|
||||
template <typename T, typename It,
|
||||
std::enable_if_t<non_ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end) {
|
||||
detail::yaml_parse_item(value, it, end, 0);
|
||||
}
|
||||
|
||||
template <typename T, typename It>
|
||||
IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end,
|
||||
std::error_code &ec) noexcept {
|
||||
try {
|
||||
from_yaml(value, it, end);
|
||||
ec = {};
|
||||
} catch (std::runtime_error &e) {
|
||||
ec = iguana::make_error_code(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename View,
|
||||
std::enable_if_t<string_container_v<View>, int> = 0>
|
||||
IGUANA_INLINE void from_yaml(T &value, const View &view) {
|
||||
from_yaml(value, std::begin(view), std::end(view));
|
||||
}
|
||||
|
||||
template <typename T, typename View,
|
||||
std::enable_if_t<string_container_v<View>, int> = 0>
|
||||
IGUANA_INLINE void from_yaml(T &value, const View &view,
|
||||
std::error_code &ec) noexcept {
|
||||
try {
|
||||
from_yaml(value, std::begin(view), std::end(view));
|
||||
ec = {};
|
||||
} catch (std::runtime_error &e) {
|
||||
ec = iguana::make_error_code(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void from_yaml_adl(iguana_adl_t *p, T &t,
|
||||
std::string_view pb_str) {
|
||||
iguana::from_yaml(t, pb_str);
|
||||
}
|
||||
|
||||
} // namespace iguana
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
#include "detail/pb_type.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
namespace iguana {
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool yaml_not_support_v = variant_v<T>;
|
||||
|
||||
// skip non-linebreak whitespaces return true when it==end
|
||||
template <typename It>
|
||||
IGUANA_INLINE bool skip_space_till_end(It &&it, It &&end) {
|
||||
while (it != end && (*it == ' ' || *it == '\t')) ++it;
|
||||
return it == end;
|
||||
}
|
||||
|
||||
// will not skip '\n'
|
||||
template <typename It>
|
||||
IGUANA_INLINE auto skip_till_newline(It &&it, It &&end) {
|
||||
if (it == end)
|
||||
IGUANA_UNLIKELY { return it; }
|
||||
std::decay_t<decltype(it)> res = it;
|
||||
while ((it != end) && (*it != '\n')) {
|
||||
if (*it == ' ')
|
||||
IGUANA_UNLIKELY {
|
||||
res = it;
|
||||
while (it != end && *it == ' ') ++it;
|
||||
}
|
||||
else if (*it == '#')
|
||||
IGUANA_UNLIKELY {
|
||||
if (*(it - 1) == ' ') {
|
||||
// it - 1 should be legal because this func is for parse value
|
||||
while ((it != end) && *it != '\n') {
|
||||
++it;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
else
|
||||
IGUANA_LIKELY { ++it; }
|
||||
}
|
||||
return (*(it - 1) == ' ') ? res : it;
|
||||
}
|
||||
|
||||
template <char... C, typename It>
|
||||
IGUANA_INLINE auto yaml_skip_till(It &&it, It &&end) {
|
||||
if (it == end)
|
||||
IGUANA_UNLIKELY { return it; }
|
||||
std::decay_t<decltype(it)> start = it;
|
||||
std::decay_t<decltype(it)> res = it;
|
||||
while ((it != end) && (!((... || (*it == C))))) {
|
||||
if (*it == '\n')
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("\\n is not expected"); }
|
||||
else if (*it == ' ' || *it == '\t')
|
||||
IGUANA_UNLIKELY {
|
||||
res = it;
|
||||
while (it != end && (*it == ' ' || *it == '\t')) ++it;
|
||||
continue;
|
||||
}
|
||||
else if (*it == '#')
|
||||
IGUANA_UNLIKELY {
|
||||
// Check if it - 1 is valid before accessing
|
||||
if (it > start && (*(it - 1) == ' ' || *(it - 1) == '\t'))
|
||||
IGUANA_UNLIKELY {
|
||||
while ((it != end) && *it != '\n') {
|
||||
++it;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
res = ++it;
|
||||
}
|
||||
|
||||
if (it == end)
|
||||
IGUANA_UNLIKELY {
|
||||
static constexpr char b[] = {C..., '\0'};
|
||||
std::string error = std::string("Expected one of these: ").append(b);
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
++it; // skip
|
||||
return res;
|
||||
}
|
||||
|
||||
// If there are '\n' , return indentation
|
||||
// If not, return minspaces + space
|
||||
// If Throw == true, check res < minspaces
|
||||
template <bool Throw = true, typename It>
|
||||
IGUANA_INLINE size_t skip_space_and_lines(It &&it, It &&end, size_t minspaces) {
|
||||
size_t res = minspaces;
|
||||
while (it != end) {
|
||||
if (*it == '\n') {
|
||||
++it;
|
||||
res = 0;
|
||||
auto start = it;
|
||||
// skip the --- line
|
||||
if ((it != end) && (*it == '-'))
|
||||
IGUANA_UNLIKELY {
|
||||
auto line_end = yaml_skip_till<false, '\n'>(it, end);
|
||||
auto line = std::string_view(
|
||||
&*start, static_cast<size_t>(std::distance(start, line_end)));
|
||||
if (line != "---") {
|
||||
it = start;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (*it == ' ' || *it == '\t') {
|
||||
++it;
|
||||
++res;
|
||||
}
|
||||
else if (*it == '#') {
|
||||
while (it != end && *it != '\n') {
|
||||
++it;
|
||||
}
|
||||
res = 0;
|
||||
}
|
||||
else {
|
||||
if constexpr (Throw) {
|
||||
if (res < minspaces)
|
||||
IGUANA_UNLIKELY { throw std::runtime_error("Indentation problem"); }
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return res; // throw in certain situations ?
|
||||
}
|
||||
|
||||
} // namespace iguana
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
#pragma once
|
||||
|
||||
#include "detail/charconv.h"
|
||||
#include "yaml_util.hpp"
|
||||
|
||||
namespace iguana {
|
||||
|
||||
template <bool Is_writing_escape = false, typename Stream, typename T,
|
||||
std::enable_if_t<yaml_not_support_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces = 0) {
|
||||
throw std::bad_function_call();
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape = false, typename Stream, typename T,
|
||||
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_yaml(T &&t, Stream &s, size_t min_spaces = 0);
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces) {
|
||||
ss.push_back('\n');
|
||||
to_yaml<Is_writing_escape>(std::forward<T>(t), ss, min_spaces);
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, bool appendLf = true, typename Stream,
|
||||
typename T, std::enable_if_t<string_container_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces) {
|
||||
if constexpr (Is_writing_escape) {
|
||||
ss.push_back('"');
|
||||
write_string_with_escape(t.data(), t.size(), ss);
|
||||
ss.push_back('"');
|
||||
}
|
||||
else if (t.size() == 0) {
|
||||
ss.push_back('~');
|
||||
}
|
||||
else {
|
||||
ss.append(t.data(), t.size());
|
||||
}
|
||||
if constexpr (appendLf)
|
||||
ss.push_back('\n');
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, bool appendLf = true, typename Stream,
|
||||
typename T, std::enable_if_t<num_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, T value, size_t min_spaces) {
|
||||
char temp[65];
|
||||
auto p = detail::to_chars(temp, value);
|
||||
ss.append(temp, p - temp);
|
||||
if constexpr (appendLf)
|
||||
ss.push_back('\n');
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, bool appendLf = true, typename Stream,
|
||||
typename T, std::enable_if_t<is_pb_type_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, T value, size_t min_spaces) {
|
||||
render_yaml_value<Is_writing_escape>(ss, value.val, min_spaces);
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, bool appendLf = true, typename Stream>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, char value,
|
||||
size_t min_spaces) {
|
||||
ss.push_back(value);
|
||||
if constexpr (appendLf)
|
||||
ss.push_back('\n');
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, bool appendLf = true, typename Stream>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, bool value,
|
||||
size_t min_spaces) {
|
||||
ss.append(value ? "true" : "false");
|
||||
if constexpr (appendLf)
|
||||
ss.push_back('\n');
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, bool appendLf = true, typename Stream,
|
||||
typename T, std::enable_if_t<enum_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, T value, size_t min_spaces) {
|
||||
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
|
||||
if constexpr (bool_v<decltype(enum_to_str)>) {
|
||||
render_yaml_value<Is_writing_escape>(
|
||||
ss, static_cast<std::underlying_type_t<T>>(value), min_spaces);
|
||||
}
|
||||
else {
|
||||
auto it = enum_to_str.find(value);
|
||||
if (it != enum_to_str.end())
|
||||
IGUANA_LIKELY {
|
||||
auto str = it->second;
|
||||
render_yaml_value<Is_writing_escape>(
|
||||
ss, std::string_view(str.data(), str.size()), min_spaces);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error(
|
||||
std::to_string(static_cast<std::underlying_type_t<T>>(value)) +
|
||||
" is a missing value in enum_value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<optional_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val,
|
||||
size_t min_spaces);
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<smart_ptr_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val,
|
||||
size_t min_spaces);
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<sequence_container_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &t,
|
||||
size_t min_spaces) {
|
||||
if (t.empty()) {
|
||||
ss.append("[]");
|
||||
ss.push_back('\n');
|
||||
}
|
||||
else {
|
||||
ss.push_back('\n');
|
||||
for (const auto &v : t) {
|
||||
ss.append(min_spaces, ' ');
|
||||
ss.append("- ");
|
||||
render_yaml_value<Is_writing_escape>(ss, v, min_spaces + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<tuple_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces) {
|
||||
ss.push_back('\n');
|
||||
for_each_tuple(
|
||||
[&ss, min_spaces](auto &v) IGUANA__INLINE_LAMBDA {
|
||||
ss.append(min_spaces, ' ');
|
||||
ss.append("- ");
|
||||
render_yaml_value<Is_writing_escape>(ss, v, min_spaces + 1);
|
||||
},
|
||||
std::forward<T>(t));
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<map_container_v<T>, int> = 0>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &t,
|
||||
size_t min_spaces) {
|
||||
ss.push_back('\n');
|
||||
for (const auto &[k, v] : t) {
|
||||
ss.append(min_spaces, ' ');
|
||||
render_yaml_value<false, false>(ss, k, 0); // key must be plaint type
|
||||
ss.append(": ");
|
||||
render_yaml_value<Is_writing_escape>(ss, v, min_spaces + 1);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<optional_v<T>, int>>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val,
|
||||
size_t min_spaces) {
|
||||
if (!val) {
|
||||
ss.append("null");
|
||||
ss.push_back('\n');
|
||||
}
|
||||
else {
|
||||
render_yaml_value<Is_writing_escape>(ss, *val, min_spaces);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<smart_ptr_v<T>, int>>
|
||||
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val,
|
||||
size_t min_spaces) {
|
||||
if (!val) {
|
||||
ss.push_back('\n');
|
||||
}
|
||||
else {
|
||||
render_yaml_value<Is_writing_escape>(ss, *val, min_spaces);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape, typename Stream, typename T,
|
||||
std::enable_if_t<ylt_refletable_v<T>, int>>
|
||||
IGUANA_INLINE void to_yaml(T &&t, Stream &s, size_t min_spaces) {
|
||||
ylt::reflection::for_each(t, [&](auto &field, auto field_name, auto index) {
|
||||
using field_type = std::remove_reference_t<decltype(field)>;
|
||||
static_assert(!std::is_pointer_v<field_type>,
|
||||
"don't use raw pointer, please use smart pointer");
|
||||
s.append(min_spaces, ' ');
|
||||
s.append(field_name);
|
||||
s.append(": ");
|
||||
if constexpr (!ylt_refletable_v<field_type>) {
|
||||
render_yaml_value<Is_writing_escape>(s, field, min_spaces + 1);
|
||||
}
|
||||
else {
|
||||
s.push_back('\n');
|
||||
to_yaml<Is_writing_escape>(field, s, min_spaces + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <bool Is_writing_escape = false, typename Stream, typename T,
|
||||
std::enable_if_t<non_ylt_refletable_v<T>, int> = 0>
|
||||
IGUANA_INLINE void to_yaml(T &&t, Stream &s) {
|
||||
if constexpr (tuple_v<T> || map_container_v<T> || sequence_container_v<T> ||
|
||||
optional_v<T> || smart_ptr_v<T>)
|
||||
render_yaml_value<Is_writing_escape>(s, std::forward<T>(t), 0);
|
||||
else
|
||||
static_assert(!sizeof(T), "don't suppport this type");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IGUANA_INLINE void to_yaml_adl(iguana_adl_t *p, const T &t,
|
||||
std::string &pb_str) {
|
||||
to_yaml(t, pb_str);
|
||||
}
|
||||
} // namespace iguana
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "common_macro.hpp"
|
||||
|
||||
#define YLT_MACRO_EXPAND(...) __VA_ARGS__
|
||||
|
||||
#define WRAP_ARGS0(w, o)
|
||||
#define WRAP_ARGS1(w, o, _1) w(o, _1)
|
||||
#include "generate/arg_list_macro_gen.hpp"
|
||||
|
||||
#define WRAP_ARGS_(w, object, ...) \
|
||||
YLT_CONCAT(WRAP_ARGS, YLT_ARG_COUNT(__VA_ARGS__)) \
|
||||
(w, object, ##__VA_ARGS__)
|
||||
#define WRAP_ARGS(w, object, ...) WRAP_ARGS_(w, object, ##__VA_ARGS__)
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#define YLT_CONCAT_(l, r) l##r
|
||||
|
||||
#define YLT_CONCAT(l, r) YLT_CONCAT_(l, r)
|
||||
|
||||
#define CONCAT_MEMBER(t, x) t.x
|
||||
|
||||
#define CONCAT_ADDR(T, x) &T::x
|
||||
|
||||
#define CONCAT_NAME(t, x) #x
|
||||
|
||||
namespace ylt::reflection {
|
||||
template <typename T>
|
||||
struct identity {};
|
||||
} // namespace ylt::reflection
|
||||
+342
@@ -0,0 +1,342 @@
|
||||
// clang-format off
|
||||
|
||||
/*The following boilerplate code was generated using a Python script:
|
||||
max_fields = 124
|
||||
macro = "#define WRAP_ARGS"
|
||||
with open("generated_args.txt", "w", encoding="utf-8") as codefile:
|
||||
codefile.write("#ifdef _MSC_VER // Microsoft compilers\n\n")
|
||||
codefile.write("#define __NARGS(")
|
||||
codefile.write(
|
||||
",".join(['_{}'.format(num) for num in range(1, max_fields+2)])
|
||||
)
|
||||
codefile.write(", N, ...) N\n")
|
||||
codefile.write("#define NARGS_1(...) YLT_MACRO_EXPAND(__NARGS(__VA_ARGS__,")
|
||||
codefile.write(
|
||||
",".join(['{}'.format(num) for num in range(max_fields,-1,-1)])
|
||||
)
|
||||
codefile.write("))\n")
|
||||
codefile.write("#define AUGMENTER(...) unused, __VA_ARGS__\n")
|
||||
codefile.write("#define YLT_ARG_COUNT(...) NARGS_1(AUGMENTER(__VA_ARGS__))\n\n")
|
||||
codefile.write("#else // Others\n\n")
|
||||
codefile.write("#define YLT_ARG_COUNT(...) __NARGS(0, ##__VA_ARGS__,")
|
||||
codefile.write(
|
||||
",".join(['{}'.format(num) for num in range(max_fields,-1,-1)])
|
||||
)
|
||||
codefile.write(")\n")
|
||||
codefile.write("#define __NARGS(")
|
||||
codefile.write(
|
||||
",".join(['_{}'.format(num) for num in range(0,max_fields+1)])
|
||||
)
|
||||
codefile.write(", N, ...) N\n")
|
||||
codefile.write("#endif\n\n")
|
||||
codefile.write(
|
||||
"\n".join(
|
||||
[
|
||||
f"{macro}{i}(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS{i-1}(w,o,__VA_ARGS__))" for i in range(2, 126)
|
||||
]
|
||||
)
|
||||
)
|
||||
with open("generated_args.txt", "w", encoding="utf-8") as codefile:
|
||||
codefile.write("#ifdef _MSC_VER // Microsoft compilers\n\n")
|
||||
codefile.write("#define __NARGS(\n")
|
||||
codefile.write(
|
||||
",".join(['_{}'.format(num) for num in range(1, max_fields+2)])
|
||||
)
|
||||
codefile.write(", N, ...) N\n")
|
||||
codefile.write("#define NARGS_1(...) YLT_MACRO_EXPAND(__NARGS(__VA_ARGS__,")
|
||||
codefile.write(
|
||||
",".join(['{}'.format(num) for num in range(max_fields,-1,-1)])
|
||||
)
|
||||
codefile.write("))\n")
|
||||
codefile.write("#define AUGMENTER(...) unused, __VA_ARGS__\n")
|
||||
codefile.write("#define YLT_ARG_COUNT(...) NARGS_1(AUGMENTER(__VA_ARGS__))\n\n")
|
||||
codefile.write("#else // Others\n\n")
|
||||
codefile.write("#define YLT_ARG_COUNT(...) __NARGS(0, ##__VA_ARGS__,")
|
||||
codefile.write(
|
||||
",".join(['{}'.format(num) for num in range(max_fields,-1,-1)])
|
||||
)
|
||||
codefile.write(")\n")
|
||||
codefile.write("#define __NARGS(")
|
||||
codefile.write(
|
||||
",".join(['_{}'.format(num) for num in range(0,max_fields+1)])
|
||||
)
|
||||
codefile.write(", N, ...) N\n")
|
||||
codefile.write("#endif\n\n")
|
||||
codefile.write(
|
||||
"\n".join(
|
||||
[
|
||||
f"{macro}{i}(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS{i-1}(w,o,__VA_ARGS__))" for i in range(2, 126)
|
||||
]
|
||||
)
|
||||
)
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER // Microsoft compilers
|
||||
|
||||
#define __NARGS(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99,_100,_101,_102,_103,_104,_105,_106,_107,_108,_109,_110,_111,_112,_113,_114,_115,_116,_117,_118,_119,_120,_121,_122,_123,_124,_125, N, ...) N
|
||||
#define NARGS_1(...) YLT_MACRO_EXPAND(__NARGS(__VA_ARGS__,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0))
|
||||
#define AUGMENTER(...) unused, __VA_ARGS__
|
||||
#define YLT_ARG_COUNT(...) NARGS_1(AUGMENTER(__VA_ARGS__))
|
||||
|
||||
#else // Others
|
||||
|
||||
#define YLT_ARG_COUNT(...) __NARGS(0, ##__VA_ARGS__,256,255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
|
||||
#define __NARGS(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99,_100,_101,_102,_103,_104,_105,_106,_107,_108,_109,_110,_111,_112,_113,_114,_115,_116,_117,_118,_119,_120,_121,_122,_123,_124,_125,_126,_127,_128,_129,_130,_131,_132,_133,_134,_135,_136,_137,_138,_139,_140,_141,_142,_143,_144,_145,_146,_147,_148,_149,_150,_151,_152,_153,_154,_155,_156,_157,_158,_159,_160,_161,_162,_163,_164,_165,_166,_167,_168,_169,_170,_171,_172,_173,_174,_175,_176,_177,_178,_179,_180,_181,_182,_183,_184,_185,_186,_187,_188,_189,_190,_191,_192,_193,_194,_195,_196,_197,_198,_199,_200,_201,_202,_203,_204,_205,_206,_207,_208,_209,_210,_211,_212,_213,_214,_215,_216,_217,_218,_219,_220,_221,_222,_223,_224,_225,_226,_227,_228,_229,_230,_231,_232,_233,_234,_235,_236,_237,_238,_239,_240,_241,_242,_243,_244,_245,_246,_247,_248,_249,_250,_251,_252,_253,_254,_255,_256, N, ...) N
|
||||
#endif
|
||||
|
||||
#define WRAP_ARGS2(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS1(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS3(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS2(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS4(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS3(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS5(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS4(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS6(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS5(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS7(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS6(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS8(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS7(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS9(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS8(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS10(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS9(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS11(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS10(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS12(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS11(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS13(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS12(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS14(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS13(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS15(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS14(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS16(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS15(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS17(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS16(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS18(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS17(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS19(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS18(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS20(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS19(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS21(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS20(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS22(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS21(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS23(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS22(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS24(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS23(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS25(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS24(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS26(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS25(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS27(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS26(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS28(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS27(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS29(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS28(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS30(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS29(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS31(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS30(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS32(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS31(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS33(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS32(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS34(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS33(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS35(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS34(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS36(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS35(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS37(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS36(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS38(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS37(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS39(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS38(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS40(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS39(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS41(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS40(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS42(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS41(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS43(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS42(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS44(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS43(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS45(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS44(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS46(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS45(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS47(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS46(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS48(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS47(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS49(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS48(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS50(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS49(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS51(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS50(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS52(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS51(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS53(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS52(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS54(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS53(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS55(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS54(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS56(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS55(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS57(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS56(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS58(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS57(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS59(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS58(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS60(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS59(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS61(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS60(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS62(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS61(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS63(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS62(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS64(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS63(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS65(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS64(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS66(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS65(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS67(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS66(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS68(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS67(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS69(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS68(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS70(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS69(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS71(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS70(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS72(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS71(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS73(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS72(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS74(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS73(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS75(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS74(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS76(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS75(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS77(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS76(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS78(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS77(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS79(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS78(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS80(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS79(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS81(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS80(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS82(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS81(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS83(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS82(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS84(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS83(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS85(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS84(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS86(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS85(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS87(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS86(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS88(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS87(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS89(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS88(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS90(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS89(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS91(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS90(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS92(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS91(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS93(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS92(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS94(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS93(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS95(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS94(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS96(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS95(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS97(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS96(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS98(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS97(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS99(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS98(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS100(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS99(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS101(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS100(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS102(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS101(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS103(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS102(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS104(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS103(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS105(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS104(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS106(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS105(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS107(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS106(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS108(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS107(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS109(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS108(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS110(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS109(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS111(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS110(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS112(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS111(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS113(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS112(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS114(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS113(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS115(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS114(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS116(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS115(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS117(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS116(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS118(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS117(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS119(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS118(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS120(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS119(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS121(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS120(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS122(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS121(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS123(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS122(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS124(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS123(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS125(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS124(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS126(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS125(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS127(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS126(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS128(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS127(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS129(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS128(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS130(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS129(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS131(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS130(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS132(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS131(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS133(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS132(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS134(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS133(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS135(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS134(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS136(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS135(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS137(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS136(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS138(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS137(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS139(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS138(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS140(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS139(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS141(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS140(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS142(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS141(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS143(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS142(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS144(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS143(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS145(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS144(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS146(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS145(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS147(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS146(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS148(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS147(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS149(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS148(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS150(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS149(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS151(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS150(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS152(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS151(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS153(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS152(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS154(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS153(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS155(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS154(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS156(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS155(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS157(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS156(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS158(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS157(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS159(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS158(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS160(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS159(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS161(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS160(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS162(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS161(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS163(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS162(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS164(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS163(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS165(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS164(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS166(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS165(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS167(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS166(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS168(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS167(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS169(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS168(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS170(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS169(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS171(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS170(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS172(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS171(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS173(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS172(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS174(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS173(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS175(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS174(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS176(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS175(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS177(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS176(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS178(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS177(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS179(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS178(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS180(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS179(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS181(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS180(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS182(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS181(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS183(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS182(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS184(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS183(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS185(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS184(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS186(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS185(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS187(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS186(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS188(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS187(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS189(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS188(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS190(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS189(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS191(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS190(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS192(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS191(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS193(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS192(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS194(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS193(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS195(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS194(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS196(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS195(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS197(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS196(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS198(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS197(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS199(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS198(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS200(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS199(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS201(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS200(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS202(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS201(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS203(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS202(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS204(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS203(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS205(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS204(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS206(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS205(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS207(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS206(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS208(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS207(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS209(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS208(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS210(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS209(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS211(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS210(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS212(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS211(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS213(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS212(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS214(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS213(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS215(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS214(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS216(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS215(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS217(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS216(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS218(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS217(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS219(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS218(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS220(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS219(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS221(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS220(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS222(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS221(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS223(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS222(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS224(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS223(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS225(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS224(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS226(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS225(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS227(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS226(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS228(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS227(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS229(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS228(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS230(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS229(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS231(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS230(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS232(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS231(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS233(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS232(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS234(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS233(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS235(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS234(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS236(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS235(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS237(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS236(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS238(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS237(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS239(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS238(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS240(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS239(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS241(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS240(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS242(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS241(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS243(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS242(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS244(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS243(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS245(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS244(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS246(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS245(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS247(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS246(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS248(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS247(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS249(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS248(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS250(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS249(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS251(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS250(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS252(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS251(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS253(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS252(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS254(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS253(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS255(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS254(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS256(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS255(w,o,__VA_ARGS__))
|
||||
#define WRAP_ARGS257(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS256(w,o,__VA_ARGS__))
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,224 @@
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#if __has_include(<ylt/util/expected.hpp>)
|
||||
#include <ylt/util/expected.hpp>
|
||||
#else
|
||||
#include "iguana/ylt/util/expected.hpp"
|
||||
#endif
|
||||
|
||||
#include "reflect26_compat.hpp"
|
||||
#include "user_reflect_macro.hpp"
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
#include "reflect26_core.hpp"
|
||||
#endif
|
||||
namespace struct_pack {
|
||||
template <typename T, uint64_t version>
|
||||
struct compatible;
|
||||
}
|
||||
namespace ylt::reflection {
|
||||
template <typename T>
|
||||
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
|
||||
#if __cpp_concepts >= 201907L
|
||||
template <typename Type>
|
||||
concept expected = requires(Type e) {
|
||||
typename remove_cvref_t<Type>::value_type;
|
||||
typename remove_cvref_t<Type>::error_type;
|
||||
typename remove_cvref_t<Type>::unexpected_type;
|
||||
e.has_value();
|
||||
e.error();
|
||||
requires std::is_same_v<void, typename remove_cvref_t<Type>::value_type> ||
|
||||
requires(Type e) {
|
||||
e.value();
|
||||
};
|
||||
};
|
||||
#else
|
||||
template <typename T, typename = void>
|
||||
struct expected_impl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct expected_impl<T, std::void_t<typename remove_cvref_t<T>::value_type,
|
||||
typename remove_cvref_t<T>::error_type,
|
||||
typename remove_cvref_t<T>::unexpected_type,
|
||||
decltype(std::declval<T>().has_value()),
|
||||
decltype(std::declval<T>().error())>>
|
||||
: std::true_type {};
|
||||
// TODO: check e.value()
|
||||
template <typename T>
|
||||
constexpr bool expected = expected_impl<T>::value;
|
||||
#endif
|
||||
|
||||
#if __cpp_concepts >= 201907L
|
||||
template <typename Type>
|
||||
concept optional = !expected<Type> && requires(Type optional) {
|
||||
optional.value();
|
||||
optional.has_value();
|
||||
optional.operator*();
|
||||
typename remove_cvref_t<Type>::value_type;
|
||||
};
|
||||
#else
|
||||
template <typename T, typename = void>
|
||||
struct optional_impl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct optional_impl<T, std::void_t<decltype(std::declval<T>().value()),
|
||||
decltype(std::declval<T>().has_value()),
|
||||
decltype(std::declval<T>().operator*()),
|
||||
typename remove_cvref_t<T>::value_type>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool optional = !expected<T> && optional_impl<T>::value;
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
struct pair_impl : std::false_type {};
|
||||
|
||||
template <typename F, typename S>
|
||||
struct pair_impl<std::pair<F, S>> : std::true_type {};
|
||||
|
||||
#if __cpp_concepts >= 201907L
|
||||
template <typename T>
|
||||
concept pair = pair_impl<remove_cvref_t<T>>::value;
|
||||
#else
|
||||
template <typename T>
|
||||
constexpr bool pair = pair_impl<remove_cvref_t<T>>::value;
|
||||
#endif
|
||||
|
||||
namespace internal {
|
||||
#if __cpp_concepts >= 201907L
|
||||
template <typename Type>
|
||||
concept tuple_size = requires(Type tuple) {
|
||||
std::tuple_size<remove_cvref_t<Type>>::value;
|
||||
};
|
||||
#else
|
||||
template <typename T, typename = void>
|
||||
struct tuple_size_impl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct tuple_size_impl<
|
||||
T, std::void_t<decltype(std::tuple_size<remove_cvref_t<T>>::value)>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool tuple_size = tuple_size_impl<T>::value;
|
||||
#endif
|
||||
|
||||
template <typename Type>
|
||||
constexpr inline bool is_compatible_v = false;
|
||||
|
||||
template <typename Type, uint64_t version>
|
||||
constexpr inline bool is_compatible_v<struct_pack::compatible<Type, version>> =
|
||||
true;
|
||||
|
||||
struct UniversalVectorType {
|
||||
template <typename T>
|
||||
operator std::vector<T>();
|
||||
};
|
||||
|
||||
struct UniversalType {
|
||||
template <typename T>
|
||||
operator T();
|
||||
};
|
||||
|
||||
struct UniversalIntegralType {
|
||||
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
|
||||
operator T();
|
||||
};
|
||||
|
||||
struct UniversalNullptrType {
|
||||
operator std::nullptr_t();
|
||||
};
|
||||
|
||||
struct UniversalOptionalType {
|
||||
template <typename U, typename = std::enable_if_t<optional<U>>>
|
||||
operator U();
|
||||
};
|
||||
|
||||
struct UniversalCompatibleType {
|
||||
template <typename U, typename = std::enable_if_t<is_compatible_v<U>>>
|
||||
operator U();
|
||||
};
|
||||
|
||||
template <typename T, typename construct_param_t, typename = void,
|
||||
typename... Args>
|
||||
struct is_constructable_impl : std::false_type {};
|
||||
template <typename T, typename construct_param_t, typename... Args>
|
||||
struct is_constructable_impl<
|
||||
T, construct_param_t,
|
||||
std::void_t<decltype(T{{Args{}}..., {construct_param_t{}}})>, Args...>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T, typename construct_param_t, typename... Args>
|
||||
constexpr bool is_constructable =
|
||||
is_constructable_impl<T, construct_param_t, void, Args...>::value;
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline constexpr std::size_t members_count_impl() {
|
||||
if constexpr (is_constructable<T, UniversalVectorType, Args...>) {
|
||||
return members_count_impl<T, Args..., UniversalVectorType>();
|
||||
}
|
||||
else if constexpr (is_constructable<T, UniversalType, Args...>) {
|
||||
return members_count_impl<T, Args..., UniversalType>();
|
||||
}
|
||||
else if constexpr (is_constructable<T, UniversalOptionalType, Args...>) {
|
||||
return members_count_impl<T, Args..., UniversalOptionalType>();
|
||||
}
|
||||
else if constexpr (is_constructable<T, UniversalIntegralType, Args...>) {
|
||||
return members_count_impl<T, Args..., UniversalIntegralType>();
|
||||
}
|
||||
else if constexpr (is_constructable<T, UniversalNullptrType, Args...>) {
|
||||
return members_count_impl<T, Args..., UniversalNullptrType>();
|
||||
}
|
||||
else if constexpr (is_constructable<T, UniversalCompatibleType, Args...>) {
|
||||
return members_count_impl<T, Args..., UniversalCompatibleType>();
|
||||
}
|
||||
else {
|
||||
return sizeof...(Args);
|
||||
}
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
template <typename T>
|
||||
inline constexpr std::size_t members_count() {
|
||||
using type = remove_cvref_t<T>;
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
if constexpr (internal::tuple_size<type>) {
|
||||
return std::tuple_size<type>::value;
|
||||
}
|
||||
else {
|
||||
return reflect26::members_count<type>();
|
||||
}
|
||||
#else
|
||||
if constexpr (is_out_ylt_refl_v<type>) {
|
||||
return refl_member_count(ylt::reflection::identity<type>{});
|
||||
}
|
||||
else if constexpr (is_inner_ylt_refl_v<type>) {
|
||||
return type::refl_member_count(ylt::reflection::identity<type>{});
|
||||
}
|
||||
else if constexpr (internal::tuple_size<type>) {
|
||||
return std::tuple_size<type>::value;
|
||||
}
|
||||
else if constexpr (std::is_aggregate_v<type>) {
|
||||
if constexpr (std::is_empty_v<type>) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return internal::members_count_impl<type>();
|
||||
}
|
||||
}
|
||||
else {
|
||||
static_assert(!sizeof(T), "not supported type!");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr std::size_t members_count_v = members_count<T>();
|
||||
} // namespace ylt::reflection
|
||||
@@ -0,0 +1,461 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#include "member_ptr.hpp"
|
||||
#include "reflect26_compat.hpp"
|
||||
#include "template_string.hpp"
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
#include "reflect26_core.hpp"
|
||||
#endif
|
||||
|
||||
namespace ylt::reflection {
|
||||
|
||||
template <typename T>
|
||||
inline constexpr auto get_alias_field_names();
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <class T>
|
||||
struct Wrapper {
|
||||
using Type = T;
|
||||
T v;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
Wrapper(T) -> Wrapper<T>;
|
||||
|
||||
// This workaround is necessary for clang.
|
||||
template <class T>
|
||||
inline constexpr auto wrap(const T& arg) noexcept {
|
||||
return Wrapper{arg};
|
||||
}
|
||||
|
||||
template <auto ptr>
|
||||
inline constexpr std::string_view get_member_name() {
|
||||
#if defined(_MSC_VER)
|
||||
constexpr std::string_view func_name = __FUNCSIG__;
|
||||
#else
|
||||
constexpr std::string_view func_name = __PRETTY_FUNCTION__;
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
auto split = func_name.substr(0, func_name.size() - 2);
|
||||
return split.substr(split.find_last_of(":.") + 1);
|
||||
#elif defined(__GNUC__)
|
||||
auto split = func_name.substr(0, func_name.rfind(")}"));
|
||||
return split.substr(split.find_last_of(":") + 1);
|
||||
#elif defined(_MSC_VER)
|
||||
auto split = func_name.substr(0, func_name.rfind("}>"));
|
||||
return split.substr(split.rfind("->") + 2);
|
||||
#else
|
||||
static_assert(false,
|
||||
"You are using an unsupported compiler. Please use GCC, Clang "
|
||||
"or MSVC or switch to the rfl::Field-syntax.");
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
struct member_tratis {};
|
||||
|
||||
template <class T, class Owner>
|
||||
struct member_tratis<T Owner::*> {
|
||||
using owner_type = Owner;
|
||||
using value_type = T;
|
||||
};
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct has_alias_struct_name_t : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_alias_struct_name_t<
|
||||
T, std::void_t<decltype(get_alias_struct_name((T*)nullptr))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct has_inner_alias_struct_name_t : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_inner_alias_struct_name_t<
|
||||
T, std::void_t<decltype(T::get_alias_struct_name((T*)nullptr))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool has_alias_struct_name_v = has_alias_struct_name_t<T>::value;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool has_inner_alias_struct_name_v =
|
||||
has_inner_alias_struct_name_t<T>::value;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct has_alias_field_names_t : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_alias_field_names_t<
|
||||
T, std::void_t<decltype(get_alias_field_names((T*)nullptr))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct has_inner_alias_field_names_t : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_inner_alias_field_names_t<
|
||||
T, std::void_t<decltype(T::get_alias_field_names((T*)nullptr))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool has_alias_field_names_v = has_alias_field_names_t<T>::value;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool has_inner_alias_field_names_v =
|
||||
has_inner_alias_field_names_t<T>::value;
|
||||
|
||||
template <typename T, typename U, size_t... Is>
|
||||
inline constexpr void init_arr_with_tuple(U& arr, std::index_sequence<Is...>) {
|
||||
constexpr auto tp = struct_to_tuple<T>();
|
||||
((arr[Is] = internal::get_member_name<internal::wrap(std::get<Is>(tp))>()),
|
||||
...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr std::array<std::string_view, members_count_v<T>>
|
||||
get_member_names() {
|
||||
using type = remove_cvref_t<T>;
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
return reflect26::member_names_array<type>();
|
||||
#else
|
||||
if constexpr (is_out_ylt_refl_v<type>) {
|
||||
return refl_member_names(ylt::reflection::identity<type>{});
|
||||
}
|
||||
else if constexpr (is_inner_ylt_refl_v<type>) {
|
||||
return type::refl_member_names(ylt::reflection::identity<type>{});
|
||||
}
|
||||
else {
|
||||
constexpr size_t Count = members_count_v<T>;
|
||||
std::array<std::string_view, Count> arr;
|
||||
#if __cplusplus >= 202002L && (!defined(_MSC_VER) || _MSC_VER >= 1930)
|
||||
constexpr auto tp = struct_to_tuple<T>();
|
||||
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
|
||||
((arr[Is] =
|
||||
internal::get_member_name<internal::wrap(std::get<Is>(tp))>()),
|
||||
...);
|
||||
}(std::make_index_sequence<Count>{});
|
||||
#else
|
||||
init_arr_with_tuple<T>(arr, std::make_index_sequence<Count>{});
|
||||
#endif
|
||||
return arr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
template <std::size_t N>
|
||||
struct member_names_linear_map {
|
||||
std::array<std::string_view, N> names;
|
||||
|
||||
constexpr std::size_t size() const noexcept { return N; }
|
||||
constexpr auto begin() const noexcept { return names.begin(); }
|
||||
constexpr auto end() const noexcept { return names.end(); }
|
||||
|
||||
static constexpr std::string_view normalized_name(std::string_view name) {
|
||||
return reflect26::normalized_member_name(name);
|
||||
}
|
||||
|
||||
constexpr std::size_t at(std::string_view name) const {
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
if (names[i] == name || normalized_name(names[i]) == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw std::out_of_range("unknown member name");
|
||||
}
|
||||
};
|
||||
#else
|
||||
template <typename T, size_t... Is>
|
||||
inline constexpr auto get_member_names_map_impl(T& name_arr,
|
||||
std::index_sequence<Is...>) {
|
||||
return frozen::unordered_map<frozen::string, size_t, sizeof...(Is)>{
|
||||
{name_arr[Is], Is}...};
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
inline constexpr auto get_member_names_map() {
|
||||
constexpr auto name_arr = get_member_names<T>();
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
return member_names_linear_map<name_arr.size()>{name_arr};
|
||||
#elif __cplusplus >= 202002L
|
||||
return [&]<size_t... Is>(std::index_sequence<Is...>) mutable {
|
||||
return frozen::unordered_map<frozen::string, size_t, name_arr.size()>{
|
||||
{name_arr[Is], Is}...};
|
||||
}(std::make_index_sequence<name_arr.size()>{});
|
||||
#else
|
||||
return get_member_names_map_impl(name_arr,
|
||||
std::make_index_sequence<name_arr.size()>{});
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename Tuple, size_t... Is>
|
||||
inline auto get_member_offset_arr_impl(T& t, Tuple& tp,
|
||||
std::index_sequence<Is...>) {
|
||||
std::array<size_t, sizeof...(Is)> arr;
|
||||
((arr[Is] = size_t((const char*)&std::get<Is>(tp) - (char*)(&t))), ...);
|
||||
return arr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline const auto& get_member_offset_arr(T&& t) {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
[[maybe_unused]] static constexpr auto arr =
|
||||
reflect26::member_offsets<remove_cvref_t<T>>();
|
||||
return arr;
|
||||
#else
|
||||
constexpr size_t Count = members_count_v<T>;
|
||||
auto tp = ylt::reflection::object_to_tuple(std::forward<T>(t));
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
[[maybe_unused]] static std::array<size_t, Count> arr = {
|
||||
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
|
||||
std::array<size_t, Count> arr;
|
||||
((arr[Is] = size_t((const char*)&std::get<Is>(tp) - (char*)(&t))), ...);
|
||||
return arr;
|
||||
}(std::make_index_sequence<Count>{})}; // namespace internal
|
||||
|
||||
return arr;
|
||||
#else
|
||||
[[maybe_unused]] static std::array<size_t, Count> arr =
|
||||
get_member_offset_arr_impl(t, tp, std::make_index_sequence<Count>{});
|
||||
return arr;
|
||||
#endif
|
||||
#endif
|
||||
} // namespace ylt::reflection
|
||||
|
||||
template <typename T>
|
||||
inline const auto& get_member_offset_arr() {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
[[maybe_unused]] static constexpr auto arr =
|
||||
reflect26::member_offsets<remove_cvref_t<T>>();
|
||||
return arr;
|
||||
#else
|
||||
return get_member_offset_arr(internal::wrapper<T>::value);
|
||||
#endif
|
||||
} // namespace ylt::reflection
|
||||
} // namespace internal
|
||||
|
||||
template <typename... Args>
|
||||
inline constexpr auto tuple_to_variant(std::tuple<Args...>) {
|
||||
return std::variant<std::add_pointer_t<Args>...>{};
|
||||
}
|
||||
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
namespace internal {
|
||||
template <typename T, typename Member>
|
||||
using copy_const_t =
|
||||
std::conditional_t<std::is_const_v<std::remove_reference_t<T>>,
|
||||
std::add_const_t<Member>, Member>;
|
||||
|
||||
template <typename T, std::size_t... Is>
|
||||
inline constexpr auto struct_variant_reflect(std::index_sequence<Is...>) {
|
||||
static constexpr auto members = reflect26::data_members_array<T>();
|
||||
return std::variant<std::add_pointer_t<
|
||||
copy_const_t<T, reflect26::meta_type_t<members[Is]>>>...>{};
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
template <typename T>
|
||||
using struct_variant_t = decltype(internal::struct_variant_reflect<T>(
|
||||
std::make_index_sequence<members_count_v<remove_cvref_t<T>>>{}));
|
||||
#else
|
||||
template <typename T>
|
||||
using struct_variant_t = decltype(tuple_to_variant(
|
||||
std::declval<decltype(struct_to_tuple<remove_cvref_t<T>>())>));
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
constexpr auto member_names_map = internal::get_member_names_map<T>();
|
||||
|
||||
template <typename T>
|
||||
inline auto member_offsets = internal::get_member_offset_arr<T>();
|
||||
|
||||
template <auto member>
|
||||
inline constexpr size_t index_of() {
|
||||
using T = typename internal::member_tratis<
|
||||
std::remove_const_t<decltype(member)>>::owner_type;
|
||||
constexpr auto name = field_string<member>();
|
||||
constexpr auto names = internal::get_member_names<T>();
|
||||
for (size_t i = 0; i < names.size(); i++) {
|
||||
if (name == names[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return names.size();
|
||||
}
|
||||
|
||||
struct field_alias_t {
|
||||
std::string_view alias_name;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr auto get_alias_field_names() {
|
||||
if constexpr (internal::has_alias_field_names_v<T>) {
|
||||
return get_alias_field_names((T*)nullptr);
|
||||
}
|
||||
else if constexpr (internal::has_inner_alias_field_names_v<T>) {
|
||||
return T::get_alias_field_names((T*)nullptr);
|
||||
}
|
||||
else {
|
||||
return std::array<std::string_view, 0>{};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr std::string_view get_struct_name() {
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
constexpr auto annotation_name = reflect26::type_name<U>();
|
||||
if constexpr (!annotation_name.empty()) {
|
||||
return annotation_name;
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
if constexpr (internal::has_alias_struct_name_v<U>) {
|
||||
return get_alias_struct_name((U*)nullptr);
|
||||
}
|
||||
else if constexpr (internal::has_inner_alias_struct_name_v<U>) {
|
||||
return U::get_alias_struct_name((U*)nullptr);
|
||||
}
|
||||
else {
|
||||
return type_string<U>();
|
||||
}
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr std::array<std::string_view, members_count_v<T>>
|
||||
get_member_names() {
|
||||
auto arr = internal::get_member_names<T>();
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
if constexpr (internal::has_alias_field_names_v<U> ||
|
||||
internal::has_inner_alias_field_names_v<U>) {
|
||||
constexpr auto alias_arr = get_alias_field_names<U>();
|
||||
for (size_t i = 0; i < alias_arr.size(); i++) {
|
||||
arr[alias_arr[i].index] = alias_arr[i].alias_name;
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
struct FixedString {
|
||||
char data[N];
|
||||
template <std::size_t... I>
|
||||
constexpr FixedString(const char (&s)[N], std::index_sequence<I...>)
|
||||
: data{s[I]...} {}
|
||||
constexpr FixedString(const char (&s)[N])
|
||||
: FixedString(s, std::make_index_sequence<N>()) {}
|
||||
constexpr std::string_view str() const {
|
||||
return std::string_view{data, N - 1};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr size_t index_of(std::string_view name) {
|
||||
constexpr auto arr = get_member_names<T>();
|
||||
for (size_t i = 0; i < arr.size(); i++) {
|
||||
if (arr[i] == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return arr.size();
|
||||
}
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
template <typename T, FixedString name>
|
||||
inline constexpr size_t index_of() {
|
||||
return index_of<T>(name.str());
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, size_t index>
|
||||
inline constexpr std::string_view name_of() {
|
||||
static_assert(index < members_count_v<T>, "index out of range");
|
||||
constexpr auto arr = get_member_names<T>();
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr std::string_view name_of(size_t index) {
|
||||
constexpr auto arr = get_member_names<T>();
|
||||
if (index >= arr.size()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
template <typename Visit, typename U, size_t... Is>
|
||||
inline constexpr void for_each_impl(Visit&& func, U& arr,
|
||||
std::index_sequence<Is...>) {
|
||||
if constexpr (std::is_invocable_v<Visit, std::string_view, size_t>) {
|
||||
(func(arr[Is], Is), ...);
|
||||
}
|
||||
else if constexpr (std::is_invocable_v<Visit, std::string_view>) {
|
||||
(func(arr[Is]), ...);
|
||||
}
|
||||
else {
|
||||
static_assert(sizeof(Visit) < 0,
|
||||
"invalid arguments, full arguments: [std::string_view, "
|
||||
"size_t], at least has std::string_view and make sure keep "
|
||||
"the order of arguments");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Visit>
|
||||
inline constexpr void for_each(Visit&& func) {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
static constexpr auto arr = get_member_names<T>();
|
||||
[[maybe_unused]] size_t index = 0;
|
||||
template for (constexpr auto name : arr) {
|
||||
if constexpr (std::is_invocable_v<Visit, std::string_view, size_t>) {
|
||||
func(name, index++);
|
||||
}
|
||||
else if constexpr (std::is_invocable_v<Visit, std::string_view>) {
|
||||
func(name);
|
||||
}
|
||||
else {
|
||||
static_assert(sizeof(Visit) < 0,
|
||||
"invalid arguments, full arguments: [std::string_view, "
|
||||
"size_t], at least has std::string_view and make sure keep "
|
||||
"the order of arguments");
|
||||
}
|
||||
}
|
||||
#elif __cplusplus >= 202002L
|
||||
constexpr auto arr = get_member_names<T>();
|
||||
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
|
||||
if constexpr (std::is_invocable_v<Visit, std::string_view, size_t>) {
|
||||
(func(arr[Is], Is), ...);
|
||||
}
|
||||
else if constexpr (std::is_invocable_v<Visit, std::string_view>) {
|
||||
(func(arr[Is]), ...);
|
||||
}
|
||||
else {
|
||||
static_assert(sizeof(Visit) < 0,
|
||||
"invalid arguments, full arguments: [std::string_view, "
|
||||
"size_t], at least has std::string_view and make sure keep "
|
||||
"the order of arguments");
|
||||
}
|
||||
}(std::make_index_sequence<arr.size()>{});
|
||||
#else
|
||||
constexpr auto arr = get_member_names<T>();
|
||||
for_each_impl(std::forward<Visit>(func), arr,
|
||||
std::make_index_sequence<arr.size()>{});
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ylt::reflection
|
||||
@@ -0,0 +1,157 @@
|
||||
#pragma once
|
||||
#include "member_count.hpp"
|
||||
#include "reflect26_compat.hpp"
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
#include "reflect26_core.hpp"
|
||||
#endif
|
||||
|
||||
// modified based on:
|
||||
// https://github.com/getml/reflect-cpp/blob/main/include/rfl/internal/bind_fake_object_to_tuple.hpp
|
||||
// thanks for alxn4's great idea!
|
||||
namespace ylt::reflection {
|
||||
namespace internal {
|
||||
|
||||
template <class T, std::size_t n>
|
||||
struct object_tuple_view_helper {
|
||||
static constexpr auto tuple_view() {
|
||||
static_assert(
|
||||
sizeof(T) < 0,
|
||||
"\n\nThis error occurs for one of two reasons:\n\n"
|
||||
"1) You have created a struct with more than 256 fields, which is "
|
||||
"unsupported. \n\n"
|
||||
"2) Your struct is not an aggregate type. You can make it aggregated, "
|
||||
"or defined a YLT_REFL macro. \n\n");
|
||||
}
|
||||
|
||||
static constexpr auto tuple_view(T&) {
|
||||
static_assert(
|
||||
sizeof(T) < 0,
|
||||
"\n\nThis error occurs for one of two reasons:\n\n"
|
||||
"1) You have created a struct with more than 256 fields, which is "
|
||||
"unsupported. \n\n"
|
||||
"2) Your struct is not an aggregate type. You can make it aggregated, "
|
||||
"or defined a YLT_REFL macro. \n\n");
|
||||
}
|
||||
|
||||
template <typename Visitor>
|
||||
static constexpr decltype(auto) tuple_view(T&&, Visitor&&) {
|
||||
static_assert(
|
||||
sizeof(T) < 0,
|
||||
"\n\nThis error occurs for one of two reasons:\n\n"
|
||||
"1) You have created a struct with more than 256 fields, which is "
|
||||
"unsupported. \n\n"
|
||||
"2) Your struct is not an aggregate type. You can make it aggregated, "
|
||||
"or defined a YLT_REFL macro. \n\n");
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct object_tuple_view_helper<T, 0> {
|
||||
static constexpr auto tuple_view() { return std::tie(); }
|
||||
static constexpr auto tuple_view(T&) { return std::tie(); }
|
||||
|
||||
template <typename Visitor>
|
||||
static constexpr decltype(auto) tuple_view(T&&, Visitor&&) {}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct wrapper {
|
||||
inline static remove_cvref_t<T> value;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
inline constexpr remove_cvref_t<T>& get_fake_object() noexcept {
|
||||
return wrapper<remove_cvref_t<T>>::value;
|
||||
}
|
||||
|
||||
#define RFL_INTERNAL_OBJECT_IF_YOU_SEE_AN_ERROR_REFER_TO_DOCUMENTATION_ON_C_ARRAYS( \
|
||||
n, ...) \
|
||||
template <class T> \
|
||||
struct object_tuple_view_helper<T, n> { \
|
||||
static constexpr auto tuple_view() { \
|
||||
auto& [__VA_ARGS__] = get_fake_object<remove_cvref_t<T>>(); \
|
||||
auto ref_tup = std::tie(__VA_ARGS__); \
|
||||
auto get_ptrs = [](auto&... _refs) { \
|
||||
return std::make_tuple(&_refs...); \
|
||||
}; \
|
||||
return std::apply(get_ptrs, ref_tup); \
|
||||
} \
|
||||
static constexpr auto tuple_view(T& t) { \
|
||||
auto& [__VA_ARGS__] = t; \
|
||||
return std::tie(__VA_ARGS__); \
|
||||
} \
|
||||
template <typename Visitor> \
|
||||
static constexpr decltype(auto) tuple_view(T&& t, Visitor&& visitor) { \
|
||||
auto&& [__VA_ARGS__] = t; \
|
||||
return visitor(__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
// such file is generate macro file
|
||||
#ifndef YLT_USE_CXX26_REFLECTION
|
||||
#include "internal/generate/member_macro.hpp"
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
inline constexpr auto tuple_view(T&& t) {
|
||||
return internal::object_tuple_view_helper<T, members_count_v<T>>::tuple_view(
|
||||
t);
|
||||
}
|
||||
|
||||
template <size_t Count, class T, typename Visitor>
|
||||
inline constexpr decltype(auto) tuple_view(T&& t, Visitor&& visitor) {
|
||||
return internal::object_tuple_view_helper<T, Count>::tuple_view(
|
||||
std::forward<T>(t), std::forward<Visitor>(visitor));
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
template <class T>
|
||||
inline constexpr auto struct_to_tuple() {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
return reflect26::data_members<remove_cvref_t<T>>();
|
||||
#else
|
||||
return internal::object_tuple_view_helper<T,
|
||||
members_count_v<T>>::tuple_view();
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline constexpr auto object_to_tuple(T&& t) {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
return reflect26::data_members<remove_cvref_t<T>>();
|
||||
#else
|
||||
using type = remove_cvref_t<T>;
|
||||
if constexpr (is_out_ylt_refl_v<type>) {
|
||||
return refl_object_to_tuple(std::forward<T>(t));
|
||||
}
|
||||
else if constexpr (is_inner_ylt_refl_v<type>) {
|
||||
return type::refl_object_to_tuple(std::forward<T>(t));
|
||||
}
|
||||
else {
|
||||
return internal::tuple_view(std::forward<T>(t));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T, typename Visitor, size_t Count = members_count_v<T>>
|
||||
inline constexpr decltype(auto) visit_members(T&& t, Visitor&& visitor) {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
return reflect26::for_each_data_member(std::forward<T>(t),
|
||||
std::forward<Visitor>(visitor));
|
||||
#else
|
||||
using type = remove_cvref_t<T>;
|
||||
if constexpr (is_out_ylt_refl_v<type>) {
|
||||
return refl_visit_members(std::forward<T>(t),
|
||||
std::forward<Visitor>(visitor));
|
||||
}
|
||||
else if constexpr (is_inner_ylt_refl_v<type>) {
|
||||
return t.refl_visit_members(std::forward<T>(t),
|
||||
std::forward<Visitor>(visitor));
|
||||
}
|
||||
else {
|
||||
return internal::tuple_view<Count>(std::forward<T>(t),
|
||||
std::forward<Visitor>(visitor));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} // namespace ylt::reflection
|
||||
@@ -0,0 +1,430 @@
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <variant>
|
||||
|
||||
#include "member_names.hpp"
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
#include "reflect26_dispatch.hpp"
|
||||
#endif
|
||||
#include "template_switch.hpp"
|
||||
|
||||
namespace ylt::reflection {
|
||||
|
||||
namespace internal {
|
||||
template <typename Member, typename T>
|
||||
inline void set_member_ptr(Member& member, T t) {
|
||||
if constexpr (std::is_constructible_v<Member, T>) {
|
||||
member = t;
|
||||
}
|
||||
else {
|
||||
std::string str = "given type: ";
|
||||
str.append(type_string<std::remove_pointer_t<Member>>());
|
||||
str.append(" is not equal with real type: ")
|
||||
.append(type_string<std::remove_pointer_t<T>>());
|
||||
throw std::invalid_argument(str);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct is_variant : std::false_type {};
|
||||
|
||||
template <typename... T>
|
||||
struct is_variant<std::variant<T...>> : std::true_type {};
|
||||
|
||||
struct switch_helper {
|
||||
template <size_t index, typename Member, class Tuple>
|
||||
static constexpr size_t run(Member& member, Tuple& t) {
|
||||
if constexpr (index >= std::tuple_size_v<Tuple>) {
|
||||
return index;
|
||||
}
|
||||
else {
|
||||
if constexpr (is_variant<Member>::value) {
|
||||
member = Member{std::in_place_index<index>, &std::get<index>(t)};
|
||||
}
|
||||
else {
|
||||
set_member_ptr(member, &std::get<index>(t));
|
||||
}
|
||||
return index;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef YLT_USE_CXX26_REFLECTION
|
||||
inline constexpr frozen::string filter_str(const frozen::string& str) {
|
||||
if (str.size() > 3 && str[0] == '_' && str[1] == '_' && str[2] == '_') {
|
||||
auto ptr = str.data() + 3;
|
||||
return frozen::string(ptr, str.size() - 3);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename value_type>
|
||||
struct offset_t {
|
||||
using type = value_type;
|
||||
size_t value;
|
||||
};
|
||||
|
||||
template <typename T, typename Tuple, size_t... Is>
|
||||
inline auto get_variant_type() {
|
||||
return std::variant<offset_t<
|
||||
ylt::reflection::remove_cvref_t<std::tuple_element_t<Is, Tuple>>>...>{};
|
||||
}
|
||||
|
||||
template <typename T, size_t... Is>
|
||||
inline constexpr auto get_variant_map_impl(std::index_sequence<Is...>) {
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
constexpr auto arr = ylt::reflection::get_member_names<U>();
|
||||
auto& offset_arr = get_member_offset_arr(wrapper<U>::value);
|
||||
using Tuple = decltype(ylt::reflection::object_to_tuple(std::declval<U>()));
|
||||
using ValueType = decltype(get_variant_type<U, Tuple, Is...>());
|
||||
return frozen::unordered_map<frozen::string, ValueType, sizeof...(Is)>{
|
||||
{filter_str(arr[Is]),
|
||||
ValueType{std::in_place_index<Is>,
|
||||
offset_t<ylt::reflection::remove_cvref_t<
|
||||
std::tuple_element_t<Is, Tuple>>>{offset_arr[Is]}}}...};
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
template <typename T, size_t... Is>
|
||||
inline auto get_variant_by_index_reflect(T& t, size_t index,
|
||||
std::index_sequence<Is...>) {
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
using variant = struct_variant_t<T>;
|
||||
static constexpr auto members = reflect26::data_members_array<U>();
|
||||
variant member_ptr;
|
||||
bool found = false;
|
||||
(
|
||||
[&] {
|
||||
if (!found && index == Is) {
|
||||
member_ptr.template emplace<Is>(std::addressof(t.[:members[Is]:]));
|
||||
found = true;
|
||||
}
|
||||
}(),
|
||||
...);
|
||||
if (!found) {
|
||||
std::string str = "index out of range, ";
|
||||
str.append("index: ")
|
||||
.append(std::to_string(index))
|
||||
.append(" is greater equal than member count ")
|
||||
.append(std::to_string(members_count_v<U>));
|
||||
throw std::out_of_range(str);
|
||||
}
|
||||
return member_ptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename T>
|
||||
inline constexpr auto get_variant_map() {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
static_assert(sizeof(T) < 0,
|
||||
"get_variant_map is not available with C++26 reflection; use "
|
||||
"reflect26::dispatch_by_name instead");
|
||||
#else
|
||||
return internal::get_variant_map_impl<T>(
|
||||
std::make_index_sequence<members_count_v<T>>{});
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Member, typename T>
|
||||
inline Member& get(T& t, size_t index) {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
Member* member_ptr = nullptr;
|
||||
bool found = reflect26::dispatch_by_index(t, index, [&](auto& field) {
|
||||
internal::set_member_ptr(member_ptr, std::addressof(field));
|
||||
});
|
||||
if (!found) {
|
||||
std::string str = "index out of range, ";
|
||||
str.append("index: ")
|
||||
.append(std::to_string(index))
|
||||
.append(" is greater equal than member count ")
|
||||
.append(std::to_string(members_count_v<remove_cvref_t<T>>));
|
||||
throw std::out_of_range(str);
|
||||
}
|
||||
return *member_ptr;
|
||||
#else
|
||||
auto ref_tp = object_to_tuple(t);
|
||||
constexpr size_t tuple_size = std::tuple_size_v<decltype(ref_tp)>;
|
||||
|
||||
if (index >= tuple_size) {
|
||||
std::string str = "index out of range, ";
|
||||
str.append("index: ")
|
||||
.append(std::to_string(index))
|
||||
.append(" is greater equal than member count ")
|
||||
.append(std::to_string(tuple_size));
|
||||
throw std::out_of_range(str);
|
||||
}
|
||||
Member* member_ptr = nullptr;
|
||||
template_switch<internal::switch_helper>(index, member_ptr, ref_tp);
|
||||
return *member_ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Member, typename T>
|
||||
inline Member& get(T& t, std::string_view name) {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
Member* member_ptr = nullptr;
|
||||
bool found = reflect26::dispatch_by_name(t, name, [&](auto& field) {
|
||||
internal::set_member_ptr(member_ptr, std::addressof(field));
|
||||
});
|
||||
if (!found) {
|
||||
throw std::out_of_range("unknown member name");
|
||||
}
|
||||
return *member_ptr;
|
||||
#else
|
||||
static constexpr auto map = member_names_map<T>;
|
||||
size_t index = map.at(name); // may throw out_of_range: unknown key.
|
||||
auto ref_tp = object_to_tuple(t);
|
||||
|
||||
Member* member_ptr = nullptr;
|
||||
template_switch<internal::switch_helper>(index, member_ptr, ref_tp);
|
||||
return *member_ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline auto get(T& t, size_t index) {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
using U = remove_cvref_t<T>;
|
||||
if constexpr (members_count_v<U> == 0) {
|
||||
throw std::out_of_range("index out of range, empty object");
|
||||
}
|
||||
else {
|
||||
return internal::get_variant_by_index_reflect(
|
||||
t, index, std::make_index_sequence<members_count_v<U>>{});
|
||||
}
|
||||
#else
|
||||
auto ref_tp = object_to_tuple(t);
|
||||
constexpr size_t tuple_size = std::tuple_size_v<decltype(ref_tp)>;
|
||||
if (index >= tuple_size) {
|
||||
std::string str = "index out of range, ";
|
||||
str.append("index: ")
|
||||
.append(std::to_string(index))
|
||||
.append(" is greater equal than member count ")
|
||||
.append(std::to_string(tuple_size));
|
||||
throw std::out_of_range(str);
|
||||
}
|
||||
|
||||
using variant = decltype(tuple_to_variant(ref_tp));
|
||||
variant member_ptr;
|
||||
template_switch<internal::switch_helper>(index, member_ptr, ref_tp);
|
||||
return member_ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr auto get(T& t, std::string_view name) {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
constexpr auto& map = member_names_map<T>;
|
||||
size_t index = map.at(name); // may throw out_of_range: unknown key.
|
||||
return get(t, index);
|
||||
#else
|
||||
constexpr auto& map = member_names_map<T>;
|
||||
size_t index = map.at(name); // may throw out_of_range: unknown key.
|
||||
return get(t, index);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <size_t index, typename T>
|
||||
inline constexpr auto& get(T& t) {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
using U = remove_cvref_t<T>;
|
||||
static_assert(index < members_count_v<U>, "index out of range");
|
||||
decltype(auto) result = [&]() -> decltype(auto) {
|
||||
static constexpr auto members = reflect26::data_members_array<U>();
|
||||
return (t.[:members[index]:]);
|
||||
}();
|
||||
return result;
|
||||
#else
|
||||
auto ref_tp = object_to_tuple(t);
|
||||
|
||||
static_assert(index < std::tuple_size_v<decltype(ref_tp)>,
|
||||
"index out of range");
|
||||
|
||||
return std::get<index>(ref_tp);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
template <FixedString name, typename T>
|
||||
inline constexpr auto& get(T& t) {
|
||||
constexpr size_t index = index_of<T, name>();
|
||||
return get<index>(t);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename Field>
|
||||
inline size_t index_of(T& t, Field& value) {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
using U = remove_cvref_t<T>;
|
||||
size_t index = 0;
|
||||
size_t result = members_count_v<U>;
|
||||
const auto value_addr =
|
||||
static_cast<const volatile void*>(std::addressof(value));
|
||||
reflect26::for_each_data_member(t, [&](auto& field, std::string_view, auto) {
|
||||
const auto field_addr =
|
||||
static_cast<const volatile void*>(std::addressof(field));
|
||||
if (result == members_count_v<U> && field_addr == value_addr) {
|
||||
result = index;
|
||||
}
|
||||
++index;
|
||||
});
|
||||
return result;
|
||||
#else
|
||||
const auto& offset_arr = member_offsets<T>;
|
||||
size_t cur_offset = (const char*)(&value) - (const char*)(&t);
|
||||
auto it = std::lower_bound(offset_arr.begin(), offset_arr.end(), cur_offset);
|
||||
if (it == offset_arr.end()) {
|
||||
return offset_arr.size();
|
||||
}
|
||||
|
||||
return std::distance(offset_arr.begin(), it);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Member>
|
||||
inline size_t index_of(Member member) {
|
||||
using T = typename member_traits<Member>::owner_type;
|
||||
static auto& t = internal::get_fake_object<T>();
|
||||
return index_of(t, t.*member);
|
||||
}
|
||||
|
||||
template <typename T, typename Field>
|
||||
inline constexpr std::string_view name_of(T& t, Field& value) {
|
||||
size_t index = index_of(t, value);
|
||||
constexpr auto arr = get_member_names<T>();
|
||||
if (index == arr.size()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
template <typename T, typename Visit, size_t... Is, typename... Args>
|
||||
inline constexpr void visit_members_impl0(Visit&& func,
|
||||
std::index_sequence<Is...>,
|
||||
Args&... args) {
|
||||
constexpr auto arr = get_member_names<T>();
|
||||
(func(args, arr[Is]), ...);
|
||||
}
|
||||
|
||||
template <typename T, typename Visit, size_t... Is, typename... Args>
|
||||
inline constexpr void visit_members_impl(Visit&& func,
|
||||
std::index_sequence<Is...>,
|
||||
Args&... args) {
|
||||
constexpr auto arr = get_member_names<T>();
|
||||
(func(args, arr[Is], Is), ...);
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
template <typename Visit, typename Field>
|
||||
inline constexpr void invoke_for_each_field(Visit& func, Field& field,
|
||||
std::string_view name,
|
||||
std::size_t index) {
|
||||
if constexpr (std::is_invocable_v<Visit&, Field&>) {
|
||||
func(field);
|
||||
}
|
||||
else if constexpr (std::is_invocable_v<Visit&, Field&, std::string_view>) {
|
||||
func(field, name);
|
||||
}
|
||||
else if constexpr (std::is_invocable_v<Visit&, Field&, std::string_view,
|
||||
std::size_t>) {
|
||||
func(field, name, index);
|
||||
}
|
||||
else {
|
||||
static_assert(sizeof(Visit) < 0,
|
||||
"invalid arguments, full arguments: [field_value&, "
|
||||
"std::string_view, size_t], at least has field_value and "
|
||||
"make sure keep the order of arguments");
|
||||
}
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
template <typename T, typename Visit>
|
||||
inline constexpr void for_each(T&& t, Visit&& func) {
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
using U = remove_cvref_t<T>;
|
||||
constexpr auto Count = members_count_v<U>;
|
||||
if constexpr (Count == 0) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
constexpr auto names = get_member_names<U>();
|
||||
reflect26::for_each_data_member(
|
||||
std::forward<T>(t),
|
||||
[&](auto& field, std::string_view name, auto index) {
|
||||
(void)name;
|
||||
internal::invoke_for_each_field(func, field, names[index], index);
|
||||
});
|
||||
}
|
||||
#else
|
||||
using Tuple = decltype(object_to_tuple(t));
|
||||
constexpr auto Count = std::tuple_size_v<Tuple>;
|
||||
if constexpr (Count == 0) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
using first_t = std::tuple_element_t<0, Tuple>;
|
||||
if constexpr (std::is_invocable_v<Visit, first_t>) {
|
||||
visit_members(t, [&func](auto&... args) {
|
||||
(func(args), ...);
|
||||
});
|
||||
}
|
||||
else {
|
||||
if constexpr (std::is_invocable_v<Visit, first_t, std::string_view>) {
|
||||
visit_members(t, [&](auto&... args) {
|
||||
#if __cplusplus >= 202002L && (!defined(_MSC_VER) || _MSC_VER >= 1930)
|
||||
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
|
||||
constexpr auto arr = get_member_names<T>();
|
||||
(func(args, arr[Is]), ...);
|
||||
}(std::make_index_sequence<sizeof...(args)>{});
|
||||
#else
|
||||
visit_members_impl0<T>(std::forward<Visit>(func),
|
||||
std::make_index_sequence<sizeof...(args)>{},
|
||||
args...);
|
||||
#endif
|
||||
});
|
||||
}
|
||||
else if constexpr (std::is_invocable_v<Visit, first_t, std::string_view,
|
||||
size_t>) {
|
||||
visit_members(t, [&](auto&... args) {
|
||||
#if __cplusplus >= 202002L && (!defined(_MSC_VER) || _MSC_VER >= 1930)
|
||||
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
|
||||
constexpr auto arr = get_member_names<T>();
|
||||
(func(args, arr[Is], Is), ...);
|
||||
}(std::make_index_sequence<sizeof...(args)>{});
|
||||
#else
|
||||
visit_members_impl<T>(std::forward<Visit>(func),
|
||||
std::make_index_sequence<sizeof...(args)>{},
|
||||
args...);
|
||||
#endif
|
||||
});
|
||||
}
|
||||
else {
|
||||
static_assert(sizeof(Visit) < 0,
|
||||
"invalid arguments, full arguments: [field_value&, "
|
||||
"std::string_view, size_t], at least has field_value and "
|
||||
"make sure keep the order of arguments");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ylt::reflection
|
||||
|
||||
#if (defined(__GNUC__) && __GNUC__ > 10) || \
|
||||
((defined(__clang__) || defined(_MSC_VER)) && __has_include(<concepts>))
|
||||
#if __cplusplus >= 202002L
|
||||
template <ylt::reflection::FixedString s>
|
||||
inline constexpr auto operator""_ylts() {
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <tuple>
|
||||
|
||||
#include "internal/common_macro.hpp"
|
||||
|
||||
namespace ylt::reflection {
|
||||
template <typename T, auto... field>
|
||||
struct private_visitor {
|
||||
friend inline constexpr auto get_private_ptrs(
|
||||
const ylt::reflection::identity<T>&) {
|
||||
constexpr auto tp = std::make_tuple(field...);
|
||||
return tp;
|
||||
}
|
||||
};
|
||||
} // namespace ylt::reflection
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
// GCC 16 uses __cpp_impl_reflection; future compilers may use __cpp_reflection
|
||||
#if (defined(__cpp_impl_reflection) && __cpp_impl_reflection >= 202406L) || \
|
||||
(defined(__cpp_reflection) && __cpp_reflection >= 202406L)
|
||||
#define YLT_USE_CXX26_REFLECTION 1
|
||||
#endif
|
||||
@@ -0,0 +1,241 @@
|
||||
#pragma once
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <meta>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace ylt::reflection::reflect26 {
|
||||
|
||||
template <std::size_t N>
|
||||
struct fixed_string {
|
||||
char data[N];
|
||||
|
||||
consteval fixed_string(const char (&str)[N]) {
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
data[i] = str[i];
|
||||
}
|
||||
}
|
||||
|
||||
consteval operator std::string_view() const { return {data, N - 1}; }
|
||||
};
|
||||
|
||||
template <fixed_string Name>
|
||||
struct field_name {
|
||||
static constexpr auto value = Name;
|
||||
};
|
||||
|
||||
template <fixed_string Name>
|
||||
struct struct_name {
|
||||
static constexpr auto value = Name;
|
||||
};
|
||||
|
||||
struct skip_base {};
|
||||
|
||||
struct skip_field {};
|
||||
|
||||
inline constexpr std::string_view normalized_member_name(
|
||||
std::string_view name) {
|
||||
if (name.size() > 3 && name[0] == '_' && name[1] == '_' && name[2] == '_') {
|
||||
name.remove_prefix(3);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
template <std::meta::info Info>
|
||||
using meta_type_t = typename[:std::meta::type_of(Info):];
|
||||
|
||||
template <std::meta::info Info>
|
||||
using remove_cvref_meta_type_t = std::remove_cvref_t<meta_type_t<Info>>;
|
||||
|
||||
template <std::meta::info Info>
|
||||
consteval auto annotations_array() {
|
||||
return std::define_static_array(std::meta::annotations_of(Info));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct is_field_name_annotation : std::false_type {};
|
||||
|
||||
template <fixed_string Name>
|
||||
struct is_field_name_annotation<field_name<Name>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_struct_name_annotation : std::false_type {};
|
||||
|
||||
template <fixed_string Name>
|
||||
struct is_struct_name_annotation<struct_name<Name>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_skip_base_annotation : std::false_type {};
|
||||
|
||||
template <>
|
||||
struct is_skip_base_annotation<skip_base> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_skip_field_annotation : std::false_type {};
|
||||
|
||||
template <>
|
||||
struct is_skip_field_annotation<skip_field> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool skip_base_v = false;
|
||||
|
||||
template <std::meta::info Info, template <typename> typename Predicate>
|
||||
consteval bool has_annotation() {
|
||||
static constexpr auto annotations = annotations_array<Info>();
|
||||
template for (constexpr auto annotation : annotations) {
|
||||
using annotation_t = remove_cvref_meta_type_t<annotation>;
|
||||
if constexpr (Predicate<annotation_t>::value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <std::meta::info Member>
|
||||
consteval std::string_view member_name() {
|
||||
static constexpr auto annotations = annotations_array<Member>();
|
||||
template for (constexpr auto annotation : annotations) {
|
||||
using annotation_t = remove_cvref_meta_type_t<annotation>;
|
||||
if constexpr (is_field_name_annotation<annotation_t>::value) {
|
||||
return annotation_t::value;
|
||||
}
|
||||
}
|
||||
return std::meta::identifier_of(Member);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
consteval std::string_view type_name() {
|
||||
static constexpr auto annotations = annotations_array<^^T>();
|
||||
template for (constexpr auto annotation : annotations) {
|
||||
using annotation_t = remove_cvref_meta_type_t<annotation>;
|
||||
if constexpr (is_struct_name_annotation<annotation_t>::value) {
|
||||
return annotation_t::value;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <std::meta::info Info>
|
||||
consteval bool has_skip_base_annotation() {
|
||||
return has_annotation<Info, is_skip_base_annotation>();
|
||||
}
|
||||
|
||||
template <std::meta::info Base>
|
||||
consteval bool should_skip_base() {
|
||||
using base_type = meta_type_t<Base>;
|
||||
if constexpr (skip_base_v<std::remove_cvref_t<base_type>>) {
|
||||
return true;
|
||||
}
|
||||
else if constexpr (has_skip_base_annotation<Base>()) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return has_skip_base_annotation<std::meta::type_of(Base)>();
|
||||
}
|
||||
}
|
||||
|
||||
template <std::meta::info Member>
|
||||
consteval bool should_skip_field() {
|
||||
return has_annotation<Member, is_skip_field_annotation>();
|
||||
}
|
||||
|
||||
template <std::meta::info Type>
|
||||
consteval void append_data_members(std::vector<std::meta::info>& members) {
|
||||
constexpr auto ctx = std::meta::access_context::unchecked();
|
||||
static constexpr auto bases =
|
||||
std::define_static_array(std::meta::bases_of(Type, ctx));
|
||||
template for (constexpr auto base : bases) {
|
||||
if constexpr (!should_skip_base<base>()) {
|
||||
append_data_members<std::meta::type_of(base)>(members);
|
||||
}
|
||||
}
|
||||
static constexpr auto direct_members =
|
||||
std::define_static_array(std::meta::nonstatic_data_members_of(Type, ctx));
|
||||
template for (constexpr auto member : direct_members) {
|
||||
if constexpr (!should_skip_field<member>()) {
|
||||
members.push_back(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
consteval auto data_members() {
|
||||
std::vector<std::meta::info> members;
|
||||
append_data_members<^^T>(members);
|
||||
return members;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
consteval auto data_members_array() {
|
||||
return std::define_static_array(data_members<std::remove_cvref_t<T>>());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
consteval std::size_t members_count() {
|
||||
return data_members<T>().size();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
consteval auto member_names_array() {
|
||||
static constexpr auto members = data_members_array<T>();
|
||||
std::array<std::string_view, members.size()> names{};
|
||||
[[maybe_unused]] std::size_t index = 0;
|
||||
template for (constexpr auto member : members) {
|
||||
names[index++] = member_name<member>();
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
template <template <typename...> typename Predicate, typename T>
|
||||
consteval std::size_t member_index_if() {
|
||||
static constexpr auto members = data_members_array<T>();
|
||||
std::size_t result = members.size();
|
||||
std::size_t index = 0;
|
||||
template for (constexpr auto member : members) {
|
||||
using member_t = remove_cvref_meta_type_t<member>;
|
||||
if constexpr (Predicate<member_t>::value) {
|
||||
if (result == members.size()) {
|
||||
result = index;
|
||||
}
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename Visitor>
|
||||
constexpr void for_each_data_member(T&& t, Visitor&& visitor) {
|
||||
static constexpr auto members = data_members_array<T>();
|
||||
[[maybe_unused]] std::size_t index = 0;
|
||||
template for (constexpr auto member : members) {
|
||||
visitor(t.[:member:], member_name<member>(), index++);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
consteval auto member_offsets() {
|
||||
static constexpr auto members = data_members_array<T>();
|
||||
std::array<std::size_t, members.size()> offsets{};
|
||||
[[maybe_unused]] std::size_t index = 0;
|
||||
template for (constexpr auto member : members) {
|
||||
auto offset = std::meta::offset_of(member);
|
||||
offsets[index++] = static_cast<std::size_t>(offset.bytes);
|
||||
}
|
||||
return offsets;
|
||||
}
|
||||
|
||||
} // namespace ylt::reflection::reflect26
|
||||
|
||||
namespace ylt::reflection {
|
||||
using reflect26::field_name;
|
||||
using reflect26::fixed_string;
|
||||
using reflect26::skip_base;
|
||||
using reflect26::skip_field;
|
||||
using reflect26::struct_name;
|
||||
} // namespace ylt::reflection
|
||||
#endif // YLT_USE_CXX26_REFLECTION
|
||||
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
#include <meta>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "member_names.hpp"
|
||||
|
||||
namespace ylt::reflection::reflect26 {
|
||||
|
||||
template <typename Func, typename Field>
|
||||
void invoke_dispatch(Func& func, Field& field, std::string_view name,
|
||||
std::size_t index) {
|
||||
if constexpr (std::is_invocable_v<Func&, Field&, std::string_view,
|
||||
std::size_t>) {
|
||||
func(field, name, index);
|
||||
}
|
||||
else if constexpr (std::is_invocable_v<Func&, Field&, std::string_view>) {
|
||||
func(field, name);
|
||||
}
|
||||
else if constexpr (std::is_invocable_v<Func&, Field&>) {
|
||||
func(field);
|
||||
}
|
||||
else {
|
||||
static_assert(sizeof(Func) < 0,
|
||||
"invalid arguments, full arguments: [field_value&, "
|
||||
"std::string_view, size_t], at least has field_value and "
|
||||
"make sure keep the order of arguments");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Func>
|
||||
bool dispatch_by_name(T& obj, std::string_view key, Func&& func) {
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
static constexpr auto members = data_members_array<U>();
|
||||
if constexpr (members.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
static constexpr auto names = ylt::reflection::get_member_names<U>();
|
||||
bool found = false;
|
||||
[[maybe_unused]] std::size_t index = 0;
|
||||
template for (constexpr auto member : members) {
|
||||
if (!found && key == normalized_member_name(names[index])) {
|
||||
invoke_dispatch(func, obj.[:member:], names[index], index);
|
||||
found = true;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t Index, typename T, typename Func>
|
||||
void dispatch_by_index(T& obj, Func&& func) {
|
||||
static constexpr auto members = data_members_array<T>();
|
||||
static_assert(Index < members.size(), "index out of range");
|
||||
func(obj.[:members[Index]:]);
|
||||
}
|
||||
|
||||
template <typename T, typename Func>
|
||||
bool dispatch_by_index(T& obj, std::size_t target, Func&& func) {
|
||||
static constexpr auto members = data_members_array<T>();
|
||||
if constexpr (members.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
bool found = false;
|
||||
[[maybe_unused]] std::size_t index = 0;
|
||||
template for (constexpr auto member : members) {
|
||||
if (!found && index == target) {
|
||||
invoke_dispatch(func, obj.[:member:], member_name<member>(), index);
|
||||
found = true;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ylt::reflection::reflect26
|
||||
#endif // YLT_USE_CXX26_REFLECTION
|
||||
@@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "frozen/string.h"
|
||||
#include "frozen/unordered_map.h"
|
||||
|
||||
namespace ylt::reflection {
|
||||
|
||||
#if defined(__clang__) || defined(_MSC_VER) || \
|
||||
(defined(__GNUC__) && __GNUC__ > 8)
|
||||
|
||||
template <typename T>
|
||||
constexpr std::string_view get_raw_name() {
|
||||
#ifdef _MSC_VER
|
||||
return __FUNCSIG__;
|
||||
#else
|
||||
return __PRETTY_FUNCTION__;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <auto T>
|
||||
constexpr std::string_view get_raw_name() {
|
||||
#ifdef _MSC_VER
|
||||
return __FUNCSIG__;
|
||||
#else
|
||||
return __PRETTY_FUNCTION__;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr std::string_view type_string() {
|
||||
constexpr std::string_view sample = get_raw_name<int>();
|
||||
constexpr size_t prefix_length = sample.find("int");
|
||||
constexpr std::string_view str = get_raw_name<T>();
|
||||
constexpr size_t suffix_length = sample.size() - prefix_length - 3;
|
||||
auto name =
|
||||
str.substr(prefix_length, str.size() - prefix_length - suffix_length);
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
constexpr std::string_view prefixes[] = {"class ", "struct ", "union ",
|
||||
"enum "};
|
||||
for (std::string_view prefix : prefixes) {
|
||||
if (name.size() >= prefix.size() &&
|
||||
name.substr(0, prefix.size()) == prefix) {
|
||||
name.remove_prefix(prefix.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return name;
|
||||
}
|
||||
|
||||
template <auto T>
|
||||
inline constexpr std::string_view enum_string() {
|
||||
constexpr std::string_view sample = get_raw_name<int>();
|
||||
constexpr size_t pos = sample.find("int");
|
||||
constexpr std::string_view str = get_raw_name<T>();
|
||||
constexpr auto next1 = str.rfind(sample[pos + 3]);
|
||||
#if defined(__clang__) || defined(_MSC_VER)
|
||||
constexpr auto s1 = str.substr(pos, next1 - pos);
|
||||
#else
|
||||
constexpr auto s1 = str.substr(pos + 5, next1 - pos - 5);
|
||||
#endif
|
||||
return s1;
|
||||
}
|
||||
|
||||
template <auto field>
|
||||
inline constexpr std::string_view field_string() {
|
||||
constexpr std::string_view raw_name = enum_string<field>();
|
||||
return raw_name.substr(raw_name.rfind(":") + 1);
|
||||
}
|
||||
|
||||
#if defined(__clang__) && (__clang_major__ >= 17)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wenum-constexpr-conversion"
|
||||
#endif
|
||||
|
||||
template <typename E, E V>
|
||||
constexpr std::string_view get_raw_name_with_v() {
|
||||
#ifdef _MSC_VER
|
||||
return __FUNCSIG__;
|
||||
#else
|
||||
return __PRETTY_FUNCTION__;
|
||||
#endif
|
||||
}
|
||||
|
||||
// True means the V is a valid enum value, and the second of the result is the
|
||||
// name
|
||||
template <typename E, E V>
|
||||
constexpr std::pair<bool, std::string_view> try_get_enum_name() {
|
||||
constexpr std::string_view sample_raw_name = get_raw_name_with_v<int, 5>();
|
||||
constexpr size_t pos = sample_raw_name.find("5");
|
||||
constexpr std::string_view enum_raw_name = get_raw_name_with_v<E, V>();
|
||||
constexpr auto enum_end = enum_raw_name.rfind(&sample_raw_name[pos + 1]);
|
||||
#ifdef _MSC_VER
|
||||
constexpr auto enum_begin = enum_raw_name.rfind(',', enum_end) + 1;
|
||||
#else
|
||||
constexpr auto enum_begin = enum_raw_name.rfind(' ', enum_end) + 1;
|
||||
#endif
|
||||
constexpr std::string_view res =
|
||||
enum_raw_name.substr(enum_begin, enum_end - enum_begin);
|
||||
|
||||
constexpr size_t pos_brackets = res.find(')');
|
||||
|
||||
size_t pos_colon = res.find("::");
|
||||
return {pos_brackets == std::string_view::npos,
|
||||
res.substr(pos_colon == std::string_view::npos ? 0 : pos_colon + 2)};
|
||||
}
|
||||
|
||||
// Enumerate the numbers in a integer sequence to see if they are legal enum
|
||||
// value
|
||||
template <typename E, std::int64_t... Is>
|
||||
constexpr inline auto get_enum_arr(
|
||||
const std::integer_sequence<std::int64_t, Is...> &) {
|
||||
constexpr std::size_t N = sizeof...(Is);
|
||||
std::array<std::string_view, N> enum_names = {};
|
||||
std::array<E, N> enum_values = {};
|
||||
std::size_t num = 0;
|
||||
(([&]() {
|
||||
constexpr auto res = try_get_enum_name<E, static_cast<E>(Is)>();
|
||||
if constexpr (res.first) {
|
||||
// the Is is a valid enum value
|
||||
enum_names[num] = res.second;
|
||||
enum_values[num] = static_cast<E>(Is);
|
||||
++num;
|
||||
}
|
||||
})(),
|
||||
...);
|
||||
return std::make_tuple(num, enum_values, enum_names);
|
||||
}
|
||||
|
||||
template <std::size_t N, const std::array<int, N> &arr, size_t... Is>
|
||||
constexpr auto array_to_seq(const std::index_sequence<Is...> &) {
|
||||
return std::integer_sequence<std::int64_t, arr[Is]...>();
|
||||
}
|
||||
|
||||
// convert array to map
|
||||
template <typename E, size_t N, size_t... Is>
|
||||
constexpr inline auto get_enum_to_str_map(
|
||||
const std::array<std::string_view, N> &enum_names,
|
||||
const std::array<E, N> &enum_values, const std::index_sequence<Is...> &) {
|
||||
return frozen::unordered_map<E, frozen::string, sizeof...(Is)>{
|
||||
{enum_values[Is], enum_names[Is]}...};
|
||||
}
|
||||
|
||||
template <typename E, size_t N, size_t... Is>
|
||||
constexpr inline auto get_str_to_enum_map(
|
||||
const std::array<std::string_view, N> &enum_names,
|
||||
const std::array<E, N> &enum_values, const std::index_sequence<Is...> &) {
|
||||
return frozen::unordered_map<frozen::string, E, sizeof...(Is)>{
|
||||
{enum_names[Is], enum_values[Is]}...};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// the default generic enum_value
|
||||
// if the user has not defined a specialization template, this will be called
|
||||
template <typename T>
|
||||
struct enum_value {
|
||||
constexpr static std::array<int, 0> value = {};
|
||||
};
|
||||
|
||||
template <bool str_to_enum, typename E>
|
||||
constexpr inline auto get_enum_map() {
|
||||
#if defined(__clang__) || defined(_MSC_VER) || \
|
||||
(defined(__GNUC__) && __GNUC__ > 8)
|
||||
constexpr auto &arr = enum_value<E>::value;
|
||||
constexpr auto arr_size = arr.size();
|
||||
if constexpr (arr_size > 0) {
|
||||
// the user has defined a specialization template
|
||||
constexpr auto arr_seq =
|
||||
array_to_seq<arr_size, arr>(std::make_index_sequence<arr_size>());
|
||||
constexpr auto t = get_enum_arr<E>(arr_seq);
|
||||
if constexpr (str_to_enum) {
|
||||
return get_str_to_enum_map<E, arr_size>(
|
||||
std::get<2>(t), std::get<1>(t),
|
||||
std::make_index_sequence<std::get<0>(t)>{});
|
||||
}
|
||||
else {
|
||||
return get_enum_to_str_map<E, arr_size>(
|
||||
std::get<2>(t), std::get<1>(t),
|
||||
std::make_index_sequence<std::get<0>(t)>{});
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__clang__) && (__clang_major__ >= 17)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
} // namespace ylt::reflection
|
||||
@@ -0,0 +1,535 @@
|
||||
#pragma once
|
||||
|
||||
namespace ylt::reflection {
|
||||
[[noreturn]] inline void unreachable() {
|
||||
// Uses compiler specific extensions if possible.
|
||||
// Even if no extension is used, undefined behavior is still raised by
|
||||
// an empty function body and the noreturn attribute.
|
||||
#ifdef __GNUC__ // GCC, Clang, ICC
|
||||
__builtin_unreachable();
|
||||
#elif defined(_MSC_VER) // msvc
|
||||
__assume(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Func, typename... Args>
|
||||
constexpr decltype(auto) inline template_switch(size_t index, Args &&...args) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return Func::template run<0>(std::forward<Args>(args)...);
|
||||
case 1:
|
||||
return Func::template run<1>(std::forward<Args>(args)...);
|
||||
case 2:
|
||||
return Func::template run<2>(std::forward<Args>(args)...);
|
||||
case 3:
|
||||
return Func::template run<3>(std::forward<Args>(args)...);
|
||||
case 4:
|
||||
return Func::template run<4>(std::forward<Args>(args)...);
|
||||
case 5:
|
||||
return Func::template run<5>(std::forward<Args>(args)...);
|
||||
case 6:
|
||||
return Func::template run<6>(std::forward<Args>(args)...);
|
||||
case 7:
|
||||
return Func::template run<7>(std::forward<Args>(args)...);
|
||||
case 8:
|
||||
return Func::template run<8>(std::forward<Args>(args)...);
|
||||
case 9:
|
||||
return Func::template run<9>(std::forward<Args>(args)...);
|
||||
case 10:
|
||||
return Func::template run<10>(std::forward<Args>(args)...);
|
||||
case 11:
|
||||
return Func::template run<11>(std::forward<Args>(args)...);
|
||||
case 12:
|
||||
return Func::template run<12>(std::forward<Args>(args)...);
|
||||
case 13:
|
||||
return Func::template run<13>(std::forward<Args>(args)...);
|
||||
case 14:
|
||||
return Func::template run<14>(std::forward<Args>(args)...);
|
||||
case 15:
|
||||
return Func::template run<15>(std::forward<Args>(args)...);
|
||||
case 16:
|
||||
return Func::template run<16>(std::forward<Args>(args)...);
|
||||
case 17:
|
||||
return Func::template run<17>(std::forward<Args>(args)...);
|
||||
case 18:
|
||||
return Func::template run<18>(std::forward<Args>(args)...);
|
||||
case 19:
|
||||
return Func::template run<19>(std::forward<Args>(args)...);
|
||||
case 20:
|
||||
return Func::template run<20>(std::forward<Args>(args)...);
|
||||
case 21:
|
||||
return Func::template run<21>(std::forward<Args>(args)...);
|
||||
case 22:
|
||||
return Func::template run<22>(std::forward<Args>(args)...);
|
||||
case 23:
|
||||
return Func::template run<23>(std::forward<Args>(args)...);
|
||||
case 24:
|
||||
return Func::template run<24>(std::forward<Args>(args)...);
|
||||
case 25:
|
||||
return Func::template run<25>(std::forward<Args>(args)...);
|
||||
case 26:
|
||||
return Func::template run<26>(std::forward<Args>(args)...);
|
||||
case 27:
|
||||
return Func::template run<27>(std::forward<Args>(args)...);
|
||||
case 28:
|
||||
return Func::template run<28>(std::forward<Args>(args)...);
|
||||
case 29:
|
||||
return Func::template run<29>(std::forward<Args>(args)...);
|
||||
case 30:
|
||||
return Func::template run<30>(std::forward<Args>(args)...);
|
||||
case 31:
|
||||
return Func::template run<31>(std::forward<Args>(args)...);
|
||||
case 32:
|
||||
return Func::template run<32>(std::forward<Args>(args)...);
|
||||
case 33:
|
||||
return Func::template run<33>(std::forward<Args>(args)...);
|
||||
case 34:
|
||||
return Func::template run<34>(std::forward<Args>(args)...);
|
||||
case 35:
|
||||
return Func::template run<35>(std::forward<Args>(args)...);
|
||||
case 36:
|
||||
return Func::template run<36>(std::forward<Args>(args)...);
|
||||
case 37:
|
||||
return Func::template run<37>(std::forward<Args>(args)...);
|
||||
case 38:
|
||||
return Func::template run<38>(std::forward<Args>(args)...);
|
||||
case 39:
|
||||
return Func::template run<39>(std::forward<Args>(args)...);
|
||||
case 40:
|
||||
return Func::template run<40>(std::forward<Args>(args)...);
|
||||
case 41:
|
||||
return Func::template run<41>(std::forward<Args>(args)...);
|
||||
case 42:
|
||||
return Func::template run<42>(std::forward<Args>(args)...);
|
||||
case 43:
|
||||
return Func::template run<43>(std::forward<Args>(args)...);
|
||||
case 44:
|
||||
return Func::template run<44>(std::forward<Args>(args)...);
|
||||
case 45:
|
||||
return Func::template run<45>(std::forward<Args>(args)...);
|
||||
case 46:
|
||||
return Func::template run<46>(std::forward<Args>(args)...);
|
||||
case 47:
|
||||
return Func::template run<47>(std::forward<Args>(args)...);
|
||||
case 48:
|
||||
return Func::template run<48>(std::forward<Args>(args)...);
|
||||
case 49:
|
||||
return Func::template run<49>(std::forward<Args>(args)...);
|
||||
case 50:
|
||||
return Func::template run<50>(std::forward<Args>(args)...);
|
||||
case 51:
|
||||
return Func::template run<51>(std::forward<Args>(args)...);
|
||||
case 52:
|
||||
return Func::template run<52>(std::forward<Args>(args)...);
|
||||
case 53:
|
||||
return Func::template run<53>(std::forward<Args>(args)...);
|
||||
case 54:
|
||||
return Func::template run<54>(std::forward<Args>(args)...);
|
||||
case 55:
|
||||
return Func::template run<55>(std::forward<Args>(args)...);
|
||||
case 56:
|
||||
return Func::template run<56>(std::forward<Args>(args)...);
|
||||
case 57:
|
||||
return Func::template run<57>(std::forward<Args>(args)...);
|
||||
case 58:
|
||||
return Func::template run<58>(std::forward<Args>(args)...);
|
||||
case 59:
|
||||
return Func::template run<59>(std::forward<Args>(args)...);
|
||||
case 60:
|
||||
return Func::template run<60>(std::forward<Args>(args)...);
|
||||
case 61:
|
||||
return Func::template run<61>(std::forward<Args>(args)...);
|
||||
case 62:
|
||||
return Func::template run<62>(std::forward<Args>(args)...);
|
||||
case 63:
|
||||
return Func::template run<63>(std::forward<Args>(args)...);
|
||||
case 64:
|
||||
return Func::template run<64>(std::forward<Args>(args)...);
|
||||
case 65:
|
||||
return Func::template run<65>(std::forward<Args>(args)...);
|
||||
case 66:
|
||||
return Func::template run<66>(std::forward<Args>(args)...);
|
||||
case 67:
|
||||
return Func::template run<67>(std::forward<Args>(args)...);
|
||||
case 68:
|
||||
return Func::template run<68>(std::forward<Args>(args)...);
|
||||
case 69:
|
||||
return Func::template run<69>(std::forward<Args>(args)...);
|
||||
case 70:
|
||||
return Func::template run<70>(std::forward<Args>(args)...);
|
||||
case 71:
|
||||
return Func::template run<71>(std::forward<Args>(args)...);
|
||||
case 72:
|
||||
return Func::template run<72>(std::forward<Args>(args)...);
|
||||
case 73:
|
||||
return Func::template run<73>(std::forward<Args>(args)...);
|
||||
case 74:
|
||||
return Func::template run<74>(std::forward<Args>(args)...);
|
||||
case 75:
|
||||
return Func::template run<75>(std::forward<Args>(args)...);
|
||||
case 76:
|
||||
return Func::template run<76>(std::forward<Args>(args)...);
|
||||
case 77:
|
||||
return Func::template run<77>(std::forward<Args>(args)...);
|
||||
case 78:
|
||||
return Func::template run<78>(std::forward<Args>(args)...);
|
||||
case 79:
|
||||
return Func::template run<79>(std::forward<Args>(args)...);
|
||||
case 80:
|
||||
return Func::template run<80>(std::forward<Args>(args)...);
|
||||
case 81:
|
||||
return Func::template run<81>(std::forward<Args>(args)...);
|
||||
case 82:
|
||||
return Func::template run<82>(std::forward<Args>(args)...);
|
||||
case 83:
|
||||
return Func::template run<83>(std::forward<Args>(args)...);
|
||||
case 84:
|
||||
return Func::template run<84>(std::forward<Args>(args)...);
|
||||
case 85:
|
||||
return Func::template run<85>(std::forward<Args>(args)...);
|
||||
case 86:
|
||||
return Func::template run<86>(std::forward<Args>(args)...);
|
||||
case 87:
|
||||
return Func::template run<87>(std::forward<Args>(args)...);
|
||||
case 88:
|
||||
return Func::template run<88>(std::forward<Args>(args)...);
|
||||
case 89:
|
||||
return Func::template run<89>(std::forward<Args>(args)...);
|
||||
case 90:
|
||||
return Func::template run<90>(std::forward<Args>(args)...);
|
||||
case 91:
|
||||
return Func::template run<91>(std::forward<Args>(args)...);
|
||||
case 92:
|
||||
return Func::template run<92>(std::forward<Args>(args)...);
|
||||
case 93:
|
||||
return Func::template run<93>(std::forward<Args>(args)...);
|
||||
case 94:
|
||||
return Func::template run<94>(std::forward<Args>(args)...);
|
||||
case 95:
|
||||
return Func::template run<95>(std::forward<Args>(args)...);
|
||||
case 96:
|
||||
return Func::template run<96>(std::forward<Args>(args)...);
|
||||
case 97:
|
||||
return Func::template run<97>(std::forward<Args>(args)...);
|
||||
case 98:
|
||||
return Func::template run<98>(std::forward<Args>(args)...);
|
||||
case 99:
|
||||
return Func::template run<99>(std::forward<Args>(args)...);
|
||||
case 100:
|
||||
return Func::template run<100>(std::forward<Args>(args)...);
|
||||
case 101:
|
||||
return Func::template run<101>(std::forward<Args>(args)...);
|
||||
case 102:
|
||||
return Func::template run<102>(std::forward<Args>(args)...);
|
||||
case 103:
|
||||
return Func::template run<103>(std::forward<Args>(args)...);
|
||||
case 104:
|
||||
return Func::template run<104>(std::forward<Args>(args)...);
|
||||
case 105:
|
||||
return Func::template run<105>(std::forward<Args>(args)...);
|
||||
case 106:
|
||||
return Func::template run<106>(std::forward<Args>(args)...);
|
||||
case 107:
|
||||
return Func::template run<107>(std::forward<Args>(args)...);
|
||||
case 108:
|
||||
return Func::template run<108>(std::forward<Args>(args)...);
|
||||
case 109:
|
||||
return Func::template run<109>(std::forward<Args>(args)...);
|
||||
case 110:
|
||||
return Func::template run<110>(std::forward<Args>(args)...);
|
||||
case 111:
|
||||
return Func::template run<111>(std::forward<Args>(args)...);
|
||||
case 112:
|
||||
return Func::template run<112>(std::forward<Args>(args)...);
|
||||
case 113:
|
||||
return Func::template run<113>(std::forward<Args>(args)...);
|
||||
case 114:
|
||||
return Func::template run<114>(std::forward<Args>(args)...);
|
||||
case 115:
|
||||
return Func::template run<115>(std::forward<Args>(args)...);
|
||||
case 116:
|
||||
return Func::template run<116>(std::forward<Args>(args)...);
|
||||
case 117:
|
||||
return Func::template run<117>(std::forward<Args>(args)...);
|
||||
case 118:
|
||||
return Func::template run<118>(std::forward<Args>(args)...);
|
||||
case 119:
|
||||
return Func::template run<119>(std::forward<Args>(args)...);
|
||||
case 120:
|
||||
return Func::template run<120>(std::forward<Args>(args)...);
|
||||
case 121:
|
||||
return Func::template run<121>(std::forward<Args>(args)...);
|
||||
case 122:
|
||||
return Func::template run<122>(std::forward<Args>(args)...);
|
||||
case 123:
|
||||
return Func::template run<123>(std::forward<Args>(args)...);
|
||||
case 124:
|
||||
return Func::template run<124>(std::forward<Args>(args)...);
|
||||
case 125:
|
||||
return Func::template run<125>(std::forward<Args>(args)...);
|
||||
case 126:
|
||||
return Func::template run<126>(std::forward<Args>(args)...);
|
||||
case 127:
|
||||
return Func::template run<127>(std::forward<Args>(args)...);
|
||||
case 128:
|
||||
return Func::template run<128>(std::forward<Args>(args)...);
|
||||
case 129:
|
||||
return Func::template run<129>(std::forward<Args>(args)...);
|
||||
case 130:
|
||||
return Func::template run<130>(std::forward<Args>(args)...);
|
||||
case 131:
|
||||
return Func::template run<131>(std::forward<Args>(args)...);
|
||||
case 132:
|
||||
return Func::template run<132>(std::forward<Args>(args)...);
|
||||
case 133:
|
||||
return Func::template run<133>(std::forward<Args>(args)...);
|
||||
case 134:
|
||||
return Func::template run<134>(std::forward<Args>(args)...);
|
||||
case 135:
|
||||
return Func::template run<135>(std::forward<Args>(args)...);
|
||||
case 136:
|
||||
return Func::template run<136>(std::forward<Args>(args)...);
|
||||
case 137:
|
||||
return Func::template run<137>(std::forward<Args>(args)...);
|
||||
case 138:
|
||||
return Func::template run<138>(std::forward<Args>(args)...);
|
||||
case 139:
|
||||
return Func::template run<139>(std::forward<Args>(args)...);
|
||||
case 140:
|
||||
return Func::template run<140>(std::forward<Args>(args)...);
|
||||
case 141:
|
||||
return Func::template run<141>(std::forward<Args>(args)...);
|
||||
case 142:
|
||||
return Func::template run<142>(std::forward<Args>(args)...);
|
||||
case 143:
|
||||
return Func::template run<143>(std::forward<Args>(args)...);
|
||||
case 144:
|
||||
return Func::template run<144>(std::forward<Args>(args)...);
|
||||
case 145:
|
||||
return Func::template run<145>(std::forward<Args>(args)...);
|
||||
case 146:
|
||||
return Func::template run<146>(std::forward<Args>(args)...);
|
||||
case 147:
|
||||
return Func::template run<147>(std::forward<Args>(args)...);
|
||||
case 148:
|
||||
return Func::template run<148>(std::forward<Args>(args)...);
|
||||
case 149:
|
||||
return Func::template run<149>(std::forward<Args>(args)...);
|
||||
case 150:
|
||||
return Func::template run<150>(std::forward<Args>(args)...);
|
||||
case 151:
|
||||
return Func::template run<151>(std::forward<Args>(args)...);
|
||||
case 152:
|
||||
return Func::template run<152>(std::forward<Args>(args)...);
|
||||
case 153:
|
||||
return Func::template run<153>(std::forward<Args>(args)...);
|
||||
case 154:
|
||||
return Func::template run<154>(std::forward<Args>(args)...);
|
||||
case 155:
|
||||
return Func::template run<155>(std::forward<Args>(args)...);
|
||||
case 156:
|
||||
return Func::template run<156>(std::forward<Args>(args)...);
|
||||
case 157:
|
||||
return Func::template run<157>(std::forward<Args>(args)...);
|
||||
case 158:
|
||||
return Func::template run<158>(std::forward<Args>(args)...);
|
||||
case 159:
|
||||
return Func::template run<159>(std::forward<Args>(args)...);
|
||||
case 160:
|
||||
return Func::template run<160>(std::forward<Args>(args)...);
|
||||
case 161:
|
||||
return Func::template run<161>(std::forward<Args>(args)...);
|
||||
case 162:
|
||||
return Func::template run<162>(std::forward<Args>(args)...);
|
||||
case 163:
|
||||
return Func::template run<163>(std::forward<Args>(args)...);
|
||||
case 164:
|
||||
return Func::template run<164>(std::forward<Args>(args)...);
|
||||
case 165:
|
||||
return Func::template run<165>(std::forward<Args>(args)...);
|
||||
case 166:
|
||||
return Func::template run<166>(std::forward<Args>(args)...);
|
||||
case 167:
|
||||
return Func::template run<167>(std::forward<Args>(args)...);
|
||||
case 168:
|
||||
return Func::template run<168>(std::forward<Args>(args)...);
|
||||
case 169:
|
||||
return Func::template run<169>(std::forward<Args>(args)...);
|
||||
case 170:
|
||||
return Func::template run<170>(std::forward<Args>(args)...);
|
||||
case 171:
|
||||
return Func::template run<171>(std::forward<Args>(args)...);
|
||||
case 172:
|
||||
return Func::template run<172>(std::forward<Args>(args)...);
|
||||
case 173:
|
||||
return Func::template run<173>(std::forward<Args>(args)...);
|
||||
case 174:
|
||||
return Func::template run<174>(std::forward<Args>(args)...);
|
||||
case 175:
|
||||
return Func::template run<175>(std::forward<Args>(args)...);
|
||||
case 176:
|
||||
return Func::template run<176>(std::forward<Args>(args)...);
|
||||
case 177:
|
||||
return Func::template run<177>(std::forward<Args>(args)...);
|
||||
case 178:
|
||||
return Func::template run<178>(std::forward<Args>(args)...);
|
||||
case 179:
|
||||
return Func::template run<179>(std::forward<Args>(args)...);
|
||||
case 180:
|
||||
return Func::template run<180>(std::forward<Args>(args)...);
|
||||
case 181:
|
||||
return Func::template run<181>(std::forward<Args>(args)...);
|
||||
case 182:
|
||||
return Func::template run<182>(std::forward<Args>(args)...);
|
||||
case 183:
|
||||
return Func::template run<183>(std::forward<Args>(args)...);
|
||||
case 184:
|
||||
return Func::template run<184>(std::forward<Args>(args)...);
|
||||
case 185:
|
||||
return Func::template run<185>(std::forward<Args>(args)...);
|
||||
case 186:
|
||||
return Func::template run<186>(std::forward<Args>(args)...);
|
||||
case 187:
|
||||
return Func::template run<187>(std::forward<Args>(args)...);
|
||||
case 188:
|
||||
return Func::template run<188>(std::forward<Args>(args)...);
|
||||
case 189:
|
||||
return Func::template run<189>(std::forward<Args>(args)...);
|
||||
case 190:
|
||||
return Func::template run<190>(std::forward<Args>(args)...);
|
||||
case 191:
|
||||
return Func::template run<191>(std::forward<Args>(args)...);
|
||||
case 192:
|
||||
return Func::template run<192>(std::forward<Args>(args)...);
|
||||
case 193:
|
||||
return Func::template run<193>(std::forward<Args>(args)...);
|
||||
case 194:
|
||||
return Func::template run<194>(std::forward<Args>(args)...);
|
||||
case 195:
|
||||
return Func::template run<195>(std::forward<Args>(args)...);
|
||||
case 196:
|
||||
return Func::template run<196>(std::forward<Args>(args)...);
|
||||
case 197:
|
||||
return Func::template run<197>(std::forward<Args>(args)...);
|
||||
case 198:
|
||||
return Func::template run<198>(std::forward<Args>(args)...);
|
||||
case 199:
|
||||
return Func::template run<199>(std::forward<Args>(args)...);
|
||||
case 200:
|
||||
return Func::template run<200>(std::forward<Args>(args)...);
|
||||
case 201:
|
||||
return Func::template run<201>(std::forward<Args>(args)...);
|
||||
case 202:
|
||||
return Func::template run<202>(std::forward<Args>(args)...);
|
||||
case 203:
|
||||
return Func::template run<203>(std::forward<Args>(args)...);
|
||||
case 204:
|
||||
return Func::template run<204>(std::forward<Args>(args)...);
|
||||
case 205:
|
||||
return Func::template run<205>(std::forward<Args>(args)...);
|
||||
case 206:
|
||||
return Func::template run<206>(std::forward<Args>(args)...);
|
||||
case 207:
|
||||
return Func::template run<207>(std::forward<Args>(args)...);
|
||||
case 208:
|
||||
return Func::template run<208>(std::forward<Args>(args)...);
|
||||
case 209:
|
||||
return Func::template run<209>(std::forward<Args>(args)...);
|
||||
case 210:
|
||||
return Func::template run<210>(std::forward<Args>(args)...);
|
||||
case 211:
|
||||
return Func::template run<211>(std::forward<Args>(args)...);
|
||||
case 212:
|
||||
return Func::template run<212>(std::forward<Args>(args)...);
|
||||
case 213:
|
||||
return Func::template run<213>(std::forward<Args>(args)...);
|
||||
case 214:
|
||||
return Func::template run<214>(std::forward<Args>(args)...);
|
||||
case 215:
|
||||
return Func::template run<215>(std::forward<Args>(args)...);
|
||||
case 216:
|
||||
return Func::template run<216>(std::forward<Args>(args)...);
|
||||
case 217:
|
||||
return Func::template run<217>(std::forward<Args>(args)...);
|
||||
case 218:
|
||||
return Func::template run<218>(std::forward<Args>(args)...);
|
||||
case 219:
|
||||
return Func::template run<219>(std::forward<Args>(args)...);
|
||||
case 220:
|
||||
return Func::template run<220>(std::forward<Args>(args)...);
|
||||
case 221:
|
||||
return Func::template run<221>(std::forward<Args>(args)...);
|
||||
case 222:
|
||||
return Func::template run<222>(std::forward<Args>(args)...);
|
||||
case 223:
|
||||
return Func::template run<223>(std::forward<Args>(args)...);
|
||||
case 224:
|
||||
return Func::template run<224>(std::forward<Args>(args)...);
|
||||
case 225:
|
||||
return Func::template run<225>(std::forward<Args>(args)...);
|
||||
case 226:
|
||||
return Func::template run<226>(std::forward<Args>(args)...);
|
||||
case 227:
|
||||
return Func::template run<227>(std::forward<Args>(args)...);
|
||||
case 228:
|
||||
return Func::template run<228>(std::forward<Args>(args)...);
|
||||
case 229:
|
||||
return Func::template run<229>(std::forward<Args>(args)...);
|
||||
case 230:
|
||||
return Func::template run<230>(std::forward<Args>(args)...);
|
||||
case 231:
|
||||
return Func::template run<231>(std::forward<Args>(args)...);
|
||||
case 232:
|
||||
return Func::template run<232>(std::forward<Args>(args)...);
|
||||
case 233:
|
||||
return Func::template run<233>(std::forward<Args>(args)...);
|
||||
case 234:
|
||||
return Func::template run<234>(std::forward<Args>(args)...);
|
||||
case 235:
|
||||
return Func::template run<235>(std::forward<Args>(args)...);
|
||||
case 236:
|
||||
return Func::template run<236>(std::forward<Args>(args)...);
|
||||
case 237:
|
||||
return Func::template run<237>(std::forward<Args>(args)...);
|
||||
case 238:
|
||||
return Func::template run<238>(std::forward<Args>(args)...);
|
||||
case 239:
|
||||
return Func::template run<239>(std::forward<Args>(args)...);
|
||||
case 240:
|
||||
return Func::template run<240>(std::forward<Args>(args)...);
|
||||
case 241:
|
||||
return Func::template run<241>(std::forward<Args>(args)...);
|
||||
case 242:
|
||||
return Func::template run<242>(std::forward<Args>(args)...);
|
||||
case 243:
|
||||
return Func::template run<243>(std::forward<Args>(args)...);
|
||||
case 244:
|
||||
return Func::template run<244>(std::forward<Args>(args)...);
|
||||
case 245:
|
||||
return Func::template run<245>(std::forward<Args>(args)...);
|
||||
case 246:
|
||||
return Func::template run<246>(std::forward<Args>(args)...);
|
||||
case 247:
|
||||
return Func::template run<247>(std::forward<Args>(args)...);
|
||||
case 248:
|
||||
return Func::template run<248>(std::forward<Args>(args)...);
|
||||
case 249:
|
||||
return Func::template run<249>(std::forward<Args>(args)...);
|
||||
case 250:
|
||||
return Func::template run<250>(std::forward<Args>(args)...);
|
||||
case 251:
|
||||
return Func::template run<251>(std::forward<Args>(args)...);
|
||||
case 252:
|
||||
return Func::template run<252>(std::forward<Args>(args)...);
|
||||
case 253:
|
||||
return Func::template run<253>(std::forward<Args>(args)...);
|
||||
case 254:
|
||||
return Func::template run<254>(std::forward<Args>(args)...);
|
||||
case 255:
|
||||
return Func::template run<255>(std::forward<Args>(args)...);
|
||||
default:
|
||||
unreachable();
|
||||
// index shouldn't bigger than 256
|
||||
}
|
||||
}
|
||||
} // namespace ylt::reflection
|
||||
@@ -0,0 +1,178 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "internal/arg_list_macro.hpp"
|
||||
#include "reflect26_compat.hpp"
|
||||
|
||||
namespace ylt::reflection {
|
||||
template <typename T>
|
||||
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
|
||||
template <class T>
|
||||
struct member_traits {
|
||||
using value_type = T;
|
||||
};
|
||||
|
||||
template <class T, class Owner>
|
||||
struct member_traits<T Owner::*> {
|
||||
using owner_type = Owner;
|
||||
using value_type = T;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using member_value_type_t = typename member_traits<T>::value_type;
|
||||
|
||||
#define YLT_REFL(STRUCT, ...) \
|
||||
template <typename Visitor> \
|
||||
inline static constexpr decltype(auto) refl_visit_members( \
|
||||
STRUCT &t, Visitor &&visitor) { \
|
||||
return visitor(WRAP_ARGS(CONCAT_MEMBER, t, ##__VA_ARGS__)); \
|
||||
} \
|
||||
template <typename Visitor> \
|
||||
inline static constexpr decltype(auto) refl_visit_members( \
|
||||
const STRUCT &t, Visitor &&visitor) { \
|
||||
return visitor(WRAP_ARGS(CONCAT_MEMBER, t, ##__VA_ARGS__)); \
|
||||
} \
|
||||
[[maybe_unused]] inline static decltype(auto) refl_object_to_tuple( \
|
||||
STRUCT &t) { \
|
||||
return std::tie(WRAP_ARGS(CONCAT_MEMBER, t, ##__VA_ARGS__)); \
|
||||
} \
|
||||
[[maybe_unused]] inline static decltype(auto) refl_object_to_tuple( \
|
||||
const STRUCT &t) { \
|
||||
return std::tie(WRAP_ARGS(CONCAT_MEMBER, t, ##__VA_ARGS__)); \
|
||||
} \
|
||||
[[maybe_unused]] inline static constexpr decltype(auto) refl_member_names( \
|
||||
const ylt::reflection::identity<STRUCT> &t) { \
|
||||
constexpr std::array<std::string_view, YLT_ARG_COUNT(__VA_ARGS__)> arr{ \
|
||||
WRAP_ARGS(CONCAT_NAME, t, ##__VA_ARGS__)}; \
|
||||
return arr; \
|
||||
} \
|
||||
[[maybe_unused]] inline static constexpr std::size_t refl_member_count( \
|
||||
const ylt::reflection::identity<STRUCT> &t) { \
|
||||
return (std::size_t)YLT_ARG_COUNT(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
template <typename T, typename Tuple, typename Visitor>
|
||||
inline constexpr auto visit_private_fields_impl(T &&t, const Tuple &tp,
|
||||
Visitor &&visitor) {
|
||||
return std::apply(
|
||||
[&](auto... args) {
|
||||
return visitor(t.*args...);
|
||||
},
|
||||
tp);
|
||||
}
|
||||
|
||||
template <typename T, typename Visitor>
|
||||
inline constexpr auto visit_private_fields(T &&t, Visitor &&visitor) {
|
||||
auto tp = get_private_ptrs(
|
||||
identity<std::remove_const_t<std::remove_reference_t<T>>>{});
|
||||
return visit_private_fields_impl(std::forward<T>(t), tp, visitor);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static decltype(auto) refl_object_to_tuple_impl(T &&t) {
|
||||
auto tp = get_private_ptrs(
|
||||
identity<std::remove_const_t<std::remove_reference_t<T>>>{});
|
||||
auto to_ref = [&t](auto... fields) {
|
||||
return std::tie(t.*fields...);
|
||||
};
|
||||
return std::apply(to_ref, tp);
|
||||
}
|
||||
|
||||
#define YLT_REFL_PRIVATE_(STRUCT, ...) \
|
||||
namespace ylt::reflection { \
|
||||
inline constexpr auto get_private_ptrs(const identity<STRUCT> &t); \
|
||||
template struct private_visitor<STRUCT, ##__VA_ARGS__>; \
|
||||
} \
|
||||
template <typename Visitor> \
|
||||
inline static constexpr decltype(auto) refl_visit_members( \
|
||||
STRUCT &t, Visitor &&visitor) { \
|
||||
return visit_private_fields(t, std::forward<Visitor>(visitor)); \
|
||||
} \
|
||||
template <typename Visitor> \
|
||||
inline static constexpr decltype(auto) refl_visit_members( \
|
||||
const STRUCT &t, Visitor &&visitor) { \
|
||||
return visit_private_fields(t, std::forward<Visitor>(visitor)); \
|
||||
} \
|
||||
[[maybe_unused]] inline static decltype(auto) refl_object_to_tuple( \
|
||||
STRUCT &t) { \
|
||||
return refl_object_to_tuple_impl(t); \
|
||||
} \
|
||||
[[maybe_unused]] inline static decltype(auto) refl_object_to_tuple( \
|
||||
const STRUCT &t) { \
|
||||
return refl_object_to_tuple_impl(t); \
|
||||
} \
|
||||
[[maybe_unused]] inline static constexpr std::size_t refl_member_count( \
|
||||
const ylt::reflection::identity<STRUCT> &t) { \
|
||||
return (std::size_t)YLT_ARG_COUNT(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define YLT_REFL_PRIVATE(STRUCT, ...) \
|
||||
[[maybe_unused]] inline static constexpr decltype(auto) refl_member_names( \
|
||||
const ylt::reflection::identity<STRUCT> &t) { \
|
||||
constexpr std::array<std::string_view, YLT_ARG_COUNT(__VA_ARGS__)> arr{ \
|
||||
WRAP_ARGS(CONCAT_NAME, t, ##__VA_ARGS__)}; \
|
||||
return arr; \
|
||||
} \
|
||||
YLT_REFL_PRIVATE_(STRUCT, WRAP_ARGS(CONCAT_ADDR, STRUCT, ##__VA_ARGS__))
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct is_out_ylt_refl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_out_ylt_refl<T, std::void_t<decltype(refl_member_count(
|
||||
std::declval<ylt::reflection::identity<T>>()))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_out_ylt_refl_v = is_out_ylt_refl<T>::value;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct is_inner_ylt_refl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_inner_ylt_refl<
|
||||
T, std::void_t<decltype(std::declval<T>().refl_member_count(
|
||||
std::declval<ylt::reflection::identity<T>>()))>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_inner_ylt_refl_v = is_inner_ylt_refl<T>::value;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct is_ylt_refl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_ylt_refl_v = is_ylt_refl<remove_cvref_t<T>>::value;
|
||||
|
||||
template <typename T>
|
||||
struct is_ylt_refl<T, std::enable_if_t<is_inner_ylt_refl_v<T>>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_ylt_refl<T, std::enable_if_t<is_out_ylt_refl_v<T>>> : std::true_type {
|
||||
};
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct is_custom_reflect : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_custom_reflect<
|
||||
T, std::void_t<decltype(ylt_custom_reflect((T *)nullptr))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_custom_refl_v =
|
||||
is_custom_reflect<remove_cvref_t<T>>::value;
|
||||
#ifdef YLT_USE_CXX26_REFLECTION
|
||||
#undef YLT_REFL
|
||||
#undef YLT_REFL_PRIVATE
|
||||
#undef YLT_REFL_PRIVATE_
|
||||
#define YLT_REFL(STRUCT, ...)
|
||||
#define YLT_REFL_PRIVATE(STRUCT, ...)
|
||||
#define YLT_REFL_PRIVATE_(STRUCT, ...)
|
||||
#endif
|
||||
|
||||
} // namespace ylt::reflection
|
||||
+2464
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,510 @@
|
||||
#pragma once
|
||||
|
||||
#include <asio/any_io_executor.hpp>
|
||||
#include <asio/awaitable.hpp>
|
||||
#include <asio/bind_executor.hpp>
|
||||
#include <asio/co_spawn.hpp>
|
||||
#include <asio/detached.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#include <asio/strand.hpp>
|
||||
#include <atomic>
|
||||
#include <coroutine>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#ifndef ORMPP_ASIO_ASYNC_SIMPLE_ADAPTER_HAS_CANCELLATION
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<asio/bind_cancellation_slot.hpp>) && \
|
||||
__has_include(<asio/cancellation_signal.hpp>) && \
|
||||
__has_include(<asio/cancellation_type.hpp>)
|
||||
#define ORMPP_ASIO_ASYNC_SIMPLE_ADAPTER_HAS_CANCELLATION 1
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ORMPP_ASIO_ASYNC_SIMPLE_ADAPTER_HAS_CANCELLATION
|
||||
#define ORMPP_ASIO_ASYNC_SIMPLE_ADAPTER_HAS_CANCELLATION 0
|
||||
#endif
|
||||
|
||||
#if ORMPP_ASIO_ASYNC_SIMPLE_ADAPTER_HAS_CANCELLATION
|
||||
#include <asio/bind_cancellation_slot.hpp>
|
||||
#include <asio/cancellation_signal.hpp>
|
||||
#include <asio/cancellation_type.hpp>
|
||||
#endif
|
||||
|
||||
#if defined(__has_include) && __has_include(<async_simple/Executor.h>)
|
||||
#include <async_simple/Executor.h>
|
||||
#define ORMPP_HAS_ASYNC_SIMPLE_EXECUTOR 1
|
||||
#else
|
||||
namespace async_simple {
|
||||
class Executor;
|
||||
} // namespace async_simple
|
||||
#define ORMPP_HAS_ASYNC_SIMPLE_EXECUTOR 0
|
||||
#endif
|
||||
|
||||
namespace adapter {
|
||||
|
||||
// Adapter: bridges asio::awaitable to async_simple::coro::Lazy
|
||||
// Allows async_simple coroutines to co_await asio operations
|
||||
// Both frameworks share the same io_context for zero-copy integration
|
||||
|
||||
template <typename T>
|
||||
class AsioAwaitableAdapter {
|
||||
private:
|
||||
static_assert(!std::is_reference_v<T>,
|
||||
"AsioAwaitableAdapter does not support reference results");
|
||||
|
||||
using stored_type = std::conditional_t<std::is_void_v<T>, std::monostate, T>;
|
||||
|
||||
asio::awaitable<T, asio::any_io_executor> awaitable_;
|
||||
asio::any_io_executor asio_executor_;
|
||||
std::optional<stored_type> result_;
|
||||
std::exception_ptr exception_;
|
||||
std::coroutine_handle<> resume_handle_;
|
||||
|
||||
void post_resume() noexcept {
|
||||
auto handle = resume_handle_;
|
||||
try {
|
||||
// Must use post (not dispatch) to avoid lifetime issues:
|
||||
// dispatch could inline resume(), destroying awaiter while still in use
|
||||
asio::post(asio_executor_, [handle]() mutable {
|
||||
handle.resume();
|
||||
});
|
||||
} catch (...) {
|
||||
// Fallback: resume directly if post fails (rare)
|
||||
// This bypasses executor but ensures progress in error case
|
||||
if (!exception_) {
|
||||
exception_ = std::current_exception();
|
||||
}
|
||||
handle.resume();
|
||||
}
|
||||
}
|
||||
|
||||
asio::awaitable<void, asio::any_io_executor> run_asio() {
|
||||
try {
|
||||
if constexpr (std::is_void_v<T>) {
|
||||
co_await std::move(awaitable_);
|
||||
result_.emplace();
|
||||
}
|
||||
else {
|
||||
result_.emplace(co_await std::move(awaitable_));
|
||||
}
|
||||
} catch (...) {
|
||||
exception_ = std::current_exception();
|
||||
}
|
||||
|
||||
post_resume();
|
||||
}
|
||||
|
||||
void start_asio() noexcept {
|
||||
try {
|
||||
asio::co_spawn(asio_executor_, run_asio(), asio::detached);
|
||||
} catch (...) {
|
||||
exception_ = std::current_exception();
|
||||
post_resume();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
AsioAwaitableAdapter(asio::awaitable<T, asio::any_io_executor> awaitable,
|
||||
asio::any_io_executor executor)
|
||||
: awaitable_(std::move(awaitable)), asio_executor_(std::move(executor)) {}
|
||||
|
||||
AsioAwaitableAdapter(const AsioAwaitableAdapter&) = delete;
|
||||
AsioAwaitableAdapter& operator=(const AsioAwaitableAdapter&) = delete;
|
||||
AsioAwaitableAdapter(AsioAwaitableAdapter&&) = default;
|
||||
AsioAwaitableAdapter& operator=(AsioAwaitableAdapter&&) = default;
|
||||
|
||||
bool await_ready() const noexcept { return false; }
|
||||
|
||||
bool await_suspend(std::coroutine_handle<> handle) noexcept {
|
||||
resume_handle_ = handle;
|
||||
try {
|
||||
asio::post(asio_executor_, [this]() {
|
||||
start_asio();
|
||||
});
|
||||
} catch (...) {
|
||||
exception_ = std::current_exception();
|
||||
return false; // Resume immediately with exception
|
||||
}
|
||||
return true; // Suspend until asio operation completes
|
||||
}
|
||||
|
||||
T await_resume() {
|
||||
if (exception_) {
|
||||
std::rethrow_exception(exception_);
|
||||
}
|
||||
if constexpr (std::is_void_v<T>) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (!result_.has_value()) {
|
||||
throw std::runtime_error(
|
||||
"asio_async_simple_adapter: missing asio result");
|
||||
}
|
||||
return std::move(*result_);
|
||||
}
|
||||
}
|
||||
|
||||
// async_simple 通过 coAwait 识别自定义 awaiter。
|
||||
auto coAwait([[maybe_unused]] async_simple::Executor* executor) noexcept {
|
||||
return std::move(*this);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline auto from_asio(asio::awaitable<T, asio::any_io_executor> awaitable,
|
||||
asio::any_io_executor executor) {
|
||||
return AsioAwaitableAdapter<T>(std::move(awaitable), executor);
|
||||
}
|
||||
|
||||
// Safe adapter variant for wider execution models.
|
||||
//
|
||||
// Compared with AsioAwaitableAdapter, this version keeps all shared state in a
|
||||
// shared_ptr, serializes Asio-side state changes through a strand, and prevents
|
||||
// resuming a destroyed parent coroutine when the awaiter is abandoned.
|
||||
template <typename T>
|
||||
class SafeAsioAwaitableAdapter {
|
||||
private:
|
||||
static_assert(!std::is_reference_v<T>,
|
||||
"SafeAsioAwaitableAdapter does not support reference results");
|
||||
|
||||
using executor_type = asio::any_io_executor;
|
||||
using strand_type = asio::strand<executor_type>;
|
||||
using stored_type = std::conditional_t<std::is_void_v<T>, std::monostate, T>;
|
||||
|
||||
struct SharedState {
|
||||
SharedState(asio::awaitable<T, executor_type> awaitable,
|
||||
executor_type executor)
|
||||
: awaitable(std::move(awaitable)),
|
||||
executor(std::move(executor)),
|
||||
strand(this->executor) {}
|
||||
|
||||
asio::awaitable<T, executor_type> awaitable;
|
||||
executor_type executor;
|
||||
strand_type strand;
|
||||
std::optional<stored_type> result;
|
||||
std::exception_ptr exception;
|
||||
std::coroutine_handle<> resume_handle;
|
||||
bool finished = false;
|
||||
std::atomic_bool await_started{false};
|
||||
std::atomic_bool abandoned{false};
|
||||
std::atomic_bool resume_observed{false};
|
||||
std::atomic<async_simple::Executor*> async_executor{nullptr};
|
||||
|
||||
#if ORMPP_ASIO_ASYNC_SIMPLE_ADAPTER_HAS_CANCELLATION
|
||||
asio::cancellation_signal cancellation;
|
||||
asio::cancellation_type pending_cancellation =
|
||||
asio::cancellation_type::none;
|
||||
#endif
|
||||
};
|
||||
|
||||
std::shared_ptr<SharedState> state_;
|
||||
|
||||
static bool should_resume(
|
||||
const std::shared_ptr<SharedState>& state) noexcept {
|
||||
return state && !state->abandoned.load(std::memory_order_acquire) &&
|
||||
!state->resume_observed.load(std::memory_order_acquire) &&
|
||||
state->resume_handle;
|
||||
}
|
||||
|
||||
static asio::awaitable<void, executor_type> run_asio(
|
||||
std::shared_ptr<SharedState> state) {
|
||||
try {
|
||||
if constexpr (std::is_void_v<T>) {
|
||||
co_await std::move(state->awaitable);
|
||||
state->result.emplace();
|
||||
}
|
||||
else {
|
||||
state->result.emplace(co_await std::move(state->awaitable));
|
||||
}
|
||||
} catch (...) {
|
||||
state->exception = std::current_exception();
|
||||
}
|
||||
|
||||
state->finished = true;
|
||||
co_return;
|
||||
}
|
||||
|
||||
static void post_resume(std::shared_ptr<SharedState> state) noexcept {
|
||||
try {
|
||||
auto strand = state->strand;
|
||||
asio::post(strand, [state = std::move(state)]() mutable {
|
||||
resume_parent(std::move(state));
|
||||
});
|
||||
} catch (...) {
|
||||
if (!state->exception) {
|
||||
state->exception = std::current_exception();
|
||||
}
|
||||
|
||||
resume_parent(std::move(state));
|
||||
}
|
||||
}
|
||||
|
||||
static void resume_parent(std::shared_ptr<SharedState> state) noexcept {
|
||||
if (!should_resume(state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto handle = state->resume_handle;
|
||||
auto* async_executor =
|
||||
state->async_executor.load(std::memory_order_acquire);
|
||||
#if ORMPP_HAS_ASYNC_SIMPLE_EXECUTOR
|
||||
if (async_executor) {
|
||||
try {
|
||||
if (async_executor->schedule([state, handle]() mutable {
|
||||
if (should_resume(state)) {
|
||||
handle.resume();
|
||||
}
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
} catch (...) {
|
||||
if (!state->exception) {
|
||||
state->exception = std::current_exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)async_executor;
|
||||
#endif
|
||||
|
||||
handle.resume();
|
||||
}
|
||||
|
||||
static void complete_asio(std::shared_ptr<SharedState> state,
|
||||
std::exception_ptr exception) noexcept {
|
||||
try {
|
||||
if (exception && !state->exception) {
|
||||
state->exception = exception;
|
||||
}
|
||||
if (!state->finished && !state->exception) {
|
||||
state->exception = std::make_exception_ptr(std::runtime_error(
|
||||
"asio_async_simple_adapter: safe asio coroutine finished without "
|
||||
"a result"));
|
||||
}
|
||||
} catch (...) {
|
||||
if (!state->exception) {
|
||||
state->exception = std::current_exception();
|
||||
}
|
||||
}
|
||||
|
||||
post_resume(std::move(state));
|
||||
}
|
||||
|
||||
static void start_asio(std::shared_ptr<SharedState> state) noexcept {
|
||||
if (state->abandoned.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto strand = state->strand;
|
||||
auto run_state = state;
|
||||
auto completion_state = state;
|
||||
#if ORMPP_ASIO_ASYNC_SIMPLE_ADAPTER_HAS_CANCELLATION
|
||||
auto cancel_slot = completion_state->cancellation.slot();
|
||||
auto completion_handler = asio::bind_executor(
|
||||
strand, [state = std::move(completion_state)](
|
||||
std::exception_ptr exception) mutable {
|
||||
complete_asio(std::move(state), exception);
|
||||
});
|
||||
auto completion_token = asio::bind_cancellation_slot(
|
||||
cancel_slot, std::move(completion_handler));
|
||||
asio::co_spawn(strand, run_asio(std::move(run_state)),
|
||||
std::move(completion_token));
|
||||
if (state->pending_cancellation != asio::cancellation_type::none) {
|
||||
state->cancellation.emit(state->pending_cancellation);
|
||||
}
|
||||
#else
|
||||
auto completion_handler = asio::bind_executor(
|
||||
strand, [state = std::move(completion_state)](
|
||||
std::exception_ptr exception) mutable {
|
||||
complete_asio(std::move(state), exception);
|
||||
});
|
||||
asio::co_spawn(strand, run_asio(std::move(run_state)),
|
||||
std::move(completion_handler));
|
||||
#endif
|
||||
} catch (...) {
|
||||
state->exception = std::current_exception();
|
||||
state->finished = true;
|
||||
post_resume(std::move(state));
|
||||
}
|
||||
}
|
||||
|
||||
#if ORMPP_ASIO_ASYNC_SIMPLE_ADAPTER_HAS_CANCELLATION
|
||||
static bool request_cancel(const std::shared_ptr<SharedState>& state,
|
||||
asio::cancellation_type type) noexcept {
|
||||
if (!state) {
|
||||
return false;
|
||||
}
|
||||
if (state->resume_observed.load(std::memory_order_acquire)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
asio::post(state->strand, [state, type]() mutable {
|
||||
if (state->finished ||
|
||||
state->resume_observed.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
state->pending_cancellation |= type;
|
||||
state->cancellation.emit(type);
|
||||
});
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static bool request_cancel(const std::shared_ptr<SharedState>&) noexcept {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void abandon(std::shared_ptr<SharedState> state) noexcept {
|
||||
if (!state || state->resume_observed.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->abandoned.store(true, std::memory_order_release);
|
||||
if (!state->await_started.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
#if ORMPP_ASIO_ASYNC_SIMPLE_ADAPTER_HAS_CANCELLATION
|
||||
request_cancel(state, asio::cancellation_type::all);
|
||||
#else
|
||||
request_cancel(state);
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
class cancellation_handle {
|
||||
public:
|
||||
cancellation_handle() = default;
|
||||
|
||||
bool valid() const noexcept { return !state_.expired(); }
|
||||
|
||||
#if ORMPP_ASIO_ASYNC_SIMPLE_ADAPTER_HAS_CANCELLATION
|
||||
bool cancel(asio::cancellation_type type =
|
||||
asio::cancellation_type::all) const noexcept {
|
||||
return request_cancel(state_.lock(), type);
|
||||
}
|
||||
#else
|
||||
bool cancel() const noexcept { return request_cancel(state_.lock()); }
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class SafeAsioAwaitableAdapter;
|
||||
explicit cancellation_handle(std::weak_ptr<SharedState> state)
|
||||
: state_(std::move(state)) {}
|
||||
|
||||
std::weak_ptr<SharedState> state_;
|
||||
};
|
||||
|
||||
SafeAsioAwaitableAdapter(asio::awaitable<T, executor_type> awaitable,
|
||||
executor_type executor)
|
||||
: state_(std::make_shared<SharedState>(std::move(awaitable),
|
||||
std::move(executor))) {}
|
||||
|
||||
~SafeAsioAwaitableAdapter() { abandon(std::move(state_)); }
|
||||
|
||||
SafeAsioAwaitableAdapter(const SafeAsioAwaitableAdapter&) = delete;
|
||||
SafeAsioAwaitableAdapter& operator=(const SafeAsioAwaitableAdapter&) = delete;
|
||||
|
||||
SafeAsioAwaitableAdapter(SafeAsioAwaitableAdapter&& other) noexcept
|
||||
: state_(std::exchange(other.state_, nullptr)) {}
|
||||
SafeAsioAwaitableAdapter& operator=(
|
||||
SafeAsioAwaitableAdapter&& other) noexcept {
|
||||
if (this != &other) {
|
||||
abandon(std::move(state_));
|
||||
state_ = std::exchange(other.state_, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool await_ready() const noexcept { return false; }
|
||||
|
||||
bool await_suspend(std::coroutine_handle<> handle) noexcept {
|
||||
if (!state_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
state_->resume_handle = handle;
|
||||
state_->await_started.store(true, std::memory_order_release);
|
||||
|
||||
try {
|
||||
asio::post(state_->strand, [state = state_]() mutable {
|
||||
start_asio(std::move(state));
|
||||
});
|
||||
} catch (...) {
|
||||
state_->exception = std::current_exception();
|
||||
state_->finished = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
T await_resume() {
|
||||
if (!state_) {
|
||||
throw std::runtime_error(
|
||||
"asio_async_simple_adapter: safe awaiter has no state");
|
||||
}
|
||||
|
||||
state_->resume_observed.store(true, std::memory_order_release);
|
||||
|
||||
if (state_->exception) {
|
||||
std::rethrow_exception(state_->exception);
|
||||
}
|
||||
if constexpr (std::is_void_v<T>) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (!state_->result.has_value()) {
|
||||
throw std::runtime_error(
|
||||
"asio_async_simple_adapter: missing safe asio result");
|
||||
}
|
||||
|
||||
auto result = std::move(*state_->result);
|
||||
state_->result.reset();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
cancellation_handle get_cancellation_handle() const noexcept {
|
||||
return cancellation_handle{state_};
|
||||
}
|
||||
|
||||
#if ORMPP_ASIO_ASYNC_SIMPLE_ADAPTER_HAS_CANCELLATION
|
||||
bool cancel(asio::cancellation_type type =
|
||||
asio::cancellation_type::all) const noexcept {
|
||||
return request_cancel(state_, type);
|
||||
}
|
||||
|
||||
static constexpr bool cancellation_supported() noexcept { return true; }
|
||||
#else
|
||||
bool cancel() const noexcept { return request_cancel(state_); }
|
||||
|
||||
static constexpr bool cancellation_supported() noexcept { return false; }
|
||||
#endif
|
||||
|
||||
auto coAwait([[maybe_unused]] async_simple::Executor* executor) noexcept {
|
||||
if (state_) {
|
||||
state_->async_executor.store(executor, std::memory_order_release);
|
||||
}
|
||||
return std::move(*this);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline auto from_asio_safe(asio::awaitable<T, asio::any_io_executor> awaitable,
|
||||
asio::any_io_executor executor) {
|
||||
return SafeAsioAwaitableAdapter<T>(std::move(awaitable), std::move(executor));
|
||||
}
|
||||
|
||||
} // namespace adapter
|
||||
+411
@@ -0,0 +1,411 @@
|
||||
#ifndef ORMPP_ASYNC_CONNECTION_POOL_HPP
|
||||
#define ORMPP_ASYNC_CONNECTION_POOL_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <asio.hpp>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "async_traits.hpp"
|
||||
|
||||
namespace ormpp {
|
||||
|
||||
// Pool configuration options
|
||||
struct pool_options {
|
||||
bool enable_dynamic_expansion =
|
||||
false; // Allow temporary connections when pool is full
|
||||
size_t max_dynamic_connections = 10; // Max temporary connections
|
||||
bool log_pool_exhaustion = true; // Log when pool is exhausted
|
||||
};
|
||||
|
||||
// Async connection pool for databases that support async operations
|
||||
template <typename DB>
|
||||
requires is_async_db_v<DB>
|
||||
class async_connection_pool
|
||||
: public std::enable_shared_from_this<async_connection_pool<DB>> {
|
||||
public:
|
||||
using executor_type = asio::any_io_executor;
|
||||
template <typename T>
|
||||
using awaitable = asio::awaitable<T, executor_type>;
|
||||
|
||||
explicit async_connection_pool(executor_type executor,
|
||||
pool_options options = {})
|
||||
: executor_(std::move(executor)), strand_(executor_), options_(options) {}
|
||||
|
||||
// Destructor - automatically cleanup connections
|
||||
~async_connection_pool() {
|
||||
if (initialized_ || !available_connections_.empty()) {
|
||||
if (in_use_count_ > 0 && options_.log_pool_exhaustion) {
|
||||
std::cerr << "[Connection Pool] Warning: Pool destroyed with "
|
||||
<< in_use_count_ << " connections still in use. "
|
||||
<< "Consider calling close_all() before destruction."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
available_connections_.clear();
|
||||
}
|
||||
|
||||
// Disable copy and move
|
||||
async_connection_pool(const async_connection_pool&) = delete;
|
||||
async_connection_pool& operator=(const async_connection_pool&) = delete;
|
||||
async_connection_pool(async_connection_pool&&) = delete;
|
||||
async_connection_pool& operator=(async_connection_pool&&) = delete;
|
||||
|
||||
// Initialize the connection pool
|
||||
awaitable<bool> init(size_t pool_size, const std::string& host,
|
||||
const std::string& user = "",
|
||||
const std::string& passwd = "",
|
||||
const std::string& db = "",
|
||||
const std::optional<int>& timeout = {},
|
||||
const std::optional<int>& port = {}) {
|
||||
if (initialized_) {
|
||||
co_return true;
|
||||
}
|
||||
if (closing_) {
|
||||
co_return false;
|
||||
}
|
||||
|
||||
pool_size_ = pool_size;
|
||||
host_ = host;
|
||||
user_ = user;
|
||||
passwd_ = passwd;
|
||||
database_ = db;
|
||||
timeout_ = timeout;
|
||||
port_ = port;
|
||||
|
||||
// Create initial connections
|
||||
for (size_t i = 0; i < pool_size_; ++i) {
|
||||
auto conn = std::make_unique<DB>(executor_);
|
||||
|
||||
bool connected = co_await conn->connect(host_, user_, passwd_, database_,
|
||||
timeout_, port_);
|
||||
if (!connected) {
|
||||
co_await disconnect_available_connections();
|
||||
co_return false;
|
||||
}
|
||||
|
||||
available_connections_.push_back(std::move(conn));
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
co_return true;
|
||||
}
|
||||
|
||||
// Get a connection from the pool (with timeout)
|
||||
// Returns a shared_ptr with custom deleter that automatically returns
|
||||
// connection to pool
|
||||
awaitable<std::shared_ptr<DB>> get(
|
||||
std::chrono::seconds timeout = std::chrono::seconds(10)) {
|
||||
auto deadline = std::chrono::steady_clock::now() + timeout;
|
||||
size_t wait_count = 0;
|
||||
|
||||
while (std::chrono::steady_clock::now() < deadline) {
|
||||
// Try to get an available connection (thread-safe via strand)
|
||||
co_await asio::post(strand_, asio::use_awaitable);
|
||||
|
||||
if (!initialized_ || closing_) {
|
||||
co_return nullptr;
|
||||
}
|
||||
|
||||
if (!available_connections_.empty()) {
|
||||
auto connection = std::move(available_connections_.front());
|
||||
available_connections_.pop_front();
|
||||
in_use_count_++;
|
||||
|
||||
// Verify connection is still alive
|
||||
bool alive = co_await connection->ping();
|
||||
if (!alive) {
|
||||
// Reconnect
|
||||
alive = co_await connection->connect(host_, user_, passwd_, database_,
|
||||
timeout_, port_);
|
||||
if (!alive) {
|
||||
co_await asio::post(strand_, asio::use_awaitable);
|
||||
if (in_use_count_ > 0) {
|
||||
--in_use_count_;
|
||||
}
|
||||
co_await connection->disconnect();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
co_await asio::post(strand_, asio::use_awaitable);
|
||||
if (!initialized_ || closing_) {
|
||||
if (in_use_count_ > 0) {
|
||||
--in_use_count_;
|
||||
}
|
||||
co_await connection->disconnect();
|
||||
continue;
|
||||
}
|
||||
|
||||
co_return make_connection_handle(std::move(connection), false);
|
||||
}
|
||||
|
||||
// Pool is exhausted
|
||||
wait_count++;
|
||||
|
||||
// Try dynamic expansion if enabled
|
||||
if (options_.enable_dynamic_expansion &&
|
||||
dynamic_connection_count_ < options_.max_dynamic_connections) {
|
||||
auto temp_conn = co_await create_dynamic_connection();
|
||||
if (temp_conn) {
|
||||
co_return temp_conn;
|
||||
}
|
||||
}
|
||||
|
||||
// Log pool exhaustion (only once per get_connection call)
|
||||
if (wait_count == 1 && options_.log_pool_exhaustion) {
|
||||
log_pool_exhausted();
|
||||
}
|
||||
|
||||
// Wait for a connection to be returned
|
||||
// Use exponential backoff: 50ms, 100ms, 200ms, max 500ms
|
||||
auto wait_time =
|
||||
std::min(std::chrono::milliseconds(
|
||||
50 * (1 << std::min(wait_count - 1, size_t(3)))),
|
||||
std::chrono::milliseconds(500));
|
||||
auto remaining = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
deadline - std::chrono::steady_clock::now());
|
||||
if (remaining <= std::chrono::milliseconds::zero()) {
|
||||
break;
|
||||
}
|
||||
wait_time = std::min(wait_time, remaining);
|
||||
|
||||
asio::steady_timer retry_timer(executor_);
|
||||
retry_timer.expires_after(wait_time);
|
||||
co_await retry_timer.async_wait(asio::use_awaitable);
|
||||
}
|
||||
|
||||
// Timeout - log and return nullptr
|
||||
if (options_.log_pool_exhaustion) {
|
||||
log_connection_timeout(timeout);
|
||||
}
|
||||
|
||||
co_return nullptr;
|
||||
}
|
||||
|
||||
// Get pool statistics
|
||||
// Returns: (pool_size, available, in_use, dynamic_count)
|
||||
awaitable<std::tuple<size_t, size_t, size_t, size_t>> get_stats() {
|
||||
co_await asio::post(strand_, asio::use_awaitable);
|
||||
co_return std::make_tuple(pool_size_, available_connections_.size(),
|
||||
in_use_count_, dynamic_connection_count_);
|
||||
}
|
||||
|
||||
// Get detailed pool status
|
||||
awaitable<std::string> get_status_string() {
|
||||
auto [total, available, in_use, dynamic] = co_await get_stats();
|
||||
|
||||
std::string status = "Connection Pool Status:\n";
|
||||
status += " Pool size: " + std::to_string(total) + "\n";
|
||||
status += " Available: " + std::to_string(available) + "\n";
|
||||
status += " In use: " + std::to_string(in_use) + "\n";
|
||||
status += " Dynamic: " + std::to_string(dynamic) + "\n";
|
||||
const auto capacity = total + dynamic;
|
||||
status += " Utilization: " +
|
||||
std::to_string(capacity == 0 ? 0 : in_use * 100 / capacity) + "%";
|
||||
|
||||
co_return status;
|
||||
}
|
||||
|
||||
// Close all connections gracefully
|
||||
// wait_for_return: if true, wait for all in-use connections to be returned
|
||||
awaitable<void> close_all(
|
||||
bool wait_for_return = false,
|
||||
std::chrono::seconds max_wait = std::chrono::seconds(30)) {
|
||||
co_await asio::post(strand_, asio::use_awaitable);
|
||||
|
||||
if (!initialized_ || closing_) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
closing_ = true;
|
||||
|
||||
if (wait_for_return && in_use_count_ > 0) {
|
||||
auto deadline = std::chrono::steady_clock::now() + max_wait;
|
||||
|
||||
while (std::chrono::steady_clock::now() < deadline) {
|
||||
co_await asio::post(strand_, asio::use_awaitable);
|
||||
|
||||
if (in_use_count_ == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (options_.log_pool_exhaustion) {
|
||||
std::cout << "[Connection Pool] Waiting for " << in_use_count_
|
||||
<< " connections to be returned..." << std::endl;
|
||||
}
|
||||
|
||||
auto remaining = deadline - std::chrono::steady_clock::now();
|
||||
if (remaining <= std::chrono::steady_clock::duration::zero()) {
|
||||
break;
|
||||
}
|
||||
|
||||
asio::steady_timer timer(executor_);
|
||||
auto wait_time = std::min(
|
||||
std::chrono::duration_cast<std::chrono::steady_clock::duration>(
|
||||
std::chrono::seconds(1)),
|
||||
remaining);
|
||||
timer.expires_after(wait_time);
|
||||
co_await timer.async_wait(asio::use_awaitable);
|
||||
}
|
||||
|
||||
if (in_use_count_ > 0) {
|
||||
std::cerr
|
||||
<< "[Connection Pool] Warning: Timeout waiting for connections. "
|
||||
<< in_use_count_ << " connections still in use." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
co_await asio::post(strand_, asio::use_awaitable);
|
||||
|
||||
// Disconnect all available connections
|
||||
for (auto& conn : available_connections_) {
|
||||
co_await conn->disconnect();
|
||||
}
|
||||
available_connections_.clear();
|
||||
|
||||
initialized_ = false;
|
||||
closing_ = false;
|
||||
|
||||
if (options_.log_pool_exhaustion) {
|
||||
std::cout << "[Connection Pool] Connection pool closed." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<DB> make_connection_handle(std::unique_ptr<DB> connection,
|
||||
bool dynamic) {
|
||||
auto raw = connection.release();
|
||||
auto weak_pool = this->weak_from_this();
|
||||
|
||||
try {
|
||||
return std::shared_ptr<DB>(raw, [weak_pool, dynamic](DB* db) mutable {
|
||||
std::unique_ptr<DB> connection(db);
|
||||
try {
|
||||
if (auto pool = weak_pool.lock()) {
|
||||
auto strand = pool->strand_;
|
||||
asio::post(strand, [pool = std::move(pool),
|
||||
connection = std::move(connection),
|
||||
dynamic]() mutable {
|
||||
pool->return_connection(std::move(connection), dynamic);
|
||||
});
|
||||
}
|
||||
} catch (...) {
|
||||
// Fallback to local destruction of the connection handle.
|
||||
}
|
||||
});
|
||||
} catch (...) {
|
||||
delete raw;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void return_connection(std::unique_ptr<DB> connection, bool dynamic) {
|
||||
if (dynamic) {
|
||||
if (dynamic_connection_count_ > 0) {
|
||||
--dynamic_connection_count_;
|
||||
}
|
||||
if (in_use_count_ > 0) {
|
||||
--in_use_count_;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (in_use_count_ > 0) {
|
||||
--in_use_count_;
|
||||
}
|
||||
|
||||
if (initialized_ && !closing_) {
|
||||
available_connections_.push_back(std::move(connection));
|
||||
}
|
||||
}
|
||||
|
||||
awaitable<void> disconnect_available_connections() {
|
||||
for (auto& conn : available_connections_) {
|
||||
co_await conn->disconnect();
|
||||
}
|
||||
available_connections_.clear();
|
||||
}
|
||||
|
||||
// Create a dynamic (temporary) connection
|
||||
awaitable<std::shared_ptr<DB>> create_dynamic_connection() {
|
||||
co_await asio::post(strand_, asio::use_awaitable);
|
||||
|
||||
if (!initialized_ || closing_ ||
|
||||
dynamic_connection_count_ >= options_.max_dynamic_connections) {
|
||||
co_return nullptr;
|
||||
}
|
||||
|
||||
dynamic_connection_count_++;
|
||||
in_use_count_++;
|
||||
|
||||
auto conn = std::make_unique<DB>(executor_);
|
||||
|
||||
bool connected = co_await conn->connect(host_, user_, passwd_, database_,
|
||||
timeout_, port_);
|
||||
if (!connected) {
|
||||
co_await asio::post(strand_, asio::use_awaitable);
|
||||
dynamic_connection_count_--;
|
||||
in_use_count_--;
|
||||
co_return nullptr;
|
||||
}
|
||||
|
||||
co_await asio::post(strand_, asio::use_awaitable);
|
||||
if (!initialized_ || closing_) {
|
||||
if (dynamic_connection_count_ > 0) {
|
||||
--dynamic_connection_count_;
|
||||
}
|
||||
if (in_use_count_ > 0) {
|
||||
--in_use_count_;
|
||||
}
|
||||
co_await conn->disconnect();
|
||||
co_return nullptr;
|
||||
}
|
||||
|
||||
co_return make_connection_handle(std::move(conn), true);
|
||||
}
|
||||
|
||||
void log_pool_exhausted() {
|
||||
// This would be called from strand context
|
||||
std::cerr << "[Connection Pool] Pool exhausted - all " << pool_size_
|
||||
<< " connections in use. Waiting for available connection..."
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void log_connection_timeout(std::chrono::seconds timeout) {
|
||||
std::cerr << "[Connection Pool] Timeout after " << timeout.count()
|
||||
<< " seconds waiting for connection. "
|
||||
<< "Consider increasing pool size or timeout." << std::endl;
|
||||
}
|
||||
|
||||
executor_type executor_;
|
||||
asio::strand<executor_type> strand_;
|
||||
pool_options options_;
|
||||
|
||||
bool initialized_ = false;
|
||||
bool closing_ = false;
|
||||
size_t pool_size_ = 0;
|
||||
size_t in_use_count_ = 0;
|
||||
size_t dynamic_connection_count_ = 0;
|
||||
|
||||
std::string host_;
|
||||
std::string user_;
|
||||
std::string passwd_;
|
||||
std::string database_;
|
||||
std::optional<int> timeout_;
|
||||
std::optional<int> port_;
|
||||
|
||||
std::deque<std::unique_ptr<DB>> available_connections_;
|
||||
};
|
||||
|
||||
} // namespace ormpp
|
||||
|
||||
#endif // ORMPP_ASYNC_CONNECTION_POOL_HPP
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace ormpp {
|
||||
|
||||
template <typename DB>
|
||||
struct db_execution_traits {
|
||||
static constexpr bool is_async = false;
|
||||
|
||||
template <typename T>
|
||||
using awaitable_type = T;
|
||||
};
|
||||
|
||||
template <typename DB>
|
||||
inline constexpr bool is_async_db_v =
|
||||
db_execution_traits<std::remove_pointer_t<DB>>::is_async;
|
||||
|
||||
template <typename DB, typename T>
|
||||
using db_awaitable_t = typename db_execution_traits<
|
||||
std::remove_pointer_t<DB>>::template awaitable_type<T>;
|
||||
|
||||
} // namespace ormpp
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
//
|
||||
// Created by qiyu on 12/14/17.
|
||||
//
|
||||
|
||||
#ifndef ORMPP_CONNECTION_POOL_HPP
|
||||
#define ORMPP_CONNECTION_POOL_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace ormpp {
|
||||
template <typename DB>
|
||||
class connection_pool {
|
||||
public:
|
||||
using DeleterType = std::function<void(DB *)>;
|
||||
static connection_pool<DB> &instance() {
|
||||
static connection_pool<DB> instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
// call_once
|
||||
void init(int maxsize, const std::string &host = "",
|
||||
const std::string &user = "", const std::string &passwd = "",
|
||||
const std::string &db = "", const std::optional<int> &timeout = {},
|
||||
const std::optional<int> &port = {}) {
|
||||
std::call_once(flag_, &connection_pool<DB>::init_impl, this, maxsize, host,
|
||||
user, passwd, db, timeout, port);
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
std::scoped_lock lock(mutex_);
|
||||
return pool_.size();
|
||||
}
|
||||
|
||||
std::unique_ptr<DB, DeleterType> get() {
|
||||
std::unique_lock lock(mutex_);
|
||||
|
||||
while (pool_.empty()) {
|
||||
if (condition_.wait_for(lock, std::chrono::seconds(3)) ==
|
||||
std::cv_status::timeout) {
|
||||
// timeout
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto conn = std::move(pool_.front());
|
||||
pool_.pop_front();
|
||||
lock.unlock();
|
||||
|
||||
if (conn == nullptr || !conn->ping()) {
|
||||
return create_connection();
|
||||
}
|
||||
|
||||
// check timeout, idle time shuold less than 8 hours
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto last = conn->get_latest_operate_time();
|
||||
auto mins =
|
||||
std::chrono::duration_cast<std::chrono::minutes>(now - last).count();
|
||||
if ((mins - 6 * 60) > 0) {
|
||||
return create_connection();
|
||||
}
|
||||
|
||||
conn->update_operate_time();
|
||||
std::unique_ptr<DB, DeleterType> ptr(conn.release(), [this](DB *t) {
|
||||
{
|
||||
std::scoped_lock lock(mutex_);
|
||||
pool_.push_back(std::unique_ptr<DB>(t));
|
||||
}
|
||||
condition_.notify_one();
|
||||
});
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
void init_impl(int maxsize, const std::string &host, const std::string &user,
|
||||
const std::string &passwd, const std::string &db,
|
||||
const std::optional<int> &timeout,
|
||||
const std::optional<int> &port) {
|
||||
args_ = std::make_tuple(host, user, passwd, db, timeout, port);
|
||||
|
||||
for (int i = 0; i < maxsize; ++i) {
|
||||
auto conn = std::make_unique<DB>();
|
||||
if (conn->connect(args_)) {
|
||||
pool_.push_back(std::move(conn));
|
||||
}
|
||||
else {
|
||||
throw std::invalid_argument("init failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<DB, DeleterType> create_connection() {
|
||||
auto conn = std::make_unique<DB>();
|
||||
if (conn->connect(args_)) {
|
||||
std::unique_ptr<DB, DeleterType> ptr(conn.release(), [this](DB *t) {
|
||||
{
|
||||
std::scoped_lock lock(mutex_);
|
||||
pool_.push_back(std::unique_ptr<DB>(t));
|
||||
}
|
||||
condition_.notify_one();
|
||||
});
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
connection_pool() = default;
|
||||
~connection_pool() = default;
|
||||
connection_pool(const connection_pool &) = delete;
|
||||
connection_pool &operator=(const connection_pool &) = delete;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
std::once_flag flag_;
|
||||
std::condition_variable condition_;
|
||||
std::deque<std::unique_ptr<DB>> pool_;
|
||||
std::tuple<std::string, std::string, std::string, std::string,
|
||||
std::optional<int>, std::optional<int>>
|
||||
args_;
|
||||
};
|
||||
|
||||
template <typename DB>
|
||||
struct conn_guard {
|
||||
conn_guard(std::shared_ptr<DB> con) : conn_(con) {}
|
||||
~conn_guard() { connection_pool<DB>::instance().return_back(conn_.lock()); }
|
||||
|
||||
private:
|
||||
std::weak_ptr<DB> conn_;
|
||||
};
|
||||
} // namespace ormpp
|
||||
|
||||
#endif // ORMPP_CONNECTION_POOL_HPP
|
||||
Vendored
+313
@@ -0,0 +1,313 @@
|
||||
//
|
||||
// Created by qiyu on 11/3/17.
|
||||
//
|
||||
|
||||
#ifndef ORM_DBNG_HPP
|
||||
#define ORM_DBNG_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "async_traits.hpp"
|
||||
#include "query.hpp"
|
||||
|
||||
namespace ormpp {
|
||||
template <typename DB>
|
||||
class dbng {
|
||||
public:
|
||||
dbng() = default;
|
||||
template <typename... Args>
|
||||
requires(std::is_constructible_v<DB, Args...>)
|
||||
explicit dbng(Args &&...args) : db_(std::forward<Args>(args)...) {}
|
||||
dbng(const dbng &) = delete;
|
||||
~dbng() {
|
||||
if constexpr (!is_async_db_v<DB>) {
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
decltype(auto) connect(
|
||||
const std::tuple<std::string, std::string, std::string, std::string,
|
||||
std::optional<int>, std::optional<int>> &tp) {
|
||||
return db_.connect(tp);
|
||||
}
|
||||
|
||||
decltype(auto) connect(const std::string &host, const std::string &user = "",
|
||||
const std::string &passwd = "",
|
||||
const std::string &db = "",
|
||||
const std::optional<int> &timeout = {},
|
||||
const std::optional<int> &port = {}) {
|
||||
return db_.connect(host, user, passwd, db, timeout, port);
|
||||
}
|
||||
|
||||
decltype(auto) disconnect() { return db_.disconnect(); }
|
||||
|
||||
template <typename T, typename... Args>
|
||||
decltype(auto) create_datatable(Args &&...args) {
|
||||
return db_.template create_datatable<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
decltype(auto) insert(const T &t, Args &&...args) {
|
||||
return db_.insert(t, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
decltype(auto) insert(const std::vector<T> &v, Args &&...args) {
|
||||
return db_.insert(v, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
decltype(auto) replace(const T &t, Args &&...args) {
|
||||
return db_.replace(t, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
decltype(auto) replace(const std::vector<T> &v, Args &&...args) {
|
||||
return db_.replace(v, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
decltype(auto) update(const T &t, Args &&...args) {
|
||||
return db_.update(t, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
decltype(auto) update(const std::vector<T> &v, Args &&...args) {
|
||||
return db_.update(v, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
decltype(auto) update_some(const T &t, Args &&...args) {
|
||||
return db_.template update<members...>(t, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
decltype(auto) update_some(const std::vector<T> &v, Args &&...args) {
|
||||
return db_.template update<members...>(v, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
decltype(auto) get_insert_id_after_insert(const T &t, Args &&...args) {
|
||||
return db_.get_insert_id_after_insert(t, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
decltype(auto) get_insert_id_after_insert(const std::vector<T> &v,
|
||||
Args &&...args) {
|
||||
return db_.get_insert_id_after_insert(v, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
decltype(auto) delete_records_s(const std::string &str = "", Args &&...args) {
|
||||
return db_.template delete_records_s<T>(str, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
decltype(auto) query_s(const std::string &str = "", Args &&...args) {
|
||||
return db_.template query_s<T>(str, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
[[deprecated]] decltype(auto) delete_records(Args &&...where_condition) {
|
||||
return db_.template delete_records<T>(
|
||||
std::forward<Args>(where_condition)...);
|
||||
}
|
||||
|
||||
// restriction, all the args are string, the first is the where condition,
|
||||
// rest are append conditions
|
||||
template <typename T, typename... Args>
|
||||
[[deprecated]] decltype(auto) query(Args &&...args) {
|
||||
return db_.template query<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// support member variable, such as: query(FID(simple::id), "<", 5)
|
||||
template <typename Pair, typename U>
|
||||
[[deprecated]] auto query(Pair pair, std::string_view oper, U &&val) {
|
||||
auto sql = build_condition(pair, oper, std::forward<U>(val));
|
||||
using T = typename ormpp::field_attribute<decltype(pair.second)>::type;
|
||||
return query<T>(sql);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto update() {
|
||||
return db_.template make_update<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto remove() {
|
||||
return db_.template make_delete<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto create_table() {
|
||||
return db_.template make_create_table<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto alter_table() {
|
||||
return db_.template make_alter_table<T>();
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto select(Args... args) {
|
||||
return db_.select(args...);
|
||||
}
|
||||
|
||||
auto select(all_t a) { return db_.select_all(); }
|
||||
|
||||
template <typename Pair, typename U>
|
||||
[[deprecated]] bool delete_records(Pair pair, std::string_view oper,
|
||||
U &&val) {
|
||||
auto sql = build_condition(pair, oper, std::forward<U>(val));
|
||||
using T = typename ormpp::field_attribute<decltype(pair.second)>::type;
|
||||
return delete_records<T>(sql);
|
||||
}
|
||||
|
||||
decltype(auto) execute(const std::string &sql) { return db_.execute(sql); }
|
||||
|
||||
// transaction
|
||||
void set_enable_transaction(bool enable = true) {
|
||||
return db_.set_enable_transaction(enable);
|
||||
}
|
||||
|
||||
decltype(auto) begin() { return db_.begin(); }
|
||||
|
||||
decltype(auto) commit() { return db_.commit(); }
|
||||
|
||||
decltype(auto) rollback() { return db_.rollback(); }
|
||||
|
||||
decltype(auto) ping() { return db_.ping(); }
|
||||
|
||||
bool has_error() { return db_.has_error(); }
|
||||
|
||||
std::string get_last_error() const { return db_.get_last_error(); }
|
||||
|
||||
int get_last_affect_rows() { return db_.get_last_affect_rows(); }
|
||||
|
||||
private:
|
||||
template <typename Pair, typename U>
|
||||
auto build_condition(Pair pair, std::string_view oper, U &&val) {
|
||||
std::string sql = "";
|
||||
using V = ylt::reflection::remove_cvref_t<U>;
|
||||
|
||||
// if field type is numeric, return type of val is numeric, to string; val
|
||||
// is string, no change; if field type is string, return type of val is
|
||||
// numeric, to string and add ''; val is string, add '';
|
||||
using return_type =
|
||||
typename field_attribute<decltype(pair.second)>::return_type;
|
||||
|
||||
if constexpr (std::is_arithmetic_v<return_type> &&
|
||||
std::is_arithmetic_v<V>) {
|
||||
append(sql, pair.first, oper, std::to_string(std::forward<U>(val)));
|
||||
}
|
||||
else if constexpr (!std::is_arithmetic_v<return_type>) {
|
||||
if constexpr (std::is_arithmetic_v<V>)
|
||||
append(sql, pair.first, oper,
|
||||
to_str(std::to_string(std::forward<U>(val))));
|
||||
else
|
||||
append(sql, pair.first, oper, to_str(std::forward<U>(val)));
|
||||
}
|
||||
else {
|
||||
append(sql, pair.first, oper, std::forward<U>(val));
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
#define HAS_MEMBER(member) \
|
||||
template <typename T, typename... Args> \
|
||||
struct has_##member { \
|
||||
private: \
|
||||
template <typename U> \
|
||||
static auto Check(int) \
|
||||
-> decltype(std::declval<U>().member(std::declval<Args>()...), \
|
||||
std::true_type()); \
|
||||
template <typename U> \
|
||||
static std::false_type Check(...); \
|
||||
\
|
||||
public: \
|
||||
enum { \
|
||||
value = std::is_same<decltype(Check<T>(0)), std::true_type>::value \
|
||||
}; \
|
||||
};
|
||||
|
||||
HAS_MEMBER(before)
|
||||
HAS_MEMBER(after)
|
||||
|
||||
#define WRAPER(func) \
|
||||
template <typename... AP, typename... Args> \
|
||||
auto wraper##_##func(Args &&...args) { \
|
||||
using result_type = decltype(std::declval<decltype(this)>()->func( \
|
||||
std::declval<Args>()...)); \
|
||||
bool r = true; \
|
||||
std::tuple<AP...> tp{}; \
|
||||
for_each_l( \
|
||||
tp, \
|
||||
[&r, &args...](auto &item) { \
|
||||
if (!r) \
|
||||
return; \
|
||||
if constexpr (has_before<decltype(item)>::value) \
|
||||
r = item.before(std::forward<Args>(args)...); \
|
||||
}, \
|
||||
std::make_index_sequence<sizeof...(AP)>{}); \
|
||||
if (!r) \
|
||||
return result_type{}; \
|
||||
auto lambda = [this, &args...] { \
|
||||
return this->func(std::forward<Args>(args)...); \
|
||||
}; \
|
||||
result_type result = std::invoke(lambda); \
|
||||
for_each_r( \
|
||||
tp, \
|
||||
[&r, &result, &args...](auto &item) { \
|
||||
if (!r) \
|
||||
return; \
|
||||
if constexpr (has_after<decltype(item), result_type>::value) \
|
||||
r = item.after(result, std::forward<Args>(args)...); \
|
||||
}, \
|
||||
std::make_index_sequence<sizeof...(AP)>{}); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
template <typename... Args, typename F, std::size_t... Idx>
|
||||
constexpr void for_each_l(std::tuple<Args...> &t, F &&f,
|
||||
std::index_sequence<Idx...>) {
|
||||
(std::forward<F>(f)(std::get<Idx>(t)), ...);
|
||||
}
|
||||
|
||||
template <typename... Args, typename F, std::size_t... Idx>
|
||||
constexpr void for_each_r(std::tuple<Args...> &t, F &&f,
|
||||
std::index_sequence<Idx...>) {
|
||||
constexpr auto size = sizeof...(Idx);
|
||||
(std::forward<F>(f)(std::get<size - Idx - 1>(t)), ...);
|
||||
}
|
||||
|
||||
public:
|
||||
WRAPER(connect);
|
||||
WRAPER(execute);
|
||||
void update_operate_time() { latest_tm_ = std::chrono::system_clock::now(); }
|
||||
|
||||
auto get_latest_operate_time() { return latest_tm_; }
|
||||
|
||||
private:
|
||||
DB db_;
|
||||
std::chrono::system_clock::time_point latest_tm_ =
|
||||
std::chrono::system_clock::now();
|
||||
};
|
||||
|
||||
template <typename DB>
|
||||
requires is_async_db_v<DB>
|
||||
struct db_execution_traits<dbng<DB>> {
|
||||
static constexpr bool is_async = true;
|
||||
|
||||
template <typename T>
|
||||
using awaitable_type =
|
||||
typename db_execution_traits<DB>::template awaitable_type<T>;
|
||||
};
|
||||
} // namespace ormpp
|
||||
|
||||
#endif // ORM_DBNG_HPP
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by qiyu on 10/20/17.
|
||||
//
|
||||
#ifndef ORM_ENTITY_HPP
|
||||
#define ORM_ENTITY_HPP
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
struct ormpp_not_null {
|
||||
std::set<std::string> fields;
|
||||
};
|
||||
|
||||
struct ormpp_key {
|
||||
std::string fields;
|
||||
};
|
||||
|
||||
struct ormpp_auto_key {
|
||||
std::string fields;
|
||||
};
|
||||
|
||||
struct ormpp_unique {
|
||||
std::set<std::string> fields;
|
||||
};
|
||||
|
||||
#endif // ORM_ENTITY_HPP
|
||||
Vendored
+1675
File diff suppressed because it is too large
Load Diff
+1940
File diff suppressed because it is too large
Load Diff
+87
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <async_simple/coro/Lazy.h>
|
||||
#include <async_simple/coro/SyncAwait.h>
|
||||
|
||||
#include <cinatra/ylt/coro_io/io_context_pool.hpp>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <ormpp/dbng.hpp>
|
||||
#include <ormpp/mysql_async.hpp>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "asio_async_simple_adapter.hpp"
|
||||
|
||||
namespace db_wrapper {
|
||||
|
||||
class mysql_async_session {
|
||||
public:
|
||||
using db_type = ormpp::dbng<ormpp::mysql_async>;
|
||||
|
||||
mysql_async_session()
|
||||
: executor_(coro_io::get_global_executor()->get_asio_executor()),
|
||||
db_(executor_) {}
|
||||
|
||||
explicit mysql_async_session(asio::any_io_executor executor)
|
||||
: executor_(std::move(executor)), db_(executor_) {}
|
||||
|
||||
db_type &raw() { return db_; }
|
||||
const db_type &raw() const { return db_; }
|
||||
|
||||
asio::any_io_executor executor() const { return executor_; }
|
||||
|
||||
template <typename T>
|
||||
auto await(asio::awaitable<T, asio::any_io_executor> awaitable) {
|
||||
return adapter::from_asio(std::move(awaitable), executor_);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto await_safe(asio::awaitable<T, asio::any_io_executor> awaitable) {
|
||||
return adapter::from_asio_safe(std::move(awaitable), executor_);
|
||||
}
|
||||
|
||||
async_simple::coro::Lazy<bool> connect(const std::string &host,
|
||||
const std::string &user,
|
||||
const std::string &passwd,
|
||||
const std::string &database,
|
||||
const std::optional<int> &timeout = {},
|
||||
const std::optional<int> &port = {}) {
|
||||
co_return co_await await(
|
||||
db_.connect(host, user, passwd, database, timeout, port));
|
||||
}
|
||||
|
||||
async_simple::coro::Lazy<bool> execute(std::string sql) {
|
||||
co_return co_await await(db_.execute(sql));
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
async_simple::coro::Lazy<int> insert(const T &value, Args &&...args) {
|
||||
co_return co_await await(db_.insert(value, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
async_simple::coro::Lazy<std::uint64_t> get_insert_id_after_insert(
|
||||
const T &value, Args &&...args) {
|
||||
co_return co_await await(
|
||||
db_.get_insert_id_after_insert(value, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
int get_last_affect_rows() { return db_.get_last_affect_rows(); }
|
||||
bool has_error() { return db_.has_error(); }
|
||||
std::string get_last_error() const { return db_.get_last_error(); }
|
||||
|
||||
private:
|
||||
asio::any_io_executor executor_;
|
||||
db_type db_;
|
||||
};
|
||||
|
||||
using mysql_async_wrapper = mysql_async_session;
|
||||
|
||||
template <typename Lazy>
|
||||
decltype(auto) sync_wait(Lazy &&lazy) {
|
||||
return async_simple::coro::syncAwait(
|
||||
std::forward<Lazy>(lazy).via(coro_io::get_global_executor()));
|
||||
}
|
||||
|
||||
} // namespace db_wrapper
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
//
|
||||
// Created by root on 4/23/18.
|
||||
//
|
||||
|
||||
#ifndef ORMPP_CONFIG_MANAGER_HPP
|
||||
#define ORMPP_CONFIG_MANAGER_HPP
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "iguana/json_reader.hpp"
|
||||
#include "iguana/json_writer.hpp"
|
||||
|
||||
namespace ormpp {
|
||||
struct ormpp_cfg {
|
||||
std::string db_ip;
|
||||
std::string user_name;
|
||||
std::string pwd;
|
||||
std::string db_name;
|
||||
int timeout;
|
||||
int db_conn_num;
|
||||
int db_port;
|
||||
};
|
||||
YLT_REFL(ormpp_cfg, db_ip, user_name, pwd, db_name, timeout, db_conn_num,
|
||||
db_port);
|
||||
|
||||
/*
|
||||
int max_thread_num = config_manager::get<int>("max_thread_num",
|
||||
"ormpp.cfg"); std::string log_path =
|
||||
config_manager::get<std::string>("log_path", "ormpp.cfg");
|
||||
|
||||
config_manager::set("max_thread_num", 6, "ormpp.cfg");
|
||||
config_manager::set("log_path", std::string("/tmp/"), "ormpp.cfg");
|
||||
*/
|
||||
class config_manager {
|
||||
public:
|
||||
config_manager() = delete;
|
||||
|
||||
template <typename T>
|
||||
inline static T get(std::string_view key, std::string_view file_path) {
|
||||
ormpp_cfg cfg{};
|
||||
bool r = from_file(cfg, file_path);
|
||||
if (!r) {
|
||||
return {};
|
||||
}
|
||||
|
||||
T val{};
|
||||
bool has_key = false;
|
||||
ylt::reflection::for_each(
|
||||
cfg, [key, &val, &has_key](auto &field, auto name, auto /*index*/) {
|
||||
if (key == name) {
|
||||
assign(val, field);
|
||||
has_key = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!has_key) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static bool set(std::string_view key, T &&val,
|
||||
std::string_view file_path) {
|
||||
ormpp_cfg cfg{};
|
||||
bool r = from_file(cfg, file_path);
|
||||
if (!r) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_key = false;
|
||||
ylt::reflection::for_each(
|
||||
cfg, [key, &val, &has_key](auto &field, auto name, auto /*index*/) {
|
||||
if (key == name) {
|
||||
assign(field, val);
|
||||
has_key = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!has_key)
|
||||
return false;
|
||||
|
||||
return to_file(cfg, file_path);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static bool from_file(T &t, std::string_view file_path) {
|
||||
std::ifstream in(file_path.data(), std::ios::binary);
|
||||
if (!in.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
in.seekg(0, std::ios::end);
|
||||
size_t len = (size_t)in.tellg();
|
||||
in.seekg(0);
|
||||
std::string str;
|
||||
str.resize(len);
|
||||
|
||||
in.read(str.data(), len);
|
||||
|
||||
iguana::from_json(t, str);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
inline static void assign(T &t, U &u) {
|
||||
if constexpr (std::is_same_v<U, T>) {
|
||||
t = u;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static bool to_file(T &t, std::string_view file_path) {
|
||||
iguana::string_stream ss;
|
||||
iguana::to_json(t, ss);
|
||||
std::ofstream out(file_path.data(), std::ios::binary);
|
||||
if (!out.is_open()) {
|
||||
return false;
|
||||
}
|
||||
out.write(ss.data(), ss.size());
|
||||
out.close();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace ormpp
|
||||
#endif // ORMPP_CONFIG_MANAGER_HPP
|
||||
Vendored
+51
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// Created by qiyu on 10/30/17.
|
||||
//
|
||||
|
||||
#ifndef ORM_PG_TYPES_H
|
||||
#define ORM_PG_TYPES_H
|
||||
|
||||
#define BOOLOID 16
|
||||
#define BYTEAOID 17
|
||||
#define CHAROID 18
|
||||
#define NAMEOID 19
|
||||
#define INT8OID 20
|
||||
#define INT2OID 21
|
||||
#define INT2VECTOROID 22
|
||||
#define INT4OID 23
|
||||
#define REGPROCOID 24
|
||||
#define TEXTOID 25
|
||||
#define OIDOID 26
|
||||
#define TIDOID 27
|
||||
#define XIDOID 28
|
||||
#define CIDOID 29
|
||||
#define OIDVECTOROID 30
|
||||
#define POINTOID 600
|
||||
#define LSEGOID 601
|
||||
#define PATHOID 602
|
||||
#define BOXOID 603
|
||||
#define POLYGONOID 604
|
||||
#define LINEOID 628
|
||||
#define FLOAT4OID 700
|
||||
#define FLOAT8OID 701
|
||||
#define ABSTIMEOID 702
|
||||
#define RELTIMEOID 703
|
||||
#define TINTERVALOID 704
|
||||
#define UNKNOWNOID 705
|
||||
#define CIRCLEOID 718
|
||||
#define CASHOID 790
|
||||
#define INETOID 869
|
||||
#define CIDROID 650
|
||||
#define BPCHAROID 1042
|
||||
#define VARCHAROID 1043
|
||||
#define DATEOID 1082
|
||||
#define TIMEOID 1083
|
||||
#define TIMESTAMPOID 1114
|
||||
#define TIMESTAMPTZOID 1184
|
||||
#define INTERVALOID 1186
|
||||
#define TIMETZOID 1266
|
||||
#define ZPBITOID 1560
|
||||
#define VARBITOID 1562
|
||||
#define NUMERICOID 1700
|
||||
|
||||
#endif // ORM_PG_TYPES_H
|
||||
+896
@@ -0,0 +1,896 @@
|
||||
//
|
||||
// Created by qiyu on 10/30/17.
|
||||
//
|
||||
|
||||
#ifndef ORM_POSTGRESQL_HPP
|
||||
#define ORM_POSTGRESQL_HPP
|
||||
|
||||
#include <libpq-fe.h>
|
||||
|
||||
#include <climits>
|
||||
#include <deque>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "iguana/detail/charconv.h"
|
||||
#include "query.hpp"
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace ormpp {
|
||||
class postgresql {
|
||||
public:
|
||||
static constexpr DBType db_type_v = DBType::postgresql;
|
||||
|
||||
~postgresql() { disconnect(); }
|
||||
|
||||
bool has_error() const { return has_error_; }
|
||||
|
||||
static void reset_error() {
|
||||
has_error_ = false;
|
||||
last_error_ = {};
|
||||
}
|
||||
|
||||
static void set_last_error(std::string last_error) {
|
||||
has_error_ = true;
|
||||
last_error_ = std::move(last_error);
|
||||
std::cout << last_error_ << std::endl;
|
||||
}
|
||||
|
||||
std::string get_last_error() const { return last_error_; }
|
||||
|
||||
// ip, user, pwd, db, timeout the sequence must be fixed like this
|
||||
bool connect(
|
||||
const std::tuple<std::string, std::string, std::string, std::string,
|
||||
std::optional<int>, std::optional<int>> &tp) {
|
||||
reset_error();
|
||||
auto sql = generate_conn_sql(tp);
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
con_ = PQconnectdb(sql.data());
|
||||
if (PQstatus(con_) != CONNECTION_OK) {
|
||||
set_last_error(PQerrorMessage(con_));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool connect(const std::string &host, const std::string &user,
|
||||
const std::string &passwd, const std::string &db,
|
||||
const std::optional<int> &timeout,
|
||||
const std::optional<int> &port) {
|
||||
return connect(std::make_tuple(host, user, passwd, db, timeout, port));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
bool disconnect(Args &&...args) {
|
||||
if (con_ != nullptr) {
|
||||
PQfinish(con_);
|
||||
con_ = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ping() { return (PQstatus(con_) == CONNECTION_OK); }
|
||||
|
||||
template <typename T, typename... Args>
|
||||
bool create_datatable(Args &&...args) {
|
||||
std::string sql = generate_createtb_sql<T>(std::forward<Args>(args)...);
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
res_ = PQexec(con_, sql.data());
|
||||
auto guard = guard_statment(res_);
|
||||
return PQresultStatus(res_) == PGRES_COMMAND_OK;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
int insert(const T &t, Args &&...args) {
|
||||
return insert_impl(OptType::insert, t, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
int insert(const std::vector<T> &v, Args &&...args) {
|
||||
return insert_impl(OptType::insert, v, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
int replace(const T &t, Args &&...args) {
|
||||
return insert_impl(OptType::replace, t, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
int replace(const std::vector<T> &v, Args &&...args) {
|
||||
return insert_impl(OptType::replace, v, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
int update(const T &t, Args &&...args) {
|
||||
return update_impl<members...>(t, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
int update(const std::vector<T> &v, Args &&...args) {
|
||||
return update_impl<members...>(v, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
uint64_t get_insert_id_after_insert(const T &t, Args &&...args) {
|
||||
auto res = insert_or_update_impl(t, generate_insert_sql<T>(db_type_v, true),
|
||||
OptType::insert, true);
|
||||
return res.has_value() ? res.value() : 0;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
uint64_t get_insert_id_after_insert(const std::vector<T> &v, Args &&...args) {
|
||||
auto res = insert_or_update_impl(v, generate_insert_sql<T>(db_type_v, true),
|
||||
OptType::insert, true);
|
||||
return res.has_value() ? res.value() : 0;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
constexpr bool delete_records(Args &&...where_conditon) {
|
||||
auto sql = generate_delete_sql<T>(db_type_v,
|
||||
std::forward<Args>(where_conditon)...);
|
||||
return execute(sql);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
uint64_t delete_records_s(const std::string &str, Args &&...args) {
|
||||
auto sql = generate_delete_sql<T>(db_type_v, str);
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
if (!prepare<T>(sql))
|
||||
return 0;
|
||||
|
||||
size_t index = 0;
|
||||
std::vector<std::optional<std::vector<char>>> param_values;
|
||||
(set_param_values(param_values, args), ...);
|
||||
auto param_values_buf = make_param_value_buffers(param_values);
|
||||
res_ = PQexecPrepared(con_, "", (int)param_values.size(),
|
||||
param_values_buf.data(), NULL, NULL, 0);
|
||||
}
|
||||
else {
|
||||
res_ = PQexec(con_, sql.data());
|
||||
}
|
||||
|
||||
auto guard = guard_statment(res_);
|
||||
if (PQresultStatus(res_) != PGRES_COMMAND_OK) {
|
||||
return 0;
|
||||
}
|
||||
return std::strtoull(PQcmdTuples(res_), nullptr, 10);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
std::enable_if_t<iguana::ylt_refletable_v<T>, std::vector<T>> query_s(
|
||||
const std::string &str, Args &&...args) {
|
||||
string_view_storage_.clear();
|
||||
std::string sql =
|
||||
contains_select(str) ? str : generate_query_sql<T>(db_type_v, str);
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
if (!prepare<T>(sql))
|
||||
return {};
|
||||
|
||||
size_t index = 0;
|
||||
std::vector<std::optional<std::vector<char>>> param_values;
|
||||
(set_param_values(param_values, args), ...);
|
||||
auto param_values_buf = make_param_value_buffers(param_values);
|
||||
res_ = PQexecPrepared(con_, "", (int)param_values.size(),
|
||||
param_values_buf.data(), NULL, NULL, 0);
|
||||
}
|
||||
else {
|
||||
res_ = PQexec(con_, sql.data());
|
||||
}
|
||||
|
||||
auto guard = guard_statment(res_);
|
||||
if (PQresultStatus(res_) != PGRES_TUPLES_OK) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<T> v;
|
||||
auto ntuples = PQntuples(res_);
|
||||
|
||||
for (auto i = 0; i < ntuples; i++) {
|
||||
T t = {};
|
||||
ylt::reflection::for_each(
|
||||
t, [this, i](auto &field, auto /*name*/, auto index) {
|
||||
assign(field, i, index);
|
||||
});
|
||||
v.push_back(std::move(t));
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
std::enable_if_t<iguana::non_ylt_refletable_v<T>, std::vector<T>> query_s(
|
||||
const std::string &sql, Args &&...args) {
|
||||
string_view_storage_.clear();
|
||||
static_assert(iguana::is_tuple<T>::value);
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
if (!prepare<T>(sql))
|
||||
return {};
|
||||
|
||||
size_t index = 0;
|
||||
std::vector<std::optional<std::vector<char>>> param_values;
|
||||
(set_param_values(param_values, args), ...);
|
||||
auto param_values_buf = make_param_value_buffers(param_values);
|
||||
res_ = PQexecPrepared(con_, "", (int)param_values.size(),
|
||||
param_values_buf.data(), NULL, NULL, 0);
|
||||
}
|
||||
else {
|
||||
res_ = PQexec(con_, sql.data());
|
||||
}
|
||||
|
||||
auto guard = guard_statment(res_);
|
||||
if (PQresultStatus(res_) != PGRES_TUPLES_OK) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<T> v;
|
||||
auto ntuples = PQntuples(res_);
|
||||
|
||||
for (auto i = 0; i < ntuples; i++) {
|
||||
T tp = {};
|
||||
size_t index = 0;
|
||||
ormpp::for_each(
|
||||
tp,
|
||||
[this, i, &index](auto &item, auto /*index*/) {
|
||||
using U = ylt::reflection::remove_cvref_t<decltype(item)>;
|
||||
if constexpr (iguana::ylt_refletable_v<U>) {
|
||||
U t = {};
|
||||
ylt::reflection::for_each(
|
||||
t, [this, &index, &t, i](auto &field, auto /*name*/,
|
||||
auto /*index*/) {
|
||||
assign(field, (int)i, index++);
|
||||
});
|
||||
item = std::move(t);
|
||||
}
|
||||
else {
|
||||
assign(item, (int)i, index++);
|
||||
}
|
||||
},
|
||||
std::make_index_sequence<std::tuple_size_v<T>>{});
|
||||
|
||||
if (index > 0)
|
||||
v.push_back(std::move(tp));
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto select(Args... args) {
|
||||
return ormpp::select(this, args...);
|
||||
}
|
||||
|
||||
auto select_all() { return ormpp::select_all(this); }
|
||||
|
||||
template <typename T>
|
||||
auto make_update() {
|
||||
return ormpp::make_update_builder<T>(this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto make_delete() {
|
||||
return ormpp::make_delete_builder<T>(this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto make_create_table() {
|
||||
return ormpp::make_create_table_builder<T>(this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto make_alter_table() {
|
||||
return ormpp::make_alter_table_builder<T>(this);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
std::enable_if_t<iguana::ylt_refletable_v<T>, std::vector<T>> query(
|
||||
Args &&...args) {
|
||||
string_view_storage_.clear();
|
||||
std::string sql = generate_query_sql<T>(db_type_v, args...);
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
res_ = PQexec(con_, sql.data());
|
||||
auto guard = guard_statment(res_);
|
||||
if (PQresultStatus(res_) != PGRES_TUPLES_OK) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<T> v;
|
||||
auto ntuples = PQntuples(res_);
|
||||
|
||||
for (auto i = 0; i < ntuples; i++) {
|
||||
T t = {};
|
||||
ylt::reflection::for_each(
|
||||
t, [this, i](auto &field, auto /*name*/, auto index) {
|
||||
assign(field, i, index);
|
||||
});
|
||||
v.push_back(std::move(t));
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T, typename Arg, typename... Args>
|
||||
std::enable_if_t<iguana::non_ylt_refletable_v<T>, std::vector<T>> query(
|
||||
const Arg &s, Args &&...args) {
|
||||
string_view_storage_.clear();
|
||||
static_assert(iguana::is_tuple<T>::value);
|
||||
constexpr auto SIZE = std::tuple_size_v<T>;
|
||||
std::string sql = s;
|
||||
constexpr auto Args_Size = sizeof...(Args);
|
||||
if (Args_Size != 0) {
|
||||
if (Args_Size != std::count(sql.begin(), sql.end(), '$'))
|
||||
return {};
|
||||
|
||||
sql = get_sql(sql, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
res_ = PQexec(con_, sql.data());
|
||||
auto guard = guard_statment(res_);
|
||||
if (PQresultStatus(res_) != PGRES_TUPLES_OK) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<T> v;
|
||||
auto ntuples = PQntuples(res_);
|
||||
|
||||
for (auto i = 0; i < ntuples; i++) {
|
||||
T tp = {};
|
||||
int index = 0;
|
||||
ormpp::for_each(
|
||||
tp,
|
||||
[this, i, &index](auto &item, auto /*index*/) {
|
||||
using U = ylt::reflection::remove_cvref_t<decltype(item)>;
|
||||
if constexpr (iguana::ylt_refletable_v<U>) {
|
||||
U t = {};
|
||||
ylt::reflection::for_each(
|
||||
t, [this, &index, i](auto &field, auto /*name*/,
|
||||
auto /*index*/) {
|
||||
assign(field, (int)i, index++);
|
||||
});
|
||||
item = std::move(t);
|
||||
}
|
||||
else {
|
||||
assign(item, (int)i, index++);
|
||||
}
|
||||
},
|
||||
std::make_index_sequence<SIZE>{});
|
||||
|
||||
if (index > 0)
|
||||
v.push_back(std::move(tp));
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
// just support execute string sql without placeholders
|
||||
bool execute(const std::string &sql) {
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
res_ = PQexec(con_, sql.data());
|
||||
auto guard = guard_statment(res_);
|
||||
if (PQresultStatus(res_) == PGRES_COMMAND_OK) {
|
||||
last_affect_rows_ = (int)std::strtoull(PQcmdTuples(res_), nullptr, 10);
|
||||
return true;
|
||||
}
|
||||
last_affect_rows_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
int get_last_affect_rows() { return last_affect_rows_; }
|
||||
|
||||
// transaction
|
||||
void set_enable_transaction(bool enable) { transaction_ = enable; }
|
||||
|
||||
bool begin() {
|
||||
res_ = PQexec(con_, "begin;");
|
||||
auto guard = guard_statment(res_);
|
||||
return PQresultStatus(res_) == PGRES_COMMAND_OK;
|
||||
}
|
||||
|
||||
bool commit() {
|
||||
res_ = PQexec(con_, "commit;");
|
||||
auto guard = guard_statment(res_);
|
||||
return PQresultStatus(res_) == PGRES_COMMAND_OK;
|
||||
}
|
||||
|
||||
bool rollback() {
|
||||
res_ = PQexec(con_, "rollback;");
|
||||
auto guard = guard_statment(res_);
|
||||
return PQresultStatus(res_) == PGRES_COMMAND_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string generate_conn_sql(
|
||||
const std::tuple<std::string, std::string, std::string, std::string,
|
||||
std::optional<int>, std::optional<int>> &tp) {
|
||||
std::string params;
|
||||
params.append("host=").append(std::get<0>(tp)).append(" ");
|
||||
params.append("user=").append(std::get<1>(tp)).append(" ");
|
||||
params.append("password=").append(std::get<2>(tp)).append(" ");
|
||||
params.append("dbname=").append(std::get<3>(tp)).append(" ");
|
||||
if (std::get<4>(tp).has_value()) {
|
||||
params.append("connect_timeout=")
|
||||
.append(std::to_string(std::get<4>(tp).value()))
|
||||
.append(" ");
|
||||
}
|
||||
if (std::get<5>(tp).has_value()) {
|
||||
params.append("port=")
|
||||
.append(std::to_string(std::get<5>(tp).value()))
|
||||
.append(" ");
|
||||
}
|
||||
params.pop_back();
|
||||
return params;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
std::string generate_createtb_sql(Args &&...args) {
|
||||
std::set<std::string> not_null;
|
||||
std::set<std::string> unique;
|
||||
std::set<std::string> auto_primary_key;
|
||||
std::set<std::string> primary_keys;
|
||||
|
||||
std::string_view auto_key = get_auto_key<T>();
|
||||
if (!auto_key.empty()) {
|
||||
auto_primary_key.insert(std::string(auto_key));
|
||||
}
|
||||
|
||||
// 宏定义的conflict keys作为联合主键,优先级比ormpp_key更高
|
||||
auto pks = get_conflict_keys<T>(db_type_v);
|
||||
if (!pks.empty()) {
|
||||
for (auto &key : pks) {
|
||||
primary_keys.insert(key);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
ylt::reflection::for_each(std::make_tuple(args...), [&](auto &item) {
|
||||
using U = std::decay_t<decltype(item)>;
|
||||
if constexpr (std::is_same_v<ormpp_auto_key, U>) {
|
||||
auto_primary_key.insert(item.fields);
|
||||
}
|
||||
else if constexpr (std::is_same_v<ormpp_key, U>) {
|
||||
if (pks.empty())
|
||||
primary_keys.insert(item.fields);
|
||||
}
|
||||
else if constexpr (std::is_same_v<ormpp_not_null, U>) {
|
||||
for (auto &name : item.fields) {
|
||||
not_null.insert(name);
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<ormpp_unique, U>) {
|
||||
if (item.fields.size() > 1) {
|
||||
std::string str;
|
||||
for (auto &name : item.fields) {
|
||||
str.append(name).append(",");
|
||||
}
|
||||
str.pop_back();
|
||||
unique.insert(str);
|
||||
}
|
||||
else {
|
||||
unique.insert(*item.fields.begin());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
auto table_name = get_short_struct_name<T>();
|
||||
const auto type_name_arr = get_type_names<T>(DBType::postgresql);
|
||||
|
||||
std::string sql;
|
||||
sql.append("CREATE TABLE IF NOT EXISTS ").append(table_name).append("(");
|
||||
T t;
|
||||
ylt::reflection::for_each(t, [&](auto &field, auto name, size_t index) {
|
||||
using item_type = std::decay_t<decltype(field)>;
|
||||
std::string type_str = type_name_arr[index];
|
||||
sql.append(name).append(" ").append(type_str);
|
||||
|
||||
std::string str_name(name);
|
||||
|
||||
if (!auto_primary_key.empty() &&
|
||||
auto_primary_key.find(str_name) != auto_primary_key.end()) {
|
||||
// remove additional type str for auto key
|
||||
for (int i = 0; i < type_str.size(); i++) {
|
||||
sql.pop_back();
|
||||
}
|
||||
if (type_str == "bigint") {
|
||||
sql.append(" bigserial ");
|
||||
}
|
||||
else {
|
||||
sql.append(" serial ");
|
||||
}
|
||||
auto_key = name;
|
||||
auto_primary_key.clear();
|
||||
}
|
||||
else if (!not_null.empty() && not_null.find(str_name) != not_null.end()) {
|
||||
sql.append(" NOT NULL");
|
||||
not_null.erase(str_name);
|
||||
}
|
||||
|
||||
sql.append(",");
|
||||
});
|
||||
|
||||
if (!auto_key.empty()) {
|
||||
sql.append("PRIMARY KEY (").append(auto_key).append("),");
|
||||
}
|
||||
else if (!primary_keys.empty()) {
|
||||
sql.append("PRIMARY KEY (");
|
||||
for (auto key : primary_keys) {
|
||||
sql.append(key).append(",");
|
||||
}
|
||||
sql.pop_back();
|
||||
sql.append("),");
|
||||
}
|
||||
|
||||
for (auto &name : unique) {
|
||||
sql.append("UNIQUE (").append(name).append("),");
|
||||
}
|
||||
sql.pop_back();
|
||||
sql.append(")");
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool prepare(const std::string &sql) {
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
res_ = PQprepare(con_, "", sql.data(), ylt::reflection::members_count_v<T>,
|
||||
nullptr);
|
||||
auto guard = guard_statment(res_);
|
||||
return PQresultStatus(res_) == PGRES_COMMAND_OK;
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
std::optional<uint64_t> stmt_execute(const T &t, OptType type,
|
||||
Args &&...args) {
|
||||
std::vector<std::optional<std::vector<char>>> param_values;
|
||||
constexpr auto arr = indexs_of<members...>();
|
||||
if constexpr (sizeof...(members) > 0) {
|
||||
(set_param_values(
|
||||
param_values,
|
||||
ylt::reflection::get<ylt::reflection::index_of<members>()>(t)),
|
||||
...);
|
||||
}
|
||||
else {
|
||||
ylt::reflection::for_each(t, [arr, ¶m_values, type, this](
|
||||
auto &field, auto name, auto index) {
|
||||
if (type == OptType::insert && is_auto_key<T>(name)) {
|
||||
return;
|
||||
}
|
||||
if constexpr (sizeof...(members) > 0) {
|
||||
for (auto idx : arr) {
|
||||
if (idx == index) {
|
||||
set_param_values(param_values, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
set_param_values(param_values, field);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(Args) == 0) {
|
||||
if (type == OptType::update) {
|
||||
ylt::reflection::for_each(
|
||||
t, [¶m_values, this](auto &field, auto name, auto /*index*/) {
|
||||
if (is_conflict_key<T>(name, db_type_v)) {
|
||||
set_param_values(param_values, field);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (param_values.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto param_values_buf = make_param_value_buffers(param_values);
|
||||
|
||||
res_ = PQexecPrepared(con_, "", (int)param_values.size(),
|
||||
param_values_buf.data(), NULL, NULL, 0);
|
||||
|
||||
auto guard = guard_statment(res_);
|
||||
auto status = PQresultStatus(res_);
|
||||
|
||||
if (status == PGRES_TUPLES_OK) {
|
||||
return std::strtoull(PQgetvalue(res_, 0, 0), nullptr, 10);
|
||||
}
|
||||
else if (status == PGRES_COMMAND_OK) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
int insert_impl(OptType type, const T &t, Args &&...args) {
|
||||
auto res = insert_or_update_impl(
|
||||
t,
|
||||
generate_insert_sql<T>(db_type_v, type == OptType::insert,
|
||||
std::forward<Args>(args)...),
|
||||
type);
|
||||
return res.has_value() ? res.value() : INT_MIN;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
int insert_impl(OptType type, const std::vector<T> &v, Args &&...args) {
|
||||
auto res = insert_or_update_impl(
|
||||
v,
|
||||
generate_insert_sql<T>(db_type_v, type == OptType::insert,
|
||||
std::forward<Args>(args)...),
|
||||
type);
|
||||
return res.has_value() ? res.value() : INT_MIN;
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
int update_impl(const T &t, Args &&...args) {
|
||||
auto sql = generate_update_sql<T, members...>(db_type_v,
|
||||
std::forward<Args>(args)...);
|
||||
if (sql.empty()) {
|
||||
set_last_error("update requires a conflict key or where condition");
|
||||
return INT_MIN;
|
||||
}
|
||||
auto res = insert_or_update_impl<members...>(t, sql, OptType::update, false,
|
||||
std::forward<Args>(args)...);
|
||||
return res.has_value() ? res.value() : INT_MIN;
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
int update_impl(const std::vector<T> &v, Args &&...args) {
|
||||
auto sql = generate_update_sql<T, members...>(db_type_v,
|
||||
std::forward<Args>(args)...);
|
||||
if (sql.empty()) {
|
||||
set_last_error("update requires a conflict key or where condition");
|
||||
return INT_MIN;
|
||||
}
|
||||
auto res = insert_or_update_impl<members...>(v, sql, OptType::update, false,
|
||||
std::forward<Args>(args)...);
|
||||
return res.has_value() ? res.value() : INT_MIN;
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
std::optional<uint64_t> insert_or_update_impl(const T &t,
|
||||
const std::string &sql,
|
||||
OptType type,
|
||||
bool get_insert_id = false,
|
||||
Args &&...args) {
|
||||
if (!prepare<T>(get_insert_id
|
||||
? sql + "returning " + get_auto_key<T>().data()
|
||||
: sql)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return stmt_execute<members...>(t, type, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
std::optional<uint64_t> insert_or_update_impl(const std::vector<T> &v,
|
||||
const std::string &sql,
|
||||
OptType type,
|
||||
bool get_insert_id = false,
|
||||
Args &&...args) {
|
||||
if (transaction_ && !begin()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!prepare<T>(get_insert_id
|
||||
? sql + "returning " + get_auto_key<T>().data()
|
||||
: sql)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> res = {0};
|
||||
for (auto &item : v) {
|
||||
res = stmt_execute<members...>(item, type, std::forward<Args>(args)...);
|
||||
if (!res.has_value()) {
|
||||
if (transaction_) {
|
||||
rollback();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
if (transaction_ && !commit()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return get_insert_id ? res : (int)v.size();
|
||||
}
|
||||
|
||||
static std::vector<const char *> make_param_value_buffers(
|
||||
std::vector<std::optional<std::vector<char>>> ¶m_values) {
|
||||
std::vector<const char *> buffers;
|
||||
buffers.reserve(param_values.size());
|
||||
for (auto &item : param_values) {
|
||||
buffers.push_back(
|
||||
!item.has_value() ? nullptr : (item->empty() ? "" : item->data()));
|
||||
}
|
||||
return buffers;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set_param_values(
|
||||
std::vector<std::optional<std::vector<char>>> ¶m_values, T &&value) {
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
if constexpr (is_optional_v<U>::value) {
|
||||
if (value.has_value()) {
|
||||
return set_param_values(param_values, std::move(value.value()));
|
||||
}
|
||||
else {
|
||||
param_values.push_back(std::nullopt);
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_enum_v<U> && !iguana::is_int64_v<U>) {
|
||||
std::vector<char> temp(20, 0);
|
||||
iguana::detail::to_chars(temp.data(), static_cast<int>(value));
|
||||
param_values.push_back(std::move(temp));
|
||||
}
|
||||
else if constexpr (std::is_integral_v<U> && !iguana::is_int64_v<U>) {
|
||||
std::vector<char> temp(20, 0);
|
||||
if constexpr (std::is_same_v<char, U>) {
|
||||
temp.front() = value;
|
||||
}
|
||||
else if constexpr (iguana::is_char_type<U>::value) {
|
||||
itoa_fwd(value, temp.data());
|
||||
}
|
||||
else {
|
||||
iguana::detail::to_chars(temp.data(), value);
|
||||
}
|
||||
param_values.push_back(std::move(temp));
|
||||
}
|
||||
else if constexpr (iguana::is_int64_v<U>) {
|
||||
std::vector<char> temp(65, 0);
|
||||
iguana::detail::to_chars(temp.data(), value);
|
||||
param_values.push_back(std::move(temp));
|
||||
}
|
||||
else if constexpr (std::is_floating_point_v<U>) {
|
||||
std::vector<char> temp(20, 0);
|
||||
sprintf(temp.data(), "%f", value);
|
||||
param_values.push_back(std::move(temp));
|
||||
}
|
||||
else if constexpr (iguana::array_v<U> || std::is_same_v<std::string, U> ||
|
||||
std::is_same_v<std::string_view, U>) {
|
||||
std::vector<char> temp = {};
|
||||
std::copy(value.data(), value.data() + value.size() + 1,
|
||||
std::back_inserter(temp));
|
||||
param_values.push_back(std::move(temp));
|
||||
}
|
||||
else if constexpr (is_db_text_type_v<U>) {
|
||||
std::vector<char> temp = {};
|
||||
std::copy(value.data(), value.data() + value.size(),
|
||||
std::back_inserter(temp));
|
||||
temp.push_back('\0');
|
||||
param_values.push_back(std::move(temp));
|
||||
}
|
||||
else if constexpr (iguana::c_array_v<U>) {
|
||||
std::vector<char> temp = {};
|
||||
std::copy(value, value + sizeof(U), std::back_inserter(temp));
|
||||
param_values.push_back(std::move(temp));
|
||||
}
|
||||
else if constexpr (std::is_same_v<blob, U>) {
|
||||
std::vector<char> temp = {};
|
||||
std::copy(value.data(), value.data() + value.size(),
|
||||
std::back_inserter(temp));
|
||||
param_values.push_back(std::move(temp));
|
||||
}
|
||||
#ifdef ORMPP_WITH_CSTRING
|
||||
else if constexpr (std::is_same_v<CString, U>) {
|
||||
std::vector<char> temp = {};
|
||||
std::copy(value.GetString(), value.GetString() + value.GetLength() + 1,
|
||||
std::back_inserter(temp));
|
||||
param_values.push_back(std::move(temp));
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
static_assert(!sizeof(U), "this type has not supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr void assign(T &&value, int row, int i) {
|
||||
if (PQgetisnull(res_, row, i) == 1) {
|
||||
value = {};
|
||||
return;
|
||||
}
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
if constexpr (is_optional_v<U>::value) {
|
||||
using value_type = typename U::value_type;
|
||||
value_type item;
|
||||
assign(item, row, i);
|
||||
value = std::move(item);
|
||||
}
|
||||
else if constexpr (std::is_enum_v<U> && !iguana::is_int64_v<U>) {
|
||||
value = static_cast<U>(std::atoi(PQgetvalue(res_, row, i)));
|
||||
}
|
||||
else if constexpr (std::is_integral_v<U> && !iguana::is_int64_v<U>) {
|
||||
value = std::atoi(PQgetvalue(res_, row, i));
|
||||
}
|
||||
else if constexpr (iguana::is_int64_v<U>) {
|
||||
value = std::atoll(PQgetvalue(res_, row, i));
|
||||
}
|
||||
else if constexpr (std::is_floating_point_v<U>) {
|
||||
value = std::atof(PQgetvalue(res_, row, i));
|
||||
}
|
||||
else if constexpr (std::is_same_v<std::string, U>) {
|
||||
value = PQgetvalue(res_, row, i);
|
||||
}
|
||||
else if constexpr (std::is_same_v<std::string_view, U>) {
|
||||
string_view_storage_.emplace_back(
|
||||
PQgetvalue(res_, row, i),
|
||||
static_cast<std::size_t>(PQgetlength(res_, row, i)));
|
||||
value = string_view_storage_.back();
|
||||
}
|
||||
else if constexpr (is_db_text_type_v<U>) {
|
||||
value.value = PQgetvalue(res_, row, i);
|
||||
}
|
||||
else if constexpr (iguana::array_v<U>) {
|
||||
auto p = PQgetvalue(res_, row, i);
|
||||
memcpy(value.data(), p, value.size());
|
||||
}
|
||||
else if constexpr (iguana::c_array_v<U>) {
|
||||
auto p = PQgetvalue(res_, row, i);
|
||||
memcpy(value, p, sizeof(U));
|
||||
}
|
||||
else if constexpr (std::is_same_v<blob, U>) {
|
||||
auto p = PQgetvalue(res_, row, i);
|
||||
value = blob(p, p + PQgetlength(res_, row, i));
|
||||
}
|
||||
#ifdef ORMPP_WITH_CSTRING
|
||||
else if constexpr (std::is_same_v<CString, U>) {
|
||||
value.SetString(PQgetvalue(res_, row, i));
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
static_assert(!sizeof(U), "this type has not supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct guard_statment {
|
||||
guard_statment(PGresult *res) : res_(res) { reset_error(); }
|
||||
~guard_statment() {
|
||||
if (res_ != nullptr) {
|
||||
auto status = PQresultStatus(res_);
|
||||
if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
|
||||
set_last_error(PQresultErrorMessage(res_));
|
||||
}
|
||||
PQclear(res_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
PGresult *res_ = nullptr;
|
||||
};
|
||||
|
||||
private:
|
||||
PGconn *con_ = nullptr;
|
||||
PGresult *res_ = nullptr;
|
||||
std::deque<std::string> string_view_storage_;
|
||||
inline static std::string last_error_;
|
||||
inline static bool has_error_ = false;
|
||||
inline static bool transaction_ = true;
|
||||
int last_affect_rows_;
|
||||
};
|
||||
} // namespace ormpp
|
||||
|
||||
#endif // ORM_POSTGRESQL_HPP
|
||||
Vendored
+2251
File diff suppressed because it is too large
Load Diff
Vendored
+930
@@ -0,0 +1,930 @@
|
||||
//
|
||||
// Created by qiyu on 10/28/17.
|
||||
//
|
||||
#ifndef ORM_SQLITE_HPP
|
||||
#define ORM_SQLITE_HPP
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <climits>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "query.hpp"
|
||||
|
||||
namespace ormpp {
|
||||
class sqlite {
|
||||
public:
|
||||
static constexpr DBType db_type_v = DBType::sqlite;
|
||||
|
||||
~sqlite() { disconnect(); }
|
||||
|
||||
bool has_error() const { return has_error_; }
|
||||
|
||||
static void reset_error() {
|
||||
has_error_ = false;
|
||||
last_error_ = {};
|
||||
}
|
||||
|
||||
static void set_last_error(std::string last_error) {
|
||||
has_error_ = true;
|
||||
last_error_ = std::move(last_error);
|
||||
std::cout << last_error_ << std::endl; // todo, write to log file
|
||||
}
|
||||
|
||||
std::string get_last_error() const { return last_error_; }
|
||||
|
||||
bool connect(
|
||||
const std::tuple<std::string, std::string, std::string, std::string,
|
||||
std::optional<int>, std::optional<int>> &tp) {
|
||||
reset_error();
|
||||
auto r = sqlite3_open(std::get<3>(tp).c_str(), &handle_);
|
||||
if (r != SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
// Use password as SQLCipher encryption key if it's not empty
|
||||
if (!std::get<2>(tp).empty()) {
|
||||
// Use password as key
|
||||
std::string key = std::get<2>(tp);
|
||||
auto r2 = sqlite3_key(handle_, key.c_str(), key.length());
|
||||
if (r2 != SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test if the key is correct by executing a simple query
|
||||
bool can_query = true;
|
||||
const char *test_query = "PRAGMA user_version;";
|
||||
sqlite3_stmt *stmt = nullptr;
|
||||
auto r3 = sqlite3_prepare_v2(handle_, test_query, -1, &stmt, nullptr);
|
||||
if (r3 != SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
can_query = false;
|
||||
}
|
||||
else {
|
||||
r3 = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
if (r3 != SQLITE_ROW) {
|
||||
set_last_error("Invalid SQLCipher key");
|
||||
can_query = false;
|
||||
}
|
||||
}
|
||||
if (!can_query) {
|
||||
disconnect();
|
||||
}
|
||||
return can_query;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool connect(const std::string &host, const std::string &user,
|
||||
const std::string &passwd, const std::string &db,
|
||||
const std::optional<int> &timeout,
|
||||
const std::optional<int> &port) {
|
||||
return connect(std::make_tuple(host, user, passwd.empty() ? user : passwd,
|
||||
db.empty() ? host : db, timeout, port));
|
||||
}
|
||||
|
||||
bool ping() { return true; }
|
||||
|
||||
template <typename... Args>
|
||||
bool disconnect(Args &&...args) {
|
||||
if (handle_ != nullptr) {
|
||||
auto r = sqlite3_close(handle_);
|
||||
handle_ = nullptr;
|
||||
if (r == SQLITE_OK) {
|
||||
return true;
|
||||
}
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
bool create_datatable(Args &&...args) {
|
||||
reset_error();
|
||||
std::string sql = generate_createtb_sql<T>(std::forward<Args>(args)...);
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
if (sqlite3_exec(handle_, sql.data(), nullptr, nullptr, nullptr) !=
|
||||
SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
int insert(const T &t, Args &&...args) {
|
||||
return insert_impl(OptType::insert, t, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
int insert(const std::vector<T> &v, Args &&...args) {
|
||||
return insert_impl(OptType::insert, v, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
int replace(const T &t, Args &&...args) {
|
||||
return insert_impl(OptType::replace, t, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
int replace(const std::vector<T> &v, Args &&...args) {
|
||||
return insert_impl(OptType::replace, v, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
int update(const T &t, Args &&...args) {
|
||||
return update_impl<members...>(t, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
int update(const std::vector<T> &v, Args &&...args) {
|
||||
return update_impl<members...>(v, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
uint64_t get_insert_id_after_insert(const T &t, Args &&...args) {
|
||||
auto res = insert_or_update_impl(t, generate_insert_sql<T>(db_type_v, true),
|
||||
OptType::insert, true);
|
||||
return res.has_value() ? res.value() : 0;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
uint64_t get_insert_id_after_insert(const std::vector<T> &v, Args &&...args) {
|
||||
auto res = insert_or_update_impl(v, generate_insert_sql<T>(db_type_v, true),
|
||||
OptType::insert, true);
|
||||
return res.has_value() ? res.value() : 0;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
bool delete_records(Args &&...where_conditon) {
|
||||
auto sql = generate_delete_sql<T>(db_type_v,
|
||||
std::forward<Args>(where_conditon)...);
|
||||
return execute(sql);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
uint64_t delete_records_s(const std::string &str, Args &&...args) {
|
||||
auto sql = generate_delete_sql<T>(db_type_v, str);
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
int result = sqlite3_prepare_v2(handle_, sql.data(), (int)sql.size(),
|
||||
&stmt_, nullptr);
|
||||
if (result != SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
size_t index = 0;
|
||||
(set_param_bind(args, ++index), ...);
|
||||
}
|
||||
|
||||
auto guard = guard_statment(stmt_);
|
||||
if (sqlite3_step(stmt_) != SQLITE_DONE) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return 0;
|
||||
}
|
||||
return sqlite3_changes(handle_);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
std::enable_if_t<iguana::ylt_refletable_v<T>, std::vector<T>> query_s(
|
||||
const std::string &str, Args &&...args) {
|
||||
string_view_storage_.clear();
|
||||
std::string sql =
|
||||
contains_select(str) ? str : generate_query_sql<T>(db_type_v, str);
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
int result = sqlite3_prepare_v2(handle_, sql.data(), (int)sql.size(),
|
||||
&stmt_, nullptr);
|
||||
if (result != SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return {};
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
size_t index = 0;
|
||||
(set_param_bind(args, ++index), ...);
|
||||
}
|
||||
|
||||
auto guard = guard_statment(stmt_);
|
||||
|
||||
std::vector<T> v;
|
||||
while (true) {
|
||||
result = sqlite3_step(stmt_);
|
||||
if (result == SQLITE_DONE)
|
||||
break;
|
||||
|
||||
if (result != SQLITE_ROW)
|
||||
break;
|
||||
|
||||
T t = {};
|
||||
ylt::reflection::for_each(t,
|
||||
[this](auto &field, auto /*name*/, auto index) {
|
||||
assign(field, index);
|
||||
});
|
||||
|
||||
v.push_back(std::move(t));
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
std::enable_if_t<iguana::non_ylt_refletable_v<T>, std::vector<T>> query_s(
|
||||
const std::string &sql, Args &&...args) {
|
||||
string_view_storage_.clear();
|
||||
static_assert(iguana::is_tuple<T>::value);
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
int result = sqlite3_prepare_v2(handle_, sql.data(), (int)sql.size(),
|
||||
&stmt_, nullptr);
|
||||
if (result != SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return {};
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
size_t index = 0;
|
||||
(set_param_bind(args, ++index), ...);
|
||||
}
|
||||
|
||||
auto guard = guard_statment(stmt_);
|
||||
|
||||
std::vector<T> v;
|
||||
while (true) {
|
||||
result = sqlite3_step(stmt_);
|
||||
if (result == SQLITE_DONE)
|
||||
break;
|
||||
|
||||
if (result != SQLITE_ROW)
|
||||
break;
|
||||
|
||||
T tp = {};
|
||||
size_t index = 0;
|
||||
ormpp::for_each(
|
||||
tp,
|
||||
[this, &index](auto &item, auto /*index*/) {
|
||||
using U = ylt::reflection::remove_cvref_t<decltype(item)>;
|
||||
if constexpr (iguana::ylt_refletable_v<U>) {
|
||||
U t = {};
|
||||
ylt::reflection::for_each(
|
||||
t,
|
||||
[this, &index](auto &field, auto /*name*/, auto /*index*/) {
|
||||
assign(field, index++);
|
||||
});
|
||||
item = std::move(t);
|
||||
}
|
||||
else {
|
||||
assign(item, index++);
|
||||
}
|
||||
},
|
||||
std::make_index_sequence<std::tuple_size_v<T>>{});
|
||||
|
||||
if (index > 0)
|
||||
v.push_back(std::move(tp));
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto select(Args... args) {
|
||||
return ormpp::select(this, args...);
|
||||
}
|
||||
|
||||
auto select_all() { return ormpp::select_all(this); }
|
||||
|
||||
template <typename T>
|
||||
auto make_update() {
|
||||
return ormpp::make_update_builder<T>(this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto make_delete() {
|
||||
return ormpp::make_delete_builder<T>(this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto make_create_table() {
|
||||
return ormpp::make_create_table_builder<T>(this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto make_alter_table() {
|
||||
return ormpp::make_alter_table_builder<T>(this);
|
||||
}
|
||||
// restriction, all the args are string, the first is the where condition,
|
||||
// rest are append conditions
|
||||
template <typename T, typename... Args>
|
||||
std::enable_if_t<iguana::ylt_refletable_v<T>, std::vector<T>> query(
|
||||
Args &&...args) {
|
||||
string_view_storage_.clear();
|
||||
std::string sql = generate_query_sql<T>(db_type_v, args...);
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
int result = sqlite3_prepare_v2(handle_, sql.data(), (int)sql.size(),
|
||||
&stmt_, nullptr);
|
||||
if (result != SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto guard = guard_statment(stmt_);
|
||||
|
||||
std::vector<T> v;
|
||||
while (true) {
|
||||
result = sqlite3_step(stmt_);
|
||||
if (result == SQLITE_DONE)
|
||||
break;
|
||||
|
||||
if (result != SQLITE_ROW)
|
||||
break;
|
||||
|
||||
T t = {};
|
||||
ylt::reflection::for_each(t,
|
||||
[this](auto &field, auto /*name*/, auto index) {
|
||||
assign(field, index);
|
||||
});
|
||||
v.push_back(std::move(t));
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T, typename Arg, typename... Args>
|
||||
std::enable_if_t<iguana::non_ylt_refletable_v<T>, std::vector<T>> query(
|
||||
const Arg &s, Args &&...args) {
|
||||
string_view_storage_.clear();
|
||||
static_assert(iguana::is_tuple<T>::value);
|
||||
constexpr auto SIZE = std::tuple_size_v<T>;
|
||||
std::string sql = s;
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
constexpr auto Args_Size = sizeof...(Args);
|
||||
if constexpr (Args_Size != 0) {
|
||||
if (Args_Size != std::count(sql.begin(), sql.end(), '?'))
|
||||
return {};
|
||||
|
||||
sql = get_sql(sql, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
int result = sqlite3_prepare_v2(handle_, sql.data(), (int)sql.size(),
|
||||
&stmt_, nullptr);
|
||||
if (result != SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto guard = guard_statment(stmt_);
|
||||
|
||||
std::vector<T> v;
|
||||
while (true) {
|
||||
result = sqlite3_step(stmt_);
|
||||
if (result == SQLITE_DONE)
|
||||
break;
|
||||
|
||||
if (result != SQLITE_ROW)
|
||||
break;
|
||||
|
||||
T tp = {};
|
||||
size_t index = 0;
|
||||
ormpp::for_each(
|
||||
tp,
|
||||
[this, &index](auto &item, auto /*index*/) {
|
||||
using U = ylt::reflection::remove_cvref_t<decltype(item)>;
|
||||
if constexpr (iguana::ylt_refletable_v<U>) {
|
||||
U t = {};
|
||||
ylt::reflection::for_each(
|
||||
t,
|
||||
[this, &index](auto &field, auto /*name*/, auto /*index*/) {
|
||||
assign(field, index++);
|
||||
});
|
||||
item = std::move(t);
|
||||
}
|
||||
else {
|
||||
assign(item, index++);
|
||||
}
|
||||
},
|
||||
std::make_index_sequence<SIZE>{});
|
||||
|
||||
if (index > 0)
|
||||
v.push_back(std::move(tp));
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
// just support execute string sql without placeholders
|
||||
bool execute(const std::string &sql) {
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
int result = sqlite3_prepare_v2(handle_, sql.data(), (int)sql.size(),
|
||||
&stmt_, nullptr);
|
||||
if (result != SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto guard = guard_statment(stmt_);
|
||||
if (sqlite3_step(stmt_) != SQLITE_DONE) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int get_last_affect_rows() { return sqlite3_changes(handle_); }
|
||||
|
||||
// transaction
|
||||
void set_enable_transaction(bool enable) { transaction_ = enable; }
|
||||
|
||||
bool begin() {
|
||||
reset_error();
|
||||
if (sqlite3_exec(handle_, "BEGIN", nullptr, nullptr, nullptr) !=
|
||||
SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool commit() {
|
||||
reset_error();
|
||||
if (sqlite3_exec(handle_, "COMMIT", nullptr, nullptr, nullptr) !=
|
||||
SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rollback() {
|
||||
reset_error();
|
||||
if (sqlite3_exec(handle_, "ROLLBACK", nullptr, nullptr, nullptr) !=
|
||||
SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T, typename... Args>
|
||||
std::string generate_createtb_sql(Args &&...args) {
|
||||
std::set<std::string> not_null;
|
||||
std::set<std::string> unique;
|
||||
std::set<std::string> auto_primary_key;
|
||||
std::set<std::string> primary_keys;
|
||||
|
||||
std::string_view auto_key = get_auto_key<T>();
|
||||
if (!auto_key.empty()) {
|
||||
auto_primary_key.insert(std::string(auto_key));
|
||||
}
|
||||
|
||||
// 宏定义的conflict keys作为联合主键,优先级比ormpp_key更高
|
||||
auto pks = get_conflict_keys<T>(db_type_v);
|
||||
if (!pks.empty()) {
|
||||
for (auto &key : pks) {
|
||||
primary_keys.insert(key);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
ylt::reflection::for_each(std::make_tuple(args...), [&](auto &item) {
|
||||
using U = std::decay_t<decltype(item)>;
|
||||
if constexpr (std::is_same_v<ormpp_auto_key, U>) {
|
||||
auto_primary_key.insert(item.fields);
|
||||
}
|
||||
else if constexpr (std::is_same_v<ormpp_key, U>) {
|
||||
if (pks.empty())
|
||||
primary_keys.insert(item.fields);
|
||||
}
|
||||
else if constexpr (std::is_same_v<ormpp_not_null, U>) {
|
||||
for (auto &name : item.fields) {
|
||||
not_null.insert(name);
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<ormpp_unique, U>) {
|
||||
if (item.fields.size() > 1) {
|
||||
std::string str;
|
||||
for (auto &name : item.fields) {
|
||||
str.append(name).append(",");
|
||||
}
|
||||
str.pop_back();
|
||||
unique.insert(str);
|
||||
}
|
||||
else {
|
||||
unique.insert(*item.fields.begin());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
auto table_name = get_short_struct_name<T>();
|
||||
const auto type_name_arr = get_type_names<T>(DBType::sqlite);
|
||||
|
||||
std::string sql;
|
||||
sql.append("CREATE TABLE IF NOT EXISTS ").append(table_name).append("(");
|
||||
T t;
|
||||
ylt::reflection::for_each(t, [&](auto &field, auto name, size_t index) {
|
||||
using item_type = std::decay_t<decltype(field)>;
|
||||
sql.append(name).append(" ").append(type_name_arr[index]);
|
||||
|
||||
std::string str_name(name);
|
||||
|
||||
if (!auto_primary_key.empty() &&
|
||||
auto_primary_key.find(str_name) != auto_primary_key.end()) {
|
||||
sql.append(" PRIMARY KEY AUTOINCREMENT ");
|
||||
if (auto_key.empty()) {
|
||||
add_auto_key_field(table_name, name);
|
||||
}
|
||||
auto_key = name;
|
||||
auto_primary_key.clear();
|
||||
}
|
||||
else if (!not_null.empty() && not_null.find(str_name) != not_null.end()) {
|
||||
sql.append(" NOT NULL");
|
||||
not_null.erase(str_name);
|
||||
}
|
||||
|
||||
sql.append(",");
|
||||
});
|
||||
|
||||
if (auto_key.empty() && !primary_keys.empty()) {
|
||||
sql.append("PRIMARY KEY (");
|
||||
for (auto key : primary_keys) {
|
||||
sql.append(key).append(",");
|
||||
}
|
||||
sql.pop_back();
|
||||
sql.append("),");
|
||||
}
|
||||
|
||||
for (auto &name : unique) {
|
||||
sql.append("UNIQUE (").append(name).append("),");
|
||||
}
|
||||
sql.pop_back();
|
||||
sql.append(")");
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
int stmt_execute(const T &t, OptType type, Args &&...args) {
|
||||
size_t index = 0;
|
||||
bool bind_ok = true;
|
||||
if constexpr (sizeof...(members) > 0) {
|
||||
[[maybe_unused]] auto r =
|
||||
((bind_ok &&
|
||||
(bind_ok = set_param_bind(
|
||||
ylt::reflection::get<ylt::reflection::index_of<members>()>(
|
||||
t),
|
||||
++index)),
|
||||
true),
|
||||
...);
|
||||
}
|
||||
else {
|
||||
ylt::reflection::for_each(t, [&bind_ok, &index, type, this](
|
||||
auto &field, auto name, auto /*index*/) {
|
||||
if ((type == OptType::insert && is_auto_key<T>(name)) || !bind_ok) {
|
||||
return;
|
||||
}
|
||||
bind_ok = set_param_bind(field, ++index);
|
||||
});
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(Args) == 0) {
|
||||
if (type == OptType::update) {
|
||||
ylt::reflection::for_each(
|
||||
t,
|
||||
[&bind_ok, &index, this](auto &field, auto name, auto /*index*/) {
|
||||
if (!bind_ok) {
|
||||
return;
|
||||
}
|
||||
if (is_conflict_key<T>(name, db_type_v)) {
|
||||
bind_ok = set_param_bind(field, ++index);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!bind_ok) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return INT_MIN;
|
||||
}
|
||||
|
||||
if (sqlite3_step(stmt_) != SQLITE_DONE) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return INT_MIN;
|
||||
}
|
||||
|
||||
int count = sqlite3_changes(handle_);
|
||||
if (count == 0) {
|
||||
return type == OptType::update ? count : INT_MIN;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool set_param_bind(T &&value, int i) {
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
if constexpr (is_optional_v<U>::value) {
|
||||
if (value.has_value()) {
|
||||
return set_param_bind(std::move(value.value()), i);
|
||||
}
|
||||
return SQLITE_OK == sqlite3_bind_null(stmt_, i);
|
||||
}
|
||||
else if constexpr (std::is_enum_v<U> && !iguana::is_int64_v<U>) {
|
||||
return SQLITE_OK == sqlite3_bind_int(stmt_, i, static_cast<int>(value));
|
||||
}
|
||||
else if constexpr (std::is_integral_v<U> && !iguana::is_int64_v<U>) {
|
||||
return SQLITE_OK == sqlite3_bind_int(stmt_, i, value);
|
||||
}
|
||||
else if constexpr (iguana::is_int64_v<U>) {
|
||||
return SQLITE_OK == sqlite3_bind_int64(stmt_, i, value);
|
||||
}
|
||||
else if constexpr (std::is_floating_point_v<U>) {
|
||||
return SQLITE_OK == sqlite3_bind_double(stmt_, i, value);
|
||||
}
|
||||
else if constexpr (iguana::array_v<U> || std::is_same_v<std::string, U> ||
|
||||
std::is_same_v<std::string_view, U>) {
|
||||
size_t len = (std::min)(std::strlen(value.data()), (size_t)value.size());
|
||||
return SQLITE_OK ==
|
||||
sqlite3_bind_text(stmt_, i, value.data(), len, nullptr);
|
||||
}
|
||||
else if constexpr (is_db_text_type_v<U>) {
|
||||
return SQLITE_OK ==
|
||||
sqlite3_bind_text(stmt_, i, value.data(), value.size(), nullptr);
|
||||
}
|
||||
else if constexpr (iguana::c_array_v<U> ||
|
||||
std::is_same_v<char,
|
||||
std::remove_pointer_t<std::decay_t<U>>>) {
|
||||
return SQLITE_OK ==
|
||||
sqlite3_bind_text(stmt_, i, value, strlen(value), nullptr);
|
||||
}
|
||||
else if constexpr (std::is_same_v<blob, U>) {
|
||||
return SQLITE_OK == sqlite3_bind_blob(stmt_, i, value.data(),
|
||||
static_cast<size_t>(value.size()),
|
||||
nullptr);
|
||||
}
|
||||
#ifdef ORMPP_WITH_CSTRING
|
||||
else if constexpr (std::is_same_v<CString, U>) {
|
||||
return SQLITE_OK ==
|
||||
sqlite3_bind_text(stmt_, i, value.GetString(),
|
||||
static_cast<size_t>(value.GetLength()), nullptr);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
static_assert(!sizeof(U), "this type has not supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void assign(T &&value, int i) {
|
||||
using U = ylt::reflection::remove_cvref_t<T>;
|
||||
if (sqlite3_column_type(stmt_, i) == SQLITE_NULL) {
|
||||
value = U{};
|
||||
return;
|
||||
}
|
||||
if constexpr (is_optional_v<U>::value) {
|
||||
using value_type = typename U::value_type;
|
||||
value_type item;
|
||||
assign(item, i);
|
||||
value = std::move(item);
|
||||
}
|
||||
else if constexpr (std::is_enum_v<U> && !iguana::is_int64_v<U>) {
|
||||
value = static_cast<U>(sqlite3_column_int(stmt_, i));
|
||||
}
|
||||
else if constexpr (std::is_integral_v<U> && !iguana::is_int64_v<U>) {
|
||||
if constexpr (std::is_same_v<U, char>) {
|
||||
value = (char)sqlite3_column_int(stmt_, i);
|
||||
}
|
||||
else {
|
||||
value = sqlite3_column_int(stmt_, i);
|
||||
}
|
||||
}
|
||||
else if constexpr (iguana::is_int64_v<U>) {
|
||||
value = sqlite3_column_int64(stmt_, i);
|
||||
}
|
||||
else if constexpr (std::is_floating_point_v<U>) {
|
||||
value = sqlite3_column_double(stmt_, i);
|
||||
}
|
||||
else if constexpr (std::is_same_v<std::string, U>) {
|
||||
value.reserve(sqlite3_column_bytes(stmt_, i));
|
||||
value.assign((const char *)sqlite3_column_text(stmt_, i),
|
||||
(size_t)sqlite3_column_bytes(stmt_, i));
|
||||
}
|
||||
else if constexpr (std::is_same_v<std::string_view, U>) {
|
||||
string_view_storage_.emplace_back(
|
||||
(const char *)sqlite3_column_text(stmt_, i),
|
||||
(size_t)sqlite3_column_bytes(stmt_, i));
|
||||
value = string_view_storage_.back();
|
||||
}
|
||||
else if constexpr (is_decimal_type_v<U>) {
|
||||
auto text = reinterpret_cast<const char *>(sqlite3_column_text(stmt_, i));
|
||||
if (text == nullptr) {
|
||||
value.value.clear();
|
||||
}
|
||||
else {
|
||||
value.value.assign(text, (size_t)sqlite3_column_bytes(stmt_, i));
|
||||
auto column_type = sqlite3_column_type(stmt_, i);
|
||||
auto exponent_pos = value.value.find_first_of("eE");
|
||||
if (column_type != SQLITE_TEXT && exponent_pos == std::string::npos) {
|
||||
auto decimal_pos = value.value.find('.');
|
||||
if constexpr (U::scale > 0) {
|
||||
if (decimal_pos == std::string::npos) {
|
||||
value.value.append(".").append(U::scale, '0');
|
||||
}
|
||||
else {
|
||||
auto current_scale = value.value.size() - decimal_pos - 1;
|
||||
if (current_scale < U::scale) {
|
||||
value.value.append(U::scale - current_scale, '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_db_text_type_v<U>) {
|
||||
auto text = reinterpret_cast<const char *>(sqlite3_column_text(stmt_, i));
|
||||
if (text == nullptr) {
|
||||
value.value.clear();
|
||||
}
|
||||
else {
|
||||
value.value.assign(text, (size_t)sqlite3_column_bytes(stmt_, i));
|
||||
}
|
||||
}
|
||||
else if constexpr (iguana::array_v<U>) {
|
||||
memcpy(value.data(), sqlite3_column_text(stmt_, i), sizeof(U));
|
||||
}
|
||||
else if constexpr (iguana::c_array_v<U>) {
|
||||
memcpy(value, sqlite3_column_text(stmt_, i), sizeof(U));
|
||||
}
|
||||
else if constexpr (std::is_same_v<blob, U>) {
|
||||
auto p = (const char *)sqlite3_column_blob(stmt_, i);
|
||||
value = blob(p, p + sqlite3_column_bytes(stmt_, i));
|
||||
}
|
||||
#ifdef ORMPP_WITH_CSTRING
|
||||
else if constexpr (std::is_same_v<CString, U>) {
|
||||
value.SetString((const char *)sqlite3_column_text(stmt_, i));
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
static_assert(!sizeof(U), "this type has not supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
int insert_impl(OptType type, const T &t, Args &&...args) {
|
||||
auto res = insert_or_update_impl(
|
||||
t, generate_insert_sql<T>(db_type_v, type == OptType::insert), type);
|
||||
return res.has_value() ? res.value() : INT_MIN;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
int insert_impl(OptType type, const std::vector<T> &v, Args &&...args) {
|
||||
auto res = insert_or_update_impl(
|
||||
v, generate_insert_sql<T>(db_type_v, type == OptType::insert), type);
|
||||
return res.has_value() ? res.value() : INT_MIN;
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
int update_impl(const T &t, Args &&...args) {
|
||||
auto sql = generate_update_sql<T, members...>(db_type_v,
|
||||
std::forward<Args>(args)...);
|
||||
if (sql.empty()) {
|
||||
set_last_error("update requires a conflict key or where condition");
|
||||
return INT_MIN;
|
||||
}
|
||||
auto res = insert_or_update_impl<members...>(t, sql, OptType::update, false,
|
||||
std::forward<Args>(args)...);
|
||||
return res.has_value() ? res.value() : INT_MIN;
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
int update_impl(const std::vector<T> &v, Args &&...args) {
|
||||
auto sql = generate_update_sql<T, members...>(db_type_v,
|
||||
std::forward<Args>(args)...);
|
||||
if (sql.empty()) {
|
||||
set_last_error("update requires a conflict key or where condition");
|
||||
return INT_MIN;
|
||||
}
|
||||
auto res = insert_or_update_impl<members...>(v, sql, OptType::update, false,
|
||||
std::forward<Args>(args)...);
|
||||
return res.has_value() ? res.value() : INT_MIN;
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
std::optional<uint64_t> insert_or_update_impl(const T &t,
|
||||
const std::string &sql,
|
||||
OptType type,
|
||||
bool get_insert_id = false,
|
||||
Args &&...args) {
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
if (sqlite3_prepare_v2(handle_, sql.data(), (int)sql.size(), &stmt_,
|
||||
nullptr) != SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto guard = guard_statment(stmt_);
|
||||
|
||||
if (stmt_execute<members...>(t, type, std::forward<Args>(args)...) ==
|
||||
INT_MIN) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return get_insert_id ? sqlite3_last_insert_rowid(handle_) : 1;
|
||||
}
|
||||
|
||||
template <auto... members, typename T, typename... Args>
|
||||
std::optional<uint64_t> insert_or_update_impl(const std::vector<T> &v,
|
||||
const std::string &sql,
|
||||
OptType type,
|
||||
bool get_insert_id = false,
|
||||
Args &&...args) {
|
||||
#ifdef ORMPP_ENABLE_LOG
|
||||
std::cout << sql << std::endl;
|
||||
#endif
|
||||
if (sqlite3_prepare_v2(handle_, sql.data(), (int)sql.size(), &stmt_,
|
||||
nullptr) != SQLITE_OK) {
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto guard = guard_statment(stmt_);
|
||||
|
||||
if (transaction_ && !begin()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
for (auto &item : v) {
|
||||
if (stmt_execute<members...>(item, type, std::forward<Args>(args)...) ==
|
||||
INT_MIN) {
|
||||
if (transaction_) {
|
||||
rollback();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (sqlite3_reset(stmt_) != SQLITE_OK) {
|
||||
if (transaction_) {
|
||||
rollback();
|
||||
}
|
||||
set_last_error(sqlite3_errmsg(handle_));
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
if (transaction_ && !commit()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return get_insert_id ? sqlite3_last_insert_rowid(handle_) : (int)v.size();
|
||||
}
|
||||
|
||||
private:
|
||||
struct guard_statment {
|
||||
guard_statment(sqlite3_stmt *stmt) : stmt_(stmt) { reset_error(); }
|
||||
~guard_statment() {
|
||||
if (stmt_ != nullptr) {
|
||||
auto status = sqlite3_finalize(stmt_);
|
||||
if (status) {
|
||||
set_last_error("close statment error code " + std::to_string(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
sqlite3_stmt *stmt_ = nullptr;
|
||||
};
|
||||
|
||||
private:
|
||||
sqlite3 *handle_ = nullptr;
|
||||
sqlite3_stmt *stmt_ = nullptr;
|
||||
std::deque<std::string> string_view_storage_;
|
||||
inline static std::string last_error_;
|
||||
inline static bool has_error_ = false;
|
||||
inline static bool transaction_ = true;
|
||||
};
|
||||
} // namespace ormpp
|
||||
|
||||
#endif // ORM_SQLITE_HPP
|
||||
+342
@@ -0,0 +1,342 @@
|
||||
//
|
||||
// Created by qiyu on 10/23/17.
|
||||
//
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "pg_types.h"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
#ifndef EXAMPLE1_TYPE_MAPPING_HPP
|
||||
#define EXAMPLE1_TYPE_MAPPING_HPP
|
||||
|
||||
namespace ormpp {
|
||||
|
||||
using blob = std::vector<char>;
|
||||
|
||||
struct date {
|
||||
std::string value;
|
||||
|
||||
date() = default;
|
||||
date(const char *v) : value(v) {}
|
||||
date(std::string v) : value(std::move(v)) {}
|
||||
|
||||
const char *data() const noexcept { return value.data(); }
|
||||
std::size_t size() const noexcept { return value.size(); }
|
||||
};
|
||||
|
||||
struct time {
|
||||
std::string value;
|
||||
|
||||
time() = default;
|
||||
time(const char *v) : value(v) {}
|
||||
time(std::string v) : value(std::move(v)) {}
|
||||
|
||||
const char *data() const noexcept { return value.data(); }
|
||||
std::size_t size() const noexcept { return value.size(); }
|
||||
};
|
||||
|
||||
struct datetime {
|
||||
std::string value;
|
||||
|
||||
datetime() = default;
|
||||
datetime(const char *v) : value(v) {}
|
||||
datetime(std::string v) : value(std::move(v)) {}
|
||||
|
||||
const char *data() const noexcept { return value.data(); }
|
||||
std::size_t size() const noexcept { return value.size(); }
|
||||
};
|
||||
|
||||
struct timestamp {
|
||||
std::string value;
|
||||
|
||||
timestamp() = default;
|
||||
timestamp(const char *v) : value(v) {}
|
||||
timestamp(std::string v) : value(std::move(v)) {}
|
||||
|
||||
const char *data() const noexcept { return value.data(); }
|
||||
std::size_t size() const noexcept { return value.size(); }
|
||||
};
|
||||
|
||||
template <int Precision, int Scale>
|
||||
struct decimal {
|
||||
static_assert(Precision > 0, "decimal precision must be greater than 0");
|
||||
static_assert(Scale >= 0, "decimal scale must be greater than or equal to 0");
|
||||
static_assert(Scale <= Precision,
|
||||
"decimal scale must be less than or equal to precision");
|
||||
|
||||
static constexpr int precision = Precision;
|
||||
static constexpr int scale = Scale;
|
||||
|
||||
std::string value;
|
||||
|
||||
decimal() = default;
|
||||
decimal(const char *v) : value(v) {}
|
||||
decimal(std::string v) : value(std::move(v)) {}
|
||||
|
||||
const char *data() const noexcept { return value.data(); }
|
||||
std::size_t size() const noexcept { return value.size(); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_decimal_type : std::false_type {};
|
||||
|
||||
template <int Precision, int Scale>
|
||||
struct is_decimal_type<decimal<Precision, Scale>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_decimal_type_v =
|
||||
is_decimal_type<std::remove_cv_t<T>>::value;
|
||||
|
||||
template <typename T>
|
||||
struct is_db_text_type : std::false_type {};
|
||||
|
||||
template <>
|
||||
struct is_db_text_type<date> : std::true_type {};
|
||||
|
||||
template <>
|
||||
struct is_db_text_type<time> : std::true_type {};
|
||||
|
||||
template <>
|
||||
struct is_db_text_type<datetime> : std::true_type {};
|
||||
|
||||
template <>
|
||||
struct is_db_text_type<timestamp> : std::true_type {};
|
||||
|
||||
template <int Precision, int Scale>
|
||||
struct is_db_text_type<decimal<Precision, Scale>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_db_text_type_v =
|
||||
is_db_text_type<std::remove_cv_t<T>>::value;
|
||||
|
||||
template <class T>
|
||||
struct identity {};
|
||||
|
||||
// type_to_name mappings are always available (pure C++, no native DB headers).
|
||||
// type_to_id (MySQL enum constants) requires <mysql.h> and is guarded below.
|
||||
|
||||
#ifdef ORMPP_ENABLE_MYSQL
|
||||
#include <mysql.h>
|
||||
#endif
|
||||
|
||||
namespace ormpp_mysql {
|
||||
inline constexpr auto type_to_name(identity<bool>) noexcept {
|
||||
return "BOOLEAN"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<char>) noexcept {
|
||||
return "TINYINT"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<short>) noexcept {
|
||||
return "SMALLINT"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<int>) noexcept {
|
||||
return "INTEGER"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<float>) noexcept {
|
||||
return "FLOAT"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<double>) noexcept {
|
||||
return "DOUBLE"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<int8_t>) noexcept {
|
||||
return "TINYINT"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<int64_t>) noexcept {
|
||||
return "BIGINT"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<uint8_t>) noexcept {
|
||||
return "SMALLINT"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<uint16_t>) noexcept {
|
||||
return "INTEGER"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<uint32_t>) noexcept {
|
||||
return "BIGINT"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<uint64_t>) noexcept {
|
||||
return "BIGINT UNSIGNED"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<blob>) noexcept { return "BLOB"sv; }
|
||||
inline constexpr auto type_to_name(identity<date>) noexcept { return "DATE"sv; }
|
||||
inline constexpr auto type_to_name(identity<time>) noexcept { return "TIME"sv; }
|
||||
inline constexpr auto type_to_name(identity<datetime>) noexcept {
|
||||
return "DATETIME"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<timestamp>) noexcept {
|
||||
return "TIMESTAMP"sv;
|
||||
}
|
||||
template <int Precision, int Scale>
|
||||
inline auto type_to_name(identity<decimal<Precision, Scale>>) {
|
||||
return "DECIMAL(" + std::to_string(Precision) + "," + std::to_string(Scale) +
|
||||
")";
|
||||
}
|
||||
inline auto type_to_name(identity<std::string>) noexcept { return "TEXT"sv; }
|
||||
inline auto type_to_name(identity<std::string_view>) noexcept {
|
||||
return "TEXT"sv;
|
||||
}
|
||||
template <size_t N>
|
||||
inline auto type_to_name(identity<std::array<char, N>>) noexcept {
|
||||
std::string s = "varchar(" + std::to_string(N) + ")";
|
||||
return s;
|
||||
}
|
||||
|
||||
// type_to_id uses MYSQL_TYPE_* constants — only available with <mysql.h>
|
||||
#ifdef ORMPP_ENABLE_MYSQL
|
||||
inline int type_to_id(identity<char>) noexcept { return MYSQL_TYPE_TINY; }
|
||||
inline int type_to_id(identity<short>) noexcept { return MYSQL_TYPE_SHORT; }
|
||||
inline int type_to_id(identity<int>) noexcept { return MYSQL_TYPE_LONG; }
|
||||
inline int type_to_id(identity<float>) noexcept { return MYSQL_TYPE_FLOAT; }
|
||||
inline int type_to_id(identity<double>) noexcept { return MYSQL_TYPE_DOUBLE; }
|
||||
inline int type_to_id(identity<int8_t>) noexcept { return MYSQL_TYPE_TINY; }
|
||||
inline int type_to_id(identity<int64_t>) noexcept {
|
||||
return MYSQL_TYPE_LONGLONG;
|
||||
}
|
||||
inline int type_to_id(identity<uint8_t>) noexcept { return MYSQL_TYPE_TINY; }
|
||||
inline int type_to_id(identity<uint16_t>) noexcept { return MYSQL_TYPE_SHORT; }
|
||||
inline int type_to_id(identity<uint32_t>) noexcept { return MYSQL_TYPE_LONG; }
|
||||
inline int type_to_id(identity<uint64_t>) noexcept {
|
||||
return MYSQL_TYPE_LONGLONG;
|
||||
}
|
||||
inline int type_to_id(identity<std::string>) noexcept {
|
||||
return MYSQL_TYPE_VAR_STRING;
|
||||
}
|
||||
inline int type_to_id(identity<std::string_view>) noexcept {
|
||||
return MYSQL_TYPE_VAR_STRING;
|
||||
}
|
||||
#endif // ORMPP_ENABLE_MYSQL
|
||||
} // namespace ormpp_mysql
|
||||
|
||||
namespace ormpp_sqlite {
|
||||
inline constexpr auto type_to_name(identity<float>) noexcept {
|
||||
return "FLOAT"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<double>) noexcept {
|
||||
return "DOUBLE"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<bool>) noexcept {
|
||||
return "INTEGER"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<char>) noexcept {
|
||||
return "INTEGER"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<short>) noexcept {
|
||||
return "INTEGER"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<int>) noexcept {
|
||||
return "INTEGER"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<int8_t>) noexcept {
|
||||
return "INTEGER"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<int64_t>) noexcept {
|
||||
return "INTEGER"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<uint8_t>) noexcept {
|
||||
return "INTEGER"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<uint16_t>) noexcept {
|
||||
return "INTEGER"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<uint32_t>) noexcept {
|
||||
return "INTEGER"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<uint64_t>) noexcept {
|
||||
return "INTEGER"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<blob>) noexcept { return "BLOB"sv; }
|
||||
inline constexpr auto type_to_name(identity<date>) noexcept { return "TEXT"sv; }
|
||||
inline constexpr auto type_to_name(identity<time>) noexcept { return "TEXT"sv; }
|
||||
inline constexpr auto type_to_name(identity<datetime>) noexcept {
|
||||
return "TEXT"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<timestamp>) noexcept {
|
||||
return "TEXT"sv;
|
||||
}
|
||||
template <int Precision, int Scale>
|
||||
inline auto type_to_name(identity<decimal<Precision, Scale>>) {
|
||||
return "DECIMAL(" + std::to_string(Precision) + "," + std::to_string(Scale) +
|
||||
")";
|
||||
}
|
||||
inline auto type_to_name(identity<std::string>) noexcept { return "TEXT"sv; }
|
||||
inline auto type_to_name(identity<std::string_view>) noexcept {
|
||||
return "TEXT"sv;
|
||||
}
|
||||
template <size_t N>
|
||||
inline auto type_to_name(identity<std::array<char, N>>) noexcept {
|
||||
std::string s = "varchar(" + std::to_string(N) + ")";
|
||||
return s;
|
||||
}
|
||||
} // namespace ormpp_sqlite
|
||||
|
||||
namespace ormpp_postgresql {
|
||||
inline constexpr auto type_to_name(identity<bool>) noexcept {
|
||||
return "integer"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<char>) noexcept { return "char"sv; }
|
||||
inline constexpr auto type_to_name(identity<short>) noexcept {
|
||||
return "smallint"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<int>) noexcept {
|
||||
return "integer"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<float>) noexcept {
|
||||
return "real"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<double>) noexcept {
|
||||
return "double precision"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<int8_t>) noexcept {
|
||||
return "char"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<int64_t>) noexcept {
|
||||
return "bigint"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<uint8_t>) noexcept {
|
||||
return "smallint"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<uint16_t>) noexcept {
|
||||
return "integer"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<uint32_t>) noexcept {
|
||||
return "bigint"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<uint64_t>) noexcept {
|
||||
return "bigint"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<blob>) noexcept {
|
||||
return "bytea"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<date>) noexcept { return "date"sv; }
|
||||
inline constexpr auto type_to_name(identity<time>) noexcept { return "time"sv; }
|
||||
inline constexpr auto type_to_name(identity<datetime>) noexcept {
|
||||
return "timestamp"sv;
|
||||
}
|
||||
inline constexpr auto type_to_name(identity<timestamp>) noexcept {
|
||||
return "timestamp"sv;
|
||||
}
|
||||
template <int Precision, int Scale>
|
||||
inline auto type_to_name(identity<decimal<Precision, Scale>>) {
|
||||
return "numeric(" + std::to_string(Precision) + "," + std::to_string(Scale) +
|
||||
")";
|
||||
}
|
||||
inline auto type_to_name(identity<std::string>) noexcept { return "text"sv; }
|
||||
inline auto type_to_name(identity<std::string_view>) noexcept {
|
||||
return "text"sv;
|
||||
}
|
||||
template <size_t N>
|
||||
inline auto type_to_name(identity<std::array<char, N>>) noexcept {
|
||||
std::string s = "varchar(" + std::to_string(N) + ")";
|
||||
return s;
|
||||
}
|
||||
} // namespace ormpp_postgresql
|
||||
|
||||
} // namespace ormpp
|
||||
|
||||
#endif // EXAMPLE1_TYPE_MAPPING_HPP
|
||||
Vendored
+719
@@ -0,0 +1,719 @@
|
||||
//
|
||||
// Created by qiyu on 10/29/17.
|
||||
//
|
||||
#ifndef ORM_UTILITY_HPP
|
||||
#define ORM_UTILITY_HPP
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
|
||||
#include "entity.hpp"
|
||||
#include "iguana/util.hpp"
|
||||
#include "type_mapping.hpp"
|
||||
|
||||
namespace ormpp {
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------//
|
||||
//-------------------------------------------------------------------------------------------------------------//
|
||||
template <auto... members>
|
||||
constexpr std::array<size_t, sizeof...(members)> indexs_of() {
|
||||
return std::array<size_t, sizeof...(members)>{
|
||||
ylt::reflection::index_of<members>()...};
|
||||
}
|
||||
|
||||
template <typename... Args, typename F, std::size_t... Idx>
|
||||
constexpr void for_each(std::tuple<Args...> &t, F &&f,
|
||||
std::index_sequence<Idx...>) {
|
||||
(std::forward<F>(f)(std::get<Idx>(t), std::integral_constant<size_t, Idx>{}),
|
||||
...);
|
||||
}
|
||||
|
||||
template <typename... Args, typename F, std::size_t... Idx>
|
||||
constexpr void for_each(const std::tuple<Args...> &t, F &&f,
|
||||
std::index_sequence<Idx...>) {
|
||||
(std::forward<F>(f)(std::get<Idx>(t), std::integral_constant<size_t, Idx>{}),
|
||||
...);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------//
|
||||
//-------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
inline auto &get_auto_key_map() {
|
||||
static std::unordered_map<std::string_view, std::string_view> map;
|
||||
return map;
|
||||
}
|
||||
|
||||
inline int add_auto_key_field(std::string_view key, std::string_view value) {
|
||||
get_auto_key_map().emplace(key, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::string_view get_short_struct_name() {
|
||||
auto struct_name = ylt::reflection::get_struct_name<T>();
|
||||
size_t pos = struct_name.rfind(":");
|
||||
if (pos == std::string_view::npos) {
|
||||
return struct_name;
|
||||
}
|
||||
// remove namespace
|
||||
return struct_name.substr(struct_name.rfind(":") + 1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline auto get_auto_key() {
|
||||
auto it = get_auto_key_map().find(get_short_struct_name<T>());
|
||||
return it == get_auto_key_map().end() ? "" : it->second;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline auto is_auto_key(std::string_view field_name) {
|
||||
auto it = get_auto_key_map().find(get_short_struct_name<T>());
|
||||
return it == get_auto_key_map().end() ? false : it->second == field_name;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define ORMPP_UNIQUE_VARIABLE(str) YLT_CONCAT(str, __COUNTER__)
|
||||
#else
|
||||
#define ORMPP_UNIQUE_VARIABLE(str) YLT_CONCAT(str, __LINE__)
|
||||
#endif
|
||||
|
||||
#define REGISTER_AUTO_KEY(STRUCT_NAME, KEY) \
|
||||
inline auto ORMPP_UNIQUE_VARIABLE(STRUCT_NAME) = ormpp::add_auto_key_field( \
|
||||
ylt::reflection::get_struct_name<STRUCT_NAME>(), #KEY);
|
||||
|
||||
inline auto &get_conflict_map() {
|
||||
static std::unordered_map<std::string_view, std::string_view> map;
|
||||
return map;
|
||||
}
|
||||
|
||||
inline int add_conflict_key_field(std::string_view key,
|
||||
std::string_view value) {
|
||||
get_conflict_map().emplace(key, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline auto get_conflict_key() {
|
||||
std::string_view struct_name = ylt::reflection::get_struct_name<T>();
|
||||
auto it = get_conflict_map().find(struct_name);
|
||||
if (it == get_conflict_map().end()) {
|
||||
auto auto_key = get_auto_key_map().find(struct_name);
|
||||
return auto_key == get_auto_key_map().end() ? "" : auto_key->second;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
#define MAKE_NAMES(...) #__VA_ARGS__,
|
||||
|
||||
#define REGISTER_CONFLICT_KEY(STRUCT_NAME, ...) \
|
||||
inline auto ORMPP_UNIQUE_VARIABLE(STRUCT_NAME) = \
|
||||
ormpp::add_conflict_key_field( \
|
||||
ylt::reflection::get_struct_name<STRUCT_NAME>(), \
|
||||
{MAKE_NAMES(__VA_ARGS__)});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Thread-safety helpers
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/// Eagerly initialize reflection metadata for type T.
|
||||
/// Call this once (single-threaded) before spawning worker threads
|
||||
/// to avoid lazy-init race conditions in multi-threaded queries.
|
||||
template <typename T>
|
||||
inline void init_reflection() {
|
||||
// Touch all reflection data to force eager initialization
|
||||
[[maybe_unused]] auto _ = ylt::reflection::get_struct_name<T>();
|
||||
[[maybe_unused]] auto _names = ylt::reflection::get_member_names<T>();
|
||||
[[maybe_unused]] auto _count = ylt::reflection::members_count_v<T>;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct is_optional_v : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_optional_v<std::optional<T>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr std::enable_if_t<iguana::ylt_refletable_v<T>, size_t> get_value() {
|
||||
return ylt::reflection::members_count_v<T>;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr std::enable_if_t<iguana::non_ylt_refletable_v<T>, size_t>
|
||||
get_value() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
struct value_of;
|
||||
|
||||
template <typename T>
|
||||
struct value_of<T> {
|
||||
static const auto value = (get_value<T>());
|
||||
};
|
||||
|
||||
template <typename T, typename... Rest>
|
||||
struct value_of<T, Rest...> {
|
||||
static const auto value = (value_of<T>::value + value_of<Rest...>::value);
|
||||
};
|
||||
|
||||
template <typename List>
|
||||
struct result_size;
|
||||
|
||||
template <template <class...> class List, class... T>
|
||||
struct result_size<List<T...>> {
|
||||
constexpr static const size_t value =
|
||||
value_of<T...>::value; // (iguana::get_value<T>() + ...);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline void append_impl(std::string &sql, const T &str) {
|
||||
if constexpr (std::is_same_v<std::string, T> ||
|
||||
std::is_same_v<std::string_view, T>) {
|
||||
if (str.empty())
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if constexpr (sizeof(str) == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sql += str;
|
||||
sql += " ";
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void append(std::string &sql, Args &&...args) {
|
||||
(append_impl(sql, std::forward<Args>(args)), ...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline auto sort_tuple(const std::tuple<Args...> &tp) {
|
||||
if constexpr (sizeof...(Args) == 2) {
|
||||
auto [a, b] = tp;
|
||||
if constexpr (!std::is_same_v<decltype(a), ormpp_key> &&
|
||||
!std::is_same_v<decltype(a), ormpp_auto_key>)
|
||||
return std::make_tuple(b, a);
|
||||
else
|
||||
return tp;
|
||||
}
|
||||
else {
|
||||
return tp;
|
||||
}
|
||||
}
|
||||
|
||||
enum class OptType { insert, update, replace };
|
||||
enum class DBType { mysql, sqlite, postgresql, unknown };
|
||||
|
||||
template <typename T>
|
||||
inline constexpr auto get_type_names(DBType type) {
|
||||
std::array<std::string, ylt::reflection::members_count_v<T>> arr = {};
|
||||
ylt::reflection::for_each(T{}, [&arr, type](auto &field, auto /*name*/,
|
||||
auto index) {
|
||||
using U = ylt::reflection::remove_cvref_t<decltype(field)>;
|
||||
std::string s;
|
||||
if (type == DBType::unknown) {
|
||||
}
|
||||
else if (type == DBType::mysql) {
|
||||
if constexpr (std::is_enum_v<U>) {
|
||||
s = "INTEGER"sv;
|
||||
}
|
||||
else if constexpr (is_optional_v<U>::value) {
|
||||
if constexpr (std::is_enum_v<typename U::value_type>) {
|
||||
s = "INTEGER"sv;
|
||||
}
|
||||
else {
|
||||
s = ormpp_mysql::type_to_name(identity<typename U::value_type>{});
|
||||
}
|
||||
}
|
||||
#ifdef ORMPP_WITH_CSTRING
|
||||
else if constexpr (std::is_same_v<CString, U>) {
|
||||
s = "TEXT"sv;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
s = ormpp_mysql::type_to_name(identity<U>{});
|
||||
}
|
||||
}
|
||||
else if (type == DBType::sqlite) {
|
||||
if constexpr (std::is_enum_v<U>) {
|
||||
s = "INTEGER"sv;
|
||||
}
|
||||
else if constexpr (is_optional_v<U>::value) {
|
||||
if constexpr (std::is_enum_v<typename U::value_type>) {
|
||||
s = "INTEGER"sv;
|
||||
}
|
||||
else {
|
||||
s = ormpp_sqlite::type_to_name(identity<typename U::value_type>{});
|
||||
}
|
||||
}
|
||||
#ifdef ORMPP_WITH_CSTRING
|
||||
else if constexpr (std::is_same_v<CString, U>) {
|
||||
s = "TEXT"sv;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
s = ormpp_sqlite::type_to_name(identity<U>{});
|
||||
}
|
||||
}
|
||||
else if (type == DBType::postgresql) {
|
||||
if constexpr (std::is_enum_v<U>) {
|
||||
s = "integer"sv;
|
||||
}
|
||||
else if constexpr (is_optional_v<U>::value) {
|
||||
if constexpr (std::is_enum_v<typename U::value_type>) {
|
||||
s = "integer"sv;
|
||||
}
|
||||
else {
|
||||
s = ormpp_postgresql::type_to_name(
|
||||
identity<typename U::value_type>{});
|
||||
}
|
||||
}
|
||||
#ifdef ORMPP_WITH_CSTRING
|
||||
else if constexpr (std::is_same_v<CString, U>) {
|
||||
s = "TEXT"sv;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
s = ormpp_postgresql::type_to_name(identity<U>{});
|
||||
}
|
||||
}
|
||||
|
||||
arr[index] = s;
|
||||
});
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
template <typename... Args, typename Func, std::size_t... Idx>
|
||||
inline void for_each0(const std::tuple<Args...> &t, Func &&f,
|
||||
std::index_sequence<Idx...>) {
|
||||
(f(std::get<Idx>(t)), ...);
|
||||
}
|
||||
|
||||
// Returns the quoted table name appropriate for the given database type.
|
||||
// PostgreSQL uses double-quotes, MySQL uses backticks, SQLite uses backticks.
|
||||
template <typename T, typename = std::enable_if_t<iguana::ylt_refletable_v<T>>>
|
||||
inline std::string get_struct_name(DBType db_type) {
|
||||
auto name = std::string(ylt::reflection::get_struct_name<T>());
|
||||
if (db_type == DBType::postgresql) {
|
||||
return "\"" + name + "\"";
|
||||
}
|
||||
return "`" + name + "`";
|
||||
}
|
||||
|
||||
// Returns field list string with quoting appropriate for the given database.
|
||||
// MySQL wraps field names in backticks; others use plain names.
|
||||
// Results are cached per DB type.
|
||||
template <typename T, typename = std::enable_if_t<iguana::ylt_refletable_v<T>>>
|
||||
inline std::string get_fields(DBType db_type) {
|
||||
// Two static caches: one for MySQL (backtick-quoted), one for others (plain)
|
||||
static std::string fields_mysql;
|
||||
static std::string fields_other;
|
||||
static std::once_flag init_mysql;
|
||||
static std::once_flag init_other;
|
||||
|
||||
std::string &fields =
|
||||
(db_type == DBType::mysql) ? fields_mysql : fields_other;
|
||||
std::once_flag &flag = (db_type == DBType::mysql) ? init_mysql : init_other;
|
||||
|
||||
std::call_once(flag, [&fields, db_type]() {
|
||||
for (const auto &it : ylt::reflection::get_member_names<T>()) {
|
||||
if (db_type == DBType::mysql) {
|
||||
fields += "`" + std::string(it) + "`";
|
||||
}
|
||||
else {
|
||||
fields += std::string(it);
|
||||
}
|
||||
fields += ",";
|
||||
}
|
||||
if (!fields.empty()) {
|
||||
fields.back() = ' ';
|
||||
}
|
||||
});
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
// Check if SQL string starts with SELECT keyword (case-insensitive)
|
||||
inline bool contains_select(const std::string &sql) {
|
||||
if (sql.empty()) {
|
||||
return false;
|
||||
}
|
||||
// Skip leading whitespace
|
||||
auto it = sql.begin();
|
||||
while (it != sql.end() && std::isspace(static_cast<unsigned char>(*it))) {
|
||||
++it;
|
||||
}
|
||||
if (it == sql.end()) {
|
||||
return false;
|
||||
}
|
||||
// Check if starts with "select" (case-insensitive)
|
||||
const char *select_keyword = "select";
|
||||
for (int i = 0; i < 6 && it != sql.end(); ++i, ++it) {
|
||||
if (std::tolower(static_cast<unsigned char>(*it)) != select_keyword[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Ensure "select" is a complete word (followed by whitespace or end)
|
||||
return it == sql.end() || std::isspace(static_cast<unsigned char>(*it));
|
||||
}
|
||||
|
||||
inline std::vector<std::string_view> split(std::string_view str) {
|
||||
if (str.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string_view> v;
|
||||
size_t start = 0;
|
||||
for (size_t i = 0; i < str.size(); i++) {
|
||||
char c = str[i];
|
||||
if (c == ' ') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::isalpha(c) || std::isdigit(c) || c == '_') {
|
||||
start++;
|
||||
}
|
||||
else {
|
||||
auto sv = str.substr(i - start, start);
|
||||
start = 0;
|
||||
v.push_back(sv);
|
||||
}
|
||||
}
|
||||
if (start != 0) {
|
||||
auto sv = str.substr(str.size() - start, start);
|
||||
v.push_back(sv);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// Returns conflict key names with quoting appropriate for the given database.
|
||||
// MySQL wraps key names in backticks; others use plain names.
|
||||
// Results are cached per DB type.
|
||||
template <typename T, typename = std::enable_if_t<iguana::ylt_refletable_v<T>>>
|
||||
inline std::vector<std::string> get_conflict_keys(DBType db_type) {
|
||||
static std::vector<std::string> res_mysql;
|
||||
static std::vector<std::string> res_other;
|
||||
static std::mutex mutex;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
std::vector<std::string> &res =
|
||||
(db_type == DBType::mysql) ? res_mysql : res_other;
|
||||
if (!res.empty()) {
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string_view keys = get_conflict_key<T>();
|
||||
auto v = split(keys);
|
||||
if (v.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
for (auto sv : v) {
|
||||
std::string str;
|
||||
if (db_type == DBType::mysql) {
|
||||
str.append("`").append(sv).append("`");
|
||||
}
|
||||
else {
|
||||
str.append(sv);
|
||||
}
|
||||
res.push_back(str);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline auto is_conflict_key(std::string_view field_name, DBType db_type) {
|
||||
for (const auto &it : get_conflict_keys<T>(db_type)) {
|
||||
if (it == field_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline std::string generate_insert_sql(DBType db_type, bool insert,
|
||||
Args &&...args) {
|
||||
if (db_type == DBType::postgresql && !insert) {
|
||||
constexpr auto Count = ylt::reflection::members_count_v<T>;
|
||||
std::string sql = "insert into ";
|
||||
auto name = get_short_struct_name<T>();
|
||||
append(sql, name);
|
||||
int index = 0;
|
||||
std::string set;
|
||||
std::string fields = "(";
|
||||
std::string values = "values(";
|
||||
for (auto i = 0; i < Count; ++i) {
|
||||
std::string field_name(ylt::reflection::name_of<T>(i));
|
||||
std::string value = "$" + std::to_string(++index);
|
||||
append(set, field_name, "=", value);
|
||||
fields += field_name;
|
||||
values += value;
|
||||
if (i < Count - 1) {
|
||||
fields += ",";
|
||||
values += ",";
|
||||
set += ",";
|
||||
}
|
||||
else {
|
||||
fields += ")";
|
||||
values += ")";
|
||||
set += ";";
|
||||
}
|
||||
}
|
||||
std::string conflict = "on conflict(";
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
append(conflict, args...);
|
||||
}
|
||||
else {
|
||||
conflict += get_conflict_key<T>();
|
||||
}
|
||||
conflict += ")";
|
||||
append(sql, fields, values, conflict, "do update set", set);
|
||||
return sql;
|
||||
}
|
||||
|
||||
std::string sql = insert ? "insert into " : "replace into ";
|
||||
constexpr auto Count = ylt::reflection::members_count_v<T>;
|
||||
auto name = get_short_struct_name<T>();
|
||||
append(sql, name);
|
||||
|
||||
int index = 0;
|
||||
std::string fields = "(";
|
||||
std::string values = "values(";
|
||||
for (size_t i = 0; i < Count; ++i) {
|
||||
std::string field_name(ylt::reflection::name_of<T>(i));
|
||||
if (insert && is_auto_key<T>(field_name)) {
|
||||
continue;
|
||||
}
|
||||
if (db_type == DBType::postgresql) {
|
||||
values += "$" + std::to_string(++index);
|
||||
}
|
||||
else {
|
||||
values += "?";
|
||||
}
|
||||
if (db_type == DBType::mysql) {
|
||||
fields += "`" + field_name + "`";
|
||||
}
|
||||
else {
|
||||
fields += field_name;
|
||||
}
|
||||
if (i < Count - 1) {
|
||||
fields += ",";
|
||||
values += ",";
|
||||
}
|
||||
else {
|
||||
fields += ")";
|
||||
values += ")";
|
||||
}
|
||||
}
|
||||
if (fields.back() != ')') {
|
||||
fields.back() = ')';
|
||||
}
|
||||
if (values.back() != ')') {
|
||||
values.back() = ')';
|
||||
}
|
||||
append(sql, fields, values);
|
||||
return sql;
|
||||
}
|
||||
|
||||
template <typename T, auto... members, typename... Args>
|
||||
inline std::string generate_update_sql(DBType db_type, Args &&...args) {
|
||||
std::string sql, fields;
|
||||
append(sql, "update", get_short_struct_name<T>(), "set");
|
||||
|
||||
size_t index = 0;
|
||||
|
||||
if constexpr (sizeof...(members) > 0) {
|
||||
if (db_type == DBType::postgresql) {
|
||||
(fields
|
||||
.append(ylt::reflection::name_of<T>(
|
||||
ylt::reflection::index_of<members>()))
|
||||
.append("=$" + std::to_string(++index) + ","),
|
||||
...);
|
||||
}
|
||||
else {
|
||||
(fields
|
||||
.append(ylt::reflection::name_of<T>(
|
||||
ylt::reflection::index_of<members>()))
|
||||
.append("=?,"),
|
||||
...);
|
||||
}
|
||||
}
|
||||
else {
|
||||
constexpr auto Count = ylt::reflection::members_count_v<T>;
|
||||
for (size_t i = 0; i < Count; ++i) {
|
||||
std::string field_name(ylt::reflection::name_of<T>(i));
|
||||
if (db_type == DBType::mysql) {
|
||||
fields.append("`").append(field_name).append("`");
|
||||
}
|
||||
else {
|
||||
fields.append(field_name);
|
||||
}
|
||||
if (db_type == DBType::postgresql) {
|
||||
fields.append("=$").append(std::to_string(++index)).append(",");
|
||||
}
|
||||
else {
|
||||
fields.append("=?,");
|
||||
}
|
||||
}
|
||||
}
|
||||
fields.pop_back();
|
||||
|
||||
std::string conflict = "where 1=1";
|
||||
bool has_condition = false;
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
append(conflict, " and", args...);
|
||||
has_condition = true;
|
||||
}
|
||||
else {
|
||||
const auto &pks = get_conflict_keys<T>(db_type);
|
||||
for (const auto &it : pks) {
|
||||
has_condition = true;
|
||||
if (db_type == DBType::postgresql) {
|
||||
append(conflict, " and", it + "=$" + std::to_string(++index));
|
||||
}
|
||||
else {
|
||||
append(conflict, " and", it + "=?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_condition) {
|
||||
return {};
|
||||
}
|
||||
|
||||
append(sql, fields, conflict);
|
||||
while (!sql.empty() && sql.back() == ' ') {
|
||||
sql.pop_back();
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
inline bool is_empty(const std::string &t) { return t.empty(); }
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline std::string generate_delete_sql(DBType db_type,
|
||||
Args &&...where_conditon) {
|
||||
std::string sql = "delete from ";
|
||||
auto name = get_short_struct_name<T>();
|
||||
append(sql, name);
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
if (!is_empty(std::forward<Args>(where_conditon)...))
|
||||
append(sql, "where", std::forward<Args>(where_conditon)...);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
inline void get_sql_conditions(std::string &) {}
|
||||
|
||||
template <typename... Args>
|
||||
inline void get_sql_conditions(std::string &sql, const std::string &arg,
|
||||
Args &&...args) {
|
||||
std::string temp = arg;
|
||||
std::transform(arg.begin(), arg.end(), temp.begin(), ::tolower);
|
||||
if (contains_select(arg)) {
|
||||
sql = arg;
|
||||
}
|
||||
else {
|
||||
if (auto pos0 = temp.find("order by"); pos0 != std::string::npos) {
|
||||
if (pos0 == 0) {
|
||||
sql = sql.substr(0, sql.find("where"));
|
||||
}
|
||||
}
|
||||
|
||||
if (auto pos0 = temp.find("limit"); pos0 != std::string::npos) {
|
||||
if (pos0 == 0) {
|
||||
sql = sql.substr(0, sql.find("where"));
|
||||
}
|
||||
}
|
||||
append(sql, arg, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline std::string generate_query_sql(DBType db_type, Args &&...args) {
|
||||
bool where = false;
|
||||
std::string sql = "select ";
|
||||
auto fields = get_fields<T>(db_type);
|
||||
auto name = get_short_struct_name<T>();
|
||||
append(sql, fields, "from", name);
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
using expander = int[];
|
||||
[[maybe_unused]] expander i{
|
||||
0, (where = where ? where : !is_empty(args), 0)...};
|
||||
}
|
||||
if (where) {
|
||||
append(sql, "where 1=1 and ");
|
||||
}
|
||||
get_sql_conditions(sql, std::forward<Args>(args)...);
|
||||
return sql;
|
||||
}
|
||||
|
||||
inline std::string escape_sql_string(std::string_view input) {
|
||||
std::string result;
|
||||
result.reserve(input.size());
|
||||
for (char c : input) {
|
||||
if (c == '\'') {
|
||||
result.append("''");
|
||||
}
|
||||
else {
|
||||
result.push_back(c);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr auto to_str(T &&t) {
|
||||
if constexpr (std::is_arithmetic_v<std::decay_t<T>>) {
|
||||
return std::to_string(std::forward<T>(t));
|
||||
}
|
||||
else {
|
||||
return std::string("'") + escape_sql_string(t) + std::string("'");
|
||||
}
|
||||
}
|
||||
|
||||
inline void get_str(std::string &sql, const std::string &s) {
|
||||
auto pos = sql.find_first_of('?');
|
||||
sql.replace(pos, 1, " ");
|
||||
sql.insert(pos, s);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline std::string get_sql(const std::string &o, Args &&...args) {
|
||||
auto sql = o;
|
||||
std::string s = "";
|
||||
(get_str(sql, to_str(args)), ...);
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct field_attribute;
|
||||
|
||||
template <typename T, typename U>
|
||||
struct field_attribute<U T::*> {
|
||||
using type = T;
|
||||
using return_type = U;
|
||||
};
|
||||
|
||||
template <typename U>
|
||||
constexpr std::string_view get_field_name(std::string_view full_name) {
|
||||
using T = typename field_attribute<U>::type;
|
||||
return ylt::reflection::get_member_names<T>()[ylt::reflection::index_of<T>(
|
||||
full_name.substr(full_name.rfind(":") + 1))];
|
||||
}
|
||||
|
||||
#define FID(field) \
|
||||
std::pair<std::string_view, decltype(&field)>( \
|
||||
ormpp::get_field_name<decltype(&field)>(std::string_view(#field)), \
|
||||
&field)
|
||||
#define SID(field) \
|
||||
get_field_name<decltype(&field)>(std::string_view(#field)).data()
|
||||
} // namespace ormpp
|
||||
|
||||
#endif // ORM_UTILITY_HPP
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
// Note: Update the version when release a new version.
|
||||
|
||||
// ORMPP_VERSION % 100 is the sub-minor version
|
||||
// ORMPP_VERSION / 100 % 1000 is the minor version
|
||||
// ORMPP_VERSION / 100000 is the major version
|
||||
#define ORMPP_VERSION 20 // 0.2.0
|
||||
+267283
File diff suppressed because it is too large
Load Diff
+13775
File diff suppressed because it is too large
Load Diff
+719
@@ -0,0 +1,719 @@
|
||||
/*
|
||||
** 2006 June 7
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This header file defines the SQLite interface for use by
|
||||
** shared libraries that want to be imported as extensions into
|
||||
** an SQLite instance. Shared libraries that intend to be loaded
|
||||
** as extensions by SQLite should #include this file instead of
|
||||
** sqlite3.h.
|
||||
*/
|
||||
#ifndef SQLITE3EXT_H
|
||||
#define SQLITE3EXT_H
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** The following structure holds pointers to all of the SQLite API
|
||||
** routines.
|
||||
**
|
||||
** WARNING: In order to maintain backwards compatibility, add new
|
||||
** interfaces to the end of this structure only. If you insert new
|
||||
** interfaces in the middle of this structure, then older different
|
||||
** versions of SQLite will not be able to load each other's shared
|
||||
** libraries!
|
||||
*/
|
||||
struct sqlite3_api_routines {
|
||||
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
||||
int (*aggregate_count)(sqlite3_context*);
|
||||
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
||||
int (*bind_double)(sqlite3_stmt*,int,double);
|
||||
int (*bind_int)(sqlite3_stmt*,int,int);
|
||||
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
||||
int (*bind_null)(sqlite3_stmt*,int);
|
||||
int (*bind_parameter_count)(sqlite3_stmt*);
|
||||
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
||||
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
||||
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
||||
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
||||
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
||||
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
||||
int (*busy_timeout)(sqlite3*,int ms);
|
||||
int (*changes)(sqlite3*);
|
||||
int (*close)(sqlite3*);
|
||||
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const char*));
|
||||
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const void*));
|
||||
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_count)(sqlite3_stmt*pStmt);
|
||||
const char * (*column_database_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_decltype)(sqlite3_stmt*,int i);
|
||||
const void * (*column_decltype16)(sqlite3_stmt*,int);
|
||||
double (*column_double)(sqlite3_stmt*,int iCol);
|
||||
int (*column_int)(sqlite3_stmt*,int iCol);
|
||||
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
|
||||
const char * (*column_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_origin_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_origin_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_table_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_table_name16)(sqlite3_stmt*,int);
|
||||
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
|
||||
const void * (*column_text16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_type)(sqlite3_stmt*,int iCol);
|
||||
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
|
||||
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
|
||||
int (*complete)(const char*sql);
|
||||
int (*complete16)(const void*sql);
|
||||
int (*create_collation)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_collation16)(sqlite3*,const void*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_function16)(sqlite3*,const void*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
|
||||
int (*data_count)(sqlite3_stmt*pStmt);
|
||||
sqlite3 * (*db_handle)(sqlite3_stmt*);
|
||||
int (*declare_vtab)(sqlite3*,const char*);
|
||||
int (*enable_shared_cache)(int);
|
||||
int (*errcode)(sqlite3*db);
|
||||
const char * (*errmsg)(sqlite3*);
|
||||
const void * (*errmsg16)(sqlite3*);
|
||||
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
|
||||
int (*expired)(sqlite3_stmt*);
|
||||
int (*finalize)(sqlite3_stmt*pStmt);
|
||||
void (*free)(void*);
|
||||
void (*free_table)(char**result);
|
||||
int (*get_autocommit)(sqlite3*);
|
||||
void * (*get_auxdata)(sqlite3_context*,int);
|
||||
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
|
||||
int (*global_recover)(void);
|
||||
void (*interruptx)(sqlite3*);
|
||||
sqlite_int64 (*last_insert_rowid)(sqlite3*);
|
||||
const char * (*libversion)(void);
|
||||
int (*libversion_number)(void);
|
||||
void *(*malloc)(int);
|
||||
char * (*mprintf)(const char*,...);
|
||||
int (*open)(const char*,sqlite3**);
|
||||
int (*open16)(const void*,sqlite3**);
|
||||
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
|
||||
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
||||
void *(*realloc)(void*,int);
|
||||
int (*reset)(sqlite3_stmt*pStmt);
|
||||
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_double)(sqlite3_context*,double);
|
||||
void (*result_error)(sqlite3_context*,const char*,int);
|
||||
void (*result_error16)(sqlite3_context*,const void*,int);
|
||||
void (*result_int)(sqlite3_context*,int);
|
||||
void (*result_int64)(sqlite3_context*,sqlite_int64);
|
||||
void (*result_null)(sqlite3_context*);
|
||||
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
|
||||
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_value)(sqlite3_context*,sqlite3_value*);
|
||||
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
|
||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
||||
const char*,const char*),void*);
|
||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
||||
char * (*xsnprintf)(int,char*,const char*,...);
|
||||
int (*step)(sqlite3_stmt*);
|
||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
||||
char const**,char const**,int*,int*,int*);
|
||||
void (*thread_cleanup)(void);
|
||||
int (*total_changes)(sqlite3*);
|
||||
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
||||
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
||||
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
||||
sqlite_int64),void*);
|
||||
void * (*user_data)(sqlite3_context*);
|
||||
const void * (*value_blob)(sqlite3_value*);
|
||||
int (*value_bytes)(sqlite3_value*);
|
||||
int (*value_bytes16)(sqlite3_value*);
|
||||
double (*value_double)(sqlite3_value*);
|
||||
int (*value_int)(sqlite3_value*);
|
||||
sqlite_int64 (*value_int64)(sqlite3_value*);
|
||||
int (*value_numeric_type)(sqlite3_value*);
|
||||
const unsigned char * (*value_text)(sqlite3_value*);
|
||||
const void * (*value_text16)(sqlite3_value*);
|
||||
const void * (*value_text16be)(sqlite3_value*);
|
||||
const void * (*value_text16le)(sqlite3_value*);
|
||||
int (*value_type)(sqlite3_value*);
|
||||
char *(*vmprintf)(const char*,va_list);
|
||||
/* Added ??? */
|
||||
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
|
||||
/* Added by 3.3.13 */
|
||||
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
int (*clear_bindings)(sqlite3_stmt*);
|
||||
/* Added by 3.4.1 */
|
||||
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
|
||||
void (*xDestroy)(void *));
|
||||
/* Added by 3.5.0 */
|
||||
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
|
||||
int (*blob_bytes)(sqlite3_blob*);
|
||||
int (*blob_close)(sqlite3_blob*);
|
||||
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
|
||||
int,sqlite3_blob**);
|
||||
int (*blob_read)(sqlite3_blob*,void*,int,int);
|
||||
int (*blob_write)(sqlite3_blob*,const void*,int,int);
|
||||
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*),
|
||||
void(*)(void*));
|
||||
int (*file_control)(sqlite3*,const char*,int,void*);
|
||||
sqlite3_int64 (*memory_highwater)(int);
|
||||
sqlite3_int64 (*memory_used)(void);
|
||||
sqlite3_mutex *(*mutex_alloc)(int);
|
||||
void (*mutex_enter)(sqlite3_mutex*);
|
||||
void (*mutex_free)(sqlite3_mutex*);
|
||||
void (*mutex_leave)(sqlite3_mutex*);
|
||||
int (*mutex_try)(sqlite3_mutex*);
|
||||
int (*open_v2)(const char*,sqlite3**,int,const char*);
|
||||
int (*release_memory)(int);
|
||||
void (*result_error_nomem)(sqlite3_context*);
|
||||
void (*result_error_toobig)(sqlite3_context*);
|
||||
int (*sleep)(int);
|
||||
void (*soft_heap_limit)(int);
|
||||
sqlite3_vfs *(*vfs_find)(const char*);
|
||||
int (*vfs_register)(sqlite3_vfs*,int);
|
||||
int (*vfs_unregister)(sqlite3_vfs*);
|
||||
int (*xthreadsafe)(void);
|
||||
void (*result_zeroblob)(sqlite3_context*,int);
|
||||
void (*result_error_code)(sqlite3_context*,int);
|
||||
int (*test_control)(int, ...);
|
||||
void (*randomness)(int,void*);
|
||||
sqlite3 *(*context_db_handle)(sqlite3_context*);
|
||||
int (*extended_result_codes)(sqlite3*,int);
|
||||
int (*limit)(sqlite3*,int,int);
|
||||
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
|
||||
const char *(*sql)(sqlite3_stmt*);
|
||||
int (*status)(int,int*,int*,int);
|
||||
int (*backup_finish)(sqlite3_backup*);
|
||||
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
|
||||
int (*backup_pagecount)(sqlite3_backup*);
|
||||
int (*backup_remaining)(sqlite3_backup*);
|
||||
int (*backup_step)(sqlite3_backup*,int);
|
||||
const char *(*compileoption_get)(int);
|
||||
int (*compileoption_used)(const char*);
|
||||
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void(*xDestroy)(void*));
|
||||
int (*db_config)(sqlite3*,int,...);
|
||||
sqlite3_mutex *(*db_mutex)(sqlite3*);
|
||||
int (*db_status)(sqlite3*,int,int*,int*,int);
|
||||
int (*extended_errcode)(sqlite3*);
|
||||
void (*log)(int,const char*,...);
|
||||
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
|
||||
const char *(*sourceid)(void);
|
||||
int (*stmt_status)(sqlite3_stmt*,int,int);
|
||||
int (*strnicmp)(const char*,const char*,int);
|
||||
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
|
||||
int (*wal_autocheckpoint)(sqlite3*,int);
|
||||
int (*wal_checkpoint)(sqlite3*,const char*);
|
||||
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
|
||||
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
|
||||
int (*vtab_config)(sqlite3*,int op,...);
|
||||
int (*vtab_on_conflict)(sqlite3*);
|
||||
/* Version 3.7.16 and later */
|
||||
int (*close_v2)(sqlite3*);
|
||||
const char *(*db_filename)(sqlite3*,const char*);
|
||||
int (*db_readonly)(sqlite3*,const char*);
|
||||
int (*db_release_memory)(sqlite3*);
|
||||
const char *(*errstr)(int);
|
||||
int (*stmt_busy)(sqlite3_stmt*);
|
||||
int (*stmt_readonly)(sqlite3_stmt*);
|
||||
int (*stricmp)(const char*,const char*);
|
||||
int (*uri_boolean)(const char*,const char*,int);
|
||||
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
||||
const char *(*uri_parameter)(const char*,const char*);
|
||||
char *(*xvsnprintf)(int,char*,const char*,va_list);
|
||||
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
||||
/* Version 3.8.7 and later */
|
||||
int (*auto_extension)(void(*)(void));
|
||||
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
|
||||
void(*)(void*));
|
||||
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
|
||||
void(*)(void*),unsigned char);
|
||||
int (*cancel_auto_extension)(void(*)(void));
|
||||
int (*load_extension)(sqlite3*,const char*,const char*,char**);
|
||||
void *(*malloc64)(sqlite3_uint64);
|
||||
sqlite3_uint64 (*msize)(void*);
|
||||
void *(*realloc64)(void*,sqlite3_uint64);
|
||||
void (*reset_auto_extension)(void);
|
||||
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
|
||||
void(*)(void*));
|
||||
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
|
||||
void(*)(void*), unsigned char);
|
||||
int (*strglob)(const char*,const char*);
|
||||
/* Version 3.8.11 and later */
|
||||
sqlite3_value *(*value_dup)(const sqlite3_value*);
|
||||
void (*value_free)(sqlite3_value*);
|
||||
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
|
||||
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
|
||||
/* Version 3.9.0 and later */
|
||||
unsigned int (*value_subtype)(sqlite3_value*);
|
||||
void (*result_subtype)(sqlite3_context*,unsigned int);
|
||||
/* Version 3.10.0 and later */
|
||||
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
|
||||
int (*strlike)(const char*,const char*,unsigned int);
|
||||
int (*db_cacheflush)(sqlite3*);
|
||||
/* Version 3.12.0 and later */
|
||||
int (*system_errno)(sqlite3*);
|
||||
/* Version 3.14.0 and later */
|
||||
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
|
||||
char *(*expanded_sql)(sqlite3_stmt*);
|
||||
/* Version 3.18.0 and later */
|
||||
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
|
||||
/* Version 3.20.0 and later */
|
||||
int (*prepare_v3)(sqlite3*,const char*,int,unsigned int,
|
||||
sqlite3_stmt**,const char**);
|
||||
int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int,
|
||||
sqlite3_stmt**,const void**);
|
||||
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
|
||||
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
|
||||
void *(*value_pointer)(sqlite3_value*,const char*);
|
||||
int (*vtab_nochange)(sqlite3_context*);
|
||||
int (*value_nochange)(sqlite3_value*);
|
||||
const char *(*vtab_collation)(sqlite3_index_info*,int);
|
||||
/* Version 3.24.0 and later */
|
||||
int (*keyword_count)(void);
|
||||
int (*keyword_name)(int,const char**,int*);
|
||||
int (*keyword_check)(const char*,int);
|
||||
sqlite3_str *(*str_new)(sqlite3*);
|
||||
char *(*str_finish)(sqlite3_str*);
|
||||
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
|
||||
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
|
||||
void (*str_append)(sqlite3_str*, const char *zIn, int N);
|
||||
void (*str_appendall)(sqlite3_str*, const char *zIn);
|
||||
void (*str_appendchar)(sqlite3_str*, int N, char C);
|
||||
void (*str_reset)(sqlite3_str*);
|
||||
int (*str_errcode)(sqlite3_str*);
|
||||
int (*str_length)(sqlite3_str*);
|
||||
char *(*str_value)(sqlite3_str*);
|
||||
/* Version 3.25.0 and later */
|
||||
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void (*xValue)(sqlite3_context*),
|
||||
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
|
||||
void(*xDestroy)(void*));
|
||||
/* Version 3.26.0 and later */
|
||||
const char *(*normalized_sql)(sqlite3_stmt*);
|
||||
/* Version 3.28.0 and later */
|
||||
int (*stmt_isexplain)(sqlite3_stmt*);
|
||||
int (*value_frombind)(sqlite3_value*);
|
||||
/* Version 3.30.0 and later */
|
||||
int (*drop_modules)(sqlite3*,const char**);
|
||||
/* Version 3.31.0 and later */
|
||||
sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64);
|
||||
const char *(*uri_key)(const char*,int);
|
||||
const char *(*filename_database)(const char*);
|
||||
const char *(*filename_journal)(const char*);
|
||||
const char *(*filename_wal)(const char*);
|
||||
/* Version 3.32.0 and later */
|
||||
const char *(*create_filename)(const char*,const char*,const char*,
|
||||
int,const char**);
|
||||
void (*free_filename)(const char*);
|
||||
sqlite3_file *(*database_file_object)(const char*);
|
||||
/* Version 3.34.0 and later */
|
||||
int (*txn_state)(sqlite3*,const char*);
|
||||
/* Version 3.36.1 and later */
|
||||
sqlite3_int64 (*changes64)(sqlite3*);
|
||||
sqlite3_int64 (*total_changes64)(sqlite3*);
|
||||
/* Version 3.37.0 and later */
|
||||
int (*autovacuum_pages)(sqlite3*,
|
||||
unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
|
||||
void*, void(*)(void*));
|
||||
/* Version 3.38.0 and later */
|
||||
int (*error_offset)(sqlite3*);
|
||||
int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
|
||||
int (*vtab_distinct)(sqlite3_index_info*);
|
||||
int (*vtab_in)(sqlite3_index_info*,int,int);
|
||||
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
|
||||
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
|
||||
/* Version 3.39.0 and later */
|
||||
int (*deserialize)(sqlite3*,const char*,unsigned char*,
|
||||
sqlite3_int64,sqlite3_int64,unsigned);
|
||||
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
|
||||
unsigned int);
|
||||
const char *(*db_name)(sqlite3*,int);
|
||||
/* Version 3.40.0 and later */
|
||||
int (*value_encoding)(sqlite3_value*);
|
||||
/* Version 3.41.0 and later */
|
||||
int (*is_interrupted)(sqlite3*);
|
||||
/* Version 3.43.0 and later */
|
||||
int (*stmt_explain)(sqlite3_stmt*,int);
|
||||
/* Version 3.44.0 and later */
|
||||
void *(*get_clientdata)(sqlite3*,const char*);
|
||||
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
||||
};
|
||||
|
||||
/*
|
||||
** This is the function signature used for all extension entry points. It
|
||||
** is also defined in the file "loadext.c".
|
||||
*/
|
||||
typedef int (*sqlite3_loadext_entry)(
|
||||
sqlite3 *db, /* Handle to the database. */
|
||||
char **pzErrMsg, /* Used to set error string on failure. */
|
||||
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
|
||||
);
|
||||
|
||||
/*
|
||||
** The following macros redefine the API routines so that they are
|
||||
** redirected through the global sqlite3_api structure.
|
||||
**
|
||||
** This header file is also used by the loadext.c source file
|
||||
** (part of the main SQLite library - not an extension) so that
|
||||
** it can get access to the sqlite3_api_routines structure
|
||||
** definition. But the main library does not want to redefine
|
||||
** the API. So the redefinition macros are only valid if the
|
||||
** SQLITE_CORE macros is undefined.
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
||||
#endif
|
||||
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
||||
#define sqlite3_bind_double sqlite3_api->bind_double
|
||||
#define sqlite3_bind_int sqlite3_api->bind_int
|
||||
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
||||
#define sqlite3_bind_null sqlite3_api->bind_null
|
||||
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
||||
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
||||
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
||||
#define sqlite3_bind_text sqlite3_api->bind_text
|
||||
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
||||
#define sqlite3_bind_value sqlite3_api->bind_value
|
||||
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
||||
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
||||
#define sqlite3_changes sqlite3_api->changes
|
||||
#define sqlite3_close sqlite3_api->close
|
||||
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
||||
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
||||
#define sqlite3_column_blob sqlite3_api->column_blob
|
||||
#define sqlite3_column_bytes sqlite3_api->column_bytes
|
||||
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
|
||||
#define sqlite3_column_count sqlite3_api->column_count
|
||||
#define sqlite3_column_database_name sqlite3_api->column_database_name
|
||||
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
|
||||
#define sqlite3_column_decltype sqlite3_api->column_decltype
|
||||
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
|
||||
#define sqlite3_column_double sqlite3_api->column_double
|
||||
#define sqlite3_column_int sqlite3_api->column_int
|
||||
#define sqlite3_column_int64 sqlite3_api->column_int64
|
||||
#define sqlite3_column_name sqlite3_api->column_name
|
||||
#define sqlite3_column_name16 sqlite3_api->column_name16
|
||||
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
|
||||
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
|
||||
#define sqlite3_column_table_name sqlite3_api->column_table_name
|
||||
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
|
||||
#define sqlite3_column_text sqlite3_api->column_text
|
||||
#define sqlite3_column_text16 sqlite3_api->column_text16
|
||||
#define sqlite3_column_type sqlite3_api->column_type
|
||||
#define sqlite3_column_value sqlite3_api->column_value
|
||||
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
||||
#define sqlite3_complete sqlite3_api->complete
|
||||
#define sqlite3_complete16 sqlite3_api->complete16
|
||||
#define sqlite3_create_collation sqlite3_api->create_collation
|
||||
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
||||
#define sqlite3_create_function sqlite3_api->create_function
|
||||
#define sqlite3_create_function16 sqlite3_api->create_function16
|
||||
#define sqlite3_create_module sqlite3_api->create_module
|
||||
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
||||
#define sqlite3_data_count sqlite3_api->data_count
|
||||
#define sqlite3_db_handle sqlite3_api->db_handle
|
||||
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
|
||||
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
|
||||
#define sqlite3_errcode sqlite3_api->errcode
|
||||
#define sqlite3_errmsg sqlite3_api->errmsg
|
||||
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
||||
#define sqlite3_exec sqlite3_api->exec
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_expired sqlite3_api->expired
|
||||
#endif
|
||||
#define sqlite3_finalize sqlite3_api->finalize
|
||||
#define sqlite3_free sqlite3_api->free
|
||||
#define sqlite3_free_table sqlite3_api->free_table
|
||||
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
||||
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
||||
#define sqlite3_get_table sqlite3_api->get_table
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_global_recover sqlite3_api->global_recover
|
||||
#endif
|
||||
#define sqlite3_interrupt sqlite3_api->interruptx
|
||||
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
||||
#define sqlite3_libversion sqlite3_api->libversion
|
||||
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
||||
#define sqlite3_malloc sqlite3_api->malloc
|
||||
#define sqlite3_mprintf sqlite3_api->mprintf
|
||||
#define sqlite3_open sqlite3_api->open
|
||||
#define sqlite3_open16 sqlite3_api->open16
|
||||
#define sqlite3_prepare sqlite3_api->prepare
|
||||
#define sqlite3_prepare16 sqlite3_api->prepare16
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_profile sqlite3_api->profile
|
||||
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
||||
#define sqlite3_realloc sqlite3_api->realloc
|
||||
#define sqlite3_reset sqlite3_api->reset
|
||||
#define sqlite3_result_blob sqlite3_api->result_blob
|
||||
#define sqlite3_result_double sqlite3_api->result_double
|
||||
#define sqlite3_result_error sqlite3_api->result_error
|
||||
#define sqlite3_result_error16 sqlite3_api->result_error16
|
||||
#define sqlite3_result_int sqlite3_api->result_int
|
||||
#define sqlite3_result_int64 sqlite3_api->result_int64
|
||||
#define sqlite3_result_null sqlite3_api->result_null
|
||||
#define sqlite3_result_text sqlite3_api->result_text
|
||||
#define sqlite3_result_text16 sqlite3_api->result_text16
|
||||
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
||||
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
||||
#define sqlite3_result_value sqlite3_api->result_value
|
||||
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
||||
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
||||
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
||||
#define sqlite3_snprintf sqlite3_api->xsnprintf
|
||||
#define sqlite3_step sqlite3_api->step
|
||||
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
||||
#define sqlite3_total_changes sqlite3_api->total_changes
|
||||
#define sqlite3_trace sqlite3_api->trace
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
||||
#endif
|
||||
#define sqlite3_update_hook sqlite3_api->update_hook
|
||||
#define sqlite3_user_data sqlite3_api->user_data
|
||||
#define sqlite3_value_blob sqlite3_api->value_blob
|
||||
#define sqlite3_value_bytes sqlite3_api->value_bytes
|
||||
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
|
||||
#define sqlite3_value_double sqlite3_api->value_double
|
||||
#define sqlite3_value_int sqlite3_api->value_int
|
||||
#define sqlite3_value_int64 sqlite3_api->value_int64
|
||||
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
|
||||
#define sqlite3_value_text sqlite3_api->value_text
|
||||
#define sqlite3_value_text16 sqlite3_api->value_text16
|
||||
#define sqlite3_value_text16be sqlite3_api->value_text16be
|
||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
||||
#define sqlite3_value_type sqlite3_api->value_type
|
||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
||||
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
|
||||
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
|
||||
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
|
||||
#define sqlite3_blob_close sqlite3_api->blob_close
|
||||
#define sqlite3_blob_open sqlite3_api->blob_open
|
||||
#define sqlite3_blob_read sqlite3_api->blob_read
|
||||
#define sqlite3_blob_write sqlite3_api->blob_write
|
||||
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
|
||||
#define sqlite3_file_control sqlite3_api->file_control
|
||||
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
|
||||
#define sqlite3_memory_used sqlite3_api->memory_used
|
||||
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
|
||||
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
|
||||
#define sqlite3_mutex_free sqlite3_api->mutex_free
|
||||
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
|
||||
#define sqlite3_mutex_try sqlite3_api->mutex_try
|
||||
#define sqlite3_open_v2 sqlite3_api->open_v2
|
||||
#define sqlite3_release_memory sqlite3_api->release_memory
|
||||
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
|
||||
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
|
||||
#define sqlite3_sleep sqlite3_api->sleep
|
||||
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
|
||||
#define sqlite3_vfs_find sqlite3_api->vfs_find
|
||||
#define sqlite3_vfs_register sqlite3_api->vfs_register
|
||||
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
|
||||
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
|
||||
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
|
||||
#define sqlite3_result_error_code sqlite3_api->result_error_code
|
||||
#define sqlite3_test_control sqlite3_api->test_control
|
||||
#define sqlite3_randomness sqlite3_api->randomness
|
||||
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
|
||||
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
|
||||
#define sqlite3_limit sqlite3_api->limit
|
||||
#define sqlite3_next_stmt sqlite3_api->next_stmt
|
||||
#define sqlite3_sql sqlite3_api->sql
|
||||
#define sqlite3_status sqlite3_api->status
|
||||
#define sqlite3_backup_finish sqlite3_api->backup_finish
|
||||
#define sqlite3_backup_init sqlite3_api->backup_init
|
||||
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
|
||||
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
|
||||
#define sqlite3_backup_step sqlite3_api->backup_step
|
||||
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
|
||||
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
|
||||
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
|
||||
#define sqlite3_db_config sqlite3_api->db_config
|
||||
#define sqlite3_db_mutex sqlite3_api->db_mutex
|
||||
#define sqlite3_db_status sqlite3_api->db_status
|
||||
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
|
||||
#define sqlite3_log sqlite3_api->log
|
||||
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
|
||||
#define sqlite3_sourceid sqlite3_api->sourceid
|
||||
#define sqlite3_stmt_status sqlite3_api->stmt_status
|
||||
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
||||
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
||||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
||||
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
||||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
||||
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
||||
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
||||
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
|
||||
/* Version 3.7.16 and later */
|
||||
#define sqlite3_close_v2 sqlite3_api->close_v2
|
||||
#define sqlite3_db_filename sqlite3_api->db_filename
|
||||
#define sqlite3_db_readonly sqlite3_api->db_readonly
|
||||
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
|
||||
#define sqlite3_errstr sqlite3_api->errstr
|
||||
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
|
||||
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
|
||||
#define sqlite3_stricmp sqlite3_api->stricmp
|
||||
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
||||
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
||||
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
||||
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
||||
/* Version 3.8.7 and later */
|
||||
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
||||
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
|
||||
#define sqlite3_bind_text64 sqlite3_api->bind_text64
|
||||
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
|
||||
#define sqlite3_load_extension sqlite3_api->load_extension
|
||||
#define sqlite3_malloc64 sqlite3_api->malloc64
|
||||
#define sqlite3_msize sqlite3_api->msize
|
||||
#define sqlite3_realloc64 sqlite3_api->realloc64
|
||||
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
|
||||
#define sqlite3_result_blob64 sqlite3_api->result_blob64
|
||||
#define sqlite3_result_text64 sqlite3_api->result_text64
|
||||
#define sqlite3_strglob sqlite3_api->strglob
|
||||
/* Version 3.8.11 and later */
|
||||
#define sqlite3_value_dup sqlite3_api->value_dup
|
||||
#define sqlite3_value_free sqlite3_api->value_free
|
||||
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
|
||||
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
|
||||
/* Version 3.9.0 and later */
|
||||
#define sqlite3_value_subtype sqlite3_api->value_subtype
|
||||
#define sqlite3_result_subtype sqlite3_api->result_subtype
|
||||
/* Version 3.10.0 and later */
|
||||
#define sqlite3_status64 sqlite3_api->status64
|
||||
#define sqlite3_strlike sqlite3_api->strlike
|
||||
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
|
||||
/* Version 3.12.0 and later */
|
||||
#define sqlite3_system_errno sqlite3_api->system_errno
|
||||
/* Version 3.14.0 and later */
|
||||
#define sqlite3_trace_v2 sqlite3_api->trace_v2
|
||||
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
|
||||
/* Version 3.18.0 and later */
|
||||
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
|
||||
/* Version 3.20.0 and later */
|
||||
#define sqlite3_prepare_v3 sqlite3_api->prepare_v3
|
||||
#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3
|
||||
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
|
||||
#define sqlite3_result_pointer sqlite3_api->result_pointer
|
||||
#define sqlite3_value_pointer sqlite3_api->value_pointer
|
||||
/* Version 3.22.0 and later */
|
||||
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
|
||||
#define sqlite3_value_nochange sqlite3_api->value_nochange
|
||||
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
|
||||
/* Version 3.24.0 and later */
|
||||
#define sqlite3_keyword_count sqlite3_api->keyword_count
|
||||
#define sqlite3_keyword_name sqlite3_api->keyword_name
|
||||
#define sqlite3_keyword_check sqlite3_api->keyword_check
|
||||
#define sqlite3_str_new sqlite3_api->str_new
|
||||
#define sqlite3_str_finish sqlite3_api->str_finish
|
||||
#define sqlite3_str_appendf sqlite3_api->str_appendf
|
||||
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
|
||||
#define sqlite3_str_append sqlite3_api->str_append
|
||||
#define sqlite3_str_appendall sqlite3_api->str_appendall
|
||||
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
|
||||
#define sqlite3_str_reset sqlite3_api->str_reset
|
||||
#define sqlite3_str_errcode sqlite3_api->str_errcode
|
||||
#define sqlite3_str_length sqlite3_api->str_length
|
||||
#define sqlite3_str_value sqlite3_api->str_value
|
||||
/* Version 3.25.0 and later */
|
||||
#define sqlite3_create_window_function sqlite3_api->create_window_function
|
||||
/* Version 3.26.0 and later */
|
||||
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
|
||||
/* Version 3.28.0 and later */
|
||||
#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain
|
||||
#define sqlite3_value_frombind sqlite3_api->value_frombind
|
||||
/* Version 3.30.0 and later */
|
||||
#define sqlite3_drop_modules sqlite3_api->drop_modules
|
||||
/* Version 3.31.0 and later */
|
||||
#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64
|
||||
#define sqlite3_uri_key sqlite3_api->uri_key
|
||||
#define sqlite3_filename_database sqlite3_api->filename_database
|
||||
#define sqlite3_filename_journal sqlite3_api->filename_journal
|
||||
#define sqlite3_filename_wal sqlite3_api->filename_wal
|
||||
/* Version 3.32.0 and later */
|
||||
#define sqlite3_create_filename sqlite3_api->create_filename
|
||||
#define sqlite3_free_filename sqlite3_api->free_filename
|
||||
#define sqlite3_database_file_object sqlite3_api->database_file_object
|
||||
/* Version 3.34.0 and later */
|
||||
#define sqlite3_txn_state sqlite3_api->txn_state
|
||||
/* Version 3.36.1 and later */
|
||||
#define sqlite3_changes64 sqlite3_api->changes64
|
||||
#define sqlite3_total_changes64 sqlite3_api->total_changes64
|
||||
/* Version 3.37.0 and later */
|
||||
#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages
|
||||
/* Version 3.38.0 and later */
|
||||
#define sqlite3_error_offset sqlite3_api->error_offset
|
||||
#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
|
||||
#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
|
||||
#define sqlite3_vtab_in sqlite3_api->vtab_in
|
||||
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
|
||||
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
|
||||
/* Version 3.39.0 and later */
|
||||
#ifndef SQLITE_OMIT_DESERIALIZE
|
||||
#define sqlite3_deserialize sqlite3_api->deserialize
|
||||
#define sqlite3_serialize sqlite3_api->serialize
|
||||
#endif
|
||||
#define sqlite3_db_name sqlite3_api->db_name
|
||||
/* Version 3.40.0 and later */
|
||||
#define sqlite3_value_encoding sqlite3_api->value_encoding
|
||||
/* Version 3.41.0 and later */
|
||||
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
|
||||
/* Version 3.43.0 and later */
|
||||
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
|
||||
/* Version 3.44.0 and later */
|
||||
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
||||
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
/* This case when the file really is being compiled as a loadable
|
||||
** extension */
|
||||
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
|
||||
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
|
||||
# define SQLITE_EXTENSION_INIT3 \
|
||||
extern const sqlite3_api_routines *sqlite3_api;
|
||||
#else
|
||||
/* This case when the file is being statically linked into the
|
||||
** application */
|
||||
# define SQLITE_EXTENSION_INIT1 /*no-op*/
|
||||
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
|
||||
# define SQLITE_EXTENSION_INIT3 /*no-op*/
|
||||
#endif
|
||||
|
||||
#endif /* SQLITE3EXT_H */
|
||||
Reference in New Issue
Block a user