更新:1、新增数据记录功能,将过程数据录入数据库;
2、增加输入点迹数据、凝聚后点迹数据记录; Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
This commit is contained in:
@@ -4,5 +4,6 @@ debug/
|
||||
release/
|
||||
build/
|
||||
rdp_logs/
|
||||
rdp_records/
|
||||
design_docs/
|
||||
.mnemon/
|
||||
Vendored
+13
-3
@@ -2,8 +2,14 @@
|
||||
rem ===========================================================================
|
||||
rem CMake configure script (MSVC 2022 / v143, C++17, x64)
|
||||
rem
|
||||
rem Usage: configure.bat <build-dir> <PHASE_SCANNING^|MECHANICAL_SCANNING> [Debug^|Release]
|
||||
rem Usage: configure.bat <build-dir> <PHASE_SCANNING^|MECHANICAL_SCANNING> [Debug^|Release] [AUTO^|ON^|OFF]
|
||||
rem e.g. configure.bat build\X256_PS PHASE_SCANNING Release
|
||||
rem configure.bat build\X256_PS Debug ON (Debug + data recording enabled)
|
||||
rem
|
||||
rem The 4th argument selects the optional data-recording debug feature
|
||||
rem (CMake cache variable RDP_DATA_RECORD). When it is omitted, CMake's AUTO
|
||||
rem default applies: Debug = enabled, Release = disabled. See
|
||||
rem docs/data_record_debug.md.
|
||||
rem
|
||||
rem The generator stays "NMake Makefiles" (the choice made during the VS Code
|
||||
rem migration); cl.exe is pinned to the MSVC 2022 x64 toolset.
|
||||
@@ -18,15 +24,17 @@ setlocal
|
||||
set "BUILD_DIR=%~1"
|
||||
set "SCAN_MODE=%~2"
|
||||
set "BUILD_TYPE=%~3"
|
||||
set "RECORD_MODE=%~4"
|
||||
if "%BUILD_DIR%"=="" (
|
||||
echo Usage: configure.bat ^<build-dir^> ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> [Debug^|Release]
|
||||
echo Usage: configure.bat ^<build-dir^> ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> [Debug^|Release] [AUTO^|ON^|OFF]
|
||||
exit /b 1
|
||||
)
|
||||
if "%SCAN_MODE%"=="" (
|
||||
echo Usage: configure.bat ^<build-dir^> ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> [Debug^|Release]
|
||||
echo Usage: configure.bat ^<build-dir^> ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> [Debug^|Release] [AUTO^|ON^|OFF]
|
||||
exit /b 1
|
||||
)
|
||||
if "%BUILD_TYPE%"=="" set "BUILD_TYPE=Release"
|
||||
if "%RECORD_MODE%"=="" set "RECORD_MODE=AUTO"
|
||||
|
||||
rem Load the MSVC 2022 environment (vcvarsall.bat + cl.exe existence check).
|
||||
call "%~dp0msvc_env.bat"
|
||||
@@ -48,11 +56,13 @@ if exist "%BUILD_DIR%" (
|
||||
echo [configure] compiler : %MSVC_CXX_COMPILER%
|
||||
echo [configure] build dir: %BUILD_DIR%
|
||||
echo [configure] mode : %SCAN_MODE% / %BUILD_TYPE%
|
||||
echo [configure] record : %RECORD_MODE% (RDP_DATA_RECORD: AUTO/ON/OFF)
|
||||
|
||||
pushd "%~dp0..\.."
|
||||
cmake -S . -B "%BUILD_DIR%" -G "NMake Makefiles" ^
|
||||
-DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^
|
||||
-DRADAR_SCAN_MODE=%SCAN_MODE% ^
|
||||
-DRDP_DATA_RECORD=%RECORD_MODE% ^
|
||||
-DCMAKE_CXX_COMPILER="%MSVC_CXX_COMPILER%"
|
||||
set "CMAKE_EXIT=%errorlevel%"
|
||||
popd
|
||||
|
||||
@@ -29,10 +29,21 @@
|
||||
- 输入/输出结构新增「归一化幅度」与「速度维 FFT 点数」相关字段(e8c56f9):`DataRev` 增加 `amp_asv`(目标归一化幅度 dB)与 `prn_fftn`(速度维 FFT 点数);`Track` 增加 `amp_asv_point` / `prn_fftn_point`;内部 `PointRecv`、`Trust_Track` 同步增加对应字段,`track_asso.cpp` 在关联实点时写入航迹、TWS 输出航迹时写回输出结构。⚠ 输入侧未打通:`data_preprocess()` 未把 `DataRev.amp_asv` / `DataRev.prn_fftn` 拷入内部 `PointRecv`,TAS 通道也未涉及这两个字段,当前这两个输出字段尚无有效数据来源(详见 README 11.2)。
|
||||
- 更新 README.md(本次文档更新):2 节补充重点目标/外推上限/输出门控与结构体字段扩展;3 节把 `requirements.md` 移回仓库根目录;5.2、6.1、6.2、7.1、8.1、8.2、9.6、9.9、10.1、11.1、11.2、12、13 节按源码同步 `Flag_Direction` 新判据、外推圈数上限、消亡圈数(普通 6 / 重点 10 / TAS 相扫 10、机扫 96)、`Track_Update_Flag` 输出门控、新增字段与结构体布局变更提示;12 节版本历史补充 50e0b5c、fc49707、42b598e、e8c56f9 四条记录。
|
||||
- 同步 `docs/data_process_flow_report.md` 与 `docs/track_init_readme.md`(本次文档更新):前者更新总体数据流表(⑥ 外推/消亡)、4.7 外推、6.1 消亡条件、7.2 TAS 外推门控、9.1 参数表与第 10 节观察;后者更新第 14 节 `Flag_Direction` 判据并补充 `amp_asv_point` / `prn_fftn_point` 两个未赋值字段。
|
||||
- **新增数据记录调试功能(可选编译)**:新增 `rdp_record.h`(`rdp_record::Init()/Shutdown()/db_path()` 与 `RDP_RECORD`/`RDP_RECORD_BATCH` 记录点宏;功能关闭时宏展开为 `((void)0)`、实参不求值,头文件不含任何第三方依赖)、`rdp_record_entity.h`(记录实体=数据表定义区,含 2 个示例实体与 `RDP_RECORD_DECLARE` 实例化声明宏)、`rdp_record.cpp`(实体注册区:`YLT_REFL` + `RDP_RECORD_DEFINE`;pimpl 隔离 ormpp/SQLite;首次写入自动 `CREATE TABLE IF NOT EXISTS`;单条写入与 `std::vector` 批量事务写入;建表/插入失败每个实体只提示一次并降级为空操作;内部互斥锁串行化单连接)。`data_process_class_dll.cpp` 的 `Data_Process_Factory::GetB()/Destroy()` 分别调用 `rdp_record::Init()/Shutdown()`(关闭时为空实现),`parameters.h` 新增记录目录宏 `RDP_RECORD_DB_DIR`(`.\rdp_records\`),`.gitignore` 增加 `rdp_records/`。
|
||||
- 新增 2 个示例记录点(`data_process.cpp`,字段为示例可按需增删):① `data_preprocess()` 的 TWS 分支逐点写入表 `RdpRecPointTws`(输入点迹 18 个字段:CPI/GNSS 时间 ms、方位/俯仰波位、PRF/频点、点迹号/总数、距离/方位/俯仰/速度/幅度/SNR/RCS 等);② `track_process()` 的 TWS/TAS 处理帧结束后批量写入表 `RdpRecTrackOut`(输出航迹 20 个字段,两种模式共用一张表并用 `track_mode` 区分;每行取输出数组 `[i][0]`,行数超过 `MAX_TRACK_NUM` 时钳位)。
|
||||
- 内置第三方源码 `third_party/ormpp/`(按上游原样保存,含 `UPSTREAM.md` 记录来源与升级方式):ormpp 0.2.0(commit `acf23989…`,Apache-2.0)+ 随其内置的 iguana 1.0.5、frozen 与 SQLite 3.49.1 amalgamation(公有领域),并保留两份 LICENSE 文件(与上游 git blob 逐字节一致)。
|
||||
- 新增 `docs/data_record_debug.md`(数据记录调试功能专项说明):开关与构建(`RDP_DATA_RECORD` 取值、`configure.bat` 第 4 参数、记录模块以 C++20 独立编译与 C 编译器要求、关闭时零代码保证)、运行期行为(记录库目录与文件名、`synchronous=OFF` 取舍、`Init`/`Shutdown` 时机、失败降级、单连接 + 互斥锁与线程模型)、记录实体(表)约定(全局命名空间、结构体名即表名、首列 `int64_t id` 自增主键、类型映射表、**禁止数组字段**及原因)、**新增记录点四步配方**(含 `RDP_RECORD_DECLARE` → `YLT_REFL` + `RDP_RECORD_DEFINE` → 记录点写法 → 自动建表,以及「忘记注册报 LNK2019」自检点)、现有 2 个示例记录点、Python/DB Browser 查询示例与已知限制。
|
||||
- 更新 README.md(本次文档更新):2 节新增「数据记录调试功能(可选编译)」特性条目;3 节目录结构补充 `third_party/ormpp/`、`rdp_record.h/.cpp`、`rdp_record_entity.h`、`docs/data_record_debug.md`、`rdp_records/` 并更新 `requirements.md` 说明;4.1 节环境要求补充内置依赖与 C 编译器;4.3 节补充 `RDP_DATA_RECORD` 与 `configure.bat` 第 4 参数用法;4.4 节产物补充 `rdp_record.lib`;4.5 节新增「第三方库告警例外(仅记录功能启用时)」;5.2 节模块职责新增 `rdp_record` 行;10.1 节编译期宏新增 `RDP_RECORD_ENABLE` / `RDP_RECORD_DB_DIR`;11.3 节补充「记录功能默认只在 Debug 启用」;12 节版本历史新增本次工作区条目;13 节参考文档新增 `docs/data_record_debug.md` 与 `third_party/ormpp/UPSTREAM.md`、更新 `requirements.md` 行。
|
||||
|
||||
### Changed
|
||||
|
||||
- TAS 波束控制改为按固定数据率调度:`Beam_Ctrl()` 仅当目标满足 `1 / DATA_RATE_TAS` 秒的数据率间隔(相对航迹时间 `T_track` 与上次波束输出 `last_track_time`)时才输出 TAS 跟踪波束,使 TAS 目标数据率不再随波束时间(Beam_Ctrl 调用频率)变化。
|
||||
- `CMakeLists.txt` 新增缓存变量 `RDP_DATA_RECORD`(`AUTO`/`ON`/`OFF`,AUTO = Debug 启用 / Release 关闭;多配置生成器下 AUTO 直接 `FATAL_ERROR` 要求显式取值)与可选静态库 `rdp_record`:该目标以 `CXX_STANDARD 20` 独立编译(ormpp 要求 `CMAKE_CXX_STANDARD 20`,而内置 Eigen 3.3.7 在 `/std:c++20` 下会因 `std::result_of` 被 MSVC 移除而编译失败,故**雷达处理代码保持 C++17**)、经 `enable_language(C)` 编译内置 sqlite3 amalgamation(`sqlite3.c` 以源文件级 `/w` 单独降级第三方告警)、`RDP_RECORD_ENABLE` 与 `ORMPP_ENABLE_SQLITE3` 经 `PUBLIC` 传递给 DLL 并将静态库链接进 DLL;关闭时不添加任何目标、`SOURCES` 列表保持不变(Release 零痕迹的关键)。配置阶段打印启用/关闭状态。
|
||||
- `parameters.h` 新增记录库目录宏 `RDP_RECORD_DB_DIR`(`.\rdp_records\`,相对宿主进程当前工作目录,目录不存在时自动创建)与开关说明注释:记录开关由构建系统定义 `RDP_RECORD_ENABLE`,**不提供手工开关宏**(避免 Release 误开)。
|
||||
- `configure.bat` 新增可选第 4 参数 `AUTO|ON|OFF`(缺省 `AUTO`,即 CMake 侧默认值)并作为 `-DRDP_DATA_RECORD` 传入,回显新增 `record` 行;脚本仍保持纯 ASCII 编码。
|
||||
- `.gitignore` 增加 `rdp_records/`(数据记录库输出目录)。
|
||||
- `requirements.md` 内容更新为数据记录调试功能需求(结构体经 ormpp 映射写入 SQLite、Release 版可屏蔽、数据分类入不同表、宏记录点、便于快速新增结构体、不使用异步写入),本功能框架已按该说明实现。
|
||||
- 本次改动的编译与运行验证:X256_PS/X256_MS × Release(记录关闭)/Debug(记录启用)四种配置均 **0 warning、0 error**;Release 产物的导出表与依赖表与改动前**逐行一致**、DLL 体积不变(PS 178,688 / MS 178,176 字节)、构建树中无记录模块目标文件、二进制内无 `sqlite` 字样;Debug(AUTO→启用)下 DLL 产物为 3,611,648 字节并额外生成 `rdp_record.lib`;运行时用临时校验程序(不提交)配合 Python `sqlite3` 验证:建库成功、一实体一表、单条与批量(事务)写入行数与取值正确、`id` 自增、`PRAGMA integrity_check` = ok、无 `-journal`/`-wal` 残留;记录目录无法创建时 DLL 接口照常返回、仅打印一次错误、后续写入静默忽略。
|
||||
- TAS 波束输出目标改为队列内调度:不再固定取队首,而是遍历 TAS 队列,选择满足数据率门控且 CPI 时间最早的目标输出;原每次 Beam_Ctrl 末尾的整队循环移位(队列轮转)已停用(代码注释保留)。
|
||||
- 数据率门控统一使用运行时参数 `Work_Parameter.DATA_RATE_TAS`(由 DLL 调用者经 `track_process_parameters_initial()` 输入,需为 >0 的有效值);`parameters.h` 中 `DATA_RATE_TAS` 编译期宏(相扫 0.3 / 机扫 0.0625)已注释停用,引导跟踪外推同步改用运行时参数。
|
||||
- 编译时以 `#pragma message` 提示当前启用的扫描体制宏(MECHANICAL_SCANNING / PHASE_SCANNING)。
|
||||
|
||||
@@ -21,6 +21,41 @@ if(NOT RADAR_SCAN_MODE STREQUAL "PHASE_SCANNING" AND NOT RADAR_SCAN_MODE STREQUA
|
||||
message(FATAL_ERROR "RADAR_SCAN_MODE 必须为 PHASE_SCANNING 或 MECHANICAL_SCANNING,当前值:${RADAR_SCAN_MODE}")
|
||||
endif()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 数据记录调试功能开关(可选编译,详见 docs/data_record_debug.md)
|
||||
# AUTO : Debug 构建启用、Release 构建关闭(默认;交付构建不做任何记录)
|
||||
# ON : 强制启用(可用于 Release 排查,需自行承担性能开销)
|
||||
# OFF : 强制关闭
|
||||
# 启用时新增独立静态库 rdp_record:以 C++20 编译(ormpp 要求)、编译内置的 sqlite3
|
||||
# amalgamation,并链接进 DLL;源码中的 RDP_RECORD 宏由 RDP_RECORD_ENABLE 控制,关闭时
|
||||
# 展开为空操作(实参不求值),记录模块完全不参与编译。
|
||||
# 依赖来源与升级方式见 third_party/ormpp/UPSTREAM.md。
|
||||
# ---------------------------------------------------------------------------
|
||||
set(RDP_DATA_RECORD "AUTO" CACHE STRING "数据记录调试功能:AUTO(Debug=ON/Release=OFF) / ON / OFF")
|
||||
set_property(CACHE RDP_DATA_RECORD PROPERTY STRINGS AUTO ON OFF)
|
||||
|
||||
if(RDP_DATA_RECORD STREQUAL "AUTO")
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
message(FATAL_ERROR "多配置生成器下 RDP_DATA_RECORD=AUTO 无法判定,请显式传入 ON 或 OFF")
|
||||
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(RDP_RECORD_ENABLED TRUE)
|
||||
else()
|
||||
set(RDP_RECORD_ENABLED FALSE)
|
||||
endif()
|
||||
elseif(RDP_DATA_RECORD STREQUAL "ON")
|
||||
set(RDP_RECORD_ENABLED TRUE)
|
||||
elseif(RDP_DATA_RECORD STREQUAL "OFF")
|
||||
set(RDP_RECORD_ENABLED FALSE)
|
||||
else()
|
||||
message(FATAL_ERROR "RDP_DATA_RECORD 必须为 AUTO / ON / OFF,当前值:${RDP_DATA_RECORD}")
|
||||
endif()
|
||||
|
||||
if(RDP_RECORD_ENABLED)
|
||||
message(STATUS "数据记录调试功能:启用(C++20 静态库 rdp_record + 内置 SQLite)")
|
||||
else()
|
||||
message(STATUS "数据记录调试功能:关闭(不编译记录模块,RDP_RECORD 为空操作)")
|
||||
endif()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 语言标准:C++17
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -79,6 +114,49 @@ set(PUBLIC_HEADERS
|
||||
|
||||
add_library(data_process_class_dll SHARED ${SOURCES})
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 数据记录模块(RDP_DATA_RECORD 启用时构建):独立静态库、C++20(ormpp 要求),
|
||||
# 与雷达处理代码(C++17)通过 rdp_record.h 的「仅声明 + 显式实例化」跨标准边界。
|
||||
# ---------------------------------------------------------------------------
|
||||
if(RDP_RECORD_ENABLED)
|
||||
enable_language(C) # 用于编译 sqlite3 amalgamation
|
||||
|
||||
add_library(rdp_record STATIC
|
||||
data_process_class_dll/rdp_record.cpp
|
||||
third_party/ormpp/thirdparty/sqlite3/sqlite3.c
|
||||
)
|
||||
set_target_properties(rdp_record PROPERTIES
|
||||
CXX_STANDARD 20
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
||||
)
|
||||
# RDP_RECORD_ENABLE 经 PUBLIC 传递给 DLL:data_process.cpp 等源码中的 RDP_RECORD 宏随之生效
|
||||
target_compile_definitions(rdp_record PUBLIC
|
||||
RDP_RECORD_ENABLE
|
||||
ORMPP_ENABLE_SQLITE3
|
||||
)
|
||||
target_include_directories(rdp_record
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/data_process_class_dll
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp/iguana
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp/ormpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp/thirdparty/sqlite3
|
||||
)
|
||||
target_compile_options(rdp_record PRIVATE
|
||||
/W3
|
||||
/utf-8
|
||||
/Zc:__cplusplus
|
||||
/FS
|
||||
)
|
||||
|
||||
# 第三方 amalgamation 单独降到 /w(源文件级选项在编译命令行中位于目标级之后,优先生效)
|
||||
set_source_files_properties(third_party/ormpp/thirdparty/sqlite3/sqlite3.c
|
||||
PROPERTIES COMPILE_OPTIONS "/w")
|
||||
|
||||
target_link_libraries(data_process_class_dll PRIVATE rdp_record)
|
||||
endif()
|
||||
|
||||
# 编译 DLL 时定义导出宏;引用 DLL 的工程不应定义该宏。
|
||||
target_compile_definitions(data_process_class_dll PRIVATE
|
||||
DATA_PROCESS_CLASS_DLL_LIBRARY
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
- **航迹生命周期管理**:外推轮数计数、超时消亡、手动删除、全清空(含各模块 reset,防重连后幽灵航迹)。
|
||||
- **防御性加固**:见 archived_docs/BUG_FIX_REPORT.md——越界防护、时间戳正序校验、方位角 ±π 环绕、asin 定义域钳位、IMM 概率奇异保护等已修复项。
|
||||
- **日志文件输出**:`RDP_LOG_ENABLE` 定义时,首次 `Data_Process_Factory::GetB()` 自动用 `rdbuf()` 将 `std::cout` 重定向到 `RDP_LOG_DIR`(`.\rdp_logs\`)下按日期+时间命名的日志文件,`Destroy()` 时恢复控制台并关闭文件;`RDP_LOG` 日志宏在每条日志前附加 `[时间戳][文件:行号]`。该宏在 `parameters.h` 中**当前为注释状态(默认不启用)**,即默认不做重定向、日志输出到控制台(见 10.1)。
|
||||
- **数据记录调试功能(可选编译)**:`rdp_record` 模块把结构体按「一个记录实体一张表」写入 SQLite(一次运行一个 db 文件、多张表),结构体到表的映射由内置的 ormpp(`third_party/ormpp`,含 iguana、frozen 与 SQLite 3.49.1 amalgamation)完成;由 CMake 选项 `RDP_DATA_RECORD`(`AUTO`/`ON`/`OFF`)控制,**AUTO 时 Debug 构建启用、Release(交付)构建完全不编译**——记录模块源码不参与编译、`RDP_RECORD`/`RDP_RECORD_BATCH` 宏展开为 `((void)0)`(实参不求值),DLL 不新增依赖与导出符号、无性能损失。启用时 `GetB()` 建目录并开库(`.\rdp_records\rdp_record_YYYYMMDD_HHMMSS.db`),首次写入某实体时自动 `CREATE TABLE IF NOT EXISTS`,同步写入、失败降级(不抛出、不影响雷达处理)。当前内置 2 个示例记录点(TWS 输入点迹 → `RdpRecPointTws`;输出航迹 → `RdpRecTrackOut`),新增记录数据只需「加结构体 + 注册 + 一行记录点」,详见 docs/data_record_debug.md(见 4.3、10.1、11.3)。
|
||||
|
||||
---
|
||||
|
||||
@@ -73,13 +74,14 @@
|
||||
├── CMakeLists.txt # CMake 构建脚本(扫描模式切换、导出宏、输出目录)
|
||||
├── CHANGELOG.md # 变更日志(Keep a Changelog 格式,按 git 提交记录维护)
|
||||
├── README.md # 本文件:项目说明(架构、接口、流程、算法、参数)
|
||||
├── requirements.md # 需求说明:日志文件输出功能修改要求(含 rdbuf() 重定向参考示例;原在 archived_docs/,现置于仓库根目录)
|
||||
├── requirements.md # 需求说明:数据记录调试功能修改要求(结构体经 ormpp 写入 SQLite、Release 可屏蔽、数据分类入不同表、宏记录点;原日志文件输出需求已实现)
|
||||
├── archived_docs/ # 过期文档留档(仅作历史参考;接口与流程以本 README 与 docs/ 专项说明为准)
|
||||
│ ├── BUG_REPORT.md # 全量逻辑 BUG 分析报告(P0~P3,37 项)
|
||||
│ └── BUG_FIX_REPORT.md # BUG 修复报告(修复明细 + 编译验证结果)
|
||||
├── docs/ # 专项说明文档(按当前源码整理,与 README 互补)
|
||||
│ ├── data_process_flow_report.md # 数据处理流程与关键逻辑说明(前处理/判圈、凝聚、关联与滤波、起始、外推消亡、TAS 波束、参数脉络)
|
||||
│ └── track_init_readme.md # 航迹起始(Track Init)详细逻辑说明(阶段流程、条件汇总、输出字段映射、风险点、代码索引)
|
||||
│ ├── track_init_readme.md # 航迹起始(Track Init)详细逻辑说明(阶段流程、条件汇总、输出字段映射、风险点、代码索引)
|
||||
│ └── data_record_debug.md # 数据记录调试功能说明(开关与构建、运行期行为、表结构约定、新增记录点四步配方、示例记录点、查询示例)
|
||||
├── design_docs/ # 设计与讨论资料留档(被 .gitignore 忽略、不入库;含设计讨论、产品介绍与 README/流程报告的导出、渲染版本)
|
||||
├── .vscode/ # VS Code 工程配置
|
||||
│ ├── settings.json # cmake.generator=NMake、MSVC2022 x64 环境变量、compile_commands
|
||||
@@ -94,11 +96,15 @@
|
||||
│ └── data_process_class_dll/
|
||||
│ ├── data_process_class_dll.pro # 迁移前的 Qt/qmake 工程文件(备份)
|
||||
│ └── data_process_class_dll.pro.user
|
||||
├── third_party/ # 内置第三方源码(按上游原样保存,来源/版本/升级方式见其 UPSTREAM.md)
|
||||
│ └── ormpp/ # ormpp 0.2.0 + iguana 1.0.5 + frozen + SQLite 3.49.1 amalgamation(Apache-2.0 / 公有领域)
|
||||
├── data_process_class_dll/ # 源码目录
|
||||
│ ├── data_process_class_dll.h # 对外接口:DataRev/Track/TrackingBeam/RadarPara 结构体、抽象基类、工厂类
|
||||
│ ├── data_process_class_dll_global.h # DLL 导入导出宏
|
||||
│ ├── data_process_class_dll.cpp # 工厂实现(GetB/Destroy 单例)
|
||||
│ ├── rdp_log.h / .cpp # 日志输出模块:rdbuf() 重定向 std::cout、RDP_LOG 宏(开关宏在 parameters.h)
|
||||
│ ├── rdp_record.h / .cpp # 数据记录调试模块:结构体→SQLite 表(内置 ormpp)、RDP_RECORD/RDP_RECORD_BATCH 宏、按实体自动建表、失败降级(开关由 CMake 选项 RDP_DATA_RECORD 控制;实现以 C++20 独立编译)
|
||||
│ ├── rdp_record_entity.h # 记录实体(=数据表)定义区:新增需要记录的数据在此加结构体并写 RDP_RECORD_DECLARE
|
||||
│ ├── data_process.h / .cpp # Data_Process 实现:接口分发、TWS/TAS 主流程、参数管理、清空/删除/转跟踪
|
||||
│ ├── struct.h # 内部结构体:PointRecv / Trust_Track / Temp_track / Tracking_Target
|
||||
│ ├── parameters.h # 编译期宏参数(按 MECHANICAL_SCANNING / PHASE_SCANNING 分两组)
|
||||
@@ -121,6 +127,7 @@
|
||||
│ ├── X256_MS/ # 机扫构建目录
|
||||
│ └── output/ # 早期打包导出(其中 data_process_class_dll.h 为旧版 32 位时间戳接口,勿作为当前 API 依据)
|
||||
├── rdp_logs/ # 日志输出目录(运行时自动创建于宿主进程当前工作目录下,被 .gitignore 忽略)
|
||||
├── rdp_records/ # 数据记录库输出目录(运行时自动创建于宿主进程当前工作目录下,被 .gitignore 忽略;仅记录功能启用时产生)
|
||||
└── .gitignore
|
||||
```
|
||||
|
||||
@@ -138,6 +145,7 @@
|
||||
| CMake | ≥ 3.21(VS2022 生成器自 3.21 起支持;本工程实际在 CMake 4.x 上验证) |
|
||||
| 生成器 | NMake Makefiles(VS Code 配置与脚本均使用该生成器) |
|
||||
| 线性代数 | 无需安装——Eigen 头文件已内置在源码目录 |
|
||||
| 数据记录(可选调试功能) | 无需安装——ormpp / iguana / frozen / SQLite amalgamation 已内置在 `third_party/ormpp`(版本与升级方式见该目录 UPSTREAM.md);启用时记录模块以 C++20 独立编译,所需的 C 编译器由同一 VS 2022 工具集提供 |
|
||||
|
||||
### 4.2 扫描体制
|
||||
|
||||
@@ -167,6 +175,18 @@ cmake --build build/X256_PS
|
||||
|
||||
configure.bat 会在构建目录中写入 `.msvc_toolset` 标记;若该目录是用其它工具集(例如旧的 MSVC 2013 缓存)配置的,脚本会先整体删除该目录再重新配置,避免新旧目标文件混链。用 `set VS2022_DIR=...`、`set MSVC_TOOLSET_VERSION=...`、`set MSVC_TARGET_ARCH=x86` 可覆盖默认的 VS 安装路径、工具集版本与目标架构。
|
||||
|
||||
数据记录调试功能由缓存变量 `RDP_DATA_RECORD` 控制,`configure.bat` 的第 4 个参数即该变量(取值 `AUTO`/`ON`/`OFF`,缺省 `AUTO`):
|
||||
|
||||
```bat
|
||||
:: AUTO(默认):Debug 构建启用、Release 构建关闭——交付构建不做任何记录
|
||||
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Debug AUTO
|
||||
:: 显式关闭(Release 交付推荐)或显式开启(临时在 Release 中排查)
|
||||
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Release OFF
|
||||
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Debug ON
|
||||
```
|
||||
|
||||
启用时配置阶段打印 `数据记录调试功能:启用(C++20 静态库 rdp_record + 内置 SQLite)`,并额外构建静态库 `rdp_record`(记录模块 + SQLite,链接进 DLL);关闭时打印 `...:关闭(不编译记录模块,RDP_RECORD 为空操作)`,记录模块源码完全不参与编译。开关语义、表结构约定与新增记录点配方见 docs/data_record_debug.md。
|
||||
|
||||
在 VS Code 中可直接运行任务(tasks.json):「CMake: 配置并构建 X256_PS (Release)」等,默认任务为 X256_PS Release。
|
||||
|
||||
### 4.4 产物
|
||||
@@ -177,6 +197,7 @@ configure.bat 会在构建目录中写入 `.msvc_toolset` 标记;若该目录
|
||||
| 导入库 data_process_class_dll.lib | build/<X256_XX>/lib/ |
|
||||
| 安装(cmake --install) | DLL/导入库 → bin、lib,公开头文件 → include |
|
||||
| compile_commands.json | 构建根目录(供 VS Code IntelliSense 使用) |
|
||||
| rdp_record.lib(仅记录功能启用时) | build/<X256_XX>/lib/ —— 记录模块静态库(含 SQLite),已链接进 DLL,**无需随 DLL 分发** |
|
||||
|
||||
产物为 **x64(AMD64)** 目标。宿主程序必须是 64 位、且使用与本 DLL 同一版本头文件重新编译——MSVC 2013 → MSVC 2022 同时伴随 x86 → x64 的 ABI 变化,旧的 32 位宿主无法加载该 DLL。
|
||||
|
||||
@@ -190,6 +211,7 @@ configure.bat 会在构建目录中写入 `.msvc_toolset` 标记;若该目录
|
||||
- MSVC 2022 在 `/W3` 下新增报出的 C4267(`size_t` → `int` 窄化)共 17 处已按同一约定修复(`track_asso.cpp`、`track_asso_tas.cpp`、`track_init.cpp`、`track_init_direct_tracking.cpp`、`tas_ctrl.cpp` 中改为 `static_cast<int>(...)`),同样不改变计算逻辑。
|
||||
- 当前四种配置(X256_PS / X256_MS × Release / Debug)均以 **0 warning、0 error** 通过编译。
|
||||
- cl.exe 的诊断信息为中文(跟随 VS 安装语言,`VSLANG=1033` 对 VS2022 的 cl.exe 无效),构建日志中偶见 `注意: 包含文件:` 行泄漏,属 CMake 依赖扫描的显示噪声,头文件依赖(`CMakeFiles/*.dir/**/*.obj.d`)记录完整,不影响增量编译。
|
||||
- **第三方库告警例外(仅记录功能启用时)**:数据记录模块的第三方依赖按上游原样内置在 `third_party/ormpp`(保留上游字节,含换行符,不转 BOM/编码),其中 `third_party/ormpp/thirdparty/sqlite3/sqlite3.c` 以源文件级 `/w` 单独编译,`data_process_class_dll/rdp_record.cpp` 用 `#pragma warning(push, 0)` / `(pop)` 包裹第三方头;除此之外本工程源码仍按上一条的 `/W3` 零告警标准编译(X256_PS/X256_MS × Release/Debug 四种配置实测 0 warning、0 error)。
|
||||
|
||||
### 4.6 编译器升级说明(MSVC 2013 → MSVC 2022)
|
||||
|
||||
@@ -291,6 +313,7 @@ classDiagram
|
||||
| kalman | 两点/三点滤波初始化、线性卡尔曼预测/滤波、EKF(3 维量测含多普勒)、马氏统计距离 d、盲速 Bind_speed |
|
||||
| coor_trans | 极坐标 ↔ 直角坐标转换 |
|
||||
| rdp_log(rdp_log.h/.cpp) | 日志输出:按 parameters.h 的 `RDP_LOG_ENABLE` 开关,在 `Init()` 中用 `rdbuf()` 将 `std::cout` 重定向到日期+时间命名的日志文件、`Shutdown()` 恢复控制台并关闭;提供 `RDP_LOG` 宏自动附加 `[时间戳][文件:行号]` 前缀 |
|
||||
| rdp_record(rdp_record.h/.cpp、rdp_record_entity.h) | 数据记录调试(可选编译):由 CMake 选项 `RDP_DATA_RECORD` 决定是否参与编译;开启时把「记录实体」(结构体)经内置 ormpp 映射写入 SQLite 表(一实体一表,首次写入自动 `CREATE TABLE IF NOT EXISTS`,一次运行一个 db 文件),`RDP_RECORD`/`RDP_RECORD_BATCH` 记录点宏在关闭时展开为空操作;`Init()`/`Shutdown()` 由工厂的 `GetB()`/`Destroy()` 调用;建表/插入失败一律降级(每个实体只提示一次),不影响雷达处理 |
|
||||
| Track_Init_Direct_Tracking / Track_Asso_Direct_Tracking | 引导跟踪(direct tracking)专用起批/关联。**当前入口为空实现、未启用**;track_asso_direct_tracking.cpp 未加入 CMake 源列表 |
|
||||
|
||||
---
|
||||
@@ -857,6 +880,8 @@ $$
|
||||
|---|---|---|---|
|
||||
| RDP_LOG_ENABLE | 注释停用(默认不启用) | 同左 | 日志文件输出开关:定义时将 `std::cout` 重定向到日志文件;**当前源码中该宏为注释状态**(parameters.h 的注释文字仍写「定义(默认)」,以代码实际状态为准),不重定向、日志输出到标准输出/控制台 |
|
||||
| RDP_LOG_DIR | `.\rdp_logs\` | 同左 | 日志文件输出目录(相对宿主进程当前工作目录,目录不存在时自动创建) |
|
||||
| RDP_RECORD_ENABLE | 由 CMake 选项 `RDP_DATA_RECORD` 定义(AUTO 时 Debug 定义) | 同左 | 数据记录调试开关:**不要在 parameters.h 中手工定义**(由构建系统传入);定义时记录模块参与编译,未定义时 `RDP_RECORD`/`RDP_RECORD_BATCH` 展开为空操作,见 4.3 与 11.3 |
|
||||
| RDP_RECORD_DB_DIR | `.\rdp_records\` | 同左 | 数据记录库输出目录(相对宿主进程当前工作目录,目录不存在时自动创建;仅记录功能启用时产生) |
|
||||
| DATA_RATE_TAS | 已停用(原 0.3) | 已停用(原 0.0625) | 编译期宏已注释停用;TAS 数据率统一由运行时参数 RadarPara.DATA_RATE_TAS 提供(见 10.2) |
|
||||
| SIGMA_R / SIGMA_A / SIGMA_E / SIGMA_V | 10.0 / 0.02 / 0.2 / 2.0(两体制相同) | 同左 | 量测误差:距离 m / 方位 rad / 俯仰 rad / 速度 m/s |
|
||||
| DOT_COH_RANGE / DOT_COH_V / DOT_COH_AZI | 80 / 2 / 6 | 同左 | 凝聚门限:距离 m / 速度 m/s / 方位 ° |
|
||||
@@ -934,6 +959,7 @@ $$
|
||||
- TAS **自动**开启/退出逻辑(tas_auto_start/tas_auto_end)注释停用,仅支持手动 tracking_start/tracking_stop。
|
||||
- RadarPara 中的起批/关联波门字段未被实现读取(见 10.2)。
|
||||
- build/output/ 下为早期打包导出物,其 data_process_class_dll.h 时间戳仍为 32 位 int,与当前 64 位接口不一致,勿作为对接依据。
|
||||
- **数据记录调试功能默认只在 Debug 启用**:CMake 选项 `RDP_DATA_RECORD` 的 AUTO 默认值为「Debug 启用、Release 关闭」,交付/Release 构建下记录模块(`rdp_record.cpp`、`sqlite3.c`)不参与编译、`RDP_RECORD` 为空操作(导出符号与依赖均无变化);如需在 Release 构建中临时排查可显式 `-DRDP_DATA_RECORD=ON`,但正式交付应保持 AUTO/OFF(见 docs/data_record_debug.md)。记录点中的字段错误只在启用记录功能的构建中才会被检查(关闭时宏丢弃实参)。
|
||||
|
||||
### 11.4 航迹起始相关的未修复观察(详见 docs/track_init_readme.md 第 15 节)
|
||||
|
||||
@@ -953,6 +979,7 @@ $$
|
||||
|
||||
| 提交 | 内容 |
|
||||
|---|---|
|
||||
| 工作区(未提交,数据记录调试功能) | 新增可选编译的数据记录调试功能:CMake 缓存变量 `RDP_DATA_RECORD`(AUTO/ON/OFF,AUTO = Debug 启用 / Release 关闭)与独立静态库 `rdp_record`(**以 C++20 编译**,雷达代码保持 C++17);内置第三方源码 `third_party/ormpp`(ormpp 0.2.0 + iguana 1.0.5 + frozen + SQLite 3.49.1 amalgamation,附 UPSTREAM.md);新增 `rdp_record.h`(`Init`/`Shutdown`/`db_path` + `RDP_RECORD`/`RDP_RECORD_BATCH` 宏,关闭时展开为空操作)、`rdp_record_entity.h`(记录实体定义区)与 `rdp_record.cpp`(实体注册区、按实体自动建表、单条/批量写入、失败降级);`Data_Process_Factory::GetB()/Destroy()` 调用 `rdp_record::Init()/Shutdown()`;`parameters.h` 新增 `RDP_RECORD_DB_DIR`(`.\rdp_records\`);`data_process.cpp` 新增 2 个示例记录点(TWS 输入点迹 → `RdpRecPointTws`、输出航迹 → `RdpRecTrackOut`);`configure.bat` 新增第 4 个参数(AUTO/ON/OFF);`.gitignore` 增加 `rdp_records/`;新增 `docs/data_record_debug.md`。验证:Release(PS/MS)导出表与依赖表与改动前逐行一致、DLL 体积不变(178,688 / 178,176 字节)、构建树无记录模块目标文件;Debug(PS/MS,AUTO→启用)0 warning 0 error;运行时校验(临时程序 + Python sqlite3)确认建库、一实体一表、批量事务写入、`id` 自增、取值一致、`integrity_check` = ok,且记录目录不可创建时接口照常返回、仅提示一次 |
|
||||
| 工作区(未提交) | 编译器由 MSVC 2013(Visual Studio 12,x86)升级为 **MSVC 2022(v143,x64)**、C++ 标准提升至 **C++17**:`CMakeLists.txt` 重写(`CMAKE_CXX_STANDARD 17`、`_MSC_VER >= 1930` 强校验、`/utf-8` `/Zc:__cplusplus` `/W3`)、`.vscode/scripts/` 全部构建脚本改为 MSVC 2022(新增共享的 `msvc_env.bat`,`configure.bat` 自动清理旧工具集构建树)、VS Code 配置同步(settings / c_cpp_properties / cmake-variants / tasks 改为 MSVC2022 x64 + C++17)、修复 MSVC 2022 新增的 17 处 C4267 窄化告警;X256_PS/X256_MS × Release/Debug 四种配置均 0 warning 0 error 通过,产物为 x64 DLL(导出符号与升级前一致,仅名称修饰随架构变化) |
|
||||
| e8c56f9 | 输入/输出结构适配「归一化幅度」与「速度维 FFT 点数」:`DataRev` 新增 `amp_asv`/`prn_fftn`,`Track` 新增 `amp_asv_point`/`prn_fftn_point`,内部 `PointRecv`/`Trust_Track` 同步新增字段;`track_asso.cpp` 关联实点时写入航迹、TWS 输出时写回输出结构(输入侧未打通,见 11.2) |
|
||||
| 42b598e | 调整「向站/背站」判断条件:判据由 `acos((-x·vx − y·vy)/r/Range_V) < π/4` 改为 `acos((-x·cos(Direction_Angle) − y·sin(Direction_Angle))/r) < π/2`(去掉按合速度归一化,锥角门限由 45° 放宽到 90°),TWS/TAS 同步 |
|
||||
@@ -994,9 +1021,11 @@ $$
|
||||
| CHANGELOG.md | 变更日志(Keep a Changelog 格式,按 git 提交记录维护) |
|
||||
| docs/data_process_flow_report.md | 数据处理流程与关键逻辑说明(按当前源码整理:前处理与判圈、凝聚、TWS/TAS 关联与滤波、航迹起始、外推消亡、TAS 波束控制、参数脉络;含与本文档的历史差异提示)——原同内容 HTML 渲染版(`data_process_flow_report.html`)已于 e0bf227 删除,HTML 版本在 git 忽略的 `design_docs/` 内留档 |
|
||||
| docs/track_init_readme.md | 航迹起始(Track Init)专项说明:分阶段流程、起始条件汇总、调参直觉、输出字段映射(含未赋值字段)、已知风险与代码索引 |
|
||||
| docs/data_record_debug.md | 数据记录调试功能说明:开关与构建(`RDP_DATA_RECORD`、C++20 隔离与 C 编译器要求)、运行期行为(目录/文件名、PRAGMA、失败降级、线程模型)、表结构约定、**新增记录点四步配方**、现有 2 个示例记录点、查询示例与已知限制 |
|
||||
| third_party/ormpp/UPSTREAM.md | 内置第三方源码的来源说明:ormpp / iguana / frozen / SQLite 版本、许可证、复现与升级步骤(含 blob 校验值) |
|
||||
| archived_docs/BUG_REPORT.md | 全量逻辑 BUG 分析(P0~P3 共 37 项,含位置、说明、建议、处置结论)——过期留档,仅作历史参考 |
|
||||
| archived_docs/BUG_FIX_REPORT.md | 修复明细、修复状态汇总表与 MSVC 2013 x86 编译验证记录——过期留档(对应 01d28e0 时期) |
|
||||
| requirements.md | 修改需求说明:日志文件输出功能(rdbuf() 重定向、parameters.h 日志开关宏、日期+时间命名日志、前缀日志宏,附参考示例)——已实现,现置于仓库根目录(原在 archived_docs/ 留档) |
|
||||
| requirements.md | 修改需求说明:数据记录调试功能(结构体经 ormpp 映射写入 SQLite、Release 版可屏蔽、数据分类入不同表、宏记录点、便于快速新增结构体、不使用异步写入)——已按该说明实现框架与 2 个示例记录点,详见 docs/data_record_debug.md(此前同名的日志文件输出需求亦已实现) |
|
||||
| backup/data_process_class_dll/data_process_class_dll.pro | 迁移前的 Qt/qmake 工程文件(源文件清单参考) |
|
||||
| .vscode/tasks.json、.vscode/cmake-variants.yaml | VS Code 构建任务与变体定义 |
|
||||
| build/X256_PS/bin/RadarConfigParam.ini | 回放测试工具(rdp_playback.exe)配置样例 |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "data_process.h"
|
||||
#include "rdp_log.h"
|
||||
#include "rdp_record.h"
|
||||
#include "rdp_record_entity.h"
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
@@ -17,6 +19,7 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
|
||||
{
|
||||
// 更新当前系统时间
|
||||
latest_timestamp = Data_Input[0].CPI_time;
|
||||
frame_id++;
|
||||
|
||||
//数据存入缓存区域 Data_buffer
|
||||
if(Data_Input[0].point_type == 0) //tws数据
|
||||
@@ -26,24 +29,35 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
|
||||
for (int i=0; i<data_num;i++)
|
||||
{
|
||||
PointRecv Data_buffer_temp;
|
||||
Data_buffer_temp.Amplitude=Data_Input[i].Amplitude;
|
||||
Data_buffer_temp.Azimuth=Data_Input[i].Azimuth/180*PI;
|
||||
Data_buffer_temp.snr = Data_Input[i].Snr;
|
||||
Data_buffer_temp.point_id = point_id++;
|
||||
Data_buffer_temp.frame_id = frame_id;
|
||||
Data_buffer_temp.amplitude=Data_Input[i].Amplitude;
|
||||
Data_buffer_temp.azimuth=Data_Input[i].Azimuth/180*PI;
|
||||
Data_buffer_temp.SNR = Data_Input[i].Snr;
|
||||
Data_buffer_temp.RCS = Data_Input[i].RCS;
|
||||
Data_buffer_temp.CPI_Time = Data_Input[i].CPI_time;
|
||||
Data_buffer_temp.CPI_time = Data_Input[i].CPI_time;
|
||||
Data_buffer_temp.GNSS_time = Data_Input[i].GNSS_time;
|
||||
Data_buffer_temp.Freq_index = Data_Input[i].Freq_index;
|
||||
Data_buffer_temp.freq_index = Data_Input[i].Freq_index;
|
||||
Data_buffer_temp.PRF_index = Data_Input[i].PRI;
|
||||
Data_buffer_temp.Range = Data_Input[i].Range;
|
||||
Data_buffer_temp.Height= Data_Input[i].Range*sin(Data_Input[i].Elevation/180*PI);
|
||||
Data_buffer_temp.Use_Flag = 0;
|
||||
Data_buffer_temp.Velocity = Data_Input[i].Velocity;
|
||||
|
||||
Data_buffer_temp.pitch_num = Data_Input[i].pitch_num;
|
||||
Data_buffer_temp.range = Data_Input[i].Range;
|
||||
Data_buffer_temp.height= Data_Input[i].Range*sin(Data_Input[i].Elevation/180*PI);
|
||||
Data_buffer_temp.use_flag = 0;
|
||||
Data_buffer_temp.velocity = Data_Input[i].Velocity;
|
||||
Data_buffer_temp.encoder_value = Data_Input[i].Encoder_value;
|
||||
Data_buffer_temp.pitch_beam_index = Data_Input[i].pitch_num;
|
||||
Data_buffer_temp.azimuth_beam_index = Data_Input[i].Beam_index_aiz;
|
||||
Data_buffer_temp.point_type = Data_Input[i].point_type;
|
||||
Data_buffer_temp.amp_asv_point = Data_Input[i].amp_asv;
|
||||
Data_buffer_temp.prn_fftn_point = Data_Input[i].prn_fftn;
|
||||
|
||||
std::memcpy(Data_buffer_temp.speed_dim, Data_Input[i].speed_dim, sizeof(Data_buffer_temp.speed_dim));
|
||||
std::memcpy(Data_buffer_temp.range_dim, Data_Input[i].range_dim, sizeof(Data_buffer_temp.range_dim));
|
||||
|
||||
//数据记录示例①:把本次接收的输入点迹写入表 RdpRecPointTws(字段为示例,可按需增删)。
|
||||
//记录功能关闭时该语句展开为空操作(实参不求值),交付构建不产生任何代码,
|
||||
//用法与新增记录点配方见 rdp_record.h 与 docs/data_record_debug.md。
|
||||
RDP_RECORD(RdpRecPoint(Data_buffer_temp));
|
||||
|
||||
Data_buffer.push_back(Data_buffer_temp);
|
||||
}
|
||||
|
||||
@@ -59,17 +73,6 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
|
||||
last_beam_num = Data_Input[0].Beam_index_aiz;
|
||||
return 2;
|
||||
}
|
||||
// if(Data_Input[0].Beam_index_aiz < last_beam_num) //TWS
|
||||
// {
|
||||
// last_beam_num = Data_Input[0].Beam_index_aiz;
|
||||
|
||||
// return 1;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// last_beam_num = Data_Input[0].Beam_index_aiz;
|
||||
// return 2;
|
||||
// }
|
||||
}
|
||||
|
||||
else if(Data_Input[0].point_type == 1) //tas数据
|
||||
@@ -82,22 +85,32 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
|
||||
for (int i=0; i<data_num;i++)
|
||||
{
|
||||
PointRecv Data_buffer_temp;
|
||||
Data_buffer_temp.Amplitude=Data_Input[i].Amplitude;
|
||||
Data_buffer_temp.Azimuth=Data_Input[i].Azimuth/180*PI;
|
||||
Data_buffer_temp.snr = Data_Input[i].Snr;
|
||||
Data_buffer_temp.point_id = point_id++;
|
||||
Data_buffer_temp.frame_id = frame_id;
|
||||
Data_buffer_temp.amplitude=Data_Input[i].Amplitude;
|
||||
Data_buffer_temp.azimuth=Data_Input[i].Azimuth/180*PI;
|
||||
Data_buffer_temp.SNR = Data_Input[i].Snr;
|
||||
Data_buffer_temp.RCS = Data_Input[i].RCS;
|
||||
Data_buffer_temp.CPI_Time = Data_Input[i].CPI_time;
|
||||
Data_buffer_temp.CPI_time = Data_Input[i].CPI_time;
|
||||
Data_buffer_temp.GNSS_time = Data_Input[i].GNSS_time;
|
||||
Data_buffer_temp.Freq_index = Data_Input[i].Freq_index;
|
||||
Data_buffer_temp.freq_index = Data_Input[i].Freq_index;
|
||||
Data_buffer_temp.PRF_index = Data_Input[i].PRI;
|
||||
Data_buffer_temp.Range = Data_Input[i].Range;
|
||||
Data_buffer_temp.Height= Data_Input[i].Range*sin(Data_Input[i].Elevation/180*PI);
|
||||
Data_buffer_temp.Use_Flag = 0;
|
||||
Data_buffer_temp.Velocity = Data_Input[i].Velocity;
|
||||
Data_buffer_temp.pitch_num = Data_Input[i].pitch_num;
|
||||
Data_buffer_temp.range = Data_Input[i].Range;
|
||||
Data_buffer_temp.height= Data_Input[i].Range*sin(Data_Input[i].Elevation/180*PI);
|
||||
Data_buffer_temp.use_flag = 0;
|
||||
Data_buffer_temp.velocity = Data_Input[i].Velocity;
|
||||
Data_buffer_temp.pitch_beam_index = Data_Input[i].pitch_num;
|
||||
Data_buffer_temp.encoder_value = Data_Input[i].Encoder_value;
|
||||
Data_buffer_temp.azimuth_beam_index = Data_Input[i].Beam_index_aiz;
|
||||
Data_buffer_temp.point_type = Data_Input[i].point_type;
|
||||
Data_buffer_temp.amp_asv_point = Data_Input[i].amp_asv;
|
||||
Data_buffer_temp.prn_fftn_point = Data_Input[i].prn_fftn;
|
||||
|
||||
std::memcpy(Data_buffer_temp.speed_dim, Data_Input[i].speed_dim, sizeof(Data_buffer_temp.speed_dim));
|
||||
std::memcpy(Data_buffer_temp.range_dim, Data_Input[i].range_dim, sizeof(Data_buffer_temp.range_dim));
|
||||
|
||||
RDP_RECORD(RdpRecPoint(Data_buffer_temp));
|
||||
|
||||
Data_buffer_tas.push_back(Data_buffer_temp);
|
||||
|
||||
// std::cout << "Azimuth:" << Data_Input[i].Azimuth
|
||||
@@ -118,6 +131,35 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
|
||||
|
||||
}
|
||||
|
||||
#ifdef RDP_RECORD_ENABLE
|
||||
//数据记录示例②:把本圈输出的航迹批量写入表 RdpRecTrackOut(TWS/TAS 共用一张表,用 track_mode 区分)。
|
||||
//该辅助函数只在记录功能开启时编译(RDP_RECORD_ENABLE 由构建系统定义),交付构建完全不参与编译
|
||||
static void rdp_record_output_tracks(struct Track Trust_Track_Output[][10], int track_num)
|
||||
{
|
||||
if (track_num <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (track_num > MAX_TRACK_NUM) //防御:输出行数理论上不应超过航迹容量(见 README 11.2 BUG-08)
|
||||
{
|
||||
track_num = MAX_TRACK_NUM;
|
||||
}
|
||||
|
||||
std::vector<RdpRecTrackOut> recs;
|
||||
recs.reserve(static_cast<size_t>(track_num));
|
||||
for (int i = 0; i < track_num; ++i)
|
||||
{
|
||||
const struct Track &t = Trust_Track_Output[i][0]; //每行第 0 列为该航迹的主记录
|
||||
recs.push_back(RdpRecTrackOut{0, t.Track_Index, t.Track_Mode, t.Point_Sum, t.Flag_Point,
|
||||
t.Flag_Direction, t.GNSS_time, t.track_time,
|
||||
t.x, t.y, t.z, t.v_x, t.v_y, t.v_z,
|
||||
t.Range, t.Azimuth, t.Elevation, t.Range_V,
|
||||
t.track_snr, t.track_rcs});
|
||||
}
|
||||
RDP_RECORD_BATCH(recs);
|
||||
}
|
||||
#endif
|
||||
|
||||
//数据处理
|
||||
int Data_Process::track_process(struct Track Trust_Track_Output[MAX_TRACK_NUM][10],
|
||||
int *Trust_track_num_Output,
|
||||
@@ -131,7 +173,6 @@ int model)
|
||||
{
|
||||
|
||||
dot_coh.dot_coh_process_buff(&Data_buffer,&point_recv,Work_Parameter);
|
||||
//std::cout << "point_recv :" <<point_recv.size();
|
||||
std::vector<PointRecv>().swap(Data_buffer);
|
||||
}
|
||||
|
||||
@@ -188,6 +229,14 @@ int model)
|
||||
//std::cout << "Extrapolate_round :" << trust_track[TAS_track_idx].Extrapolate_round;
|
||||
}
|
||||
|
||||
//数据记录示例②:本圈输出的航迹(仅 TWS/TAS 处理帧有输出;记录功能关闭时不产生任何代码)
|
||||
#ifdef RDP_RECORD_ENABLE
|
||||
if (model == 1 || model == 3)
|
||||
{
|
||||
rdp_record_output_tracks(Trust_Track_Output, *Trust_track_num_Output);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,10 @@ private:
|
||||
|
||||
long long latest_timestamp = 0; //最新时间戳
|
||||
|
||||
long long point_id = 0; //点迹id
|
||||
|
||||
long long frame_id = 0; //帧id
|
||||
|
||||
struct RadarPara Work_Parameter; //工作参数 参数设置函数外部输入
|
||||
|
||||
Dot_Coh dot_coh; //点迹凝聚
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "data_process_class_dll.h"
|
||||
#include "data_process.h"
|
||||
#include "rdp_log.h"
|
||||
#include "rdp_record.h"
|
||||
|
||||
/*
|
||||
Data_process_class_dll::Data_process_class_dll()
|
||||
@@ -19,6 +20,11 @@ Data_process_class_dll *Data_Process_Factory::GetB()
|
||||
{
|
||||
//初始化日志输出:RDP_LOG_ENABLE 开启时重定向 std::cout 到日志文件
|
||||
//rdp_log::Init();
|
||||
|
||||
//初始化数据记录:RDP_DATA_RECORD 开启时创建记录目录、打开本次运行的记录库;
|
||||
//关闭时 rdp_record::Init() 为空实现,不产生任何开销
|
||||
rdp_record::Init();
|
||||
|
||||
p=new Data_Process();
|
||||
}
|
||||
|
||||
@@ -36,5 +42,8 @@ void Data_Process_Factory::Destroy()
|
||||
|
||||
//恢复 std::cout 到控制台并关闭日志文件
|
||||
//rdp_log::Shutdown();
|
||||
|
||||
//关闭记录库(RDP_DATA_RECORD 关闭时为空实现)
|
||||
rdp_record::Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "rdp_record.h"
|
||||
#include "rdp_record_entity.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
|
||||
@@ -14,20 +17,20 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
|
||||
|
||||
for (unsigned int loop_of_point=0; loop_of_point<data_input->size()-1;loop_of_point++ )
|
||||
{
|
||||
if( (*data_input)[loop_of_point].Use_Flag!=1)
|
||||
if( (*data_input)[loop_of_point].use_flag!=1)
|
||||
{
|
||||
float point_0_R=static_cast<float>((*data_input)[loop_of_point].Range);
|
||||
float point_0_V=static_cast<float>((*data_input)[loop_of_point].Velocity);
|
||||
float point_0_F=static_cast<float>((*data_input)[loop_of_point].Azimuth);
|
||||
float point_0_A=static_cast<float>((*data_input)[loop_of_point].Amplitude);
|
||||
float point_0_R=static_cast<float>((*data_input)[loop_of_point].range);
|
||||
float point_0_V=static_cast<float>((*data_input)[loop_of_point].velocity);
|
||||
float point_0_F=static_cast<float>((*data_input)[loop_of_point].azimuth);
|
||||
float point_0_A=static_cast<float>((*data_input)[loop_of_point].amplitude);
|
||||
for (unsigned int i=loop_of_point+1;i<data_input->size();i++)
|
||||
{
|
||||
if((*data_input)[loop_of_point].Use_Flag!=1 && (*data_input)[i].Use_Flag!=1)
|
||||
if((*data_input)[loop_of_point].use_flag!=1 && (*data_input)[i].use_flag!=1)
|
||||
{
|
||||
float point_1_R=static_cast<float>((*data_input)[i].Range);
|
||||
float point_1_V=static_cast<float>((*data_input)[i].Velocity);
|
||||
float point_1_F=static_cast<float>((*data_input)[i].Azimuth);
|
||||
float point_1_A=static_cast<float>((*data_input)[i].Amplitude);
|
||||
float point_1_R=static_cast<float>((*data_input)[i].range);
|
||||
float point_1_V=static_cast<float>((*data_input)[i].velocity);
|
||||
float point_1_F=static_cast<float>((*data_input)[i].azimuth);
|
||||
float point_1_A=static_cast<float>((*data_input)[i].amplitude);
|
||||
|
||||
//凝聚条件: 距离、方位接近
|
||||
if(Work_Parameter.work_mode == 0)
|
||||
@@ -36,16 +39,16 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
|
||||
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)//
|
||||
&& point_0_A <point_1_A )//
|
||||
{
|
||||
point_0_R=static_cast<float>((*data_input)[i].Range);
|
||||
point_0_V=static_cast<float>((*data_input)[i].Velocity);
|
||||
point_0_A=static_cast<float>((*data_input)[i].Amplitude);
|
||||
point_0_F=static_cast<float>((*data_input)[i].Azimuth);
|
||||
(*data_input)[loop_of_point].Use_Flag=1;
|
||||
point_0_R=static_cast<float>((*data_input)[i].range);
|
||||
point_0_V=static_cast<float>((*data_input)[i].velocity);
|
||||
point_0_A=static_cast<float>((*data_input)[i].amplitude);
|
||||
point_0_F=static_cast<float>((*data_input)[i].azimuth);
|
||||
(*data_input)[loop_of_point].use_flag=1;
|
||||
}
|
||||
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)//
|
||||
&& point_0_A >= point_1_A)//
|
||||
{
|
||||
(*data_input)[i].Use_Flag=1;
|
||||
(*data_input)[i].use_flag=1;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -54,16 +57,16 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
|
||||
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
|
||||
&& point_0_A <point_1_A )//
|
||||
{
|
||||
point_0_R=static_cast<float>((*data_input)[i].Range);
|
||||
point_0_V=static_cast<float>((*data_input)[i].Velocity);
|
||||
point_0_A=static_cast<float>((*data_input)[i].Amplitude);
|
||||
point_0_F=static_cast<float>((*data_input)[i].Azimuth);
|
||||
(*data_input)[loop_of_point].Use_Flag=1;
|
||||
point_0_R=static_cast<float>((*data_input)[i].range);
|
||||
point_0_V=static_cast<float>((*data_input)[i].velocity);
|
||||
point_0_A=static_cast<float>((*data_input)[i].amplitude);
|
||||
point_0_F=static_cast<float>((*data_input)[i].azimuth);
|
||||
(*data_input)[loop_of_point].use_flag=1;
|
||||
}
|
||||
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
|
||||
&& point_0_A >= point_1_A)//
|
||||
{
|
||||
(*data_input)[i].Use_Flag=1;
|
||||
(*data_input)[i].use_flag=1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -77,7 +80,7 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
|
||||
std::vector <PointRecv>::iterator Iter;
|
||||
for (Iter=data_input->begin(); Iter!=data_input->end();)
|
||||
{
|
||||
if((*Iter).Use_Flag==1 || (*Iter).Range <R_MIN || (*Iter).Range > R_MAX )
|
||||
if((*Iter).use_flag==1 || (*Iter).range <R_MIN || (*Iter).range > R_MAX )
|
||||
{
|
||||
data_input->erase(Iter);
|
||||
Iter=data_input->begin();
|
||||
@@ -130,20 +133,20 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
|
||||
|
||||
for (unsigned int loop_of_point=0; loop_of_point<data_tmp.size()-1;loop_of_point++ )
|
||||
{
|
||||
if( data_tmp[loop_of_point].Use_Flag!=1)
|
||||
if( data_tmp[loop_of_point].use_flag!=1)
|
||||
{
|
||||
float point_0_R=static_cast<float>(data_tmp[loop_of_point].Range);
|
||||
float point_0_V=static_cast<float>(data_tmp[loop_of_point].Velocity);
|
||||
float point_0_F=static_cast<float>(data_tmp[loop_of_point].Azimuth);
|
||||
float point_0_A=static_cast<float>(data_tmp[loop_of_point].Amplitude);
|
||||
float point_0_R=static_cast<float>(data_tmp[loop_of_point].range);
|
||||
float point_0_V=static_cast<float>(data_tmp[loop_of_point].velocity);
|
||||
float point_0_F=static_cast<float>(data_tmp[loop_of_point].azimuth);
|
||||
float point_0_A=static_cast<float>(data_tmp[loop_of_point].amplitude);
|
||||
for (unsigned int i=loop_of_point+1;i<data_tmp.size();i++)
|
||||
{
|
||||
if(data_tmp[loop_of_point].Use_Flag!=1 && data_tmp[i].Use_Flag!=1)
|
||||
if(data_tmp[loop_of_point].use_flag!=1 && data_tmp[i].use_flag!=1)
|
||||
{
|
||||
float point_1_R=static_cast<float>(data_tmp[i].Range);
|
||||
float point_1_V=static_cast<float>(data_tmp[i].Velocity);
|
||||
float point_1_F=static_cast<float>(data_tmp[i].Azimuth);
|
||||
float point_1_A=static_cast<float>(data_tmp[i].Amplitude);
|
||||
float point_1_R=static_cast<float>(data_tmp[i].range);
|
||||
float point_1_V=static_cast<float>(data_tmp[i].velocity);
|
||||
float point_1_F=static_cast<float>(data_tmp[i].azimuth);
|
||||
float point_1_A=static_cast<float>(data_tmp[i].amplitude);
|
||||
|
||||
//凝聚条件: 距离、方位接近
|
||||
if(Work_Parameter.work_mode == 0)
|
||||
@@ -152,16 +155,16 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
|
||||
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)//
|
||||
&& point_0_A <point_1_A )//
|
||||
{
|
||||
point_0_R=static_cast<float>(data_tmp[i].Range);
|
||||
point_0_V=static_cast<float>(data_tmp[i].Velocity);
|
||||
point_0_A=static_cast<float>(data_tmp[i].Amplitude);
|
||||
point_0_F=static_cast<float>(data_tmp[i].Azimuth);
|
||||
data_tmp[loop_of_point].Use_Flag=1;
|
||||
point_0_R=static_cast<float>(data_tmp[i].range);
|
||||
point_0_V=static_cast<float>(data_tmp[i].velocity);
|
||||
point_0_A=static_cast<float>(data_tmp[i].amplitude);
|
||||
point_0_F=static_cast<float>(data_tmp[i].azimuth);
|
||||
data_tmp[loop_of_point].use_flag=1;
|
||||
}
|
||||
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)//
|
||||
&& point_0_A >= point_1_A)//
|
||||
{
|
||||
data_tmp[i].Use_Flag=1;
|
||||
data_tmp[i].use_flag=1;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -170,16 +173,16 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
|
||||
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
|
||||
&& point_0_A <point_1_A )//
|
||||
{
|
||||
point_0_R=static_cast<float>(data_tmp[i].Range);
|
||||
point_0_V=static_cast<float>(data_tmp[i].Velocity);
|
||||
point_0_A=static_cast<float>(data_tmp[i].Amplitude);
|
||||
point_0_F=static_cast<float>(data_tmp[i].Azimuth);
|
||||
data_tmp[loop_of_point].Use_Flag=1;
|
||||
point_0_R=static_cast<float>(data_tmp[i].range);
|
||||
point_0_V=static_cast<float>(data_tmp[i].velocity);
|
||||
point_0_A=static_cast<float>(data_tmp[i].amplitude);
|
||||
point_0_F=static_cast<float>(data_tmp[i].azimuth);
|
||||
data_tmp[loop_of_point].use_flag=1;
|
||||
}
|
||||
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
|
||||
&& point_0_A >= point_1_A)//
|
||||
{
|
||||
data_tmp[i].Use_Flag=1;
|
||||
data_tmp[i].use_flag=1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -193,7 +196,7 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
|
||||
std::vector <PointRecv>::iterator Iter;
|
||||
for (Iter=data_tmp.begin(); Iter!=data_tmp.end();)
|
||||
{
|
||||
if((*Iter).Use_Flag==1 || (*Iter).Range <R_MIN || (*Iter).Range > R_MAX )
|
||||
if((*Iter).use_flag==1 || (*Iter).range <R_MIN || (*Iter).range > R_MAX )
|
||||
{
|
||||
data_tmp.erase(Iter);
|
||||
Iter=data_tmp.begin();
|
||||
@@ -223,7 +226,8 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
|
||||
|
||||
for (size_t i=0;i<data_input_buff.size();i++)
|
||||
{
|
||||
(*point_recv).push_back(data_input_buff[i]);
|
||||
(*point_recv).push_back(data_input_buff[i]);
|
||||
RDP_RECORD(RdpRecPointCohesionTWS(data_input_buff[i]));
|
||||
}
|
||||
|
||||
std::vector<PointRecv>().swap(data_input_buff);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "rdp_record.h"
|
||||
#include "rdp_record_entity.h"
|
||||
using namespace std;
|
||||
|
||||
int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *data_input, //输入点迹
|
||||
@@ -14,36 +16,36 @@ int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *dat
|
||||
|
||||
for (size_t loop_of_point=0; loop_of_point<data_input->size()-1;loop_of_point++ )
|
||||
{
|
||||
if( (*data_input)[loop_of_point].Use_Flag!=1)
|
||||
if( (*data_input)[loop_of_point].use_flag!=1)
|
||||
{
|
||||
float point_0_R=static_cast<float>((*data_input)[loop_of_point].Range);
|
||||
float point_0_V=static_cast<float>((*data_input)[loop_of_point].Velocity);
|
||||
float point_0_F=static_cast<float>((*data_input)[loop_of_point].Azimuth);
|
||||
float point_0_A=static_cast<float>((*data_input)[loop_of_point].Amplitude);
|
||||
float point_0_R=static_cast<float>((*data_input)[loop_of_point].range);
|
||||
float point_0_V=static_cast<float>((*data_input)[loop_of_point].velocity);
|
||||
float point_0_F=static_cast<float>((*data_input)[loop_of_point].azimuth);
|
||||
float point_0_A=static_cast<float>((*data_input)[loop_of_point].amplitude);
|
||||
for ( size_t i=loop_of_point+1;i<(*data_input).size();i++)
|
||||
{
|
||||
if((*data_input)[loop_of_point].Use_Flag!=1 && (*data_input)[i].Use_Flag!=1)
|
||||
if((*data_input)[loop_of_point].use_flag!=1 && (*data_input)[i].use_flag!=1)
|
||||
{
|
||||
float point_1_R=static_cast<float>((*data_input)[i].Range);
|
||||
float point_1_V=static_cast<float>((*data_input)[i].Velocity);
|
||||
float point_1_F=static_cast<float>((*data_input)[i].Azimuth);
|
||||
float point_1_A=static_cast<float>((*data_input)[i].Amplitude);
|
||||
float point_1_R=static_cast<float>((*data_input)[i].range);
|
||||
float point_1_V=static_cast<float>((*data_input)[i].velocity);
|
||||
float point_1_F=static_cast<float>((*data_input)[i].azimuth);
|
||||
float point_1_A=static_cast<float>((*data_input)[i].amplitude);
|
||||
|
||||
//凝聚条件: 距离、方位接近
|
||||
float delta_F = fabs(point_0_F-point_1_F)<2*PI-fabs(point_0_F-point_1_F) ? fabs(point_0_F-point_1_F) :2*PI-fabs(point_0_F-point_1_F);
|
||||
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)
|
||||
&& point_0_A <point_1_A )
|
||||
{
|
||||
point_0_R=static_cast<float>((*data_input)[i].Range);
|
||||
point_0_V=static_cast<float>((*data_input)[i].Velocity);
|
||||
point_0_A=static_cast<float>((*data_input)[i].Amplitude);
|
||||
point_0_F=static_cast<float>((*data_input)[i].Azimuth);
|
||||
(*data_input)[loop_of_point].Use_Flag=1;
|
||||
point_0_R=static_cast<float>((*data_input)[i].range);
|
||||
point_0_V=static_cast<float>((*data_input)[i].velocity);
|
||||
point_0_A=static_cast<float>((*data_input)[i].amplitude);
|
||||
point_0_F=static_cast<float>((*data_input)[i].azimuth);
|
||||
(*data_input)[loop_of_point].use_flag=1;
|
||||
}
|
||||
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)
|
||||
&& point_0_A >= point_1_A)
|
||||
{
|
||||
(*data_input)[i].Use_Flag=1;
|
||||
(*data_input)[i].use_flag=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +56,7 @@ int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *dat
|
||||
std::vector <PointRecv>::iterator Iter;
|
||||
for (Iter=data_input->begin(); Iter!=data_input->end();)
|
||||
{
|
||||
if((*Iter).Use_Flag==1 || (*Iter).Range <R_MIN || (*Iter).Range > R_MAX )
|
||||
if((*Iter).use_flag==1 || (*Iter).range <R_MIN || (*Iter).range > R_MAX )
|
||||
{
|
||||
data_input->erase(Iter);
|
||||
Iter=data_input->begin();
|
||||
@@ -68,6 +70,7 @@ int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *dat
|
||||
for (size_t i=0;i<data_input->size();i++)
|
||||
{
|
||||
(*point_recv_tas).push_back((*data_input)[i]);
|
||||
RDP_RECORD(RdpRecPointCohesionTAS((*data_input)[i]));
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -18,6 +18,15 @@
|
||||
//日志文件输出目录(相对当前工作目录的路径,目录不存在时自动创建)
|
||||
#define RDP_LOG_DIR ".\\rdp_logs\\"
|
||||
|
||||
/**********************************数据记录调试开关*******************************************/
|
||||
// 数据记录调试功能(结构体 -> SQLite 数据表,ORM 由内置 ormpp 完成)由 CMake 选项
|
||||
// RDP_DATA_RECORD(AUTO/ON/OFF)控制:AUTO 时 Debug 构建启用、Release 构建关闭(交付默认)。
|
||||
// 启用时由构建系统定义 RDP_RECORD_ENABLE,记录模块(rdp_record.cpp + sqlite3)才参与编译;
|
||||
// 关闭时 RDP_RECORD 宏展开为 ((void)0),实参不求值,不做任何记录、不留任何运行开销。
|
||||
// 注意:此处不提供手工开关宏,请在构建配置中选择(详见 docs/data_record_debug.md)。
|
||||
//记录数据库存放目录(相对宿主进程当前工作目录,不存在时自动创建)
|
||||
#define RDP_RECORD_DB_DIR ".\\rdp_records\\"
|
||||
|
||||
//#define MECHANICAL_SCANNING
|
||||
//#define PHASE_SCANNING
|
||||
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
/***********************************数据记录调试模块实现****************************************/
|
||||
// 本文件仅在构建系统定义 RDP_RECORD_ENABLE(CMake 选项 RDP_DATA_RECORD 开启)时参与编译,
|
||||
// 且以 C++20 编译(ormpp 要求);雷达处理代码仍为 C++17,二者通过 rdp_record.h 的
|
||||
// 「仅声明 + 显式实例化」跨标准边界,详见 docs/data_record_debug.md。
|
||||
#ifdef RDP_RECORD_ENABLE
|
||||
|
||||
#include "rdp_record.h"
|
||||
#include "rdp_record_entity.h"
|
||||
#include "parameters.h"
|
||||
#include "rdp_log.h"
|
||||
|
||||
#include <climits>
|
||||
#include <direct.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
#include <vector>
|
||||
|
||||
//第三方库(ormpp / iguana)模板代码在 /W3 下可能产生告警,按 third_party/ormpp/UPSTREAM.md
|
||||
//的约定在此隔离;本文件自身的逻辑仍按工程 /W3 标准编译
|
||||
#pragma warning(push, 0)
|
||||
#include "dbng.hpp"
|
||||
#include "sqlite.hpp"
|
||||
#pragma warning(pop)
|
||||
|
||||
//记录实体写入函数的显式实例化宏(与 rdp_record_entity.h 的 RDP_RECORD_DECLARE 配套使用)
|
||||
#define RDP_RECORD_DEFINE(Entity) \
|
||||
template void rdp_record::Write<Entity>(const Entity&); \
|
||||
template void rdp_record::Write<Entity>(const std::vector<Entity>&)
|
||||
|
||||
namespace
|
||||
{
|
||||
//记录模块内部状态(Pimpl:ormpp / SQLite 类型不出现在 rdp_record.h 中)
|
||||
struct Record_State
|
||||
{
|
||||
ormpp::dbng<ormpp::sqlite> db; //SQLite 连接(单连接,写入时串行使用)
|
||||
std::string path; //当前 db 文件全路径
|
||||
bool ready; //是否已成功打开并完成初始化
|
||||
bool failed; //初始化失败:后续写入全部忽略,不再反复报错
|
||||
std::mutex mtx; //写入串行化(宿主多线程调用时保护单连接)
|
||||
|
||||
Record_State() : ready(false), failed(false) {}
|
||||
};
|
||||
|
||||
std::unique_ptr<Record_State>& State()
|
||||
{
|
||||
//函数内静态:宿主未调用 Data_Process_Factory::Destroy() 时由析构兜底关库
|
||||
static std::unique_ptr<Record_State> s;
|
||||
return s;
|
||||
}
|
||||
|
||||
//当前本地时间字符串,格式 "YYYYMMDD_HHMMSS"(与 rdp_log 的日志文件命名风格一致)
|
||||
bool TimeStamp(char* buf, size_t size)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
struct tm local;
|
||||
if (localtime_s(&local, &now) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return strftime(buf, size, "%Y%m%d_%H%M%S", &local) != 0;
|
||||
}
|
||||
|
||||
//写入失败提示(「每个实体只提示一次」由调用方的静态标志控制)
|
||||
void ReportError(Record_State& st, const char* what)
|
||||
{
|
||||
RDP_LOG << "[rdp_record] " << what << " failed: " << st.db.get_last_error() << std::endl;
|
||||
}
|
||||
|
||||
//确保实体的数据表存在:每个实体类型只尝试建表一次(失败后该实体永久跳过)
|
||||
//注意:ormpp 的 ormpp_auto_key 定义在全局命名空间(third_party/ormpp/ormpp/entity.hpp),
|
||||
// 传该参数可在建表时登记自增主键,使后续插入自动跳过 id 列
|
||||
template <typename T>
|
||||
bool EnsureTable(Record_State& st)
|
||||
{
|
||||
static const bool created = st.db.create_datatable<T>(::ormpp_auto_key{"id"});
|
||||
return created;
|
||||
}
|
||||
|
||||
//写入前置处理:自动建表;返回 true 表示可以向该表写入
|
||||
template <typename T>
|
||||
bool PrepareWrite(Record_State& st)
|
||||
{
|
||||
static bool reported = false;
|
||||
if (EnsureTable<T>(st))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!reported)
|
||||
{
|
||||
reported = true;
|
||||
ReportError(st, "create table");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace rdp_record
|
||||
{
|
||||
bool Init()
|
||||
{
|
||||
std::unique_ptr<Record_State>& sp = State();
|
||||
if (sp.get() != 0)
|
||||
{
|
||||
return sp->ready; //已经初始化过(含失败的幂等返回)
|
||||
}
|
||||
|
||||
sp.reset(new Record_State());
|
||||
Record_State& st = *sp;
|
||||
|
||||
//创建记录目录(目录已存在时 _mkdir 返回 EEXIST,忽略;逻辑与 rdp_log.cpp 一致)
|
||||
std::string dir = RDP_RECORD_DB_DIR;
|
||||
while (dir.size() > 0 && (dir[dir.size() - 1] == '\\' || dir[dir.size() - 1] == '/'))
|
||||
{
|
||||
dir.erase(dir.size() - 1);
|
||||
}
|
||||
if (dir.size() > 0)
|
||||
{
|
||||
_mkdir(dir.c_str());
|
||||
}
|
||||
|
||||
//按当前日期+时间生成记录库文件名:rdp_record_YYYYMMDD_HHMMSS.db
|
||||
char stamp[32] = {0};
|
||||
if (!TimeStamp(stamp, sizeof(stamp)))
|
||||
{
|
||||
RDP_LOG << "[rdp_record] cannot get local time, recording disabled" << std::endl;
|
||||
st.failed = true;
|
||||
return false;
|
||||
}
|
||||
st.path = RDP_RECORD_DB_DIR;
|
||||
st.path += "rdp_record_";
|
||||
st.path += stamp;
|
||||
st.path += ".db";
|
||||
|
||||
if (!st.db.connect(st.path))
|
||||
{
|
||||
RDP_LOG << "[rdp_record] cannot open database: " << st.path << std::endl;
|
||||
st.failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//调试用途的性能取舍:关闭同步刷盘(提交时不再 fsync),崩溃时可能丢失尾部数据;
|
||||
//如需崩溃安全可改为 PRAGMA synchronous=NORMAL(或在 sqlite3 层自行开启 WAL)。
|
||||
//注意:ormpp 的 execute() 只接受「不返回结果行」的语句,故不在此设置 journal_mode。
|
||||
st.db.execute("PRAGMA synchronous=OFF;");
|
||||
|
||||
st.ready = true;
|
||||
RDP_LOG << "[rdp_record] enabled, database: " << st.path << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
std::unique_ptr<Record_State>& sp = State();
|
||||
if (sp.get() == 0 || !sp->ready)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(sp->mtx);
|
||||
RDP_LOG << "[rdp_record] shutdown, database: " << sp->path << std::endl;
|
||||
sp->db.disconnect();
|
||||
sp->ready = false; //path 保留,便于宿主在关库后仍能取到本次记录库路径
|
||||
}
|
||||
|
||||
std::string db_path()
|
||||
{
|
||||
std::unique_ptr<Record_State>& sp = State();
|
||||
return (sp.get() == 0) ? std::string() : sp->path;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Write(const T& v)
|
||||
{
|
||||
std::unique_ptr<Record_State>& sp = State();
|
||||
if (sp.get() == 0 || !sp->ready || sp->failed)
|
||||
{
|
||||
return; //未初始化 / 初始化失败 / 已关库:静默忽略
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(sp->mtx);
|
||||
if (!PrepareWrite<T>(*sp))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//写入失败时每个实体只提示一次:不重试、不抛出、不影响雷达数据处理
|
||||
static bool reported = false;
|
||||
if (sp->db.insert(v) == INT_MIN && !reported)
|
||||
{
|
||||
reported = true;
|
||||
ReportError(*sp, "insert");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Write(const std::vector<T>& v)
|
||||
{
|
||||
if (v.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<Record_State>& sp = State();
|
||||
if (sp.get() == 0 || !sp->ready || sp->failed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(sp->mtx);
|
||||
if (!PrepareWrite<T>(*sp))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//写入失败时每个实体只提示一次:不重试、不抛出、不影响雷达数据处理
|
||||
static bool reported = false;
|
||||
if (sp->db.insert(v) == INT_MIN && !reported)
|
||||
{
|
||||
reported = true;
|
||||
ReportError(*sp, "insert");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************记录实体注册区*********************************************/
|
||||
// 新增记录实体时在此追加两行(字段顺序必须与 rdp_record_entity.h 中的结构体声明顺序一致):
|
||||
// YLT_REFL(结构体名, 字段1, 字段2, ...);
|
||||
// RDP_RECORD_DEFINE(结构体名);
|
||||
|
||||
//输入点迹
|
||||
YLT_REFL(RdpRecPoint, id,point_id,frame_id,range,azimuth, velocity,amplitude ,height,SNR,RCS,
|
||||
encoder_value,amp_asv_point,prn_fftn_point,CPI_time,GNSS_time,use_flag,azimuth_beam_index,pitch_beam_index,PRF_index,freq_index,point_type,point_section_asso);
|
||||
RDP_RECORD_DEFINE(RdpRecPoint);
|
||||
|
||||
//TWS凝聚后点迹
|
||||
YLT_REFL(RdpRecPointCohesionTWS, id,point_id,frame_id,range,azimuth, velocity,amplitude ,height,SNR,RCS,
|
||||
encoder_value,amp_asv_point,prn_fftn_point,CPI_time,GNSS_time,use_flag,azimuth_beam_index,pitch_beam_index,PRF_index,freq_index,point_type,point_section_asso);
|
||||
RDP_RECORD_DEFINE(RdpRecPointCohesionTWS);
|
||||
|
||||
//TAS凝聚后点迹
|
||||
YLT_REFL(RdpRecPointCohesionTAS, id,point_id,frame_id,range,azimuth, velocity,amplitude ,height,SNR,RCS,
|
||||
encoder_value,amp_asv_point,prn_fftn_point,CPI_time,GNSS_time,use_flag,azimuth_beam_index,pitch_beam_index,PRF_index,freq_index,point_type,point_section_asso);
|
||||
RDP_RECORD_DEFINE(RdpRecPointCohesionTAS);
|
||||
|
||||
// 示例②:本圈输出的航迹
|
||||
YLT_REFL(RdpRecTrackOut, id, track_index, track_mode, point_sum, flag_point, flag_direction,
|
||||
gnss_time_ms, track_time_s, x, y, z, v_x, v_y, v_z, range_m, azi_deg, elev_deg,
|
||||
range_v_mps, snr, rcs);
|
||||
RDP_RECORD_DEFINE(RdpRecTrackOut);
|
||||
|
||||
#endif // RDP_RECORD_ENABLE
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef RDP_RECORD_H
|
||||
#define RDP_RECORD_H
|
||||
#pragma once
|
||||
/***********************************数据记录调试模块********************************************/
|
||||
// 功能(完整说明见 docs/data_record_debug.md):
|
||||
// 1) 把结构体数据按「一个记录实体一张表」的方式写入 SQLite 数据库,结构体到数据表的映射由
|
||||
// 内置的 ormpp 完成(third_party/ormpp,见该目录 UPSTREAM.md);
|
||||
// 2) 整个功能由构建开关控制:CMake 选项 RDP_DATA_RECORD(AUTO/ON/OFF)开启时由构建系统定义
|
||||
// RDP_RECORD_ENABLE,rdp_record.cpp 与 sqlite3 amalgamation 才参与编译;关闭(交付/Release
|
||||
// 构建默认)时本模块完全不编译,RDP_RECORD / RDP_RECORD_BATCH 展开为 ((void)0)、实参不求值,
|
||||
// 不留任何代码与运行开销;
|
||||
// 3) 生命周期:Data_Process_Factory::GetB() 调用 rdp_record::Init()(建目录、开库),
|
||||
// Data_Process_Factory::Destroy() 调用 rdp_record::Shutdown()(关库);Init 之前、初始化
|
||||
// 失败之后或 Shutdown 之后的写入一律静默忽略,不影响雷达数据处理;
|
||||
// 4) 记录点用法(新增数据 = 加结构体 + 注册 + 一行记录点,配方见 docs/data_record_debug.md):
|
||||
// 单条:RDP_RECORD(RdpRecPointTws{0, 值1, 值2, ...});
|
||||
// 批量:RDP_RECORD_BATCH(recs); // recs 为 std::vector<某记录实体>
|
||||
// 需要语句/局部变量的记录点请整体用 #ifdef RDP_RECORD_ENABLE ... #endif 包裹,
|
||||
// 保证关闭记录功能时既无代码也无「变量未使用」告警;
|
||||
// 5) 记录实体类型必须先在 rdp_record_entity.h 中声明并注册,否则链接期报 LNK2019
|
||||
// (extern template 已阻止 C++17 调用方隐式实例化)。
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace rdp_record
|
||||
{
|
||||
bool Init(); //初始化记录库(由 Data_Process_Factory::GetB() 调用)
|
||||
void Shutdown(); //关闭记录库(由 Data_Process_Factory::Destroy() 调用)
|
||||
std::string db_path(); //当前记录库文件全路径(未启用或未成功打开时返回空串)
|
||||
|
||||
//写入单条记录实体(T 为记录实体类型,须在 rdp_record.cpp 中显式实例化)
|
||||
template <typename T> void Write(const T& v);
|
||||
//批量写入同一记录实体的多条记录(内部使用事务)
|
||||
template <typename T> void Write(const std::vector<T>& v);
|
||||
}
|
||||
|
||||
#ifdef RDP_RECORD_ENABLE
|
||||
//记录宏:把指定记录实体写入数据库(首次写入该实体时自动建表)
|
||||
#define RDP_RECORD(...) ::rdp_record::Write(__VA_ARGS__)
|
||||
//批量记录宏:把 std::vector<记录实体> 一次性写入(每批一个事务)
|
||||
#define RDP_RECORD_BATCH(...) ::rdp_record::Write(__VA_ARGS__)
|
||||
#else
|
||||
//记录功能关闭:记录模块不参与编译,宏展开为空(实参不求值)
|
||||
#define RDP_RECORD(...) ((void)0)
|
||||
#define RDP_RECORD_BATCH(...) ((void)0)
|
||||
namespace rdp_record
|
||||
{
|
||||
inline bool Init() { return false; }
|
||||
inline void Shutdown() {}
|
||||
inline std::string db_path() { return std::string(); }
|
||||
template <typename T> inline void Write(const T&) {}
|
||||
template <typename T> inline void Write(const std::vector<T>&) {}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RDP_RECORD_H
|
||||
@@ -0,0 +1,232 @@
|
||||
#ifndef RDP_RECORD_ENTITY_H
|
||||
#define RDP_RECORD_ENTITY_H
|
||||
#pragma once
|
||||
/*******************************数据记录实体(=数据库表)定义区*********************************/
|
||||
// 使用说明(完整配方见 docs/data_record_debug.md):
|
||||
// 1) 新增一张记录表:在本文件「记录实体定义区」添加结构体,并在其后紧跟一行
|
||||
// RDP_RECORD_DECLARE(结构体名);
|
||||
// 再到 rdp_record.cpp 的「记录实体注册区」添加两行
|
||||
// YLT_REFL(结构体名, 字段1, 字段2, ...); // 顺序必须与结构体声明顺序一致
|
||||
// RDP_RECORD_DEFINE(结构体名);
|
||||
// 最后在需要记录的位置写 RDP_RECORD(结构体名{0, 值1, 值2, ...}); 或 RDP_RECORD_BATCH(向量);
|
||||
// 建表无需任何代码:首次写入该实体时自动 CREATE TABLE IF NOT EXISTS;
|
||||
// 2) 命名与建表约定:结构体定义在全局命名空间(勿放入 namespace),结构体名即数据库表名
|
||||
// (ormpp 取「短结构体名」),建议以 RdpRec 开头;首个字段固定为 int64_t id(自增主键,
|
||||
// 写入时固定填 0,由 SQLite 分配);
|
||||
// 3) 字段类型仅限标量:int / int64_t / float / double / bool(SQLite 侧映射 INTEGER / INTEGER /
|
||||
// FLOAT / DOUBLE / INTEGER)。禁止使用数组(含 float speed_dim[129]、char[N] 等)——ormpp 对
|
||||
// C 数组按 C 字符串语义(strlen)绑定,会读越界或写入错误数据;大批量数组请另建「元素展开表」
|
||||
// 或改用 blob 字段;
|
||||
// 4) 表结构变更(增删字段)不会迁移已存在的旧表:本模块每次运行生成新的 db 文件,天然规避。
|
||||
#include "rdp_record.h"
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "struct.h"
|
||||
|
||||
//记录实体的写入函数实例化声明宏:声明「该实体的 Write 在 rdp_record.cpp 中显式实例化」,
|
||||
//阻止 C++17 调用方隐式实例化(本模块的 ormpp 实现以 C++20 编译,见 docs/data_record_debug.md)
|
||||
#define RDP_RECORD_DECLARE(Entity) \
|
||||
extern template void rdp_record::Write<Entity>(const Entity&); \
|
||||
extern template void rdp_record::Write<Entity>(const std::vector<Entity>&)
|
||||
|
||||
// ============================ 记录实体定义区(在此新增结构体) ============================
|
||||
// 输入点迹 —— 输入的原始点迹数据
|
||||
struct RdpRecPoint
|
||||
{
|
||||
int64_t id; //自增主键(写入时固定填 0)
|
||||
long long point_id; //点迹ID
|
||||
long long frame_id; //帧ID
|
||||
double range; //径向距离
|
||||
double azimuth; //方位 (弧度)
|
||||
double velocity ; //速度
|
||||
double amplitude ; //幅度
|
||||
double height; //高度
|
||||
double SNR; //信噪比
|
||||
double RCS; //目标RCS
|
||||
float encoder_value; //码盘值
|
||||
float amp_asv_point; //归一化幅度dB
|
||||
float prn_fftn_point; //点迹速度维FFT点数
|
||||
long long CPI_time; //CPI时间
|
||||
long long GNSS_time; //GNSS时间
|
||||
int use_flag; //点迹使用标志 1使用 0未使用
|
||||
int azimuth_beam_index; //方位波位号
|
||||
int pitch_beam_index; //俯仰波位号
|
||||
int PRF_index; //PRF号
|
||||
int freq_index; //频点
|
||||
int point_type; //TAS 1 TWS0
|
||||
int point_section_asso; //点迹区
|
||||
|
||||
RdpRecPoint(){
|
||||
id = 0;
|
||||
}
|
||||
|
||||
RdpRecPoint(PointRecv& p){
|
||||
id = 0;
|
||||
point_id = p.point_id;
|
||||
frame_id = p.frame_id;
|
||||
range = p.range;
|
||||
azimuth = p.azimuth;
|
||||
velocity = p.velocity;
|
||||
amplitude = p.amplitude;
|
||||
height = p.height;
|
||||
SNR = p.SNR;
|
||||
RCS = p.RCS;
|
||||
encoder_value = p.encoder_value;
|
||||
amp_asv_point = p.amp_asv_point;
|
||||
prn_fftn_point = p.prn_fftn_point;
|
||||
CPI_time = p.CPI_time;
|
||||
GNSS_time = p.GNSS_time;
|
||||
use_flag = p.use_flag;
|
||||
azimuth_beam_index = p.azimuth_beam_index;
|
||||
pitch_beam_index = p.pitch_beam_index;
|
||||
PRF_index = p.PRF_index;
|
||||
freq_index = p.freq_index;
|
||||
point_type = p.point_type;
|
||||
point_section_asso = p.point_section_asso;
|
||||
}
|
||||
};
|
||||
RDP_RECORD_DECLARE(RdpRecPoint);
|
||||
|
||||
// TWS凝聚后点迹 —— 凝聚后的点迹数据
|
||||
struct RdpRecPointCohesionTWS
|
||||
{
|
||||
int64_t id; //自增主键(写入时固定填 0)
|
||||
long long point_id; //点迹ID
|
||||
long long frame_id; //帧ID
|
||||
double range; //径向距离
|
||||
double azimuth; //方位 (弧度)
|
||||
double velocity ; //速度
|
||||
double amplitude ; //幅度
|
||||
double height; //高度
|
||||
double SNR; //信噪比
|
||||
double RCS; //目标RCS
|
||||
float encoder_value; //码盘值
|
||||
float amp_asv_point; //归一化幅度dB
|
||||
float prn_fftn_point; //点迹速度维FFT点数
|
||||
long long CPI_time; //CPI时间
|
||||
long long GNSS_time; //GNSS时间
|
||||
int use_flag; //点迹使用标志 1使用 0未使用
|
||||
int azimuth_beam_index; //方位波位号
|
||||
int pitch_beam_index; //俯仰波位号
|
||||
int PRF_index; //PRF号
|
||||
int freq_index; //频点
|
||||
int point_type; //TAS 1 TWS0
|
||||
int point_section_asso; //点迹区
|
||||
|
||||
RdpRecPointCohesionTWS(){
|
||||
id = 0;
|
||||
}
|
||||
|
||||
RdpRecPointCohesionTWS(PointRecv& p){
|
||||
id = 0;
|
||||
point_id = p.point_id;
|
||||
frame_id = p.frame_id;
|
||||
range = p.range;
|
||||
azimuth = p.azimuth;
|
||||
velocity = p.velocity;
|
||||
amplitude = p.amplitude;
|
||||
height = p.height;
|
||||
SNR = p.SNR;
|
||||
RCS = p.RCS;
|
||||
encoder_value = p.encoder_value;
|
||||
amp_asv_point = p.amp_asv_point;
|
||||
prn_fftn_point = p.prn_fftn_point;
|
||||
CPI_time = p.CPI_time;
|
||||
GNSS_time = p.GNSS_time;
|
||||
use_flag = p.use_flag;
|
||||
azimuth_beam_index = p.azimuth_beam_index;
|
||||
pitch_beam_index = p.pitch_beam_index;
|
||||
PRF_index = p.PRF_index;
|
||||
freq_index = p.freq_index;
|
||||
point_type = p.point_type;
|
||||
point_section_asso = p.point_section_asso;
|
||||
}
|
||||
};
|
||||
RDP_RECORD_DECLARE(RdpRecPointCohesionTWS);
|
||||
|
||||
// TAS凝聚后点迹 —— 凝聚后的点迹数据
|
||||
struct RdpRecPointCohesionTAS
|
||||
{
|
||||
int64_t id; //自增主键(写入时固定填 0)
|
||||
long long point_id; //点迹ID
|
||||
long long frame_id; //帧ID
|
||||
double range; //径向距离
|
||||
double azimuth; //方位 (弧度)
|
||||
double velocity ; //速度
|
||||
double amplitude ; //幅度
|
||||
double height; //高度
|
||||
double SNR; //信噪比
|
||||
double RCS; //目标RCS
|
||||
float encoder_value; //码盘值
|
||||
float amp_asv_point; //归一化幅度dB
|
||||
float prn_fftn_point; //点迹速度维FFT点数
|
||||
long long CPI_time; //CPI时间
|
||||
long long GNSS_time; //GNSS时间
|
||||
int use_flag; //点迹使用标志 1使用 0未使用
|
||||
int azimuth_beam_index; //方位波位号
|
||||
int pitch_beam_index; //俯仰波位号
|
||||
int PRF_index; //PRF号
|
||||
int freq_index; //频点
|
||||
int point_type; //TAS 1 TWS0
|
||||
int point_section_asso; //点迹区
|
||||
|
||||
RdpRecPointCohesionTAS(){
|
||||
id = 0;
|
||||
}
|
||||
|
||||
RdpRecPointCohesionTAS(PointRecv& p){
|
||||
id = 0;
|
||||
point_id = p.point_id;
|
||||
frame_id = p.frame_id;
|
||||
range = p.range;
|
||||
azimuth = p.azimuth;
|
||||
velocity = p.velocity;
|
||||
amplitude = p.amplitude;
|
||||
height = p.height;
|
||||
SNR = p.SNR;
|
||||
RCS = p.RCS;
|
||||
encoder_value = p.encoder_value;
|
||||
amp_asv_point = p.amp_asv_point;
|
||||
prn_fftn_point = p.prn_fftn_point;
|
||||
CPI_time = p.CPI_time;
|
||||
GNSS_time = p.GNSS_time;
|
||||
use_flag = p.use_flag;
|
||||
azimuth_beam_index = p.azimuth_beam_index;
|
||||
pitch_beam_index = p.pitch_beam_index;
|
||||
PRF_index = p.PRF_index;
|
||||
freq_index = p.freq_index;
|
||||
point_type = p.point_type;
|
||||
point_section_asso = p.point_section_asso;
|
||||
}
|
||||
};
|
||||
RDP_RECORD_DECLARE(RdpRecPointCohesionTAS);
|
||||
|
||||
// 示例②:本圈输出的航迹 —— 记录点位于 data_process.cpp::track_process()「数据记录示例②」处,
|
||||
// TWS/TAS 共用一张表并用 track_mode 区分;每行取输出数组的 [i][0]
|
||||
struct RdpRecTrackOut
|
||||
{
|
||||
int64_t id; //自增主键(写入时固定填 0)
|
||||
int track_index; //航迹号
|
||||
int track_mode; //跟踪模式 TAS 1/TWS 0
|
||||
int point_sum; //该行点数(起批输出为历史点数 L)
|
||||
int flag_point; //1实点/0补点
|
||||
int flag_direction; //1靠近/0远离
|
||||
int64_t gnss_time_ms; //GNSS 时间(ms)
|
||||
float track_time_s; //航迹时间(s)
|
||||
float x; //x坐标(m)
|
||||
float y; //y坐标(m)
|
||||
float z; //z坐标(m)
|
||||
float v_x; //x速度(m/s)
|
||||
float v_y; //y速度(m/s)
|
||||
float v_z; //z速度(m/s)
|
||||
float range_m; //径向距离(m)
|
||||
float azi_deg; //方位(度)
|
||||
float elev_deg; //俯仰(度)
|
||||
float range_v_mps; //速度(m/s,接口字段 Range_V)
|
||||
float snr; //信噪比
|
||||
float rcs; //RCS
|
||||
};
|
||||
RDP_RECORD_DECLARE(RdpRecTrackOut);
|
||||
|
||||
#endif // RDP_RECORD_ENTITY_H
|
||||
@@ -9,27 +9,28 @@ using namespace std;
|
||||
// 点迹数据结构
|
||||
struct PointRecv
|
||||
{
|
||||
double Range; //径向距离
|
||||
double Azimuth; //方位 (弧度)
|
||||
double Velocity ; //速度
|
||||
double Amplitude ; //幅度
|
||||
double Height; //高度
|
||||
double snr; //信噪比
|
||||
long long point_id; //点迹ID
|
||||
long long frame_id; //帧ID
|
||||
double range; //径向距离
|
||||
double azimuth; //方位 (弧度)
|
||||
double velocity ; //速度
|
||||
double amplitude ; //幅度
|
||||
double height; //高度
|
||||
double SNR; //信噪比
|
||||
double RCS; //目标RCS
|
||||
float encoder_value; //码盘值
|
||||
float amp_asv_point; //归一化幅度dB
|
||||
float prn_fftn_point; //点迹速度维FFT点数
|
||||
long long CPI_Time; //CPI时间
|
||||
long long GNSS_time; //GNSS时间
|
||||
int Point_index; //点迹号 1~50
|
||||
int Point_Sum; //点迹总数
|
||||
int Use_Flag; //点迹使用标志 1使用 0未使用
|
||||
int beam_index; //波位号
|
||||
long long CPI_time; //CPI时间
|
||||
long long GNSS_time; //GNSS时间
|
||||
int use_flag; //点迹使用标志 1使用 0未使用
|
||||
int azimuth_beam_index; //方位波位号
|
||||
int pitch_beam_index; //俯仰波位号
|
||||
int PRF_index; //PRF号
|
||||
int Freq_index; //频点
|
||||
int track_mode; //TAS 1 TWS0
|
||||
int freq_index; //频点
|
||||
int point_type; //TAS 1 TWS0
|
||||
int point_section_asso; //点迹区
|
||||
|
||||
int pitch_num; // 俯仰波位号
|
||||
|
||||
float speed_dim[129]; // 目标的十字星数据,目标点速度维129个点
|
||||
float range_dim[8]; // 目标的十字星数据,目标点距离维8个点
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
|
||||
std::vector <PointRecv>::iterator Iter;
|
||||
for (Iter= point_process.begin(); Iter!=point_process.end();)
|
||||
{
|
||||
if((*Iter).Use_Flag==1 )
|
||||
if((*Iter).use_flag==1 )
|
||||
{
|
||||
point_process.erase(Iter);
|
||||
Iter=point_process.begin();
|
||||
@@ -443,7 +443,7 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
{
|
||||
|
||||
//时间差
|
||||
double delta_T = (point_process[loop_of_point].CPI_Time - (*trust_track)[loop_of_track].T_track)/1000.0;
|
||||
double delta_T = (point_process[loop_of_point].CPI_time - (*trust_track)[loop_of_track].T_track)/1000.0;
|
||||
//航迹X P
|
||||
double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6];
|
||||
memcpy(X1,(*trust_track)[loop_of_track].X1,6*sizeof(double));
|
||||
@@ -457,12 +457,12 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
double r_track = sqrt(pow((*trust_track)[loop_of_track].X[0],2)+pow((*trust_track)[loop_of_track].X[3],2));
|
||||
|
||||
//量测信息
|
||||
double Z[3] = {point_process[loop_of_point].Range,
|
||||
point_process[loop_of_point].Azimuth,
|
||||
point_process[loop_of_point].Velocity};
|
||||
double Z[3] = {point_process[loop_of_point].range,
|
||||
point_process[loop_of_point].azimuth,
|
||||
point_process[loop_of_point].velocity};
|
||||
double prt = point_process[loop_of_point].PRF_index;
|
||||
double freq_ind = point_process[loop_of_point].Freq_index;
|
||||
double h_point = point_process[loop_of_point].Height;
|
||||
double freq_ind = point_process[loop_of_point].freq_index;
|
||||
double h_point = point_process[loop_of_point].height;
|
||||
|
||||
//计算统计距离d
|
||||
double d1,d2,d3;
|
||||
@@ -498,8 +498,8 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
double& t_x0 = (*trust_track)[loop_of_track].X[0];
|
||||
double& t_y0 = (*trust_track)[loop_of_track].X[3];
|
||||
double a_track = atan2(t_y0, t_x0); //航迹方位角
|
||||
double& r_point = point_process[loop_of_point].Range; //点迹距离
|
||||
double a_delta = abs(a_track - point_process[loop_of_point].Azimuth); //方位差
|
||||
double& r_point = point_process[loop_of_point].range; //点迹距离
|
||||
double a_delta = abs(a_track - point_process[loop_of_point].azimuth); //方位差
|
||||
if(a_delta > PI){
|
||||
a_delta = 2*PI - a_delta;
|
||||
}
|
||||
@@ -567,9 +567,9 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
double d3=associated_info[min_index-1].d3;
|
||||
|
||||
//卡尔曼滤波
|
||||
double Z[3]={point_process[point_index-1].Range,point_process[point_index-1].Azimuth,point_process[point_index-1].Velocity};
|
||||
double Z[3]={point_process[point_index-1].range,point_process[point_index-1].azimuth,point_process[point_index-1].velocity};
|
||||
double prt = point_process[point_index-1].PRF_index;
|
||||
double freq_ind = point_process[point_index-1].Freq_index;
|
||||
double freq_ind = point_process[point_index-1].freq_index;
|
||||
double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6];
|
||||
memcpy(X1,(*trust_track)[track_index-1].X1,6*sizeof(double));
|
||||
memcpy(X2,(*trust_track)[track_index-1].X2,6*sizeof(double));
|
||||
@@ -578,7 +578,7 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
memcpy(P2,(*trust_track)[track_index-1].P2,6*6*sizeof(double));
|
||||
memcpy(P3,(*trust_track)[track_index-1].P3,6*6*sizeof(double));
|
||||
double v_track = sqrt(pow((*trust_track)[track_index-1].X[1],2)+pow((*trust_track)[track_index-1].X[4],2));
|
||||
double delta_T = (point_process[point_index-1].CPI_Time - (*trust_track)[track_index-1].T_track)/1000.0;
|
||||
double delta_T = (point_process[point_index-1].CPI_time - (*trust_track)[track_index-1].T_track)/1000.0;
|
||||
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3,Work_Parameter);
|
||||
|
||||
@@ -645,24 +645,24 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
}
|
||||
|
||||
//更新航迹时间
|
||||
(*trust_track)[track_index-1].T_track = point_process[point_index-1].CPI_Time;
|
||||
(*trust_track)[track_index-1].T_track = point_process[point_index-1].CPI_time;
|
||||
(*trust_track)[track_index-1].GNSS_time = point_process[point_index-1].GNSS_time;
|
||||
|
||||
//更新关联上的点迹信息
|
||||
(*trust_track)[track_index-1].range_point=point_process[point_index-1].Range;
|
||||
(*trust_track)[track_index-1].azi_point=point_process[point_index-1].Azimuth/PI*180;
|
||||
(*trust_track)[track_index-1].elev_point= asin(point_process[point_index-1].Height/point_process[point_index-1].Range)/PI*180;
|
||||
(*trust_track)[track_index-1].vr_point=point_process[point_index-1].Velocity;
|
||||
(*trust_track)[track_index-1].range_point=point_process[point_index-1].range;
|
||||
(*trust_track)[track_index-1].azi_point=point_process[point_index-1].azimuth/PI*180;
|
||||
(*trust_track)[track_index-1].elev_point= asin(point_process[point_index-1].height/point_process[point_index-1].range)/PI*180;
|
||||
(*trust_track)[track_index-1].vr_point=point_process[point_index-1].velocity;
|
||||
(*trust_track)[track_index-1].point_type = 0;
|
||||
(*trust_track)[track_index-1].prf_point = point_process[point_index-1].PRF_index;
|
||||
(*trust_track)[track_index-1].snr_point = point_process[point_index-1].snr;
|
||||
(*trust_track)[track_index-1].Amplitude= point_process[point_index-1].Amplitude; //幅度
|
||||
(*trust_track)[track_index-1].snr_point = point_process[point_index-1].SNR;
|
||||
(*trust_track)[track_index-1].Amplitude= point_process[point_index-1].amplitude; //幅度
|
||||
|
||||
(*trust_track)[track_index-1].RCS = point_process[point_index-1].RCS;
|
||||
(*trust_track)[track_index-1].amp_asv_point = point_process[point_index-1].amp_asv_point;
|
||||
(*trust_track)[track_index-1].prn_fftn_point = point_process[point_index-1].prn_fftn_point;
|
||||
|
||||
(*trust_track)[track_index-1].pitch_num = point_process[point_index-1].pitch_num;
|
||||
(*trust_track)[track_index-1].pitch_num = point_process[point_index-1].pitch_beam_index;
|
||||
|
||||
std::memcpy((*trust_track)[track_index-1].speed_dim, point_process[point_index-1].speed_dim, sizeof((*trust_track)[track_index-1].speed_dim));
|
||||
std::memcpy((*trust_track)[track_index-1].range_dim, point_process[point_index-1].range_dim, sizeof((*trust_track)[track_index-1].range_dim));
|
||||
@@ -701,7 +701,7 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
associated_num=static_cast<int>(associated_info.size());
|
||||
|
||||
//在接收点迹中 将关联上的点迹的使用标记位置为1 等待删除
|
||||
point_process[point_index-1].Use_Flag = 1;
|
||||
point_process[point_index-1].use_flag = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,7 +882,7 @@ void Track_Asso:: track_hight_update(int updata_
|
||||
if(updata_track_index<=static_cast<int>(trust_track->size()) && asso_point_index<=static_cast<int>(point_process.size()))
|
||||
{
|
||||
|
||||
(*trust_track)[updata_track_index-1].Hight_smooth.push_back(point_process[asso_point_index-1].Height);
|
||||
(*trust_track)[updata_track_index-1].Hight_smooth.push_back(point_process[asso_point_index-1].height);
|
||||
|
||||
int height_win_length;
|
||||
|
||||
|
||||
@@ -223,14 +223,14 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
|
||||
//量测信息
|
||||
// double Z[2]={point_process[loop_of_point].Range*cos(point_process[loop_of_point].Azimuth),
|
||||
// point_process[loop_of_point].Range*sin(point_process[loop_of_point].Azimuth)};
|
||||
double Z[3] = {point_process[loop_of_point].Range,
|
||||
point_process[loop_of_point].Azimuth,
|
||||
point_process[loop_of_point].Velocity};
|
||||
double Z[3] = {point_process[loop_of_point].range,
|
||||
point_process[loop_of_point].azimuth,
|
||||
point_process[loop_of_point].velocity};
|
||||
// double v_point = point_process[loop_of_point].Velocity;
|
||||
double prt = point_process[loop_of_point].PRF_index;
|
||||
double freq_ind = point_process[loop_of_point].Freq_index;
|
||||
double delta_T = (point_process[loop_of_point].CPI_Time - T_track)/1000.0;
|
||||
double h_point = point_process[loop_of_point].Height;
|
||||
double freq_ind = point_process[loop_of_point].freq_index;
|
||||
double delta_T = (point_process[loop_of_point].CPI_time - T_track)/1000.0;
|
||||
double h_point = point_process[loop_of_point].height;
|
||||
|
||||
//计算统计距离d
|
||||
double d1,d2,d3;
|
||||
@@ -329,13 +329,13 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
|
||||
double d3=associated_info[min_index-1].d3;
|
||||
|
||||
//卡尔曼滤波
|
||||
double Z[3]={point_process[point_index-1].Range,point_process[point_index-1].Azimuth,point_process[point_index-1].Velocity};
|
||||
double Z[3]={point_process[point_index-1].range,point_process[point_index-1].azimuth,point_process[point_index-1].velocity};
|
||||
double prt = point_process[point_index-1].PRF_index;
|
||||
double freq_ind = point_process[point_index-1].Freq_index;
|
||||
double freq_ind = point_process[point_index-1].freq_index;
|
||||
// double Z[2];
|
||||
// Z[0]=point_process[point_index-1].Range*cos(point_process[point_index-1].Azimuth);
|
||||
// Z[1]=point_process[point_index-1].Range*sin(point_process[point_index-1].Azimuth);
|
||||
double delta_T = (point_process[point_index-1].CPI_Time - T_track)/1000.0;
|
||||
double delta_T = (point_process[point_index-1].CPI_time - T_track)/1000.0;
|
||||
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3,Work_Parameter);
|
||||
|
||||
@@ -401,19 +401,19 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
|
||||
}
|
||||
|
||||
//更新航迹时间
|
||||
(*trust_track).T_track = point_process[point_index-1].CPI_Time;
|
||||
(*trust_track).T_track = point_process[point_index-1].CPI_time;
|
||||
|
||||
//更新关联上的点迹信息
|
||||
(*trust_track).range_point=point_process[point_index-1].Range;
|
||||
(*trust_track).azi_point=point_process[point_index-1].Azimuth/PI*180;
|
||||
(*trust_track).elev_point= asin(point_process[point_index-1].Height/point_process[point_index-1].Range)/PI*180;
|
||||
(*trust_track).vr_point=point_process[point_index-1].Velocity;
|
||||
(*trust_track).range_point=point_process[point_index-1].range;
|
||||
(*trust_track).azi_point=point_process[point_index-1].azimuth/PI*180;
|
||||
(*trust_track).elev_point= asin(point_process[point_index-1].height/point_process[point_index-1].range)/PI*180;
|
||||
(*trust_track).vr_point=point_process[point_index-1].velocity;
|
||||
(*trust_track).point_type = 1;
|
||||
(*trust_track).prf_point = point_process[point_index-1].PRF_index;
|
||||
(*trust_track).snr_point = point_process[point_index-1].snr;
|
||||
(*trust_track).snr_point = point_process[point_index-1].SNR;
|
||||
(*trust_track).Extrapolate_round = 0; //连续未用实点更新时间
|
||||
(*trust_track).point_flag=1; //实点
|
||||
(*trust_track).Amplitude= point_process[point_index-1].Amplitude; //幅度
|
||||
(*trust_track).Amplitude= point_process[point_index-1].amplitude; //幅度
|
||||
(*trust_track).associate_point_number = (*trust_track).associate_point_number+1; //关联点数+1
|
||||
(*trust_track).RCS = point_process[point_index-1].RCS;
|
||||
|
||||
@@ -648,7 +648,7 @@ void Track_Asso_Direct_Tracking::track_hight_update(int asso_point
|
||||
)
|
||||
{
|
||||
|
||||
(*trust_track).Hight_smooth.push_back(point_process[asso_point_index-1].Height);
|
||||
(*trust_track).Hight_smooth.push_back(point_process[asso_point_index-1].height);
|
||||
|
||||
int height_win_length;
|
||||
if((*trust_track).range_point <= 1000)
|
||||
|
||||
@@ -289,19 +289,19 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
//量测信息
|
||||
// double Z[2]={point_process[loop_of_point].Range*cos(point_process[loop_of_point].Azimuth),
|
||||
// point_process[loop_of_point].Range*sin(point_process[loop_of_point].Azimuth)};
|
||||
double Z[3] = {point_process[loop_of_point].Range,
|
||||
point_process[loop_of_point].Azimuth,
|
||||
point_process[loop_of_point].Velocity};
|
||||
double Z[3] = {point_process[loop_of_point].range,
|
||||
point_process[loop_of_point].azimuth,
|
||||
point_process[loop_of_point].velocity};
|
||||
// double v_point = point_process[loop_of_point].Velocity;
|
||||
double prt = point_process[loop_of_point].PRF_index;
|
||||
double freq_ind = point_process[loop_of_point].Freq_index;
|
||||
double delta_T = (point_process[loop_of_point].CPI_Time - T_track)/1000.0;
|
||||
double freq_ind = point_process[loop_of_point].freq_index;
|
||||
double delta_T = (point_process[loop_of_point].CPI_time - T_track)/1000.0;
|
||||
if (delta_T <= 0){
|
||||
RDP_LOG << "Warning: delta_T <= 0, skipping point association for point index " << point_process[loop_of_point].CPI_Time << " " << T_track << std::endl;
|
||||
RDP_LOG << "Warning: delta_T <= 0, skipping point association for point index " << point_process[loop_of_point].CPI_time << " " << T_track << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
double h_point = point_process[loop_of_point].Height;
|
||||
double h_point = point_process[loop_of_point].height;
|
||||
|
||||
//计算统计距离d
|
||||
double d1,d2,d3;
|
||||
@@ -371,13 +371,13 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
double d3=associated_info[min_index-1].d3;
|
||||
|
||||
//卡尔曼滤波
|
||||
double Z[3]={point_process[point_index-1].Range,point_process[point_index-1].Azimuth,point_process[point_index-1].Velocity};
|
||||
double Z[3]={point_process[point_index-1].range,point_process[point_index-1].azimuth,point_process[point_index-1].velocity};
|
||||
double prt = point_process[point_index-1].PRF_index;
|
||||
double freq_ind = point_process[point_index-1].Freq_index;
|
||||
double freq_ind = point_process[point_index-1].freq_index;
|
||||
// double Z[2];
|
||||
// Z[0]=point_process[point_index-1].Range*cos(point_process[point_index-1].Azimuth);
|
||||
// Z[1]=point_process[point_index-1].Range*sin(point_process[point_index-1].Azimuth);
|
||||
double delta_T = (point_process[point_index-1].CPI_Time - T_track)/1000.0;
|
||||
double delta_T = (point_process[point_index-1].CPI_time - T_track)/1000.0;
|
||||
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3,Work_Parameter);
|
||||
|
||||
@@ -446,25 +446,25 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
}
|
||||
|
||||
//更新航迹时间
|
||||
(*trust_track)[i].T_track = point_process[point_index-1].CPI_Time;
|
||||
(*trust_track)[i].T_track = point_process[point_index-1].CPI_time;
|
||||
(*trust_track)[i].GNSS_time = point_process[point_index-1].GNSS_time;
|
||||
|
||||
//更新关联上的点迹信息
|
||||
(*trust_track)[i].range_point=point_process[point_index-1].Range;
|
||||
(*trust_track)[i].azi_point=point_process[point_index-1].Azimuth/PI*180;
|
||||
(*trust_track)[i].elev_point= asin(point_process[point_index-1].Height/point_process[point_index-1].Range)/PI*180;
|
||||
(*trust_track)[i].vr_point=point_process[point_index-1].Velocity;
|
||||
(*trust_track)[i].range_point=point_process[point_index-1].range;
|
||||
(*trust_track)[i].azi_point=point_process[point_index-1].azimuth/PI*180;
|
||||
(*trust_track)[i].elev_point= asin(point_process[point_index-1].height/point_process[point_index-1].range)/PI*180;
|
||||
(*trust_track)[i].vr_point=point_process[point_index-1].velocity;
|
||||
(*trust_track)[i].point_type = 1;
|
||||
(*trust_track)[i].prf_point = point_process[point_index-1].PRF_index;
|
||||
(*trust_track)[i].snr_point = point_process[point_index-1].snr;
|
||||
(*trust_track)[i].snr_point = point_process[point_index-1].SNR;
|
||||
(*trust_track)[i].Extrapolate_round = 0; //连续未用实点更新时间
|
||||
(*trust_track)[i].point_flag=1; //实点
|
||||
(*trust_track)[i].Amplitude= point_process[point_index-1].Amplitude; //幅度
|
||||
(*trust_track)[i].Amplitude= point_process[point_index-1].amplitude; //幅度
|
||||
(*trust_track)[i].associate_point_number = (*trust_track)[i].associate_point_number+1; //关联点数+1
|
||||
(*trust_track)[i].RCS = point_process[point_index-1].RCS;
|
||||
(*trust_track)[i].Track_Update_Flag = 1; //航迹更新标志
|
||||
|
||||
(*trust_track)[i].pitch_num = point_process[point_index-1].pitch_num;
|
||||
(*trust_track)[i].pitch_num = point_process[point_index-1].pitch_beam_index;
|
||||
|
||||
std::memcpy((*trust_track)[i].speed_dim, point_process[point_index-1].speed_dim, sizeof((*trust_track)[i].speed_dim));
|
||||
std::memcpy((*trust_track)[i].range_dim, point_process[point_index-1].range_dim, sizeof((*trust_track)[i].range_dim));
|
||||
@@ -730,7 +730,7 @@ void Track_Asso_Tas::track_hight_update(int tas_
|
||||
for(size_t i=0; i<trust_track->size(); i++)
|
||||
if((*trust_track)[i].Track_Index==tas_track_idx)
|
||||
{
|
||||
(*trust_track)[i].Hight_smooth.push_back(point_process[asso_point_index-1].Height);
|
||||
(*trust_track)[i].Hight_smooth.push_back(point_process[asso_point_index-1].height);
|
||||
|
||||
int height_win_length;
|
||||
if((*trust_track)[i].range_point <= 1000)
|
||||
|
||||
@@ -58,7 +58,7 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
|
||||
std::vector <PointRecv>::iterator Iter;
|
||||
for (Iter=point_process.begin(); Iter!=point_process.end();)
|
||||
{
|
||||
if((*Iter).Use_Flag==1)
|
||||
if((*Iter).use_flag==1)
|
||||
{
|
||||
point_process.erase(Iter);
|
||||
Iter=point_process.begin();
|
||||
@@ -78,22 +78,22 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
|
||||
for (unsigned int i=0;i<point_process.size();i++)
|
||||
{
|
||||
Temp_track temp_track_tmp = {};
|
||||
temp_track_tmp.r = point_process[i].Range;
|
||||
temp_track_tmp.azi = point_process[i].Azimuth;
|
||||
temp_track_tmp.height = point_process[i].Height;
|
||||
temp_track_tmp.Amp = point_process[i].Amplitude;
|
||||
temp_track_tmp.vr = point_process[i].Velocity;
|
||||
temp_track_tmp.snr = point_process[i].snr;
|
||||
temp_track_tmp.r = point_process[i].range;
|
||||
temp_track_tmp.azi = point_process[i].azimuth;
|
||||
temp_track_tmp.height = point_process[i].height;
|
||||
temp_track_tmp.Amp = point_process[i].amplitude;
|
||||
temp_track_tmp.vr = point_process[i].velocity;
|
||||
temp_track_tmp.snr = point_process[i].SNR;
|
||||
temp_track_tmp.RCS = point_process[i].RCS;
|
||||
temp_track_tmp.pitch_num = point_process[i].pitch_num;
|
||||
temp_track_tmp.pitch_num = point_process[i].pitch_beam_index;
|
||||
std::memcpy(temp_track_tmp.speed_dim, point_process[i].speed_dim, sizeof(temp_track_tmp.speed_dim));
|
||||
std::memcpy(temp_track_tmp.range_dim, point_process[i].range_dim, sizeof(temp_track_tmp.range_dim));
|
||||
temp_track_tmp.asso_flag = 0;
|
||||
temp_track_tmp.X[0]=point_process[i].Range*cos(point_process[i].Azimuth);
|
||||
temp_track_tmp.X[0]=point_process[i].range*cos(point_process[i].azimuth);
|
||||
temp_track_tmp.X[1]=0;
|
||||
temp_track_tmp.X[2]=point_process[i].Range*sin(point_process[i].Azimuth);
|
||||
temp_track_tmp.X[2]=point_process[i].range*sin(point_process[i].azimuth);
|
||||
temp_track_tmp.X[3]=0;
|
||||
temp_track_tmp.T = point_process[i].CPI_Time;
|
||||
temp_track_tmp.T = point_process[i].CPI_time;
|
||||
temp_track_tmp.GNSS_time = point_process[i].GNSS_time;
|
||||
temp_track_tmp.buff_round = 1;
|
||||
// temp_track_tmp.Temp_track_section_idx = floor(point_process[i].beam_index/BEAM_NUM_DOT_SECTION)+1;
|
||||
@@ -166,15 +166,15 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
|
||||
|
||||
//点迹信息
|
||||
// double Z[2]={ point_process[i].Range*cos(point_process[i].Azimuth),point_process[i].Range*sin(point_process[i].Azimuth)} ;
|
||||
double Z[3]={point_process[i].Range,
|
||||
point_process[i].Azimuth,
|
||||
point_process[i].Velocity};
|
||||
double vr_point=point_process[i].Velocity;
|
||||
double r_point=point_process[i].Range;
|
||||
double Z[3]={point_process[i].range,
|
||||
point_process[i].azimuth,
|
||||
point_process[i].velocity};
|
||||
double vr_point=point_process[i].velocity;
|
||||
double r_point=point_process[i].range;
|
||||
double prt = point_process[i].PRF_index;
|
||||
double freq_ind = point_process[i].Freq_index;
|
||||
double h_point = point_process[i].Height;
|
||||
double T_point = static_cast<double>(point_process[i].CPI_Time);
|
||||
double freq_ind = point_process[i].freq_index;
|
||||
double h_point = point_process[i].height;
|
||||
double T_point = static_cast<double>(point_process[i].CPI_time);
|
||||
|
||||
//航迹信息
|
||||
double X[4];
|
||||
@@ -226,7 +226,7 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
|
||||
asso_info_tmp.d = d;
|
||||
asso_info.push_back(asso_info_tmp);
|
||||
(*temp_track)[j][L-1].asso_flag = 1;
|
||||
point_process[i].Use_Flag = 1;
|
||||
point_process[i].use_flag = 1;
|
||||
//std::cout << "v_temp_track : " << v_temp_track << "vr_point : "<< vr_point;
|
||||
}
|
||||
}
|
||||
@@ -249,30 +249,30 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
|
||||
|
||||
//关联上的点
|
||||
Temp_track asso_track_info_tmp = {};
|
||||
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].Range;
|
||||
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].Azimuth;
|
||||
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].Height;
|
||||
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].Amplitude;
|
||||
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_Time;
|
||||
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].range;
|
||||
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].azimuth;
|
||||
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].height;
|
||||
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].amplitude;
|
||||
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_time;
|
||||
asso_track_info_tmp.GNSS_time = point_process[asso_info[i].point_idx-1].GNSS_time;
|
||||
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].Velocity;
|
||||
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].snr;
|
||||
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].velocity;
|
||||
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].SNR;
|
||||
asso_track_info_tmp.RCS = point_process[asso_info[i].point_idx-1].RCS;
|
||||
asso_track_info_tmp.asso_flag=0;
|
||||
asso_track_info_tmp.buff_round = 1;
|
||||
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_num;
|
||||
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_beam_index;
|
||||
std::memcpy(asso_track_info_tmp.speed_dim, point_process[asso_info[i].point_idx-1].speed_dim, sizeof(asso_track_info_tmp.speed_dim));
|
||||
std::memcpy(asso_track_info_tmp.range_dim, point_process[asso_info[i].point_idx-1].range_dim, sizeof(asso_track_info_tmp.range_dim));
|
||||
asso_track_info_tmp.d = asso_info[i].d;
|
||||
// asso_track_info_tmp.Temp_track_section_idx = floor(point_process[asso_info[i].point_idx-1].beam_index/BEAM_NUM_DOT_SECTION)+1;
|
||||
double Z0[2]={(*temp_track)[asso_info[i].track_idx-1][L-1].r*cos((*temp_track)[asso_info[i].track_idx-1][L-1].azi),
|
||||
(*temp_track)[asso_info[i].track_idx-1][L-1].r*sin((*temp_track)[asso_info[i].track_idx-1][L-1].azi)};
|
||||
double Z1[2]={point_process[asso_info[i].point_idx-1].Range*cos(point_process[asso_info[i].point_idx-1].Azimuth),
|
||||
point_process[asso_info[i].point_idx-1].Range*sin(point_process[asso_info[i].point_idx-1].Azimuth)};
|
||||
double Z1[2]={point_process[asso_info[i].point_idx-1].range*cos(point_process[asso_info[i].point_idx-1].azimuth),
|
||||
point_process[asso_info[i].point_idx-1].range*sin(point_process[asso_info[i].point_idx-1].azimuth)};
|
||||
double X[4];
|
||||
double P[4][4];
|
||||
kalman Kalman;
|
||||
double T = ( point_process[asso_info[i].point_idx-1].CPI_Time - (*temp_track)[asso_info[i].track_idx-1][L-1].T)/1000.0;
|
||||
double T = ( point_process[asso_info[i].point_idx-1].CPI_time - (*temp_track)[asso_info[i].track_idx-1][L-1].T)/1000.0;
|
||||
|
||||
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
|
||||
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
|
||||
@@ -328,11 +328,11 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
|
||||
//点迹信息
|
||||
double x_point, y_point, vr_point;
|
||||
coor_trans Coor_trans;
|
||||
Coor_trans.polar2cart(&x_point,&y_point,point_process[i].Range,point_process[i].Azimuth);
|
||||
vr_point=point_process[i].Velocity;
|
||||
double r_point=point_process[i].Range;
|
||||
double h_point = point_process[i].Height;
|
||||
double T_point = static_cast<double>(point_process[i].CPI_Time);
|
||||
Coor_trans.polar2cart(&x_point,&y_point,point_process[i].range,point_process[i].azimuth);
|
||||
vr_point=point_process[i].velocity;
|
||||
double r_point=point_process[i].range;
|
||||
double h_point = point_process[i].height;
|
||||
double T_point = static_cast<double>(point_process[i].CPI_time);
|
||||
|
||||
//航迹信息
|
||||
double x_track_head, y_track_head;
|
||||
@@ -376,7 +376,7 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
|
||||
asso_info_tmp.track_idx = static_cast<int>(j)+1;
|
||||
asso_info.push_back(asso_info_tmp);
|
||||
(*temp_track)[j][0].asso_flag = 1;
|
||||
point_process[i].Use_Flag = 1;
|
||||
point_process[i].use_flag = 1;
|
||||
//std::cout << "v_track_head: " << v_track_head << "vr_point: " << vr_point;
|
||||
}
|
||||
|
||||
@@ -395,16 +395,16 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
|
||||
|
||||
//第二个点
|
||||
struct Temp_track asso_track_info_tmp = {};
|
||||
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].Range;
|
||||
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].Azimuth;
|
||||
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].Height;
|
||||
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].Amplitude;
|
||||
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].Velocity;
|
||||
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].snr;
|
||||
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].range;
|
||||
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].azimuth;
|
||||
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].height;
|
||||
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].amplitude;
|
||||
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].velocity;
|
||||
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].SNR;
|
||||
asso_track_info_tmp.RCS = point_process[asso_info[i].point_idx-1].RCS;
|
||||
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_Time;
|
||||
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_time;
|
||||
asso_track_info_tmp.GNSS_time = point_process[asso_info[i].point_idx-1].GNSS_time;
|
||||
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_num;
|
||||
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_beam_index;
|
||||
std::memcpy(asso_track_info_tmp.speed_dim, point_process[asso_info[i].point_idx-1].speed_dim, sizeof(asso_track_info_tmp.speed_dim));
|
||||
std::memcpy(asso_track_info_tmp.range_dim, point_process[asso_info[i].point_idx-1].range_dim, sizeof(asso_track_info_tmp.range_dim));
|
||||
asso_track_info_tmp.asso_flag = 0;
|
||||
@@ -412,12 +412,12 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
|
||||
// asso_track_info_tmp.Temp_track_section_idx = floor(point_process[asso_info[i].point_idx-1].beam_index/BEAM_NUM_DOT_SECTION)+1;;
|
||||
double Z0[2]={(*temp_track)[asso_info[i].track_idx-1][0].r*cos((*temp_track)[asso_info[i].track_idx-1][0].azi),
|
||||
(*temp_track)[asso_info[i].track_idx-1][0].r*sin((*temp_track)[asso_info[i].track_idx-1][0].azi)};
|
||||
double Z1[2]={point_process[asso_info[i].point_idx-1].Range*cos(point_process[asso_info[i].point_idx-1].Azimuth),
|
||||
point_process[asso_info[i].point_idx-1].Range*sin(point_process[asso_info[i].point_idx-1].Azimuth)};
|
||||
double Z1[2]={point_process[asso_info[i].point_idx-1].range*cos(point_process[asso_info[i].point_idx-1].azimuth),
|
||||
point_process[asso_info[i].point_idx-1].range*sin(point_process[asso_info[i].point_idx-1].azimuth)};
|
||||
double X[4];
|
||||
double P[4][4];
|
||||
kalman Kalman;
|
||||
double T = (point_process[asso_info[i].point_idx-1].CPI_Time - (*temp_track)[asso_info[i].track_idx-1][0].T)/1000.0;
|
||||
double T = (point_process[asso_info[i].point_idx-1].CPI_time - (*temp_track)[asso_info[i].track_idx-1][0].T)/1000.0;
|
||||
|
||||
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
|
||||
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
|
||||
|
||||
@@ -44,15 +44,15 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
|
||||
|
||||
//点迹信息
|
||||
// double Z[2]={ point_process[i].Range*cos(point_process[i].Azimuth),point_process[i].Range*sin(point_process[i].Azimuth)} ;
|
||||
double Z[3]={point_process[i].Range,
|
||||
point_process[i].Azimuth,
|
||||
point_process[i].Velocity};
|
||||
double vr_point=point_process[i].Velocity;
|
||||
double r_point=point_process[i].Range;
|
||||
double Z[3]={point_process[i].range,
|
||||
point_process[i].azimuth,
|
||||
point_process[i].velocity};
|
||||
double vr_point=point_process[i].velocity;
|
||||
double r_point=point_process[i].range;
|
||||
double prt = point_process[i].PRF_index;
|
||||
double freq_ind = point_process[i].Freq_index;
|
||||
double h_point = point_process[i].Height;
|
||||
double T_point = static_cast<double>(point_process[i].CPI_Time);
|
||||
double freq_ind = point_process[i].freq_index;
|
||||
double h_point = point_process[i].height;
|
||||
double T_point = static_cast<double>(point_process[i].CPI_time);
|
||||
|
||||
//航迹信息
|
||||
double X[4];
|
||||
@@ -89,7 +89,7 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
|
||||
asso_info_tmp.track_idx = static_cast<int>(j)+1;
|
||||
asso_info.push_back(asso_info_tmp);
|
||||
(*temp_track)[j][L-1].asso_flag = 1;
|
||||
point_process[i].Use_Flag = 1;
|
||||
point_process[i].use_flag = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,15 +111,15 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
|
||||
|
||||
//关联上的点
|
||||
Temp_track asso_track_info_tmp = {};
|
||||
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].Range;
|
||||
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].Azimuth;
|
||||
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].Height;
|
||||
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].Amplitude;
|
||||
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_Time;
|
||||
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].Velocity;
|
||||
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].snr;
|
||||
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].range;
|
||||
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].azimuth;
|
||||
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].height;
|
||||
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].amplitude;
|
||||
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_time;
|
||||
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].velocity;
|
||||
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].SNR;
|
||||
asso_track_info_tmp.RCS = point_process[asso_info[i].point_idx-1].RCS;
|
||||
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_num;
|
||||
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_beam_index;
|
||||
std::memcpy(asso_track_info_tmp.speed_dim, point_process[asso_info[i].point_idx-1].speed_dim, sizeof(asso_track_info_tmp.speed_dim));
|
||||
std::memcpy(asso_track_info_tmp.range_dim, point_process[asso_info[i].point_idx-1].range_dim, sizeof(asso_track_info_tmp.range_dim));
|
||||
asso_track_info_tmp.asso_flag=0;
|
||||
@@ -127,12 +127,12 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
|
||||
// asso_track_info_tmp.Temp_track_section_idx = floor(point_process[asso_info[i].point_idx-1].beam_index/BEAM_NUM_DOT_SECTION)+1;
|
||||
double Z0[2]={(*temp_track)[asso_info[i].track_idx-1][L-1].r*cos((*temp_track)[asso_info[i].track_idx-1][L-1].azi),
|
||||
(*temp_track)[asso_info[i].track_idx-1][L-1].r*sin((*temp_track)[asso_info[i].track_idx-1][L-1].azi)};
|
||||
double Z1[2]={point_process[asso_info[i].point_idx-1].Range*cos(point_process[asso_info[i].point_idx-1].Azimuth),
|
||||
point_process[asso_info[i].point_idx-1].Range*sin(point_process[asso_info[i].point_idx-1].Azimuth)};
|
||||
double Z1[2]={point_process[asso_info[i].point_idx-1].range*cos(point_process[asso_info[i].point_idx-1].azimuth),
|
||||
point_process[asso_info[i].point_idx-1].range*sin(point_process[asso_info[i].point_idx-1].azimuth)};
|
||||
double X[4];
|
||||
double P[4][4];
|
||||
kalman Kalman;
|
||||
double T = ( point_process[asso_info[i].point_idx-1].CPI_Time - (*temp_track)[asso_info[i].track_idx-1][L-1].T)/1000.0;
|
||||
double T = ( point_process[asso_info[i].point_idx-1].CPI_time - (*temp_track)[asso_info[i].track_idx-1][L-1].T)/1000.0;
|
||||
|
||||
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
|
||||
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
|
||||
@@ -187,11 +187,11 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
|
||||
//点迹信息
|
||||
double x_point, y_point, vr_point;
|
||||
coor_trans Coor_trans;
|
||||
Coor_trans.polar2cart(&x_point,&y_point,point_process[i].Range,point_process[i].Azimuth);
|
||||
vr_point=point_process[i].Velocity;
|
||||
double r_point=point_process[i].Range;
|
||||
double h_point = point_process[i].Height;
|
||||
double T_point = static_cast<double>(point_process[i].CPI_Time);
|
||||
Coor_trans.polar2cart(&x_point,&y_point,point_process[i].range,point_process[i].azimuth);
|
||||
vr_point=point_process[i].velocity;
|
||||
double r_point=point_process[i].range;
|
||||
double h_point = point_process[i].height;
|
||||
double T_point = static_cast<double>(point_process[i].CPI_time);
|
||||
|
||||
//航迹信息
|
||||
double x_track_head, y_track_head;
|
||||
@@ -229,7 +229,7 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
|
||||
asso_info_tmp.track_idx = static_cast<int>(j)+1;
|
||||
asso_info.push_back(asso_info_tmp);
|
||||
(*temp_track)[j][0].asso_flag = 1;
|
||||
point_process[i].Use_Flag = 1;
|
||||
point_process[i].use_flag = 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -247,15 +247,15 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
|
||||
|
||||
//第二个点
|
||||
struct Temp_track asso_track_info_tmp = {};
|
||||
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].Range;
|
||||
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].Azimuth;
|
||||
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].Height;
|
||||
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].Amplitude;
|
||||
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].Velocity;
|
||||
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].snr;
|
||||
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].range;
|
||||
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].azimuth;
|
||||
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].height;
|
||||
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].amplitude;
|
||||
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].velocity;
|
||||
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].SNR;
|
||||
asso_track_info_tmp.RCS = point_process[asso_info[i].point_idx-1].RCS;
|
||||
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_Time;
|
||||
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_num;
|
||||
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_time;
|
||||
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_beam_index;
|
||||
std::memcpy(asso_track_info_tmp.speed_dim, point_process[asso_info[i].point_idx-1].speed_dim, sizeof(asso_track_info_tmp.speed_dim));
|
||||
std::memcpy(asso_track_info_tmp.range_dim, point_process[asso_info[i].point_idx-1].range_dim, sizeof(asso_track_info_tmp.range_dim));
|
||||
asso_track_info_tmp.asso_flag = 0;
|
||||
@@ -263,12 +263,12 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
|
||||
// asso_track_info_tmp.Temp_track_section_idx = floor(point_process[asso_info[i].point_idx-1].beam_index/BEAM_NUM_DOT_SECTION)+1;;
|
||||
double Z0[2]={(*temp_track)[asso_info[i].track_idx-1][0].r*cos((*temp_track)[asso_info[i].track_idx-1][0].azi),
|
||||
(*temp_track)[asso_info[i].track_idx-1][0].r*sin((*temp_track)[asso_info[i].track_idx-1][0].azi)};
|
||||
double Z1[2]={point_process[asso_info[i].point_idx-1].Range*cos(point_process[asso_info[i].point_idx-1].Azimuth),
|
||||
point_process[asso_info[i].point_idx-1].Range*sin(point_process[asso_info[i].point_idx-1].Azimuth)};
|
||||
double Z1[2]={point_process[asso_info[i].point_idx-1].range*cos(point_process[asso_info[i].point_idx-1].azimuth),
|
||||
point_process[asso_info[i].point_idx-1].range*sin(point_process[asso_info[i].point_idx-1].azimuth)};
|
||||
double X[4];
|
||||
double P[4][4];
|
||||
kalman Kalman;
|
||||
double T = (point_process[asso_info[i].point_idx-1].CPI_Time - (*temp_track)[asso_info[i].track_idx-1][0].T)/1000.0;
|
||||
double T = (point_process[asso_info[i].point_idx-1].CPI_time - (*temp_track)[asso_info[i].track_idx-1][0].T)/1000.0;
|
||||
|
||||
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
|
||||
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
# 数据记录调试功能(rdp_record)
|
||||
|
||||
> 面向调试的数据记录框架:把内部结构体按「一个记录实体一张表」写入 SQLite,供事后查询分析。
|
||||
> 本功能**默认只在 Debug 构建中启用**,Release/交付构建完全不编译(无依赖、无代码、无性能损失)。
|
||||
> 相关代码:`data_process_class_dll/rdp_record.h`、`rdp_record_entity.h`、`rdp_record.cpp`;
|
||||
> 第三方依赖来源见 `third_party/ormpp/UPSTREAM.md`。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目的与范围
|
||||
|
||||
- **目的**:调试期把关键中间数据落库(点迹、航迹、关联结果……),用于事后 SQL 查询与对比分析;
|
||||
- **框架性设计**:本功能**不预设记录哪些数据**。新增一类记录数据的成本固定为三步:
|
||||
加记录实体结构体 → 注册 → 在需要的位置写一行记录点宏(配方见第 5 节);
|
||||
- **非目标**:不做异步写入、不做查询/回放工具、不做数据裁剪与轮转、不追求多线程写入性能。
|
||||
|
||||
## 2. 开关与构建
|
||||
|
||||
| 项目 | 说明 |
|
||||
|---|---|
|
||||
| 开关 | CMake 缓存变量 `RDP_DATA_RECORD`,取值 `AUTO`(默认)/ `ON` / `OFF` |
|
||||
| AUTO 语义 | `Debug` 构建启用、`Release`(及其它)构建关闭 |
|
||||
| 生效方式 | 启用时构建系统定义 `RDP_RECORD_ENABLE` 并额外编译静态库 `rdp_record`(`rdp_record.cpp` + 内置 `sqlite3.c`)并链接进 DLL |
|
||||
| 关闭时的保证 | 记录模块源码**不参与编译**;`RDP_RECORD(...)` / `RDP_RECORD_BATCH(...)` 展开为 `((void)0)`(实参不求值,也不会产生「变量已初始化但未使用」告警);DLL 不新增依赖与导出符号 |
|
||||
|
||||
```bat
|
||||
:: 交付/Release:关闭(AUTO 的默认结果,也可显式 OFF)
|
||||
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Release OFF
|
||||
.vscode\scripts\build.bat build/X256_PS
|
||||
|
||||
:: Debug:启用(AUTO 的默认结果,也可显式 ON)
|
||||
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Debug ON
|
||||
.vscode\scripts\build.bat build/X256_PS
|
||||
|
||||
:: 直接用 cmake(需先进入 MSVC 2022 x64 环境)
|
||||
cmake -S . -B build/debug_rec -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Debug ^
|
||||
-DRADAR_SCAN_MODE=PHASE_SCANNING -DRDP_DATA_RECORD=ON
|
||||
cmake --build build/debug_rec
|
||||
```
|
||||
|
||||
配置阶段会打印开关状态:`数据记录调试功能:启用(C++20 静态库 rdp_record + 内置 SQLite)` 或 `...:关闭(不编译记录模块,RDP_RECORD 为空操作)`。
|
||||
|
||||
**语言标准与依赖**:记录模块以 **C++20** 独立编译(ormpp 要求 `CMAKE_CXX_STANDARD 20`),并编译内置的 SQLite amalgamation(因此启用时需要 C 编译器,随 VS 2022 工具集提供);**雷达处理代码与内置 Eigen 仍为 C++17**,二者通过 `rdp_record.h` 的「仅声明 + 显式实例化」跨标准边界:调用方(C++17)只把记录实体按 `const` 引用递给 `rdp_record::Write<T>()`,实现在 `rdp_record.cpp`(C++20)中显式实例化。
|
||||
|
||||
## 3. 运行期行为
|
||||
|
||||
| 项目 | 说明 |
|
||||
|---|---|
|
||||
| 存放目录 | `RDP_RECORD_DB_DIR`(`parameters.h`,默认 `.\rdp_records\`,相对宿主进程当前工作目录,不存在时自动创建) |
|
||||
| 文件名 | `rdp_record_YYYYMMDD_HHMMSS.db`(**每次运行一个新文件**,与 `rdp_logs` 的日志命名风格一致) |
|
||||
| 初始化 | `Data_Process_Factory::GetB()` 内调用 `rdp_record::Init()`(幂等):建目录 → 开库 → 打印记录库路径 |
|
||||
| 关闭 | `Data_Process_Factory::Destroy()` 内调用 `rdp_record::Shutdown()`;宿主未调用时由静态析构兜底关库 |
|
||||
| 建表 | 首次写入某记录实体时自动执行 `CREATE TABLE IF NOT EXISTS`(每实体只尝试一次) |
|
||||
| PRAGMA | `synchronous=OFF`(调试取舍:提交时不再 fsync,崩溃可能丢失尾部数据;如需崩溃安全可改为 `NORMAL` 或自行在 sqlite3 层开启 WAL)。未设置 `journal_mode`——ormpp 的 `execute()` 只接受不返回结果行的语句 |
|
||||
| 线程模型 | 单连接 + 内部互斥锁串行化写入;宿主多线程调用不会破坏数据,但**不保证高并发写入性能**(调试功能,按要求不做异步) |
|
||||
| 失败降级 | 目录不可建/库打不开:`Init()` 返回 `false`,只打印一次日志,之后所有写入静默忽略;建表或插入失败:**每个实体只提示一次**,不重试、不抛出、不影响雷达数据处理 |
|
||||
| 自增主键 | 每个记录实体的首字段固定 `int64_t id`:建表时登记自增主键,插入时自动跳过该列(写入时固定填 `0`,由 SQLite 分配) |
|
||||
|
||||
## 4. 记录实体(表)约定
|
||||
|
||||
1. 结构体定义在**全局命名空间**(勿放进 `namespace`),推荐以 `RdpRec` 开头;
|
||||
2. **结构体名即表名**(ormpp 取「短结构体名」),无需任何建表代码;
|
||||
3. 首字段固定为 `int64_t id`;
|
||||
4. 字段类型仅限标量,映射关系:
|
||||
|
||||
| C++ 类型 | SQLite 类型 | 备注 |
|
||||
|---|---|---|
|
||||
| `int` | `INTEGER` | 枚举同理(按整数存) |
|
||||
| `int64_t` / `long long` | `INTEGER` | 毫秒时间戳用它 |
|
||||
| `float` | `FLOAT` | |
|
||||
| `double` | `DOUBLE` | |
|
||||
| `bool` | `INTEGER` | |
|
||||
|
||||
5. **禁止数组字段**(如 `float speed_dim[129]`、`char[N]`):ormpp 对 C 数组按 C 字符串语义(`strlen`)绑定,会读越界或写入错误数据。需要记录大批量数组时,另建「元素展开表」(一行一个元素)或改用 blob 字段;
|
||||
6. 命名约定:字段名带单位后缀(`_ms` 毫秒、`_m` 米、`_deg` 度、`_mps` 米/秒、`_s` 秒),避免单位歧义;
|
||||
7. **表结构变更不迁移旧表**:增删字段后不会修改已存在的表结构;由于每次运行生成新的 db 文件,正常调试流程天然规避该问题(若手工复用旧库,需自行建新表或删库)。
|
||||
|
||||
## 5. 新增记录点:四步配方
|
||||
|
||||
以「记录航迹号 + 高度」为例:
|
||||
|
||||
**第 1 步 — 声明实体**(`rdp_record_entity.h`「记录实体定义区」,紧随其后写一行 `RDP_RECORD_DECLARE`):
|
||||
|
||||
```cpp
|
||||
struct RdpRecTrackHeight
|
||||
{
|
||||
int64_t id; //自增主键(写入时固定填 0)
|
||||
int track_index; //航迹号
|
||||
double height_m; //高度(m)
|
||||
};
|
||||
RDP_RECORD_DECLARE(RdpRecTrackHeight);
|
||||
```
|
||||
|
||||
**第 2 步 — 注册实体**(`rdp_record.cpp`「记录实体注册区」,字段顺序必须与结构体声明顺序一致):
|
||||
|
||||
```cpp
|
||||
YLT_REFL(RdpRecTrackHeight, id, track_index, height_m);
|
||||
RDP_RECORD_DEFINE(RdpRecTrackHeight);
|
||||
```
|
||||
|
||||
**第 3 步 — 埋记录点**(在需要记录的位置):
|
||||
|
||||
```cpp
|
||||
//单条(就地构造;宏为可变参数,允许花括号内有逗号)
|
||||
RDP_RECORD(RdpRecTrackHeight{0, trust_track[i].Track_Index, trust_track[i].Height});
|
||||
|
||||
//批量(需要语句/局部变量时,整体用 #ifdef 包裹,保证交付构建零代码)
|
||||
#ifdef RDP_RECORD_ENABLE
|
||||
std::vector<RdpRecTrackHeight> recs;
|
||||
for (size_t i = 0; i < trust_track.size(); ++i)
|
||||
{
|
||||
recs.push_back(RdpRecTrackHeight{0, trust_track[i].Track_Index, trust_track[i].Height});
|
||||
}
|
||||
RDP_RECORD_BATCH(recs);
|
||||
#endif
|
||||
```
|
||||
|
||||
**第 4 步 — 无需建表**:首次写入该实体时自动 `CREATE TABLE IF NOT EXISTS`;不同实体自动落到不同表。
|
||||
|
||||
> 检查点:若忘记第 2 步(注册/实例化),链接期会报 **LNK2019**(`extern template` 已阻止隐式实例化),这正是配方的自检点。
|
||||
|
||||
## 6. 现有示例记录点
|
||||
|
||||
| 记录点 | 位置 | 表 | 说明 |
|
||||
|---|---|---|---|
|
||||
| ① TWS 输入点迹 | `data_process.cpp::data_preprocess()`(`point_type == 0` 分支逐点循环内) | `RdpRecPointTws` | 记录 `DataRev` 输入字段(距离 m、方位/俯仰 度、速度 m/s、SNR、RCS、波位/PRF/频点、CPI/GNSS 时间 ms 等 18 个字段) |
|
||||
| ② 本圈输出航迹 | `data_process.cpp::track_process()`(`model == 1` 或 `model == 3` 处理帧结束后批量写入) | `RdpRecTrackOut` | TWS/TAS **共用一张表**,用 `track_mode` 区分;每行取输出数组的 `[i][0]`(该航迹的主记录);行数超过 `MAX_TRACK_NUM` 时钳位,避免越界读 |
|
||||
|
||||
两个示例的字段均为**示例**:按第 5 节增删字段即可,不影响框架。
|
||||
|
||||
## 7. 查询与分析
|
||||
|
||||
```python
|
||||
# 取最近一次运行的记录库并查看表结构、行数与抽样数据(Python 自带 sqlite3 即可)
|
||||
import glob, os, sqlite3
|
||||
path = max(glob.glob(r'rdp_records\rdp_record_*.db'), key=os.path.getmtime)
|
||||
con = sqlite3.connect(path)
|
||||
print(path)
|
||||
print(con.execute("select name from sqlite_master where type='table'").fetchall())
|
||||
print(con.execute("pragma table_info(RdpRecPointTws)").fetchall())
|
||||
print(con.execute("select count(*) from RdpRecPointTws").fetchone())
|
||||
print(con.execute("select * from RdpRecPointTws order by id desc limit 3").fetchall())
|
||||
print(con.execute("pragma integrity_check").fetchone())
|
||||
```
|
||||
|
||||
也可用 DB Browser for SQLite 直接打开 db 文件;同一 db 内所有表一一对应各记录实体。
|
||||
|
||||
## 8. 注意事项与已知限制
|
||||
|
||||
- **交付构建不要开启**:`Release` + `RDP_DATA_RECORD=ON` 仅用于临时排查,会带来 sqlite3 编译与运行时写入开销;正式交付保持 `AUTO`/`OFF`;
|
||||
- **Release 下记录点不做类型检查**:关闭时宏丢弃实参,因此记录点里的字段拼写/类型错误只在启用记录功能的构建中才会暴露——新增记录点后请至少构建一次开启记录功能的配置;
|
||||
- `NaN`/`Inf` 会由 SQLite 存为 `NULL`(`sqlite3_bind_double` 行为),分析时需注意;
|
||||
- 记录库是调试产物:进程异常退出(未调用 `Destroy()`)时,`synchronous=OFF` 下可能丢失最后若干条记录;
|
||||
- IntelliSense 可能对 `rdp_record.cpp` 按 `c_cpp_properties.json` 的 C++17 提示(该文件实际按 C++20 编译,`build/<...>/compile_commands.json` 中带有 `/std:c++20`),以实际构建结果为准;
|
||||
- `third_party/ormpp/` 为上游原样内置(ormpp 0.2.0 / iguana 1.0.5 / SQLite 3.49.1,Apache-2.0 与公有领域),升级方式与校验值见该目录 `UPSTREAM.md`。
|
||||
@@ -1,3 +1,19 @@
|
||||
# 雷达数据处理项目修改说明
|
||||
|
||||
## 概述
|
||||
新增一个数据记录调试功能,将各类结构体数据写入到数据库中,供后续查询分析。
|
||||
|
||||
## 具体需求
|
||||
|
||||
* 本次只设计功能框架,具体存储哪些数据不在此次考虑范围内;
|
||||
* 数据库使用SQLite;
|
||||
* 使用ormpp库实现结构体到数据表的隐射,项目地址:https://github.com/qicosmos/ormpp;
|
||||
* 数据记录功能在release版可以屏蔽,不做任何记录,避免交付版本的性能损失;
|
||||
* 数据分类记录,所有数据存储到一个db文件中,不同的数据存储到不同的表中;
|
||||
* 使用宏或其他方式,在代码中添加记录点,将指定数据写入数据库;
|
||||
* 能够快速添加新的结构体数据,便于后续增加需要记录的数据;
|
||||
* 作为一个调试功能,不必使用异步写入;
|
||||
|
||||
## 其他
|
||||
|
||||
* 如果有任何不明确或者有问题的地方,可以直接向我提问;
|
||||
|
||||
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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user