Compare commits
13
Commits
V1.5.5
...
c01185bf9c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c01185bf9c | ||
|
|
b4aed8a37b | ||
|
|
e98280409e | ||
|
|
56b5449b9f | ||
|
|
5cb67ccef8 | ||
|
|
9f37fba3e7 | ||
|
|
3cab2339d2 | ||
|
|
a6a7877a86 | ||
|
|
b227ae1957 | ||
|
|
c3c7e668d2 | ||
|
|
a4f32e4036 | ||
|
|
ff9b111d34 | ||
|
|
2934e3b59b |
@@ -4,5 +4,6 @@ debug/
|
||||
release/
|
||||
build/
|
||||
rdp_logs/
|
||||
rdp_records/
|
||||
design_docs/
|
||||
.mnemon/
|
||||
Vendored
+10
-10
@@ -2,10 +2,10 @@
|
||||
"version": 4,
|
||||
"configurations": [
|
||||
{
|
||||
"compilerPath": "D:\\APP\\VisualStudio2013\\VC\\bin\\cl.exe",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++11",
|
||||
"intelliSenseMode": "windows-msvc-x86",
|
||||
"compilerPath": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\bin\\Hostx64\\x64\\cl.exe",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "windows-msvc-x64",
|
||||
"defines": [
|
||||
"WIN32",
|
||||
"_WINDOWS",
|
||||
@@ -20,14 +20,14 @@
|
||||
"${workspaceFolder}/data_process_class_dll/Eigen",
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"name": "X256_PS (MSVC2013 x86)",
|
||||
"name": "X256_PS (MSVC2022 x64)",
|
||||
"compileCommands": "${workspaceFolder}/build/X256_PS/compile_commands.json"
|
||||
},
|
||||
{
|
||||
"compilerPath": "D:\\APP\\VisualStudio2013\\VC\\bin\\cl.exe",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++11",
|
||||
"intelliSenseMode": "windows-msvc-x86",
|
||||
"compilerPath": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\bin\\Hostx64\\x64\\cl.exe",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "windows-msvc-x64",
|
||||
"defines": [
|
||||
"WIN32",
|
||||
"_WINDOWS",
|
||||
@@ -42,7 +42,7 @@
|
||||
"${workspaceFolder}/data_process_class_dll/Eigen",
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"name": "X256_MS (MSVC2013 x86)",
|
||||
"name": "X256_MS (MSVC2022 x64)",
|
||||
"compileCommands": "${workspaceFolder}/build/X256_MS/compile_commands.json"
|
||||
}
|
||||
]
|
||||
|
||||
Vendored
+2
-2
@@ -3,11 +3,11 @@ buildType:
|
||||
choices:
|
||||
release:
|
||||
short: Release
|
||||
long: Release (MSVC2013 x86)
|
||||
long: Release (MSVC2022 x64, C++17)
|
||||
buildType: Release
|
||||
debug:
|
||||
short: Debug
|
||||
long: Debug (MSVC2013 x86)
|
||||
long: Debug (MSVC2022 x64, C++17)
|
||||
buildType: Debug
|
||||
|
||||
scanMode:
|
||||
|
||||
Vendored
+12
-1
@@ -1,11 +1,22 @@
|
||||
@echo off
|
||||
rem ===========================================================================
|
||||
rem CMake build script (MSVC 2022 / v143, C++17, x64)
|
||||
rem
|
||||
rem Usage: build.bat <build-dir>
|
||||
rem e.g. build.bat build\X256_PS
|
||||
rem
|
||||
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
|
||||
rem ===========================================================================
|
||||
setlocal
|
||||
set "BUILD_DIR=%~1"
|
||||
if "%BUILD_DIR%"=="" (
|
||||
echo Usage: build.bat ^<build-dir^>
|
||||
exit /b 1
|
||||
)
|
||||
call D:\APP\VisualStudio2013\VC\vcvarsall.bat x86 >nul
|
||||
|
||||
rem Load the MSVC 2022 environment (vcvarsall.bat + cl.exe existence check).
|
||||
call "%~dp0msvc_env.bat"
|
||||
if errorlevel 1 exit /b %errorlevel%
|
||||
|
||||
cmake --build "%BUILD_DIR%"
|
||||
exit /b %errorlevel%
|
||||
|
||||
Vendored
+12
-1
@@ -1,11 +1,22 @@
|
||||
@echo off
|
||||
rem ===========================================================================
|
||||
rem CMake clean script (MSVC 2022 / v143, C++17, x64)
|
||||
rem
|
||||
rem Usage: clean.bat <build-dir>
|
||||
rem e.g. clean.bat build\X256_PS
|
||||
rem
|
||||
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
|
||||
rem ===========================================================================
|
||||
setlocal
|
||||
set "BUILD_DIR=%~1"
|
||||
if "%BUILD_DIR%"=="" (
|
||||
echo Usage: clean.bat ^<build-dir^>
|
||||
exit /b 1
|
||||
)
|
||||
call D:\APP\VisualStudio2013\VC\vcvarsall.bat x86 >nul
|
||||
|
||||
rem Load the MSVC 2022 environment (vcvarsall.bat + cl.exe existence check).
|
||||
call "%~dp0msvc_env.bat"
|
||||
if errorlevel 1 exit /b %errorlevel%
|
||||
|
||||
cmake --build "%BUILD_DIR%" --target clean
|
||||
exit /b %errorlevel%
|
||||
|
||||
Vendored
+57
-5
@@ -1,21 +1,73 @@
|
||||
@echo off
|
||||
rem ===========================================================================
|
||||
rem CMake configure script (MSVC 2022 / v143, C++17, x64)
|
||||
rem
|
||||
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.
|
||||
rem
|
||||
rem A build directory that was configured by an older compiler (for example the
|
||||
rem MSVC 2013 cache) is removed first: neither its CMake cache nor its object
|
||||
rem files can be reused across a compiler upgrade.
|
||||
rem
|
||||
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
|
||||
rem ===========================================================================
|
||||
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"
|
||||
call D:\APP\VisualStudio2013\VC\vcvarsall.bat x86 >nul
|
||||
if "%RECORD_MODE%"=="" set "RECORD_MODE=AUTO"
|
||||
|
||||
rem Load the MSVC 2022 environment (vcvarsall.bat + cl.exe existence check).
|
||||
call "%~dp0msvc_env.bat"
|
||||
if errorlevel 1 exit /b %errorlevel%
|
||||
|
||||
rem Drop a build tree left behind by another toolset.
|
||||
if not exist "%BUILD_DIR%\CMakeCache.txt" goto :configure
|
||||
set "OLD_TOOLSET="
|
||||
if exist "%BUILD_DIR%\.msvc_toolset" set /p OLD_TOOLSET=<"%BUILD_DIR%\.msvc_toolset"
|
||||
if /i "%OLD_TOOLSET%"=="%MSVC_TOOLSET_VERSION%" goto :configure
|
||||
echo [configure] "%BUILD_DIR%" was configured with MSVC toolset "%OLD_TOOLSET%" - rebuilding from scratch
|
||||
rmdir /s /q "%BUILD_DIR%"
|
||||
if exist "%BUILD_DIR%" (
|
||||
echo [ERROR] cannot remove "%BUILD_DIR%", please delete it manually and retry.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:configure
|
||||
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%
|
||||
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
|
||||
exit /b %CMAKE_EXIT%
|
||||
if not "%CMAKE_EXIT%"=="0" exit /b %CMAKE_EXIT%
|
||||
|
||||
rem Remember which toolset produced this build tree (checked on the next run).
|
||||
> "%BUILD_DIR%\.msvc_toolset" echo %MSVC_TOOLSET_VERSION%
|
||||
exit /b 0
|
||||
|
||||
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
@echo off
|
||||
rem ===========================================================================
|
||||
rem Shared MSVC 2022 (v143) environment setup.
|
||||
rem
|
||||
rem Called by configure.bat / build.bat / clean.bat. It:
|
||||
rem 1) locates the MSVC 2022 toolset and cl.exe (default Hostx64\x64);
|
||||
rem 2) calls vcvarsall.bat to set PATH / INCLUDE / LIB (nmake comes with it).
|
||||
rem
|
||||
rem NOTE: this script does NOT use setlocal, so the variables it defines stay
|
||||
rem in the caller's environment (all callers run under setlocal).
|
||||
rem
|
||||
rem Override the defaults with these environment variables:
|
||||
rem VS2022_DIR Visual Studio 2022 install root
|
||||
rem MSVC_TOOLSET_VERSION MSVC toolset version
|
||||
rem MSVC_HOST_ARCH compiler host architecture (x64 / x86)
|
||||
rem MSVC_TARGET_ARCH target architecture (x64 / x86)
|
||||
rem
|
||||
rem Exit code: 0 = ok, non-zero = setup failed (caller must abort).
|
||||
rem
|
||||
rem Encoding: keep this file ASCII-only (cmd.exe parses .bat in the OEM code
|
||||
rem page, UTF-8 comments would break parsing on non-UTF-8 consoles).
|
||||
rem ===========================================================================
|
||||
|
||||
if not defined VS2022_DIR set "VS2022_DIR=D:\APP\VisualStudio2022"
|
||||
if not defined MSVC_TOOLSET_VERSION set "MSVC_TOOLSET_VERSION=14.44.35207"
|
||||
if not defined MSVC_HOST_ARCH set "MSVC_HOST_ARCH=x64"
|
||||
if not defined MSVC_TARGET_ARCH set "MSVC_TARGET_ARCH=x64"
|
||||
|
||||
set "MSVC_TOOLSET_DIR=%VS2022_DIR%\VC\Tools\MSVC\%MSVC_TOOLSET_VERSION%"
|
||||
set "MSVC_CXX_COMPILER=%MSVC_TOOLSET_DIR%\bin\Host%MSVC_HOST_ARCH%\%MSVC_TARGET_ARCH%\cl.exe"
|
||||
set "VCVARS_BAT=%VS2022_DIR%\VC\Auxiliary\Build\vcvarsall.bat"
|
||||
|
||||
if not exist "%VCVARS_BAT%" (
|
||||
echo [ERROR] vcvarsall.bat not found: "%VCVARS_BAT%"
|
||||
echo Please set VS2022_DIR to the Visual Studio 2022 install root.
|
||||
exit /b 1
|
||||
)
|
||||
if not exist "%MSVC_CXX_COMPILER%" (
|
||||
echo [ERROR] cl.exe not found: "%MSVC_CXX_COMPILER%"
|
||||
echo Please check MSVC_TOOLSET_VERSION / MSVC_HOST_ARCH / MSVC_TARGET_ARCH.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
call "%VCVARS_BAT%" %MSVC_TARGET_ARCH% >nul
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] "vcvarsall.bat %MSVC_TARGET_ARCH%" failed.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
exit /b 0
|
||||
Vendored
+8
-4
@@ -2,11 +2,15 @@
|
||||
"cmake.configureOnOpen": false,
|
||||
"cmake.generator": "NMake Makefiles",
|
||||
"cmake.buildDirectory": "${workspaceFolder}/build/X256_PS",
|
||||
"cmake.configureArgs": [
|
||||
"-DCMAKE_CXX_COMPILER=D:/APP/VisualStudio2022/VC/Tools/MSVC/14.44.35207/bin/Hostx64/x64/cl.exe"
|
||||
],
|
||||
"cmake.environment": {
|
||||
"PATH": "D:\\APP\\VisualStudio2013\\VC\\BIN;D:\\APP\\VisualStudio2013\\Common7\\IDE;C:\\Program Files (x86)\\Windows Kits\\8.1\\bin\\x86;${env:PATH}",
|
||||
"INCLUDE": "D:\\APP\\VisualStudio2013\\VC\\INCLUDE;D:\\APP\\VisualStudio2013\\VC\\ATLMFC\\INCLUDE;C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\shared;C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\um;C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\winrt;${env:INCLUDE}",
|
||||
"LIB": "D:\\APP\\VisualStudio2013\\VC\\LIB;D:\\APP\\VisualStudio2013\\VC\\ATLMFC\\LIB;C:\\Program Files (x86)\\Windows Kits\\8.1\\lib\\winv6.3\\um\\x86;${env:LIB}"
|
||||
"PATH": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\bin\\Hostx64\\x64;${env:PATH}",
|
||||
"INCLUDE": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\include;D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\ATLMFC\\include;D:\\APP\\VisualStudio2022\\VC\\Auxiliary\\VS\\include;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\ucrt;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\um;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\shared;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\winrt;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\cppwinrt;${env:INCLUDE}",
|
||||
"LIB": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\ATLMFC\\lib\\x64;D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\lib\\x64;C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.26100.0\\ucrt\\x64;C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.26100.0\\um\\x64;${env:LIB}"
|
||||
},
|
||||
"C_Cpp.default.cppStandard": "c++11",
|
||||
"C_Cpp.default.cppStandard": "c++17",
|
||||
"C_Cpp.default.intelliSenseMode": "windows-msvc-x64",
|
||||
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/X256_PS/compile_commands.json"
|
||||
}
|
||||
|
||||
Vendored
+8
-8
@@ -5,7 +5,7 @@
|
||||
},
|
||||
"tasks": [
|
||||
{
|
||||
"label": "CMake: 配置 X256_PS (Release, MSVC2013 x86)",
|
||||
"label": "CMake: 配置 X256_PS (Release, MSVC2022 x64)",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}\\.vscode\\scripts\\configure.bat",
|
||||
"args": [
|
||||
@@ -16,7 +16,7 @@
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "CMake: 配置 X256_PS (Debug, MSVC2013 x86)",
|
||||
"label": "CMake: 配置 X256_PS (Debug, MSVC2022 x64)",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}\\.vscode\\scripts\\configure.bat",
|
||||
"args": [
|
||||
@@ -60,7 +60,7 @@
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "CMake: 配置 X256_MS (Release, MSVC2013 x86)",
|
||||
"label": "CMake: 配置 X256_MS (Release, MSVC2022 x64)",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}\\.vscode\\scripts\\configure.bat",
|
||||
"args": [
|
||||
@@ -71,7 +71,7 @@
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "CMake: 配置 X256_MS (Debug, MSVC2013 x86)",
|
||||
"label": "CMake: 配置 X256_MS (Debug, MSVC2022 x64)",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}\\.vscode\\scripts\\configure.bat",
|
||||
"args": [
|
||||
@@ -118,7 +118,7 @@
|
||||
"label": "CMake: 配置并构建 X256_PS (Release)",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": [
|
||||
"CMake: 配置 X256_PS (Release, MSVC2013 x86)",
|
||||
"CMake: 配置 X256_PS (Release, MSVC2022 x64)",
|
||||
"CMake: 构建 X256_PS (Release)"
|
||||
],
|
||||
"group": {
|
||||
@@ -131,7 +131,7 @@
|
||||
"label": "CMake: 配置并构建 X256_PS (Debug)",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": [
|
||||
"CMake: 配置 X256_PS (Debug, MSVC2013 x86)",
|
||||
"CMake: 配置 X256_PS (Debug, MSVC2022 x64)",
|
||||
"CMake: 构建 X256_PS (Debug)"
|
||||
],
|
||||
"group": "build",
|
||||
@@ -141,7 +141,7 @@
|
||||
"label": "CMake: 配置并构建 X256_MS (Release)",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": [
|
||||
"CMake: 配置 X256_MS (Release, MSVC2013 x86)",
|
||||
"CMake: 配置 X256_MS (Release, MSVC2022 x64)",
|
||||
"CMake: 构建 X256_MS (Release)"
|
||||
],
|
||||
"group": "build",
|
||||
@@ -151,7 +151,7 @@
|
||||
"label": "CMake: 配置并构建 X256_MS (Debug)",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": [
|
||||
"CMake: 配置 X256_MS (Debug, MSVC2013 x86)",
|
||||
"CMake: 配置 X256_MS (Debug, MSVC2022 x64)",
|
||||
"CMake: 构建 X256_MS (Debug)"
|
||||
],
|
||||
"group": "build",
|
||||
|
||||
@@ -9,6 +9,24 @@
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
|
||||
## [1.5.6] - 2026-10-08
|
||||
|
||||
> 本次发布把上一轮「工作区(未提交)」条目整体归档,并补充 a4f32e4 / c3c7e66 / b227ae1 三次提交的改动。
|
||||
> 版本号宏位于 `parameters.h`(`MAJOR_VERSION` / `MINOR_VERSION` / `PATCH_VERSION`),当前为 **1.5.6**。
|
||||
|
||||
### Added
|
||||
|
||||
- **新增版本号输出(b227ae1)**:`parameters.h` 新增版本宏组——`MAJOR_VERSION` / `MINOR_VERSION` / `PATCH_VERSION`(当前 1 / 5 / 6)、扫描体制后缀 `VERSION_INFO`(相扫 `PS` / 机扫 `MS`)以及拼装宏 `VERSION_STRING`(`STRINGIFY` 两级宏展开,输出形如 `V1.5.6.PS`)。`Data_process_class_dll` 基类新增公开成员 `const char* version_info`,`Data_Process_Factory::GetB()` 创建实例后赋值为 `VERSION_STRING`;宿主可在拿到实例指针后直接读取该字段上报/核对 DLL 版本。
|
||||
- **数据记录表扩充为 6 张在用表(7 个实体定义)(a4f32e4 / c3c7e66 / b227ae1)**:记录实体由「2 个示例」扩展为覆盖主要中间数据的 6 张在用表——`RdpRecPoint`(TWS/TAS 输入点迹,用 `point_type` 区分,逐点写入)、`RdpRecPointCohesionTWS` / `RdpRecPointCohesionTAS`(凝聚后点迹,分别写入)、`RdpRecTempTrack`(临时航迹,`track_init_process_logic()` 末尾在消亡临时航迹之后逐点写入)、`RdpRecTrustTrack`(可靠航迹,TWS 输出循环内按本圈更新航迹逐条写入)、`RdpRecAssoInfo`(点航关联信息,TWS 关联的候选/取中/删表三处写入)。原示例实体 `RdpRecTrackOut`(本圈输出航迹)与其在 `track_process()` 中的批量记录点已整体注释停用(实体定义保留注释,故 `rdp_record_entity.h` 中共 7 个实体定义),改由 `RdpRecTrustTrack` 记录内部真实航迹。
|
||||
- **点迹/航迹记录新增 ID 与圈次维度(c3c7e66、b227ae1)**:`Data_Process` 新增自增成员 `point_id` / `frame_id` / `circle_id`(`data_process.h`)——每接收一帧 `frame_id` 自增;每个点迹 `point_id` 自增并随 `PointRecv` 落库;`circle_id` 在 TWS 满一圈(`Beam_index_aiz == 0`)、TAS 每帧处理结束时自增,写入该圈/帧的所有点迹。内部结构同步新增标识字段:`PointRecv` 增 `circle_id`;`Temp_track` 增 `temp_track_id` / `point_id` / `circle_id`(`Track_Init` 新增自增成员 `temp_track_id`);`Trust_Track` 增 `track_id` / `point_id` / `circle_id`(起批时取自临时航迹末点,关联成功时刷新为被关联点迹的 ID)。据此可把点迹、凝聚点迹、临时航迹、可靠航迹、关联信息按 `point_id` / `track_id` / `circle_id` 串成一条完整链路。
|
||||
- **新增 `Asso_Info` 关联信息结构体(b227ae1)**:原 `Track_Asso::model_filter()` 内定义的局部结构体 `asso_info` 提升为 `struct.h` 中的全局 `Asso_Info`,在原有 `d1/d2/d3/d_min/point_index/track_index` 基础上补充 `point_id` / `track_id` / `circle_id`、门限相关量(`delta_T` / `delta_h` / `R` / `VT`、三个布尔量 `thd_d` / `thd_h` / `thd_r`)以及「本候选是否被最近邻取中」标志 `min_d_flag`,使关联信息可被完整记录与分析。
|
||||
- **可靠航迹补充极坐标量快照(b227ae1)**:`Trust_Track` 新增 `Range` / `Azimuth` / `Elevation` / `Velocity` 四个字段,在 TWS 输出环节由本圈写入输出结构的距离/方位/俯仰/合速度回填(`Velocity` 取 `Range_V`,即 IMM 融合速度的模),供记录表落库;注意它们只在航迹当圈**有输出**时被刷新。
|
||||
- 新增 CHANGELOG.md,采用 Keep a Changelog 格式记录项目变更历史。
|
||||
- `RadarPara` 结构体新增 `DATA_RATE_TAS`(TAS 模式数据率)字段,由 DLL 调用者通过 `track_process_parameters_initial()` 输入(b3e2f52)。
|
||||
- `Tracking_Target` 新增 `last_track_time` 字段,记录各 TAS 目标最近一次波束输出时间(f438ac5)。
|
||||
@@ -24,10 +42,28 @@
|
||||
- 输出航迹补充「靠近/远离」方向标志 `Flag_Direction`(f249fdc):TWS 关联输出(`track_asso.cpp`)与 TAS 关联输出(`track_asso_tas.cpp`)在写入 `vr_point` 后追加 `Flag_Direction = vr_point > 0 ? 1 : 0`,即径向速度为正判为靠近(1)、为零或负判为远离(0)。该字段此前已在 `data_process_class_dll.h` 的 `Track` 结构体中声明(注释「方向标志 1 靠近/0 远离」)但从未被赋值,宿主读到的是未初始化/旧值。
|
||||
- 新增三份流程与专项文档(f249fdc):`docs/data_process_flow_report.md`(数据处理流程与关键逻辑说明:总体数据流、前处理与判圈、凝聚、TWS/TAS 关联与滤波、航迹起始、外推消亡与航迹号管理、TAS 波束控制、参数脉络与关键约定)、`docs/data_process_flow_report.html`(同内容的 HTML 渲染版)、`docs/track_init_readme.md`(航迹起始 Track Init 详细逻辑说明:分阶段流程、起始条件汇总、参数调参、输出字段映射、已知风险与代码索引)。
|
||||
- 更新 README.md(7d5a56f、f249fdc):目录结构新增 `docs/` 目录;2、7.2、8.1 节满圈判据改为「方位波位号 == 0」并说明 `last_beam_num` 不再参与判断;8.3、9.3 节改写点-航迹头关联判据与起批输出历史点数;9.6 节补充方向标志输出与点迹消费说明;2、10.1 节 `RDP_LOG_ENABLE` 说明改为当前实际状态(注释停用、默认不重定向);11.4 节新增航迹起始未修复观察;版本历史表补充 7d5a56f、f249fdc,参考文档表补充 `docs/` 三份文档。
|
||||
- 新增手动重点目标接口(50e0b5c):`data_process_class_dll.h` 增加 `primary_target_add(int track_index)`(将指定批号转为重点目标)与 `primary_target_cancel(int track_index)`(取消重点目标)两个虚接口(基类为空实现、返回 0),`Data_Process` 中按批号把 `Trust_Track::primary_target_flag` 置 1 / 清 0;重点航迹的消亡改用独立阈值 `TRACK_DIE_ROUND_PRIMARY`(= 10,两体制相同),普通航迹仍用 `TRACK_DIE_ROUND`。
|
||||
- 新增未关联航迹的外推圈数上限宏(50e0b5c,`parameters.h`):`MAX_EXTRAPOLATE_NUM = 3`(TWS 通道,两体制相同)、`MAX_EXTRAPOLATE_NUM_TAS`(TAS 通道,相扫 5 / 机扫 48)。
|
||||
- 输入/输出结构新增「归一化幅度」与「速度维 FFT 点数」相关字段(e8c56f9):`DataRev` 增加 `amp_asv`(目标归一化幅度 dB)与 `prn_fftn`(速度维 FFT 点数);`Track` 增加 `amp_asv_point` / `prn_fftn_point`;内部 `PointRecv`、`Trust_Track` 同步增加对应字段,`track_asso.cpp` 在关联实点时写入航迹、TWS 输出航迹时写回输出结构。⚠ 当时输入侧未打通(该描述后于本次发布更正为「已打通」,见 Changed 末条):`data_preprocess()` 未把 `DataRev.amp_asv` / `DataRev.prn_fftn` 拷入内部 `PointRecv`;**TAS 通道至今未涉及这两个字段**,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 个示例实体」已扩展为 7 个,见本版 Added 首条。)
|
||||
- 新增 2 个示例记录点(`data_process.cpp`,字段为示例可按需增删):① `data_preprocess()` 的 TWS 分支逐点写入表 **`RdpRecPointTws`**(输入点迹字段:CPI/GNSS 时间 ms、方位/俯仰波位、PRF/频点、点迹号/总数、距离/方位/俯仰/速度/幅度/SNR/RCS 等,当前实体为 22 个字段);② `track_process()` 的 TWS/TAS 处理帧结束后批量写入表 **`RdpRecTrackOut`**(输出航迹 20 个字段,两种模式共用一张表并用 `track_mode` 区分;每行取输出数组 `[i][0]`,行数超过 `MAX_TRACK_NUM` 时钳位)。⚠ 这两个实体名已随 a4f32e4 / b227ae1 演进:输入点迹表更名并扩展为 `RdpRecPoint`(TWS/TAS 共用、增 `point_id`/`frame_id`/`circle_id`),输出航迹示例整体停用、改由 `RdpRecTrustTrack` 记录内部航迹(详见本版 Added 首条的 6 张在用表清单)。
|
||||
- 内置第三方源码 `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 个示例记录点(现已扩展为 6 张在用表 + 1 个停用实体,见本版 Added 首条)、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
|
||||
|
||||
> 以下条目为上一轮「工作区(未提交)」状态的改动,随本次 1.5.6 发布一并归档(内容未改写)。
|
||||
|
||||
- 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)。
|
||||
@@ -44,17 +80,38 @@
|
||||
- 关联波门 `ASSO_THORD` 由 3 调大至 4(e0bf227,`parameters.h` 相扫/机扫两组宏同步):判据写作 `d*d < ASSO_THORD*ASSO_THORD`,而 `d` 本身已是马氏距离平方,等效门限由 `d < 3` 放宽为 `d < 4`(TWS 分距离段的比例门限随之由 9/1000、9/100 变为 16/1000、16/100),配合高度门限判定提高机动目标的关联概率;调参时仍需注意这一层「二次方」语义(BUG-20 有意保留)。
|
||||
- TWS 与 TAS 关联的高度门限统一为同一组按航迹距离分段的取值(e0bf227):`r_track ≤ 1000 m` 时 `|h_track − h_point| ≤ 150 m`、`1000~2000 m` 时 ≤ 200 m、`2000~3000 m` 时 ≤ 250 m、`3000~5000 m` 时 ≤ 300 m、`> 5000 m` 不约束。其中 TWS 侧为该门限首次生效(原代码块被注释),TAS 侧 `2000~3000 m` 段由 200 m 放宽到 250 m。
|
||||
- TAS 波束输出的数据率门控条件修正(e0bf227,`tas_ctrl.cpp::tas_beam_output()`):原判据同时要求「距上次波束输出超过 DATA_RATE_TAS」与「航迹时间 T_track 与最新时间戳之差超过 DATA_RATE_TAS」,后者与「选取 CPI 时间最早目标」的语义冲突,会额外抑制本可输出的目标;现仅保留 `delta_T_last_track = (latest_timestamp − last_track_time)/1000 > DATA_RATE_TAS` 一条门控,`delta_T` 改为在选中该目标后按 `(latest_timestamp − CPI_time)/1000` 计算并用于外推,使 TAS 目标输出间隔严格跟随数据率。
|
||||
- 输出航迹改为「只输出本圈有更新的航迹」(50e0b5c):TWS `track_asso_process()` 与 TAS `track_asso_process_tas()` 的输出循环新增 `Track_Update_Flag == 1` 门控,输出后立即清零;该标志在关联到实点或外推成功时置 1。此前每个 TWS 周期都会把航迹表中的全部可靠航迹各输出一行(`Point_Sum = 1`),宿主不能再据输出行数推断航迹总数(当圈新起批的航迹仍由 `track_init` 的输出路径直接写入)。
|
||||
- TWS 未关联航迹外推增加圈数上限(50e0b5c,`track_asso.cpp::model_filter()`):仅当 `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(=3)时按 `DATA_RATE_SHORT/MIDDLE/FAR` 预测一帧、推进航迹时间并置 `Track_Update_Flag = 1`;超过上限后不再推进位置与时间,只累加 `Extrapolate_round`、置 `point_flag = 0` 与 `Track_Update_Flag = 0`(该航迹此后不再输出,位置冻结在最后一次预测处),直到重新关联到实点才恢复。避免长时间未关联的航迹被反复外推到远处。
|
||||
- TAS 未关联航迹外推同样增加上限(50e0b5c,`track_asso_tas.cpp`),但判定方向与 TWS 相反:`Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)时才做预测、推进航迹时间并输出,未达该轮数时只累加 `Extrapolate_round` 且不输出(位置与航迹时间停在最后一次关联处),达到上限后才恢复逐帧外推输出(见 README 11.2 的观察)。
|
||||
- 航迹消亡圈数与判据调整(50e0b5c,`parameters.h`、`track_die.cpp`、`track_die_tas.cpp`):`TRACK_DIE_ROUND` 由 4 调整为 6(两体制),新增重点航迹专用 `TRACK_DIE_ROUND_PRIMARY = 10`,`TRACK_DIE_ROUND_TAS` 相扫由 5 调整为 10(机扫保持 96);消亡判据由 `Extrapolate_round >= 阈值` 改为 `> 阈值`,并把 `manual_delete_flag == 1` 与轮数条件合并为统一的 `die_flag` 判定,TWS 侧按 `primary_target_flag` 在 6 / 10 之间二选一。因每圈未关联只自增 1,普通 TWS 航迹实际连续 7 圈无实点关联才消亡(重点航迹 11 圈,TAS 相扫 11 圈 / 机扫 97 圈)。
|
||||
- 「靠近/远离」方向标志 `Flag_Direction` 改为按滤波速度矢量判定(fc49707 引入、42b598e 调整,`track_asso.cpp` 与 `track_asso_tas.cpp` 同步):不再使用受多普勒速度模糊影响、符号可能取反的实测径向速度(原 `Flag_Direction = vr_point > 0 ? 1 : 0`),改为用航迹 IMM 融合状态的速度方向角 `Direction_Angle = atan2(X[4], X[1])` 与位置矢量判定:`acos((-x·cos(Direction_Angle) − y·sin(Direction_Angle)) / r) < π/2` 时置 1(靠近),否则 0(远离),其中 `x`/`y` 为经 `Sys_delay` 补偿后的位置、`r` 为其模。该判据等价于「滤波速度矢量的径向分量指向雷达(径向接近)即判为靠近」;中间版本曾按合速度归一化并把门限取为 π/4(45° 锥角),42b598e 去掉归一化并把门限放宽到 π/2(90°)。
|
||||
- 手动操作接口遍历命中目标后立即 `break`(fc49707,`data_process.cpp`):`primary_target_add` / `primary_target_cancel` / `tracking_start` / `tracking_stop` 在写入标志后退出循环,不再继续扫描整张航迹表(批号唯一,结果等价)。
|
||||
- `requirements.md` 由 `archived_docs/` 移回仓库根目录(工作区改动,文件内容未变):README 目录结构(第 3 节)与参考文档表(第 13 节)中的引用路径同步改回 `requirements.md`。
|
||||
- **构建工具链升级:编译器由 Microsoft Visual C++ 2013(Visual Studio 12,`_MSC_VER` 1800,x86)升级为 Microsoft Visual C++ 2022(v143 工具集,`_MSC_VER` 1944,cl 19.44.35228,x64),C++ 标准由 C++11 提升为 C++17**。`CMakeLists.txt` 的 `cmake_minimum_required` 由 3.10 提升到 3.21(VS2022 生成器自 3.21 起支持),新增 `CMAKE_CXX_STANDARD 17`(含 `CMAKE_CXX_STANDARD_REQUIRED ON`)与「必须使用 MSVC 2022(`_MSC_VER >= 1930`)」的配置期强校验(不满足直接 `FATAL_ERROR`),并在配置阶段打印实际编译器路径、`_MSC_VER` 与所选 C++ 标准。
|
||||
- `CMakeLists.txt` 编译选项调整:`/utf-8` 由「MSVC >= 1900 条件追加」改为对 MSVC 无条件启用;新增 `/Zc:__cplusplus`(MSVC 默认把 `__cplusplus` 报为 `199711L`,加该选项后正确报 `201703L`,使依赖该宏判断的第三方库——内置 Eigen 3.3.7——走标准分支);显式追加 `/W3`(CMake 3.15 起策略 CMP0092 不再把 `/W3` 放入 `CMAKE_CXX_FLAGS`,显式恢复以保持原有警告级别);`/Zc:wchar_t`、`/FS` 与 Release 专用的 `/Zc:strictStrings` 保持不变。
|
||||
- **全部构建脚本改为 MSVC 2022**:新增 `.vscode/scripts/msvc_env.bat`(定位 VS2022 工具集与 `cl.exe`、校验路径存在后调用 `vcvarsall.bat x64`,可用 `VS2022_DIR` / `MSVC_TOOLSET_VERSION` / `MSVC_HOST_ARCH` / `MSVC_TARGET_ARCH` 环境变量覆盖默认值),`configure.bat` / `build.bat` / `clean.bat` 统一改为调用该脚本并显式传入 `-DCMAKE_CXX_COMPILER`;四个脚本一律保持**纯 ASCII 编码**(cmd.exe 按 OEM 代码页解析 .bat,中文注释会破坏其解析)。
|
||||
- `configure.bat` 新增「旧工具集构建树自动清理」:构建目录存在 `CMakeCache.txt` 但缺少 `.msvc_toolset` 标记(或标记与当前工具集版本不一致)时,先整体删除该目录再重新配置。原因是 MSVC 2013 的 CMake 缓存与目标文件不能跨编译器复用——nmake 仅按时间戳判断,会把旧目标文件当作最新而跳过重编译,导致新旧工具集目标文件混链。
|
||||
- VS Code 工程配置同步为 MSVC 2022 x64 + C++17:`settings.json` 的 `cmake.environment` 改为 VS2022 v143 x64 与 Windows SDK 10.0.26100.0 的 `PATH`/`INCLUDE`/`LIB`,新增 `cmake.configureArgs` 固定 `cl.exe` 路径,`C_Cpp.default.cppStandard` 改为 `c++17` 并新增 `C_Cpp.default.intelliSenseMode`;`c_cpp_properties.json` 的 `compilerPath` 指向新 `cl.exe`、`intelliSenseMode` 改为 `windows-msvc-x64`、配置名改为「X256_PS/X256_MS (MSVC2022 x64)」;`cmake-variants.yaml` 与 `tasks.json` 的变体/任务描述同步为 MSVC2022 x64。
|
||||
- 产物架构由 x86 变为 **x64(AMD64)**:宿主程序必须为 64 位、并使用同一版本头文件重新编译后才能加载新 DLL(`rdp_playback.exe` 回放工具同样需升级为 x64 版本)。
|
||||
- TWS 关联中 ΔT 判定的分支结构理顺(b227ae1,`track_asso.cpp::model_filter()`):原写法为 `if (delta_T <= 0) { 空分支 } else { IMM/EKF 更新 }`,现改为 `if (delta_T > 0) { 置 min_d_flag、IMM/EKF 更新 }`,行为与原来等价(ΔT > 0 才做滤波更新,ΔT ≤ 0 仍只消费点迹),但空分支被移除、可读性提高;原 ΔT≤0 的告警日志注释块一并删除。
|
||||
- 记录点由「输出结构」改为「内部结构」(b227ae1):`data_process.cpp` 中按输出数组批量记录航迹的辅助函数 `rdp_record_output_tracks()` 与 `RDP_RECORD_BATCH` 调用整体移除,改在 `track_asso.cpp` 的输出循环内对内部 `Trust_Track` 逐条 `RDP_RECORD(RdpRecTrustTrack(...))`——记录的是航迹的完整内部状态(含 IMM 的 `X`/`P`/`X1`~`X3`/`P2`/`P3`/`u`),不再受输出数组容量与列映射的限制;范围仍限于本圈有输出(`Track_Update_Flag==1`)的航迹,且仅 TWS 通道。
|
||||
- `Temp_track` 字段命名规范化(c3c7e66,`struct.h`):`snr` → `SNR`、`Amp` → `amplitude`、`T` → `CPI_time`、`Temp_track_section_idx` → `temp_track_section_idx`,并新增 `temp_track_id` / `point_id` / `circle_id` / `pitch_num`;`track_init.cpp`、`track_init_direct_tracking.cpp` 中的引用同步更名(含注释停用代码)。
|
||||
- 输入点迹的两个「新字段」已打通(实际始于 a4f32e4 的 `data_process.cpp` 改写,本次发布确认并更正文档):`data_preprocess()` 现已把 `DataRev.amp_asv` / `DataRev.prn_fftn` 拷入内部 `PointRecv`(TWS 与 TAS 两条分支均赋值),并随 `RdpRecPoint` 落库;此前 README/CHANGELOG 记录的「输入侧未打通、输出字段无有效数据来源」已不成立(TAS 关联/输出路径仍未写回 `Track` 的这两个字段,见 11.2)。
|
||||
|
||||
### Fixed
|
||||
|
||||
- 修正文档中「`data_preprocess()` 未搬入 `amp_asv` / `prn_fftn`」的过期描述(本次文档更新):README 11.2、`docs/data_process_flow_report.md`、`docs/track_init_readme.md` 三处同步更正为「已搬入」,并保留「TAS 输出路径未写回」这一仍然成立的部分。
|
||||
- 修复 MSVC `/W3` 下全部 C4819 / C4018 / C4244 编译警告:源码文件改为带 BOM 的 UTF-8 编码;signed/unsigned 不匹配处改用 `size_t` 循环变量或显式 `static_cast<int>` 比较;double/float、__int64/double 等窄化转换改为 `static_cast` 显式转换,保持原有计算逻辑不变(涉及 `data_process.cpp`、`dot_coh.cpp`、`dot_coh_tas.cpp`、`tas_ctrl.cpp`、`track_asso.cpp`、`track_asso_tas.cpp`、`track_init.cpp`、`track_init_direct_tracking.cpp`、`track_index_mangement.cpp` 等)。
|
||||
- 修复 `TAS_Ctrl::tas_beam_output()` 把航迹向量下标 `earliest_track_idx` 误赋给 `Tracking_beam->TAS_track_index` 的问题(1521592):现输出实际航迹号 `(*trust_track)[earliest_track_idx].Track_Index`,避免跟踪波束批号与目标不符。
|
||||
- 修正 README.md(第 2、5.2、8.1、8.3、9.3、13 节)与 `docs/data_process_flow_report.md`(第 0、1、6.1~6.4 节、附录)中航迹起始的步骤顺序描述(纯文档勘误,无代码改动):两份文档原写作「航迹头(1 点)→ 点-航迹头关联(2 点)→ 点-临时航迹关联(≥3 点)→ 起批」,与 `track_init.cpp`(L49-126)每圈的实际执行顺序相反;现更正为「① 点迹先与 ≥2 点临时航迹关联(延长已有临时航迹)→ ② 再与 1 点航迹头关联(两点卡尔曼初始化出 2 点临时航迹)→ ③ 两个阶段都结束后统一删除被消费(`Use_Flag==1`)的点迹、剩余点迹逐条转为新航迹头 → ④ 长度达标的临时航迹经三点卡尔曼初始化转为可靠航迹」,并同步修正流程图与状态机;另补充两点此前的描述缺失:两个关联阶段互不排斥(航迹头阶段不检查临时航迹阶段留下的 `Use_Flag`,同一点迹可同时延长一条临时航迹并开启一条 2 点临时航迹)、两类关联都不做一对一分配(同一条临时航迹可被同圈多个点延长)。
|
||||
- 修复速度模糊时「向站/背站」判断有误的问题(fc49707、42b598e):`Flag_Direction` 不再由受多普勒模糊影响、符号可能取反的实测径向速度 `vr_point` 决定,改为按航迹滤波速度矢量与雷达视线的几何关系判定(判据见 Changed),TWS、TAS 两通道行为一致。
|
||||
- 修复升级到 MSVC 2022 后在 `/W3` 下新增报出的 **17 处 C4267**(`size_t` → `int` 窄化,可能丢失数据)告警:`track_asso.cpp`(7 处)、`track_init.cpp`(5 处)、`track_init_direct_tracking.cpp`(4 处)、`track_asso_tas.cpp`(1 处)、`tas_ctrl.cpp`(1 处)按项目既有约定改为 `static_cast<int>(...)` 显式转换,不改变任何计算逻辑、对外接口与结构体布局;被编辑的源文件均已恢复「带 BOM 的 UTF-8」编码,与仓库其余文件保持一致。修复后 X256_PS/X256_MS × Release/Debug 四种配置均为 **0 warning、0 error**。
|
||||
|
||||
### Removed
|
||||
|
||||
- 删除已注释停用的无用代码残留(d6cdbc0):`data_process.cpp` 中 TWS/TAS 分支的 `std::cout` 调试输出注释块,以及 `dot_coh.cpp` 中早期「1 km 以内 SNR < 20 dB 点迹过滤」的注释代码块。仅清理注释,不改变任何运行逻辑。
|
||||
- 删除 `docs/data_process_flow_report.html`(e0bf227):该 HTML 渲染版与 `docs/data_process_flow_report.md` 内容重复且已明显滞后,保留价值不足;同内容的 HTML 已在 git 忽略的 `design_docs/` 内另行留档。README 目录结构、参考文档表与版本历史表中指向该文件的条目同步移除。
|
||||
- 删除 `track_asso.cpp` TWS 输出循环中已注释停用的 `manual_tracking_flag == 0` 过滤代码块(fc49707):该过滤从未生效,删除后 TWS 输出仍会把手动转 TAS(`Track_Mode==1`)的航迹一并输出(BUG-25 未修复,见 README 11.2)。
|
||||
|
||||
## [1.5.5] - 2026-08-27
|
||||
|
||||
|
||||
+131
-15
@@ -1,8 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
|
||||
# 雷达数据处理动态链接库
|
||||
# 开发环境已由 Qt/qmake 迁移到 VS Code + CMake。
|
||||
# 编译器保持为 Microsoft Visual C++ Compiler 12.0 (x86)。
|
||||
# 编译器已由 Microsoft Visual C++ Compiler 12.0 (VS2013) 升级为
|
||||
# Microsoft Visual C++ Compiler 2022(v143 工具集,_MSC_VER 19.4x,x64)。
|
||||
# 语言标准同步提升为 C++17。
|
||||
#
|
||||
# 注意:VS2022 生成器("Visual Studio 17 2022")需要 CMake >= 3.21,
|
||||
# 因此本工程的最低 CMake 版本随之提高;NMake/Ninja 生成器同样满足。
|
||||
project(data_process_class_dll LANGUAGES CXX)
|
||||
|
||||
# 扫描模式:
|
||||
@@ -16,6 +21,68 @@ 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
|
||||
# ---------------------------------------------------------------------------
|
||||
# CMAKE_CXX_STANDARD 由 CMake 转换为 MSVC 的 /std:c++17(CMake >= 3.10 起支持)。
|
||||
# CMAKE_CXX_EXTENSIONS OFF 在 MSVC 下没有对应开关(只影响 GCC/Clang 的 -std=gnu++xx),
|
||||
# 保留它是为了在换用其它编译器时同样不启用语言扩展。
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 编译器约束:必须使用 MSVC 2022(v143,_MSC_VER >= 1930)
|
||||
# ---------------------------------------------------------------------------
|
||||
if(NOT MSVC)
|
||||
message(FATAL_ERROR "本工程仅支持 MSVC (cl.exe) 编译器,当前编译器:${CMAKE_CXX_COMPILER_ID}")
|
||||
endif()
|
||||
|
||||
if(MSVC_VERSION LESS 1930)
|
||||
message(FATAL_ERROR
|
||||
"本工程要求 MSVC 2022 (v143) 及以上版本(_MSC_VER >= 1930),"
|
||||
"当前 _MSC_VER=${MSVC_VERSION}(编译器:${CMAKE_CXX_COMPILER})。\n"
|
||||
"请先运行 VS2022 的 vcvarsall.bat x64(见 .vscode/scripts/msvc_env.bat)后再配置。")
|
||||
endif()
|
||||
|
||||
message(STATUS "编译器:${CMAKE_CXX_COMPILER}(MSVC _MSC_VER=${MSVC_VERSION})")
|
||||
message(STATUS "C++ 标准:C++${CMAKE_CXX_STANDARD}")
|
||||
|
||||
# 导出编译命令,便于 VS Code C/C++ 插件 IntelliSense 使用。
|
||||
# Visual Studio 生成器不支持该选项,因此仅对 Makefile/Ninja 生成器开启。
|
||||
if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
@@ -47,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
|
||||
@@ -69,25 +179,31 @@ target_include_directories(data_process_class_dll PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/data_process_class_dll
|
||||
)
|
||||
|
||||
# 原工程使用 MSVC 2013(Visual Studio 12)编译器;保持兼容,不强制更高 C++ 标准。
|
||||
# 源码使用 C++11 特性(nullptr、类内成员初始化等),MSVC 2013 默认即可支持。
|
||||
|
||||
if(MSVC)
|
||||
# 与原 qmake 生成的编译选项保持一致。
|
||||
# 注意:MSVC 2013 的 Debug 运行库与 /Zc:strictStrings 同时使用会触发 STL 编译错误,
|
||||
# 原 qmake 工程也仅在 Release 配置中使用该选项。
|
||||
target_compile_options(data_process_class_dll PRIVATE
|
||||
# 显式恢复 /W3 警告级别。
|
||||
# CMake 3.15(策略 CMP0092)起不再把 /W3 放进 CMAKE_CXX_FLAGS,
|
||||
# 而本工程此前(cmake_minimum_required 3.10)一直以 /W3 构建,
|
||||
# README 4.5 记录的全部 C4819/C4018/C4244 告警清理也基于该级别,
|
||||
# 因此这里显式指定,保持警告级别不变。
|
||||
/W3
|
||||
# MSVC 2022 完整支持 /utf-8(VS2015 Update 2 起),
|
||||
# 源码本身为 UTF-8 with BOM,显式指定后中文字符串/注释不再受系统代码页影响(消除 C4819)。
|
||||
/utf-8
|
||||
# 让 __cplusplus 正确报告 201703L(MSVC 默认始终为 199711L)。
|
||||
# C++17 下依赖 __cplusplus 判断的第三方库(Eigen 3.3.7 等)据此走标准分支。
|
||||
/Zc:__cplusplus
|
||||
/Zc:wchar_t
|
||||
# NMake 生成器下并行/增量编译时统一 PDB 写入。
|
||||
/FS
|
||||
$<$<CONFIG:Release>:/Zc:strictStrings>
|
||||
)
|
||||
|
||||
# 修复 C4819(源文件含当前代码页无法表示的字符)。
|
||||
# /utf-8 从 VS2015 Update 2 开始支持;本工程使用 MSVC 2013 时无该选项,
|
||||
# 通过为源码文件添加 UTF-8 BOM 解决 C4819,这里仅对支持 /utf-8 的新版 MSVC 开启。
|
||||
if(MSVC_VERSION GREATER_EQUAL 1900)
|
||||
target_compile_options(data_process_class_dll PRIVATE /utf-8)
|
||||
endif()
|
||||
# 与原 qmake 生成的编译选项保持一致:Release 配置额外开启严格字符串字面量检查。
|
||||
# (原 MSVC 2013 因 Debug 运行库与 /Zc:strictStrings 冲突而仅限 Release,
|
||||
# 升级到 MSVC 2022 后该限制已不存在,此处仍沿用“仅 Release”以保持行为不变。)
|
||||
target_compile_options(data_process_class_dll PRIVATE
|
||||
$<$<CONFIG:Release>:/Zc:strictStrings>
|
||||
)
|
||||
endif()
|
||||
|
||||
# 输出目录:build/bin 下为 DLL/PDB,build/lib 下为导入库。
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# 雷达数据处理 DLL(data_process_class_dll)
|
||||
|
||||
> X256 雷达数据处理动态链接库:将雷达输出的原始点迹(TWS/TAS)处理为稳定可靠的目标航迹,并给出 TAS 跟踪波束控制指令。
|
||||
> 仓库 simianzhen256-v1.5.5-ms-vscode(上游 simianzhen256,v1.5.5;已由 Qt/qmake 工程迁移为 VS Code + CMake 工程)。
|
||||
> 仓库 simianzhen256-v1.5.5-ms-vscode(目录名沿用旧版本,当前代码版本为 **V1.5.6**;已由 Qt/qmake 工程迁移为 VS Code + CMake 工程,编译器为 MSVC 2022 / v143,C++17,x64 目标)。
|
||||
>
|
||||
> 版本号由 `parameters.h` 的 `MAJOR_VERSION` / `MINOR_VERSION` / `PATCH_VERSION`(当前 1.5.6)与体制后缀拼装为 `VERSION_STRING`(相扫 `V1.5.6.PS` / 机扫 `V1.5.6.MS`),实例创建后可从 `Data_Process_Factory::GetB()` 返回对象的公开成员 `version_info`(`const char*`)读取。
|
||||
|
||||
---
|
||||
|
||||
@@ -35,7 +37,7 @@
|
||||
- 航迹外推、消亡与航迹号管理
|
||||
- TAS 手动转跟踪与跟踪波束调度输出
|
||||
|
||||
**技术栈**:C++(MSVC 2013 兼容,C++11 特性)、Eigen 3(随仓库内置,仅用 Eigen/Dense)、CMake ≥ 3.10。
|
||||
**技术栈**:C++(MSVC 2022 / v143 工具集,C++17)、Eigen 3(随仓库内置,仅用 Eigen/Dense)、CMake ≥ 3.21。
|
||||
|
||||
**处理约定**:
|
||||
|
||||
@@ -55,12 +57,16 @@
|
||||
- **两帧滑窗点迹凝聚**:TWS 点迹先与上一帧缓存合并凝聚,再进入关联,兼顾跨波位(CPI)相邻点迹的分裂问题;近程模式增加速度维约束。
|
||||
- **逻辑法航迹起始**:每圈按固定顺序推进——① 未关联点迹先与 ≥2 点临时航迹做 EKF 波门 + 折角约束关联(延长临时航迹),② 再与 1 点航迹头做速度法关联(两点卡尔曼初始化出 2 点临时航迹),③ 两步都做完后删除被消费的点迹、剩余点迹转成新航迹头,④ 长度达标的临时航迹经三点卡尔曼初始化转可靠航迹;带起批屏蔽区与重复航迹去重。
|
||||
- **3 模型 IMM 滤波**:三个 Singer 模型(低/中/高过程噪声),量测为「距离-方位-径向速度」三维的 EKF,多普勒残差按盲速折叠,似然用 3×3 新息协方差。
|
||||
- **最近邻数据关联**:三模型统计距离波门 `ASSO_THORD = 4`(等效 d < 4,TWS 另按航迹距离分段收紧模型 3 波门),再叠加高度门限(≤1000 m 150 m / 2000 m 200 m / 3000 m 250 m / 5000 m 300 m,更远不约束)与 TWS 弦距-位移距离门限。ΔT ≤ 0(点迹时间戳不晚于航迹时间)的候选不更新航迹状态、仅消费该点迹(标记已使用),避免其继续起批而生成虚假 TWS 航迹。关联输出的航迹同时给出**靠近/远离方向标志**:`Flag_Direction = vr_point > 0 ? 1 : 0`(1 靠近 / 0 远离),TWS、TAS 两通道一致。
|
||||
- **最近邻数据关联**:三模型统计距离波门 `ASSO_THORD = 4`(等效 d < 4,TWS 另按航迹距离分段收紧模型 3 波门),再叠加高度门限(≤1000 m 150 m / 2000 m 200 m / 3000 m 250 m / 5000 m 300 m,更远不约束)与 TWS 弦距-位移距离门限。ΔT ≤ 0(点迹时间戳不晚于航迹时间)的候选不更新航迹状态、仅消费该点迹(标记已使用),避免其继续起批而生成虚假 TWS 航迹。关联输出的航迹同时给出**靠近/远离方向标志**:按航迹 IMM 融合速度矢量与「雷达→目标」位置矢量的夹角判定——`acos((-x·cos(Direction_Angle) − y·sin(Direction_Angle)) / r) < π/2` 置 1(靠近),否则 0(远离),等价于「滤波速度的径向分量指向雷达」,不再使用受多普勒模糊影响的实测径向速度 `vr_point`;TWS、TAS 两通道一致(见 9.6)。
|
||||
- **重点目标与外推上限**:`primary_target_add` / `primary_target_cancel` 可按批号手动标记/取消重点目标,重点航迹的消亡轮数放宽到 `TRACK_DIE_ROUND_PRIMARY`(10,普通航迹 6);未关联航迹的外推圈数受限(TWS ≤ `MAX_EXTRAPOLATE_NUM`=3,TAS 相扫 5 / 机扫 48),超限后停止外推。**输出航迹只包含本圈有更新的航迹**(`Track_Update_Flag` 门控),宿主不能再用输出行数推断航迹总数(见 9.9)。
|
||||
- **点迹附加信息字段扩展(需同步重编译宿主)**:`DataRev` 新增 `amp_asv` / `prn_fftn`,`Track` 新增 `amp_asv_point` / `prn_fftn_point`,结构体布局随之变化,宿主必须使用同一版本头文件重新编译对接;输入侧已打通(TWS 输出有效,TAS 输出仍未写回),详见 11.2。
|
||||
- **高度维独立平滑**:按距离分段选择滑动平均窗长(5/6/7/9 点),抑制俯仰测量跳变。
|
||||
- **TAS 波束控制**:手动转 TAS 目标入队(队列长度与容量随扫描体制切换),按最新时间戳外推输出跟踪波束(距离/方位/俯仰/批号)。
|
||||
- **航迹生命周期管理**:外推轮数计数、超时消亡、手动删除、全清空(含各模块 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)。
|
||||
- **版本号输出**:`parameters.h` 的 `MAJOR_VERSION`/`MINOR_VERSION`/`PATCH_VERSION`(当前 1.5.6)经两级 `STRINGIFY` 宏拼成 `VERSION_STRING`(`V1.5.6.PS` / `V1.5.6.MS`);`Data_process_class_dll` 基类公开成员 `const char* version_info` 由 `Data_Process_Factory::GetB()` 在创建实例时赋值,宿主可直接读取以核对/上报 DLL 版本(b227ae1)。
|
||||
- **数据记录调试功能(可选编译)**:`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`,同步写入、失败降级(不抛出、不影响雷达处理)。当前内置 **6 张在用记录表**(输入点迹、TWS/TAS 凝聚后点迹、临时航迹、可靠航迹、点航关联信息,共 7 个实体定义、其中 1 个已停用),并新增 `point_id`/`frame_id`/`circle_id`/`temp_track_id`/`track_id` 等关联标识,可把一次处理的点迹→凝聚→临时航迹→可靠航迹→关联信息串成完整链路;新增记录数据只需「加结构体 + 注册 + 一行记录点」,详见 docs/data_record_debug.md(见 4.3、10.1、11.3)。
|
||||
|
||||
---
|
||||
|
||||
@@ -71,30 +77,39 @@
|
||||
├── CMakeLists.txt # CMake 构建脚本(扫描模式切换、导出宏、输出目录)
|
||||
├── CHANGELOG.md # 变更日志(Keep a Changelog 格式,按 git 提交记录维护)
|
||||
├── README.md # 本文件:项目说明(架构、接口、流程、算法、参数)
|
||||
├── requirements.md # 需求说明:数据记录调试功能修改要求(结构体经 ormpp 写入 SQLite、Release 可屏蔽、数据分类入不同表、宏记录点;原日志文件输出需求已实现)
|
||||
├── archived_docs/ # 过期文档留档(仅作历史参考;接口与流程以本 README 与 docs/ 专项说明为准)
|
||||
│ ├── BUG_REPORT.md # 全量逻辑 BUG 分析报告(P0~P3,37 项)
|
||||
│ ├── BUG_FIX_REPORT.md # BUG 修复报告(修复明细 + 编译验证结果)
|
||||
│ └── requirements.md # 需求说明:日志文件输出功能修改要求(含 rdbuf() 重定向参考示例)
|
||||
│ └── 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 # 数据记录调试功能说明(开关与构建、运行期行为、表结构约定、新增记录点四步配方、6 张在用记录表清单与字段、查询示例)
|
||||
├── design_docs/ # 设计与讨论资料留档(被 .gitignore 忽略、不入库;含设计讨论、产品介绍与 README/流程报告的导出、渲染版本)
|
||||
├── .vscode/ # VS Code 工程配置
|
||||
│ ├── settings.json # cmake.generator=NMake、MSVC2013 x86 环境变量、compile_commands
|
||||
│ ├── settings.json # cmake.generator=NMake、MSVC2022 x64 环境变量、compile_commands
|
||||
│ ├── cmake-variants.yaml # X256_PS / X256_MS × Release / Debug 变体
|
||||
│ ├── tasks.json # 配置/构建/清理任务
|
||||
│ └── scripts/{configure,build,clean}.bat
|
||||
│ ├── tasks.json # 配置/构建/清理任务(MSVC2022 x64)
|
||||
│ └── scripts/ # 构建脚本(纯 ASCII,避免 cmd 代码页问题)
|
||||
│ ├── msvc_env.bat # 共享环境设置:定位 VS2022/cl.exe 并调用 vcvarsall.bat(x64)
|
||||
│ ├── configure.bat # CMake 配置(含旧工具集构建树自动清理)
|
||||
│ ├── build.bat # CMake 构建
|
||||
│ └── clean.bat # CMake 清理
|
||||
├── backup/
|
||||
│ └── 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.h # 对外接口:DataRev/Track/TrackingBeam/RadarPara 结构体、抽象基类(公开成员 version_info)、工厂类
|
||||
│ ├── 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)
|
||||
│ ├── data_process.h / .cpp # Data_Process 实现:接口分发、TWS/TAS 主流程、参数管理、清空/删除/转跟踪
|
||||
│ ├── struct.h # 内部结构体:PointRecv / Trust_Track / Temp_track / Tracking_Target
|
||||
│ ├── rdp_record.h / .cpp # 数据记录调试模块:结构体→SQLite 表(内置 ormpp)、RDP_RECORD/RDP_RECORD_BATCH 宏、按实体自动建表、失败降级(开关由 CMake 选项 RDP_DATA_RECORD 控制;实现以 C++20 独立编译)
|
||||
│ ├── rdp_record_entity.h # 记录实体(=数据表)定义区:6 张在用表 + 1 个已停用实体(输入点迹、TWS/TAS 凝聚后点迹、临时航迹、可靠航迹、关联信息;输出航迹示例已停用)+ RDP_RECORD_DECLARE 声明宏;新增需要记录的数据在此加结构体
|
||||
│ ├── data_process.h / .cpp # Data_Process 实现:接口分发、TWS/TAS 主流程、参数管理、清空/删除/转跟踪;含 point_id/frame_id/circle_id 自增计数(记录用)
|
||||
│ ├── struct.h # 内部结构体:PointRecv / Trust_Track / Temp_track / Asso_Info / Tracking_Target
|
||||
│ ├── parameters.h # 编译期宏参数(按 MECHANICAL_SCANNING / PHASE_SCANNING 分两组)
|
||||
│ ├── dot_coh.h / .cpp # TWS 点迹凝聚(含两帧滑窗缓冲)
|
||||
│ ├── dot_coh_tas.h / .cpp # TAS 点迹凝聚
|
||||
@@ -115,6 +130,7 @@
|
||||
│ ├── X256_MS/ # 机扫构建目录
|
||||
│ └── output/ # 早期打包导出(其中 data_process_class_dll.h 为旧版 32 位时间戳接口,勿作为当前 API 依据)
|
||||
├── rdp_logs/ # 日志输出目录(运行时自动创建于宿主进程当前工作目录下,被 .gitignore 忽略)
|
||||
├── rdp_records/ # 数据记录库输出目录(运行时自动创建于宿主进程当前工作目录下,被 .gitignore 忽略;仅记录功能启用时产生)
|
||||
└── .gitignore
|
||||
```
|
||||
|
||||
@@ -126,11 +142,13 @@
|
||||
|
||||
| 组件 | 要求 |
|
||||
|---|---|
|
||||
| 操作系统 | Windows(构建脚本按 D:\APP\VisualStudio2013 与 Windows Kits 8.1 路径配置) |
|
||||
| 编译器 | Microsoft Visual C++ 2013(Visual Studio 12)**x86**(与原 Qt 工程保持一致) |
|
||||
| CMake | ≥ 3.10 |
|
||||
| 操作系统 | Windows(构建脚本默认按 D:\APP\VisualStudio2022 与 Windows SDK 10.0.26100.0 配置,可用环境变量覆盖) |
|
||||
| 编译器 | Microsoft Visual C++ 2022(v143 工具集,`_MSC_VER` 19.44)**x64**(`D:\APP\VisualStudio2022\VC\Tools\MSVC\14.44.35207\bin\Hostx64\x64\cl.exe`) |
|
||||
| C++ 标准 | C++17(`/std:c++17`;由 `CMAKE_CXX_STANDARD 17` + `CMAKE_CXX_STANDARD_REQUIRED ON` 强制) |
|
||||
| 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 扫描体制
|
||||
|
||||
@@ -144,12 +162,13 @@
|
||||
### 4.3 命令行构建
|
||||
|
||||
```bat
|
||||
:: 需在 MSVC 2013 x86 环境中执行(先运行 vcvarsall.bat x86)
|
||||
cmake -S . -B build/X256_PS -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DRADAR_SCAN_MODE=PHASE_SCANNING
|
||||
:: 需在 MSVC 2022 x64 环境中执行(先运行 vcvarsall.bat x64)
|
||||
cmake -S . -B build/X256_PS -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DRADAR_SCAN_MODE=PHASE_SCANNING ^
|
||||
-DCMAKE_CXX_COMPILER="D:/APP/VisualStudio2022/VC/Tools/MSVC/14.44.35207/bin/Hostx64/x64/cl.exe"
|
||||
cmake --build build/X256_PS
|
||||
```
|
||||
|
||||
或直接使用仓库提供的脚本(内部已调用 vcvarsall.bat):
|
||||
或直接使用仓库提供的脚本(内部已通过 msvc_env.bat 调用 vcvarsall.bat x64 并校验 cl.exe):
|
||||
|
||||
```bat
|
||||
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Release
|
||||
@@ -157,6 +176,20 @@ cmake --build build/X256_PS
|
||||
.vscode\scripts\clean.bat 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 产物
|
||||
@@ -167,14 +200,37 @@ cmake --build build/X256_PS
|
||||
| 导入库 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 分发** |
|
||||
|
||||
仓库 build/X256_PS/bin/ 内还带有 **rdp_playback.exe**(回放测试工具,依赖 Qt5Core.dll、sqlite3.dll,配置样例 RadarConfigParam.ini,可回放 .db 记录的点迹数据验证 DLL 输出),不在本仓库源码范围内。
|
||||
产物为 **x64(AMD64)** 目标。宿主程序必须是 64 位、且使用与本 DLL 同一版本头文件重新编译——MSVC 2013 → MSVC 2022 同时伴随 x86 → x64 的 ABI 变化,旧的 32 位宿主无法加载该 DLL。
|
||||
|
||||
仓库 build/X256_PS/bin/ 内原附带 **rdp_playback.exe**(回放测试工具,依赖 Qt5Core.dll、sqlite3.dll,配置样例 RadarConfigParam.ini,可回放 .db 记录的点迹数据验证 DLL 输出),该工具不在本仓库源码范围内、当前构建目录中亦不存在,需自行放置;由于 DLL 已改为 x64,配套的 rdp_playback.exe 必须是 **x64 版本**(同时其自带 Qt5Core.dll / sqlite3.dll 也需为 x64),32 位版本无法加载新版 DLL。
|
||||
|
||||
### 4.5 编译警告处理
|
||||
|
||||
- 源码文件统一使用 **带 BOM 的 UTF-8** 编码保存,兼容 MSVC 2013(该版本不支持 `/utf-8`),消除 C4819。
|
||||
- `CMakeLists.txt` 对 MSVC 19.0 及以上版本自动追加 `/utf-8` 编译选项;使用 MSVC 2013 构建时请保持源码为 UTF-8 with BOM。
|
||||
- 修复了 `/W3` 下全部 C4018(signed/unsigned 不匹配)与 C4244(类型转换可能丢失数据)警告:循环变量按需改为 `size_t`,窄化转换改为 `static_cast` 显式转换,不改变原有计算逻辑。
|
||||
- 源码文件统一使用 **带 BOM 的 UTF-8** 编码保存;`CMakeLists.txt` 对 MSVC 统一追加 `/utf-8`,中文字符串与注释不再受系统代码页影响(消除 C4819),BOM 仅作为编辑器/旧工具的兼容保留。
|
||||
- `CMakeLists.txt` 显式指定 `/W3`:CMake 3.15(策略 CMP0092)起不再把 `/W3` 放进 `CMAKE_CXX_FLAGS`,本工程此前一直以 `/W3` 构建,故显式恢复以保持警告级别不变。
|
||||
- 修复了 `/W3` 下全部 C4819(源文件字符集)、C4018(signed/unsigned 不匹配)、C4244(类型转换可能丢失数据)警告:循环变量按需改为 `size_t`,窄化转换改为 `static_cast` 显式转换,不改变原有计算逻辑。
|
||||
- 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)
|
||||
|
||||
| 项目 | 升级前 | 升级后 |
|
||||
|---|---|---|
|
||||
| 编译器 | Microsoft Visual C++ 2013(VS 12.0,`_MSC_VER` 1800) | Microsoft Visual C++ 2022(v143,`_MSC_VER` 1944,cl 19.44.35228) |
|
||||
| 目标架构 | x86(32 位) | x64(AMD64) |
|
||||
| C++ 标准 | C++11(编译器默认) | C++17(`/std:c++17`) |
|
||||
| CMake 最低版本 | 3.10 | 3.21 |
|
||||
| 新增/调整编译选项 | — | `/utf-8`(改为无条件)、`/Zc:__cplusplus`、`/W3`(显式恢复) |
|
||||
|
||||
- **`/Zc:__cplusplus`**:MSVC 默认把 `__cplusplus` 报为 `199711L`,加该选项后正确报 `201703L`,使依赖 `__cplusplus` 判断的第三方库(内置 Eigen 3.3.7)走标准分支。已验证 Eigen 3.3.7 在此组合下编译通过。
|
||||
- **`/Zc:strictStrings` 仍仅在 Release 启用**:原限制(MSVC 2013 Debug 运行库与该选项冲突)已不存在,此处保持与原 qmake 工程一致的行为。
|
||||
- **旧构建目录需清理**:MSVC 2013 的 CMake 缓存与目标文件不能跨编译器复用(nmake 会因目标文件时间戳较新而跳过重编译,导致新旧目标文件混链)。`configure.bat` 已内置自动清理:构建目录中无 `.msvc_toolset` 标记或标记与当前工具集不一致时,先删除整个构建目录再配置。
|
||||
- **源码改动**:仅补齐 17 处 `static_cast<int>(...)` 显式窄化转换以消除 MSVC 2022 新增的 C4267 告警,未改动任何计算逻辑、接口与结构体布局;DLL 导出符号与升级前一致(差异仅为 x86 → x64 的名称修饰,如 `QAE` → `QEAA`、`PAV` → `PEAV`)。
|
||||
- **兼容性风险点已核查**:C++17 移除的 `std::auto_ptr`、`std::random_shuffle`、`std::unary_function`/`binary_function`、`std::binder1st/2nd` 等在本工程源码与内置 Eigen 3.3.7 中均未被使用(Eigen 的相应特化本身带有 `__cplusplus`/`EIGEN_COMP_MSVC` 版本保护)。
|
||||
|
||||
---
|
||||
|
||||
@@ -189,6 +245,7 @@ cmake --build build/X256_PS
|
||||
classDiagram
|
||||
class Data_process_class_dll {
|
||||
<<abstract, exported API>>
|
||||
+const char* version_info
|
||||
+data_preprocess(DataRev[150]) int
|
||||
+track_process(Track[][10], int*, int[], int*, int) int
|
||||
+Beam_Ctrl(TrackingBeam*, Track[][10], int*)
|
||||
@@ -248,18 +305,19 @@ classDiagram
|
||||
|
||||
| 类 / 文件 | 职责 |
|
||||
|---|---|
|
||||
| Data_Process(data_process.cpp) | 对外接口实现与流程编排:输入缓存、按 model 分发 TWS/TAS 流水线、参数管理、航迹清空/删除、手动转/停 TAS、波束控制转发 |
|
||||
| Data_Process(data_process.cpp) | 对外接口实现与流程编排:输入缓存、按 model 分发 TWS/TAS 流水线、参数管理、航迹清空/删除、手动转/停 TAS、手动标记/取消重点目标(primary_target_add/cancel)、波束控制转发 |
|
||||
| Dot_Coh | TWS 点迹凝聚:两帧滑窗合并凝聚(dot_coh_process_buff) |
|
||||
| Dot_Coh_TAS | TAS 点迹凝聚(单帧,距离/方位/速度三维门限) |
|
||||
| Track_Asso | TWS 点航关联 + IMM 滤波:模型交互 → 门限筛选(NN)→ 各模型 EKF → 概率更新 → 模型输出;未关联航迹外推;高度平滑;ΔT≤0(乱序/重复时间戳)的候选取出后不更新滤波、不刷新航迹,仅消费该点迹 |
|
||||
| Track_Asso_Tas | TAS 航迹关联(只处理指定批号 tas_track_idx,门限与 TWS 略有差异) |
|
||||
| Track_Asso | TWS 点航关联 + IMM 滤波:模型交互 → 门限筛选(NN)→ 各模型 EKF → 概率更新 → 模型输出(只输出 `Track_Update_Flag==1` 的航迹);未关联航迹按上限外推;高度平滑;按滤波速度矢量写 `Flag_Direction`;ΔT≤0(乱序/重复时间戳)的候选取出后不更新滤波、不刷新航迹,仅消费该点迹 |
|
||||
| Track_Asso_Tas | TAS 航迹关联(只处理指定批号 tas_track_idx,门限与 TWS 略有差异;输出同样受 `Track_Update_Flag` 门控,外推上限 `MAX_EXTRAPOLATE_NUM_TAS` 的判定方向与 TWS 相反) |
|
||||
| Track_Init | 逻辑法航迹起始,每圈顺序固定:点-临时航迹关联(延长 ≥2 点临时航迹)→ 点-航迹头关联(两点起始)→ 剩余点转新航迹头 → 长度达标者起批(三点卡尔曼初始化);另含起批屏蔽区判断与临时航迹消亡 |
|
||||
| Track_Die / Track_Die_Tas | 可靠航迹消亡:外推轮数超限或手动删除标志置位时输出消亡批号并删除 |
|
||||
| Track_Die / Track_Die_Tas | 可靠航迹消亡:未关联轮数超限(普通 TWS 6 圈 / 重点航迹 10 圈 / TAS 相扫 10、机扫 96)或手动删除标志置位时输出消亡批号并删除 |
|
||||
| TAS_Ctrl | TAS 波束控制:手动目标入队(tas_target_add)、失效目标出队(tas_target_del)、跟踪波束预测输出(tas_beam_output:数据率门控 + 队内选择最早 CPI 目标)、调度时刻记录(last_track_time) |
|
||||
| Track_Ind_Mangement | 航迹号 1~500 的顺序分配与回绕复用 |
|
||||
| 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()` 调用;建表/插入失败一律降级(每个实体只提示一次),不影响雷达处理。**当前 7 个实体定义、6 张在用表**:`RdpRecPoint`(输入点迹,TWS/TAS 共用)、`RdpRecPointCohesionTWS`、`RdpRecPointCohesionTAS`、`RdpRecTempTrack`、`RdpRecTrustTrack`、`RdpRecAssoInfo`(另有已停用的 `RdpRecTrackOut`),并有数组转字符串辅助 `arrayToString()`(用于把 `X`/`P` 等状态数组存为文本列) |
|
||||
| Track_Init_Direct_Tracking / Track_Asso_Direct_Tracking | 引导跟踪(direct tracking)专用起批/关联。**当前入口为空实现、未启用**;track_asso_direct_tracking.cpp 未加入 CMake 源列表 |
|
||||
|
||||
---
|
||||
@@ -270,8 +328,8 @@ classDiagram
|
||||
|
||||
| 结构体 | 用途 | 要点 |
|
||||
|---|---|---|
|
||||
| DataRev | 输入点迹 | 距离(m)、方位/俯仰(°)、速度(m/s)、幅度/门限/SNR/RCS、码盘值、CPI/GNSS 时间(ms, long long)、方位波位(0-24)/俯仰波位(0-13)、PRI(µs,**不允许为 0**)、频点号、点数、点迹号、使用标记、TAS 批号、点迹类型、十字星数据 speed_dim[129]/range_dim[8] |
|
||||
| Track | 输出航迹 | 位置/速度、极坐标量、航迹号、更新点数、方向标志 Flag_Direction(关联输出赋值:1 靠近 / 0 远离,见 9.6)、实虚点标志 Flag_Point、一步预测、跟踪模式、航向角、目标类型、关联点信息、航迹时间(s)/GNSS、SNR/RCS、十字星数据透传 |
|
||||
| DataRev | 输入点迹 | 距离(m)、方位/俯仰(°)、速度(m/s)、幅度/门限/SNR/RCS、码盘值、CPI/GNSS 时间(ms, long long)、方位波位(0-24)/俯仰波位(0-13)、PRI(µs,**不允许为 0**)、频点号、点数、点迹号、使用标记、TAS 批号、点迹类型、归一化幅度 `amp_asv`(dB)、速度维 FFT 点数 `prn_fftn`、十字星数据 speed_dim[129]/range_dim[8] |
|
||||
| Track | 输出航迹 | 位置/速度、极坐标量、航迹号、更新点数、方向标志 Flag_Direction(仅关联/外推输出路径赋值:按滤波速度矢量判定,1 靠近 / 0 远离,见 9.6;起批输出不写该字段)、实虚点标志 Flag_Point、一步预测、跟踪模式、航向角 Direction_Angle、目标类型、关联点信息、归一化幅度 `amp_asv_point` / 速度维 FFT 点数 `prn_fftn_point`、航迹时间(s)/GNSS、SNR/RCS、十字星数据透传 |
|
||||
| TrackingBeam | TAS 跟踪波束指令 | open_flag、type、Range、Azi、Elev、TAS_track_index |
|
||||
| Target_direct_tracking | 引导跟踪信息 | 批号、距离/方位/俯仰、开关(接口未启用) |
|
||||
| RadarPara | 工作参数 | 见 [参数说明](#10-参数说明) |
|
||||
@@ -280,13 +338,16 @@ classDiagram
|
||||
|
||||
| 结构体 | 用途 | 关键字段 |
|
||||
|---|---|---|
|
||||
| PointRecv | 内部点迹 | 距离/方位(弧度)/速度/幅度/高度/SNR/RCS/CPI/GNSS 时间、点迹号、使用标志、波位/PRF/频点、跟踪模式、点迹区号、十字星数据 |
|
||||
| Trust_Track | 可靠航迹(IMM 全状态) | 航迹号、高度、航迹时间、更新标志、外推轮数 Extrapolate_round、实虚点标志、手动删除/手动 TAS 标志、关联点数、高度平滑缓冲 Hight_smooth、关联点信息、融合状态 X[6]/P[6][6]、三个子模型 X1/X2/X3、P1/P2/P3、模型概率 u[3] |
|
||||
| Temp_track | 临时航迹(航迹起始阶段) | 4 维状态 X[4]/P[4][4]、极坐标点信息、时间戳、buff_round(已缓存圈数)、区号、关联标志 |
|
||||
| Tracking_Target | TAS 队列元素 | 批号 Index、空标志 empty_flag |
|
||||
| PointRecv | 内部点迹 | 距离/方位(弧度)/速度/幅度/高度/SNR/RCS/CPI/GNSS 时间、点迹号 `point_id` / 帧号 `frame_id` / 圈号 `circle_id`(记录链路用,c3c7e66 新增)、使用标志、波位/PRF/频点、跟踪模式、点迹区号、归一化幅度 `amp_asv_point` / 速度维 FFT 点数 `prn_fftn_point`、十字星数据 |
|
||||
| Trust_Track | 可靠航迹(IMM 全状态) | 航迹号、标识字段 `track_id` / `point_id` / `circle_id`(b227ae1 新增,起批时取临时航迹末点的 `temp_track_id`/`point_id`/`circle_id`,关联成功时刷新为被关联点迹的 ID)、高度、极坐标量快照 `Range` / `Azimuth` / `Elevation` / `Velocity`(b227ae1 新增,仅在本圈有输出时回填)、航迹时间、更新标志 Track_Update_Flag(**输出门控**:仅置 1 的航迹当圈被输出,输出后清零)、外推轮数 Extrapolate_round、实虚点标志、手动删除/手动 TAS/重点目标(primary_target_flag,决定消亡用 6 还是 10 圈)标志、关联点数、高度平滑缓冲 Hight_smooth、关联点信息、归一化幅度 `amp_asv_point` / 速度维 FFT 点数 `prn_fftn_point`、融合状态 X[6]/P[6][6]、三个子模型 X1/X2/X3、P1/P2/P3、模型概率 u[3] |
|
||||
| Temp_track | 临时航迹(航迹起始阶段) | 4 维状态 X[4]/P[4][4]、极坐标点信息(`r`/`azi`/`vr`/`height`/`SNR`/`RCS`/`amplitude`)、时间戳 `CPI_time` / `GNSS_time`、`buff_round`(已缓存圈数)、标识字段 `temp_track_id` / `point_id` / `circle_id`、区号 `temp_track_section_idx`、俯仰波位号 `pitch_num`、关联标志(c3c7e66 起字段统一更名,如原 `snr`/`Amp`/`T`) |
|
||||
| Asso_Info | 点航关联候选(track_asso.cpp 使用) | `point_id` / `track_id` / `circle_id`、三模型统计距离 `d1`/`d2`/`d3` 与最小值 `d_min`、点迹/航迹下标、`delta_T`/`delta_h`/`R`/`VT`、门限判定结果 `thd_d`/`thd_h`/`thd_r`、是否被最近邻取中 `min_d_flag`(b227ae1 由局部结构体提升到 struct.h,用于记录 `RdpRecAssoInfo`) |
|
||||
| Tracking_Target | TAS 队列元素 | 批号 Index、空标志 empty_flag、最近波束输出时间 last_track_time |
|
||||
| Direct_Tracking_Target | 引导跟踪目标 | ID、方位/俯仰/距离、空标志 |
|
||||
|
||||
> 说明:Trust_Track 的状态布局为 [x, vx, ax, y, vy, ay],x/y 两轴解耦,每轴为「位置-速度-加速度」。
|
||||
>
|
||||
> ⚠ **结构体布局变更**:`DataRev` 新增 `amp_asv` / `prn_fftn`,`Track` 新增 `amp_asv_point` / `prn_fftn_point`,内部 `PointRecv` / `Trust_Track` / `Temp_track` 亦有新增与更名字段。宿主与 DLL 必须使用同一版本头文件并**同步重新编译**,否则字段错位。对外结构体(`data_process_class_dll.h`)在本版中仅新增了基类成员 `version_info`,`DataRev` / `Track` / `RadarPara` 布局自 1.5.5 起未再变化。
|
||||
|
||||
---
|
||||
|
||||
@@ -296,8 +357,9 @@ classDiagram
|
||||
|
||||
| 接口 | 返回值 | 说明 |
|
||||
|---|---|---|
|
||||
| Data_Process_Factory::GetB() | 实例指针 | 获取单例(首次调用时创建) |
|
||||
| Data_Process_Factory::GetB() | 实例指针 | 获取单例(首次调用时创建,并写入 `version_info`) |
|
||||
| Data_Process_Factory::Destroy() | — | 销毁实例并将指针置空 |
|
||||
| version_info(基类公开成员) | `const char*` | DLL 版本字符串,由 `GetB()` 赋值为 `VERSION_STRING`(相扫 `V1.5.6.PS` / 机扫 `V1.5.6.MS`);只读使用,不要释放(b227ae1) |
|
||||
| track_process_parameters_initial(RadarPara) | 0 成功 / -1 失败 | 初始化工作参数;校验 3 ≤ track_start_point_num ≤ 9 |
|
||||
| track_process_parameters_modify(RadarPara) | 0 / -1 | 运行时修改参数(校验同上) |
|
||||
| data_preprocess(DataRev[150]) | 0/1/2/3 | 输入一帧点迹并缓存,返回处理模式(见下表) |
|
||||
@@ -307,11 +369,17 @@ classDiagram
|
||||
| track_delete(n, idx[]) | 0 | 按批号标记手动删除(下一轮消亡输出) |
|
||||
| tracking_start(track_index) | 0 | 手动转 TAS 跟踪(置 manual_tracking_flag,由 Beam_Ctrl 入队) |
|
||||
| tracking_stop(track_index) | 0 | 手动取消 TAS(清标志并 Track_Mode=0;队列项延至下一次 Beam_Ctrl 移除) |
|
||||
| primary_target_add(track_index) | 0 | 手动将指定批号标为**重点目标**(置 `primary_target_flag=1`,消亡轮数放宽到 10 圈;批号不存在时不报错、无副作用) |
|
||||
| primary_target_cancel(track_index) | 0 | 取消指定批号的重点目标标记(置 `primary_target_flag=0`,恢复普通消亡轮数) |
|
||||
| tracking_point(azimuth) | 0 | 手动打跟踪波束——**未实现(保留空实现)** |
|
||||
| direct_tracking_process(...) | 0 | 引导跟踪——**未实现(保留空实现)** |
|
||||
|
||||
> **输出语义(50e0b5c 起)**:`track_process(model=1/3)` 只把本圈**有更新**的航迹写入 `Trust_Track_Output`(关联到实点、外推成功,或 TAS 指定批号被更新),行数由 `Trust_track_num_Output` 给出;未更新(含外推超限)的航迹当圈不出现在输出中,宿主不能据输出行数推断航迹总数。输出数组内容由 DLL 覆盖写入指定行,其余行保持宿主原值(DLL 不清零整块数组)。
|
||||
|
||||
> 日志输出为 DLL 内部功能(不对外导出,接口见 10.1 的宏开关):首次 `GetB()` 创建实例时自动初始化(`RDP_LOG_ENABLE` 定义则将 `std::cout` 重定向到 `.\rdp_logs\` 下按日期+时间命名的日志文件,该宏当前为注释状态,见 10.1),`Destroy()` 销毁实例时恢复 `std::cout` 并关闭日志文件。
|
||||
|
||||
> 数据记录同样为 DLL 内部功能(不对外导出):记录功能启用时,`data_preprocess` 逐点写输入点迹、`dot_coh`/`dot_coh_tas` 写凝聚后点迹、`track_init` 写临时航迹、`track_asso` 写可靠航迹与关联信息,全部落到 `.\rdp_records\rdp_record_YYYYMMDD_HHMMSS.db` 的 6 张在用表中(表清单与字段见 docs/data_record_debug.md)。
|
||||
|
||||
### 7.2 data_preprocess 返回值的含义
|
||||
|
||||
| 返回值 | 含义 | 触发条件 |
|
||||
@@ -357,7 +425,7 @@ flowchart TD
|
||||
B --> C["track_process(1)"]
|
||||
C --> D["dot_coh 两帧滑窗凝聚 → point_recv"]
|
||||
D --> E["track_asso: IMM 交互 → 点航波门筛选(NN)→ EKF 滤波 → 模型输出"]
|
||||
E --> F["track_die: 外推超限/手动删除 → 输出消亡批号"]
|
||||
E --> F["track_die: 连续未关联超限 / 手动删除 → 输出消亡批号"]
|
||||
F --> G["track_init: 点-临时航迹关联 → 点-航迹头关联 → 剩余点转新航迹头 → 起批转可靠航迹"]
|
||||
G --> H["输出 Trust_Track_Output(含起批历史点)"]
|
||||
H --> I["Beam_Ctrl: 手动 TAS 目标入队 / 波束输出"]
|
||||
@@ -365,6 +433,8 @@ flowchart TD
|
||||
|
||||
其中 model=2(未满一圈)只执行滑窗凝聚 dot_coh_process_buff,把上一帧凝聚完成的点输出到 point_recv 供下一圈的关联/起批使用,当前帧未凝聚点作为下一次的缓存——即「凝聚在前、关联在后、缓存一帧」的两帧滑窗结构。
|
||||
|
||||
输出环节(`track_asso_process()` 末尾)只写 `Track_Update_Flag==1` 的航迹:关联到实点、或外推成功(`Extrapolate_round <= MAX_EXTRAPOLATE_NUM` = 3)时置 1;外推超限的航迹不再推进位置与航迹时间,当圈也不输出,直到重新关联上实点才恢复(详见 9.9)。
|
||||
|
||||
### 8.2 TAS 流程(model=3)
|
||||
|
||||
```mermaid
|
||||
@@ -373,11 +443,13 @@ flowchart TD
|
||||
B --> C["dot_coh_tas 单帧凝聚 → point_recv_tas"]
|
||||
C --> D["track_asso_tas: 仅对 tas_track_idx 批号做 IMM+EKF 关联(含高度门限)"]
|
||||
D --> E["未关联 → 按 latest_timestamp 外推(ΔT≤0 跳过)"]
|
||||
E --> F["track_die_tas: TAS 超时/手动删除 → 消亡输出"]
|
||||
E --> F["track_die_tas: TAS 连续未关联超上限 / 手动删除 → 消亡输出"]
|
||||
F --> G["输出该批号航迹(point_type=1)"]
|
||||
G --> H["Beam_Ctrl: 数据率门控 + 队内最早 CPI 目标 + 预测波束输出"]
|
||||
```
|
||||
|
||||
TAS 输出同样受 `Track_Update_Flag` 门控:只有 `Track_Index == tas_track_idx` 且本圈有更新(关联到实点、或外推成功)时才向 `Trust_Track_Output` 写一行(`point_type=1`)。TAS 外推上限为 `MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48),判定方向与 TWS 相反(`Extrapolate_round >= 上限` 时才外推并输出),详见 9.9 与 11.2。
|
||||
|
||||
### 8.3 航迹起始流程(逻辑法)
|
||||
|
||||
```mermaid
|
||||
@@ -713,7 +785,16 @@ VT<100\ \text{且}\ R>300\ \Rightarrow 拒绝;\qquad
|
||||
VT>300\ \text{且}\ R>1200\ \Rightarrow 拒绝
|
||||
$$
|
||||
|
||||
候选对按 d_min = min(d1,d2,d3) 全局排序,**最近邻**原则逐个取点:每条航迹每圈最多关联一个点、每个点只属于一条航迹。关联成功后:三模型分别 EKF 更新 → 模型概率更新 → 更新航迹时间/幅度/SNR/RCS/十字星数据 → 高度平滑。关联输出的航迹同时写入**方向标志**(f249fdc):`Flag_Direction = (vr_point > 0) ? 1 : 0`,即径向速度为正判为靠近(1)、为零或负判为远离(0),取值来自航迹记录的径向速度(最近一次关联点迹的速度)。
|
||||
候选对按 d_min = min(d1,d2,d3) 全局排序,**最近邻**原则逐个取点:每条航迹每圈最多关联一个点、每个点只属于一条航迹。关联成功后:三模型分别 EKF 更新 → 模型概率更新 → 更新航迹时间/幅度/SNR/RCS/十字星数据 → 高度平滑。
|
||||
|
||||
**靠近/远离方向标志 Flag_Direction(fc49707 起、42b598e 调整,TWS/TAS 同步)**:不再使用实测径向速度(原 `Flag_Direction = vr_point > 0 ? 1 : 0`,实测多普勒在速度模糊时符号可能取反),改为按航迹 IMM 融合状态的速度矢量与位置矢量判定:
|
||||
|
||||
$$
|
||||
D=\operatorname{atan2}(X[4],X[1])\ \text{(速度方向角)},\qquad
|
||||
Flag\_Direction=\begin{cases}1,& \arccos\!\left(\dfrac{-x\cos D-y\sin D}{r}\right)<\dfrac{\pi}{2}\\[2mm]0,&\text{否则}\end{cases}
|
||||
$$
|
||||
|
||||
其中 x、y 为经 `Sys_delay` 补偿后的航迹位置,r = √(x²+y²),`Direction_Angle` 输出字段即 D 的度数形式。该判据等价于「目标速度方向与『雷达→目标』位置方向的夹角大于 90°」,即**滤波速度矢量的径向分量指向雷达(径向接近)时判为靠近(1)**,否则远离(0);中间版本曾按合速度归一化并把门限取为 π/4(45° 锥角),42b598e 去掉归一化并放宽到 π/2。外推(虚点)输出同样按该判据写 `Flag_Direction`。
|
||||
|
||||
**ΔT ≤ 0 的候选(604f158 起的行为)**:ΔT = (点迹 CPI 时间 − 航迹 T_track)/1000,ΔT ≤ 0 表示该点迹时间戳不晚于航迹时间(典型场景:航迹已按 TAS 点迹更新过,本圈 TWS 点迹的时间戳更早,或存在重复时间戳)。这种候选**不再被剔除**,仍参与最近邻最小 d 的比较与选取,但选中后:
|
||||
|
||||
@@ -723,7 +804,7 @@ $$
|
||||
- 该候选对连同该航迹本圈的其余候选对被一并移出候选表,因此该航迹本圈不会再关联其他点迹;由于时间戳更早的候选仍参与最小 d 比较,它可能先于有效候选(ΔT>0)被取中;
|
||||
- 该点迹的 `Use_Flag` 被置 1 消费掉:`track_asso_process()` 在关联结束后会把 `Use_Flag==1` 的点迹从 `point_recv` 中删除(track_init 入口也会再剔除一次 `Use_Flag==1` 的点),因此该点迹不会进入航迹起始、不会再参与起批,避免生成虚假 TWS 航迹。
|
||||
|
||||
> 历史行为:604f158 之前,ΔT ≤ 0 的候选在选取时被直接剔除并 `continue`,点迹保持未被使用,可能被后续用于航迹起始。该分支原有的 `RDP_LOG` 告警日志现已注释停用;TAS 关联(track_asso_tas.cpp,见下)中同类告警日志仍启用。
|
||||
> 历史行为:604f158 之前,ΔT ≤ 0 的候选在选取时被直接剔除并 `continue`,点迹保持未被使用,可能被后续用于航迹起始。该分支原有的 `RDP_LOG` 告警日志现已注释停用;TAS 关联(track_asso_tas.cpp,见下)中同类告警日志仍启用。**b227ae1 起该分支的写法由 `if (delta_T <= 0) {空} else {...}` 调整为 `if (delta_T > 0) {...}`**,判定结果与滤波行为不变;同一提交为每个候选补充了 `min_d_flag`(被最近邻取中时置 1)并配合 `RdpRecAssoInfo` 记录全部候选与取中结果(见 5.2 的 rdp_record 行)。
|
||||
|
||||
#### TAS(track_asso_tas.cpp)
|
||||
|
||||
@@ -735,7 +816,7 @@ $$
|
||||
|
||||
高度门限按航迹距离分段:≤1000 m:|h_t−h_p| ≤ 150 m;1000~2000 m:≤ 200 m;2000~3000 m:≤ 250 m;3000~5000 m:≤ 300 m;>5000 m:不约束(与 TWS 通道 9.6 的取值相同)。
|
||||
|
||||
输出航迹同样写入**方向标志** `Flag_Direction`(由 `vr_point` 符号决定,f249fdc),规则与 TWS 通道一致。
|
||||
输出航迹同样写入**方向标志** `Flag_Direction`,判据与 TWS 通道完全一致(fc49707/42b598e:按 IMM 融合速度矢量与位置矢量的夹角判定,见上文 TWS 小节)。
|
||||
|
||||
> ⚠ **波门语义提示**:代码中 d 本身已是马氏距离平方,但门限写作 d*d < ASSO_THORD*ASSO_THORD,等效于 d < 4(而非 √d < 4)。该行为在 BUG-20 中被识别,**有意保留未改**;ASSO_THORD/1000、/100 已改为浮点除法(BUG-21 修复)。TAS 不区分距离段,`ASSO_THORD` 原值直接作为门限。
|
||||
|
||||
@@ -757,16 +838,27 @@ $$
|
||||
|
||||
### 9.9 航迹外推与消亡
|
||||
|
||||
**外推**:未关联到实点的 TWS 航迹(manual_tracking_flag==0)按工作模式数据率 DATA_RATE_SHORT/MIDDLE/FAR 预测一帧(ΔT≤0 的候选虽被消费但未刷新航迹状态,其 `point_flag` 仍为 0,故同样在本环节外推,见 9.6);TAS 通道按 latest_timestamp − T_track 预测(ΔT ≤ 0 跳过);预测用各子模型 Singer F/Qj 推进,Extrapolate_round+1,point_flag=0(虚点/补点),航迹时间同步推进。
|
||||
#### 外推
|
||||
|
||||
**消亡**:
|
||||
- **TWS**:未关联到实点的航迹(`manual_tracking_flag==0`)按工作模式数据率 DATA_RATE_SHORT/MIDDLE/FAR 预测一帧(ΔT≤0 的候选虽被消费但未刷新航迹状态,其 `point_flag` 仍为 0,故同样在本环节外推,见 9.6)。预测用各子模型 Singer F/Qj 推进,`Extrapolate_round+1`,`point_flag=0`(虚点/补点),航迹时间同步推进,并置 `Track_Update_Flag=1`(本圈输出)。
|
||||
- **TWS 外推上限(50e0b5c)**:仅当 `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(=3)时才做上述预测;超过上限后不再推进位置与航迹时间,只执行 `Extrapolate_round+1`、`point_flag=0`、`Track_Update_Flag=0`——该航迹当圈不输出、位置冻结在最后一次预测处,直到重新关联到实点(`Extrapolate_round` 清零)才恢复。目的是避免长时间未关联的航迹被反复外推到远处。
|
||||
- **TAS**:按 `latest_timestamp − T_track` 预测(ΔT ≤ 0 跳过),航迹时间推进到最新时间戳。上限判定为 `Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)时才预测并输出,未达该轮数时只累加 `Extrapolate_round` 且不输出(位置与航迹时间停在最后一次关联处)。⚠ 判定方向与 TWS 的 `<=` 相反,见 11.2。
|
||||
- **输出门控**:`track_asso_process()` / `track_asso_process_tas()` 的输出循环只写 `Track_Update_Flag==1` 的航迹,写完即清零;因此「本圈未更新」的航迹(外推超限等)不会出现在 `Trust_Track_Output` 中。
|
||||
|
||||
#### 消亡(track_die.cpp / track_die_tas.cpp)
|
||||
|
||||
判据为 `Extrapolate_round > 阈值`(50e0b5c 起由 `>=` 改为 `>`),满足其一即消亡:
|
||||
|
||||
| 通道 | 条件 |
|
||||
|---|---|
|
||||
| TWS | Track_Mode==0 且 Extrapolate_round ≥ TRACK_DIE_ROUND(=4),或 manual_delete_flag==1 |
|
||||
| TAS | Track_Mode==1 且 Extrapolate_round ≥ TRACK_DIE_ROUND_TAS(相扫 5 / 机扫 96),或 manual_delete_flag==1 |
|
||||
| TWS 普通航迹 | Track_Mode==0 且 primary_target_flag==0 且 Extrapolate_round > TRACK_DIE_ROUND(=6) |
|
||||
| TWS 重点航迹 | Track_Mode==0 且 primary_target_flag==1 且 Extrapolate_round > TRACK_DIE_ROUND_PRIMARY(=10) |
|
||||
| TAS 航迹 | Track_Mode==1 且 Extrapolate_round > TRACK_DIE_ROUND_TAS(相扫 10 / 机扫 96) |
|
||||
| 任意航迹 | manual_delete_flag==1(手动删除标记,不区分通道) |
|
||||
|
||||
消亡批号写入 Track_die_Index_Output 并删除本地航迹。
|
||||
消亡批号写入 Track_die_Index_Output 并删除本地航迹。由于 `Extrapolate_round` 每圈未关联只自增 1,普通 TWS 航迹实际连续 7 圈无实点关联才消亡(重点航迹 11 圈;TAS 相扫 11 圈 / 机扫 97 圈)。`track_process()` 中的调用顺序为 `track_asso → track_die → track_init`(data_process.cpp),即消亡先于航迹起始执行。
|
||||
|
||||
> 重点目标标记(`primary_target_add` / `primary_target_cancel`)只影响消亡轮数,不改变关联、滤波、波束控制等其他环节,也不体现在输出航迹字段中。
|
||||
|
||||
### 9.10 TAS 波束控制(tas_ctrl.cpp)
|
||||
|
||||
@@ -796,6 +888,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 / 方位 ° |
|
||||
@@ -804,8 +898,11 @@ $$
|
||||
| R_MIN / R_MAX | 100 / 100000 | 同左 | 有效量程(m) |
|
||||
| TRACK_START_THRESHOLD | 8 | 同左 | 起批波门(d² 门限 64;越大越容易起批) |
|
||||
| ALPHA_START | 60 | 同左 | 起批折角(°) |
|
||||
| TRACK_DIE_ROUND | 4 | 4 | TWS 航迹消亡外推轮数 |
|
||||
| TRACK_DIE_ROUND_TAS | 5 | 96 | TAS 航迹消亡外推轮数 |
|
||||
| TRACK_DIE_ROUND | 6 | 6 | TWS **普通**航迹消亡圈数(判据 Extrapolate_round > 6;50e0b5c 起由 4 调整为 6,判据由 ≥ 改为 >) |
|
||||
| TRACK_DIE_ROUND_PRIMARY | 10 | 10 | TWS **重点航迹**消亡圈数(primary_target_flag==1 时套用;50e0b5c 新增) |
|
||||
| TRACK_DIE_ROUND_TAS | 10 | 96 | TAS 航迹消亡圈数(判据 Extrapolate_round > 阈值;相扫 50e0b5c 起由 5 调整为 10,机扫保持 96) |
|
||||
| MAX_EXTRAPOLATE_NUM | 3 | 3 | TWS 未关联航迹最大外推圈数(Extrapolate_round ≤ 3 才继续外推;50e0b5c 新增) |
|
||||
| MAX_EXTRAPOLATE_NUM_TAS | 5 | 48 | TAS 最大外推圈数(判据为 Extrapolate_round ≥ 该值时才外推,方向与 TWS 相反,见 9.9 与 11.2;50e0b5c 新增) |
|
||||
| ASSO_THORD | 4 | 同左 | 关联波门(判据 d*d < ASSO_THORD²,d 已是马氏距离平方,等效 d < 4;e0bf227 起由 3 调大至 4) |
|
||||
| H_F_WIN_LEN | 3 | 同左 | 高度平滑窗长基数(实际 +2/+3/+4/+6) |
|
||||
| TAS_QUEUE_LENGTH / MAX_TAS_NUM | 4 / 4 | 1 / 1 | TAS 队列长度 / 最大 TAS 目标数 |
|
||||
@@ -843,8 +940,9 @@ $$
|
||||
- **时间戳/时序**:TWS 关联 ΔT≤0 的候选不参与滤波更新、不刷新航迹状态并消费该点迹(604f158 起由「剔除候选、点迹保持未使用」改为当前行为,仍属 BUG-15 处置范围);TAS 关联 ΔT≤0 拒绝(BUG-16);TAS 外推负时间差跳过(BUG-17);Bind_speed 除零保护(BUG-19)。
|
||||
- **数值稳定性**:方位残差 ±π 环绕(BUG-22);IMM 概率零分母/奇异保护(BUG-23);EKF 似然 3×3 新息协方差(BUG-24);航向角 atan2(BUG-26);asin 定义域钳位(BUG-27);近程模型 3 门限浮点除法(BUG-21);track_clear_all 全模块 reset(BUG-28);未初始化变量修复(BUG-11/12/14)。
|
||||
- **后修订(e0bf227,BUG_FIX_REPORT.md 成文之后)**:TWS 关联高度门限由注释状态改为实际生效(`track_asso.cpp`),统计距离/高度/距离三类判据拆为独立布尔量,`ASSO_THORD` 由 3 调大至 4;TAS 关联高度门限 2000~3000 m 段放宽至 250 m;修复 `tas_beam_output()` 数据率门控误判(详见 CHANGELOG 的 Unreleased 与 9.6、9.10)。
|
||||
- **后修订(50e0b5c、fc49707、42b598e、e8c56f9,BUG_FIX_REPORT.md 成文之后)**:输出航迹增加 `Track_Update_Flag` 门控(只输出本圈有更新的航迹);未关联航迹外推增加圈数上限;消亡判据改为 `>`,正常/重点航迹分别定为 6 / 10 圈(TAS 相扫 10、机扫 96);`Flag_Direction` 改用滤波速度矢量判定,修复速度模糊时的向站/背站误判;手动操作接口补 `break`;`DataRev`/`Track` 新增归一化幅度与速度维 FFT 点数字段(输入侧其后已打通,TAS 输出仍未写回,见 11.2)。
|
||||
|
||||
### 11.2 有意保留 / 未修复(标注于 archived_docs/BUG_REPORT.md)
|
||||
### 11.2 有意保留 / 未修复(archived_docs/BUG_REPORT.md 标注项 + 后续源码观察)
|
||||
|
||||
- **BUG-01**:基类无虚析构,Destroy() 按基类指针 delete。
|
||||
- **BUG-02**:Hight_smooth 只增不减,长时运行内存持续增长。
|
||||
@@ -853,11 +951,14 @@ $$
|
||||
- **BUG-08**:消亡航迹输出数组容量未传入,仅约定 ≤500。
|
||||
- **BUG-13**:Work_Parameter 等成员构造后未初始化,必须先调 track_process_parameters_initial 再处理。
|
||||
- **BUG-18**:卡尔曼两点/三点初始化对零/负时间差无保护(起批时间戳必须严格递增)。
|
||||
- **BUG-20**:门限对马氏距离平方再次平方(等效 d<3),有意保留。
|
||||
- **BUG-25**:TWS 关联/输出未排除 Track_Mode==1 的 TAS 航迹(原过滤逻辑被注释)。
|
||||
- **BUG-20**:门限对马氏距离平方再次平方(e0bf227 把 `ASSO_THORD` 调为 4 后等效 d < 4),有意保留。
|
||||
- **BUG-25**:TWS 关联/输出未排除 Track_Mode==1 的 TAS 航迹(原 `manual_tracking_flag==0` 过滤代码块已于 fc49707 删除,未恢复过滤)。
|
||||
- **BUG-29**:tracking_stop 后 TAS 队列项延迟到下一次 Beam_Ctrl 才移除。
|
||||
- **BUG-33**:起批关联未显式校验 ΔT>0。
|
||||
- **BUG-36**:TAS 波束关闭时只清 open_flag,其余字段保留旧值。
|
||||
- **归一化幅度 / 速度维 FFT 点数(e8c56f9 新增,输入侧已打通)**:`data_preprocess()` 现已在 TWS、TAS 两条分支都把 `DataRev.amp_asv` / `DataRev.prn_fftn` 拷入内部 `PointRecv`(`data_process.cpp`,并随 `RdpRecPoint` 落库),因此 TWS 关联路径(`track_asso.cpp` 写航迹、输出时写回 `Track`)取到的是有效数据。**TAS 通道仍未打通**:`track_asso_tas.cpp` 的关联与输出路径没有涉及这两个字段,TAS 输出航迹中 `amp_asv_point` / `prn_fftn_point` 仍保持宿主原值,宿主暂不应依赖 TAS 侧的这两个字段。
|
||||
- **TAS 外推上限判据方向与 TWS 相反(50e0b5c)**:TWS 用 `Extrapolate_round <= MAX_EXTRAPOLATE_NUM` 外推,TAS 用 `Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`,实际效果是 TAS 航迹在未达上限的若干圈内既不外推也不输出(位置与航迹时间停在最后一次关联处),达到上限后才恢复逐帧外推输出;如属笔误,宜与 TWS 侧统一。
|
||||
- **Flag_Direction 判据的 acos 无定义域钳位(fc49707/42b598e)**:对 `(-x·cos D − y·sin D)/r` 直接取 `acos`(与 BUG-27 同类问题),浮点舍入使参数略微越过 ±1 时返回 NaN,比较为假、按「远离 0」输出;`r` 为 0 时还存在除零。
|
||||
|
||||
### 11.3 未启用功能
|
||||
|
||||
@@ -866,6 +967,8 @@ $$
|
||||
- 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)。记录点中的字段错误只在启用记录功能的构建中才会被检查(关闭时宏丢弃实参)。
|
||||
- **记录表的两个观察**:① 可靠航迹表 `RdpRecTrustTrack` 在输出循环内写库,而该循环入口已把 `Track_Update_Flag` 清零(`track_asso.cpp` 先置 0 再输出),因此表中的 `Track_Update_Flag` 恒为 0,判断「本圈是否更新」应改用该表是否出现对应 `track_id`/`circle_id` 的记录;② `Trust_Track` 的极坐标量快照 `Range`/`Azimuth`/`Elevation`/`Velocity` 只在航迹当圈有输出时回填,长期未输出(外推超限)的航迹在表中该项为 0 或旧值。
|
||||
|
||||
### 11.4 航迹起始相关的未修复观察(详见 docs/track_init_readme.md 第 15 节)
|
||||
|
||||
@@ -881,10 +984,21 @@ $$
|
||||
|
||||
## 12. 版本历史
|
||||
|
||||
当前代码版本:**V1.5.6**(`parameters.h` 的 `PATCH_VERSION`;对应最近一次提交 b227ae1,2026-10-08)。版本号自本版起可运行时读取(`version_info`,见 7.1)。
|
||||
|
||||
近期提交(git log --oneline,新→旧):
|
||||
|
||||
| 提交 | 内容 |
|
||||
|---|---|
|
||||
| b227ae1(**V1.5.6** 版本号来源) | 数据处理增加版本号输出:`parameters.h` 新增版本宏(`MAJOR_VERSION`/`MINOR_VERSION`/`PATCH_VERSION` = 1.5.6、体制后缀 `VERSION_INFO`、拼装宏 `VERSION_STRING`),基类新增公开成员 `const char* version_info` 并由 `GetB()` 赋值;Debug 下记录可靠航迹与关联信息:`struct.h` 新增全局 `Asso_Info`(含 `min_d_flag` 与门限量)、`Trust_Track` 新增 `track_id`/`point_id`/`circle_id` 与极坐标量快照 `Range`/`Azimuth`/`Elevation`/`Velocity`,新增 `RdpRecTrustTrack`/`RdpRecAssoInfo` 两张表并在 `track_asso.cpp` 输出循环与关联三处埋点,移除 `data_process.cpp` 中按输出数组批量记录的旧示例(`RdpRecTrackOut` 实体注释停用);`track_asso.cpp` 的 ΔT 分支改写为 `if (delta_T > 0)`(行为等价) |
|
||||
| c3c7e66 | 增加临时航迹录入数据库(新增 `RdpRecTempTrack` 表与 `track_init.cpp` 记录点)、输入点迹增加圈 ID(`PointRecv::circle_id`、`Data_Process::circle_id` 在满圈/TAS 帧末自增);`Temp_track` 字段更名(`snr`→`SNR`、`Amp`→`amplitude`、`T`→`CPI_time`、`Temp_track_section_idx`→`temp_track_section_idx`)并新增 `temp_track_id`/`point_id`/`circle_id`/`pitch_num`;临时航迹起批时把 ID 透传给 `Trust_Track` |
|
||||
| a4f32e4 | 新增数据记录功能:把过程数据录入数据库(凝聚后点迹 `RdpRecPointCohesionTWS/TAS`,输入点迹改为 `RdpRecPoint`)、`data_process.h` 新增 `point_id`/`frame_id`/`circle_id` 计数、`dot_coh`/`dot_coh_tas` 埋记录点;<br>(该提交同时把上一轮的 CMake/工具链与记录框架改动一并提交,内容见下方「工作区(未提交…)」两条) |
|
||||
| 工作区(已随本次发布归档,数据记录调试功能) | 新增可选编译的数据记录调试功能: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\`);`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 输出时写回输出结构(TAS 侧未打通,见 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 同步 |
|
||||
| fc49707 | 「向站/背站」判断改用滤波速度矢量:`Flag_Direction` 不再取 `vr_point > 0`(实测径向速度受多普勒模糊影响、符号可能取反),改按航迹 IMM 速度方向与位置矢量的夹角判定(TWS/TAS 同步);`primary_target_add/cancel`、`tracking_start/stop` 命中目标后 `break`;删除 TWS 输出循环中的注释残留代码块 |
|
||||
| 50e0b5c | 新增手动重点目标接口(`primary_target_add`/`primary_target_cancel` + `Trust_Track::primary_target_flag`)与重点航迹消亡圈数 `TRACK_DIE_ROUND_PRIMARY`(10);输出航迹改为只输出 `Track_Update_Flag==1` 的本圈更新航迹;未关联航迹外推限制在 `MAX_EXTRAPOLATE_NUM`(3)圈内,TAS 侧新增 `MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48);`TRACK_DIE_ROUND` 由 4 调整为 6、`TRACK_DIE_ROUND_TAS` 相扫由 5 调整为 10,消亡判据改为 `>`;同步更新 README/CHANGELOG/docs |
|
||||
| e0bf227 | 关联判定重构并加强:TWS `track_asso.cpp` 新增按航迹距离分段的高度门限(原高度门限代码块为注释状态)、统计距离/高度/距离三类判据拆为 `thd_d`/`thd_h`/`thd_r` 布尔量;`parameters.h` 的 `ASSO_THORD` 由 3 调大至 4(等效门限 d < 4,提高机动目标关联概率);TAS `track_asso_tas.cpp` 高度门限 2000~3000 m 段由 200 m 放宽至 250 m;修复 `tas_beam_output()` 的数据率门控条件(去掉与「取最早 CPI 目标」冲突的 T_track 判据);删除 `docs/data_process_flow_report.html`;`.gitignore` 增加 `design_docs/`;同步更新 README/CHANGELOG/docs |
|
||||
| f249fdc | 输出航迹新增「靠近/远离」方向标志:`track_asso.cpp`(TWS)与 `track_asso_tas.cpp`(TAS)在写入 `vr_point` 后补 `Flag_Direction = vr_point > 0 ? 1 : 0`;新增 `docs/data_process_flow_report.md`、`docs/data_process_flow_report.html`、`docs/track_init_readme.md` 三份流程与专项文档(其中 HTML 版已于 e0bf227 删除) |
|
||||
| 7d5a56f | 满圈判据由「波位号变小」改为「方位波位号 == 0」(避免机扫转台反转误判,`data_process.cpp`);点-航迹头关联改为统一速度区间 `V_MIN·ΔT ≤ dis ≤ V_MAX·ΔT` 并新增 `dis ≥ |vr_point|·ΔT`、`dis ≥ |v_track_head|·ΔT` 两条多普勒-几何一致性约束,限制切向高速短航迹(`track_init.cpp`);同步更新 README/CHANGELOG |
|
||||
@@ -921,9 +1035,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、失败降级、线程模型)、表结构约定、**新增记录点四步配方**、当前 6 张在用记录表与字段、查询示例与已知限制 |
|
||||
| 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 时期) |
|
||||
| archived_docs/requirements.md | 修改需求说明:日志文件输出功能(rdbuf() 重定向、parameters.h 日志开关宏、日期+时间命名日志、前缀日志宏,附参考示例)——已实现,留档 |
|
||||
| requirements.md | 修改需求说明:数据记录调试功能(结构体经 ormpp 映射写入 SQLite、Release 版可屏蔽、数据分类入不同表、宏记录点、便于快速新增结构体、不使用异步写入)——框架已按该说明实现,记录表已从 2 个示例扩展为 6 张在用表(7 个实体定义),详见 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,42 +0,0 @@
|
||||
# 雷达数据处理项目修改说明
|
||||
|
||||
## 概述
|
||||
|
||||
增加日志文件输出功能,具体需求如下:
|
||||
- 将标准输出std::cout重定向到日志文件,考虑使用 rdbuf() 方法,见[参考1](#参考1)
|
||||
- 在parameters.h中添加一个日志输出开关的宏定义,开关关闭时,不重定向输出到文件
|
||||
- 日志文件输出路径为“.\\rdp_logs\\”,日志文件名按当前日期+时间命名
|
||||
- 增加宏定义,在输出日志时添加时间戳、代码文件、代码行数等关键信息再输出到标准输出
|
||||
|
||||
## 参考
|
||||
|
||||
### 参考1
|
||||
使用 rdbuf() 方法重定向标准输出,更符合C++的流式风格,通过替换 std::cout 的底层流缓冲区(streambuf),将其指向一个文件输出流(std::ofstream)。
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
int main() {
|
||||
std::ofstream file("output.txt");
|
||||
if (!file.is_open()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 保存原始的 cout 缓冲区,以便之后恢复
|
||||
auto original_buf = std::cout.rdbuf();
|
||||
|
||||
// 将 cout 的缓冲区替换为 file 的缓冲区
|
||||
std::cout.rdbuf(file.rdbuf());
|
||||
|
||||
// 此后的输出都会写入文件
|
||||
std::cout << "这条信息会写入文件。" << std::endl;
|
||||
|
||||
// 恢复 cout 的原始缓冲区,让输出回到控制台
|
||||
std::cout.rdbuf(original_buf);
|
||||
// file 对象会在析构时自动关闭
|
||||
|
||||
std::cout << "这条信息会回到控制台。" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
@@ -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,36 @@ 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.circle_id = circle_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);
|
||||
}
|
||||
|
||||
@@ -51,7 +66,9 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
|
||||
if(Data_Input[0].Beam_index_aiz == 0) //TWS
|
||||
{
|
||||
last_beam_num = Data_Input[0].Beam_index_aiz;
|
||||
|
||||
circle_id++;
|
||||
// std::cout << Data_Input[0].CPI_time << std::endl;
|
||||
// RDP_LOG<<"CPI_time: "<<Data_Input[0].CPI_time<<std::endl;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
@@ -59,17 +76,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 +88,33 @@ 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.circle_id = circle_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
|
||||
@@ -107,7 +124,7 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
|
||||
// << "data_num:" << data_num;
|
||||
|
||||
}
|
||||
|
||||
circle_id++;
|
||||
return 3;
|
||||
}
|
||||
|
||||
@@ -131,7 +148,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);
|
||||
}
|
||||
|
||||
@@ -144,6 +160,7 @@ int model)
|
||||
&trust_track,
|
||||
Trust_Track_Output,
|
||||
Trust_track_num_Output,
|
||||
latest_timestamp,
|
||||
Work_Parameter
|
||||
);
|
||||
|
||||
|
||||
@@ -98,6 +98,12 @@ private:
|
||||
|
||||
long long latest_timestamp = 0; //最新时间戳
|
||||
|
||||
long long point_id = 0; //点迹id
|
||||
|
||||
long long frame_id = 0; //帧id
|
||||
|
||||
long long circle_id = 0; //圈id
|
||||
|
||||
struct RadarPara Work_Parameter; //工作参数 参数设置函数外部输入
|
||||
|
||||
Dot_Coh dot_coh; //点迹凝聚
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "data_process_class_dll.h"
|
||||
#include "data_process.h"
|
||||
#include "rdp_log.h"
|
||||
#include "rdp_record.h"
|
||||
#include "parameters.h"
|
||||
|
||||
/*
|
||||
Data_process_class_dll::Data_process_class_dll()
|
||||
@@ -19,7 +21,13 @@ 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();
|
||||
p->version_info = VERSION_STRING;
|
||||
}
|
||||
|
||||
return p;
|
||||
@@ -36,5 +44,8 @@ void Data_Process_Factory::Destroy()
|
||||
|
||||
//恢复 std::cout 到控制台并关闭日志文件
|
||||
//rdp_log::Shutdown();
|
||||
|
||||
//关闭记录库(RDP_DATA_RECORD 关闭时为空实现)
|
||||
rdp_record::Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#define DATA_PROCESS_CLASS_DLL_H
|
||||
|
||||
#include "data_process_class_dll_global.h"
|
||||
#include <string>
|
||||
|
||||
/***************************************************** 结构体 ********************************************************/
|
||||
// 输入点迹的数据结构 用户可见
|
||||
@@ -176,6 +177,7 @@ class DATA_PROCESS_CLASS_DLLSHARED_EXPORT Data_process_class_dll
|
||||
|
||||
public:
|
||||
// Data_process_class_dll();
|
||||
const char* version_info;
|
||||
|
||||
//数据预处理函数
|
||||
virtual int data_preprocess(struct DataRev Data_Input[150]) //雷达的输入点迹 主程序创建全局变量 输入数据
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -7,17 +7,45 @@
|
||||
//频点(GHz)
|
||||
#define FREQ0 9.2
|
||||
|
||||
/**********************************版本信息*************************************************/
|
||||
#define MAJOR_VERSION 1
|
||||
#define MINOR_VERSION 5
|
||||
#define PATCH_VERSION 7
|
||||
#ifdef PHASE_SCANNING
|
||||
#define VERSION_INFO "PS"
|
||||
#else
|
||||
#define VERSION_INFO "MS"
|
||||
#endif
|
||||
|
||||
#define STRINGIFY_IMPL(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_IMPL(x)
|
||||
|
||||
#define VERSION_STRING \
|
||||
"V" STRINGIFY(MAJOR_VERSION) "." \
|
||||
STRINGIFY(MINOR_VERSION) "." \
|
||||
STRINGIFY(PATCH_VERSION) "." \
|
||||
VERSION_INFO
|
||||
|
||||
/**********************************日志输出开关**********************************************/
|
||||
// 日志文件输出功能开关:
|
||||
// 1) 定义(默认)RDP_LOG_ENABLE:启用日志文件输出 —— 调用 Data_Process_Factory::GetB()
|
||||
// 时自动将 std::cout 重定向到日志文件,日志文件存放于 RDP_LOG_DIR 目录,
|
||||
// 文件名按当前日期+时间自动命名(如 rdp_20240826_153000.log);
|
||||
// 2) 注释掉 RDP_LOG_ENABLE:关闭 —— 不做重定向,日志仍输出到标准输出(控制台)。
|
||||
//#define RDP_LOG_ENABLE
|
||||
// #define RDP_LOG_ENABLE
|
||||
|
||||
//日志文件输出目录(相对当前工作目录的路径,目录不存在时自动创建)
|
||||
#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,273 @@
|
||||
/***********************************数据记录调试模块实现****************************************/
|
||||
// 本文件仅在构建系统定义 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,circle_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,circle_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,circle_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(RdpRecTempTrack, id, temp_track_id,point_id,circle_id,X,P,r,azi,vr,height,SNR,RCS,amplitude, CPI_time,GNSS_time,d,buff_round,temp_track_section_idx,asso_flag,pitch_num);
|
||||
RDP_RECORD_DEFINE(RdpRecTempTrack);
|
||||
|
||||
//可靠航迹
|
||||
YLT_REFL(RdpRecTrustTrack, id,track_id,point_id,circle_id,Track_Index,Range,Azimuth,Elevation,Velocity,Amplitude,Height,T_track,GNSS_time,Track_Sum,Point_Index,Track_Mode,
|
||||
Track_Update_Flag,Extrapolate_round,point_flag,approach_flag,manual_delete_flag,manual_tracking_flag,
|
||||
associate_point_number,primary_target_flag,Target_Type,Track_section_idx,
|
||||
range_point,azi_point,elev_point,vr_point,prf_point,point_type,snr_point,
|
||||
amp_asv_point,prn_fftn_point,RCS,pitch_num,X,P,X1,X2,X3,P2,P3,u);
|
||||
RDP_RECORD_DEFINE(RdpRecTrustTrack);
|
||||
|
||||
//关联信息
|
||||
YLT_REFL(RdpRecAssoInfo, id,point_id,track_id,circle_id,d1,d2,d3,d_min,point_index,track_index,
|
||||
delta_T,delta_h,R,VT,
|
||||
tas_flag,thd_d,thd_h,thd_r,
|
||||
min_d_flag);
|
||||
RDP_RECORD_DEFINE(RdpRecAssoInfo);
|
||||
|
||||
// // 示例②:本圈输出的航迹
|
||||
// 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,486 @@
|
||||
#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 <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <cstddef>
|
||||
|
||||
#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>&)
|
||||
|
||||
// 基本类型元素转字符串
|
||||
template <typename T>
|
||||
std::string elementToString(const T& value);
|
||||
|
||||
// 数组版本
|
||||
template <typename T, std::size_t N>
|
||||
std::string arrayToString(const T (&arr)[N]);
|
||||
|
||||
// 非数组元素:直接转字符串
|
||||
template <typename T>
|
||||
std::string arrayToString(const T& value);
|
||||
|
||||
// ============================ 记录实体定义区(在此新增结构体) ============================
|
||||
// 输入点迹 —— 输入的原始点迹数据
|
||||
struct RdpRecPoint
|
||||
{
|
||||
int64_t id; //自增主键(写入时固定填 0)
|
||||
long long point_id; //点迹ID
|
||||
long long frame_id; //帧ID
|
||||
long long circle_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;
|
||||
circle_id = p.circle_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
|
||||
long long circle_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;
|
||||
circle_id = p.circle_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
|
||||
long long circle_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;
|
||||
circle_id = p.circle_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);
|
||||
|
||||
// 零时航迹
|
||||
struct RdpRecTempTrack
|
||||
{
|
||||
int64_t id; //自增主键(写入时固定填 0)
|
||||
long long temp_track_id; //临时航迹ID
|
||||
long long point_id; //关联上的点迹ID
|
||||
long long circle_id; //处理圈数ID
|
||||
|
||||
std::string X; //状态
|
||||
std::string P; //协方差
|
||||
|
||||
double r; //距离
|
||||
double azi; //方位
|
||||
double vr; //径向速度
|
||||
double height; //高度
|
||||
double SNR; //信噪比
|
||||
double RCS; //RCS
|
||||
double amplitude; //幅度
|
||||
long long CPI_time; //时间戳
|
||||
long long GNSS_time; //GNSS时间戳
|
||||
double d; //关联上的点的d(马氏距离)
|
||||
int buff_round; //已缓存圈数
|
||||
int temp_track_section_idx; //临时航迹区号 与点迹区的划分相同
|
||||
int asso_flag; //关联标志 已关联:1 未关联:0
|
||||
int pitch_num; // 俯仰波位号
|
||||
|
||||
RdpRecTempTrack(){
|
||||
id = 0;
|
||||
}
|
||||
|
||||
RdpRecTempTrack(Temp_track& p){
|
||||
id = 0;
|
||||
temp_track_id = p.temp_track_id;
|
||||
point_id = p.point_id;
|
||||
circle_id = p.circle_id;
|
||||
X = arrayToString(p.X);
|
||||
P = arrayToString(p.P);
|
||||
r = p.r;
|
||||
azi = p.azi;
|
||||
vr = p.vr;
|
||||
height = p.height;
|
||||
RCS = p.RCS;
|
||||
SNR = p.SNR;
|
||||
amplitude = p.amplitude;
|
||||
CPI_time = p.CPI_time;
|
||||
GNSS_time = p.GNSS_time;
|
||||
d = p.d;
|
||||
buff_round = p.buff_round;
|
||||
temp_track_section_idx = p.temp_track_section_idx;
|
||||
asso_flag = p.asso_flag;
|
||||
pitch_num = p.pitch_num;
|
||||
}
|
||||
};
|
||||
RDP_RECORD_DECLARE(RdpRecTempTrack);
|
||||
|
||||
// 可靠航迹
|
||||
struct RdpRecTrustTrack
|
||||
{
|
||||
int64_t id; //自增主键(写入时固定填 0)
|
||||
long long track_id; //航迹ID
|
||||
long long point_id; //关联上的点迹ID
|
||||
long long circle_id; //处理圈数ID
|
||||
int Track_Index; //航迹号
|
||||
double Range; //距离
|
||||
double Azimuth; //方位
|
||||
double Elevation; //俯仰
|
||||
double Velocity; //航向速度
|
||||
double Amplitude; //幅度
|
||||
double Height; //高度
|
||||
long long T_track; //航迹时间
|
||||
long long GNSS_time; //GNSS时间
|
||||
int Track_Sum; //航迹总数
|
||||
int Point_Index; //该航迹上的第几个点
|
||||
int Track_Mode; //跟踪模式
|
||||
int Track_Update_Flag; //航迹更新标志 1已更新/0未更新
|
||||
int Extrapolate_round; //航迹持续外推时间
|
||||
int point_flag; //是否实点 1实点 0外推
|
||||
int approach_flag; //接近标志 1接近 0远离
|
||||
int manual_delete_flag; //手动航迹删除
|
||||
int manual_tracking_flag; //手动TAS
|
||||
int associate_point_number; //关联的点数
|
||||
int primary_target_flag; //重点目标标记
|
||||
int Target_Type; //目标类型 搜索目标 低空目标 地面目标
|
||||
int Track_section_idx; //航迹区号
|
||||
double range_point; //关联上的点信息(距离、方位、俯仰)
|
||||
double azi_point;
|
||||
double elev_point;
|
||||
double vr_point;
|
||||
double prf_point;
|
||||
int point_type; //TWS TAS
|
||||
double snr_point; //信噪比
|
||||
float amp_asv_point; //关联上的点迹信息(归一化幅度)
|
||||
float prn_fftn_point; //点迹速度维FFT点数
|
||||
double RCS;
|
||||
int pitch_num; // 俯仰波位号
|
||||
|
||||
std::string X; //IMM
|
||||
std::string P;
|
||||
|
||||
std::string X1;
|
||||
std::string X2;
|
||||
std::string X3;
|
||||
std::string P2;
|
||||
std::string P3;
|
||||
std::string u;
|
||||
|
||||
RdpRecTrustTrack(){
|
||||
id = 0;
|
||||
}
|
||||
|
||||
RdpRecTrustTrack(Trust_Track& p){
|
||||
id = 0;
|
||||
track_id = p.track_id;
|
||||
point_id = p.point_id;
|
||||
circle_id = p.circle_id;
|
||||
Track_Index = p.Track_Index;
|
||||
Range = p.Range;
|
||||
Azimuth = p.Azimuth;
|
||||
Elevation = p.Elevation;
|
||||
Velocity = p.Velocity;
|
||||
Amplitude = p.Amplitude;
|
||||
Height = p.Height;
|
||||
T_track = p.T_track;
|
||||
GNSS_time = p.GNSS_time;
|
||||
Track_Sum = p.Track_Sum;
|
||||
Point_Index = p.Point_Index;
|
||||
Track_Mode = p.Track_Mode;
|
||||
Track_Update_Flag = p.Track_Update_Flag;
|
||||
Extrapolate_round = p.Extrapolate_round;
|
||||
point_flag = p.point_flag;
|
||||
approach_flag = p.approach_flag;
|
||||
manual_delete_flag = p.manual_delete_flag;
|
||||
manual_tracking_flag = p.manual_tracking_flag;
|
||||
associate_point_number = p.associate_point_number;
|
||||
primary_target_flag = p.primary_target_flag;
|
||||
Target_Type = p.Target_Type; //目标类型 搜索目标 低空目标 地面目标
|
||||
Track_section_idx = p.Track_section_idx; //航迹区号
|
||||
range_point = p.range_point; //关联上的点信息(距离、方位、俯仰)
|
||||
azi_point = p.azi_point;
|
||||
elev_point = p.elev_point;
|
||||
vr_point = p.vr_point;
|
||||
prf_point = p.prf_point;
|
||||
point_type = p.point_type; //TWS TAS
|
||||
snr_point = p.snr_point; //信噪比
|
||||
amp_asv_point = p.amp_asv_point; //关联上的点迹信息(归一化幅度)
|
||||
prn_fftn_point = p.prn_fftn_point; //点迹速度维FFT点数
|
||||
RCS = p.RCS;
|
||||
pitch_num = p.pitch_num; // 俯仰波位号
|
||||
|
||||
X = arrayToString(p.X); //IMM
|
||||
P = arrayToString(p.P);
|
||||
|
||||
X1 = arrayToString(p.X1);
|
||||
X2 = arrayToString(p.X2);
|
||||
X3 = arrayToString(p.X3);
|
||||
P2 = arrayToString(p.P2);
|
||||
P3 = arrayToString(p.P3);
|
||||
u = arrayToString(p.u);
|
||||
}
|
||||
};
|
||||
RDP_RECORD_DECLARE(RdpRecTrustTrack);
|
||||
|
||||
// 关联信息
|
||||
struct RdpRecAssoInfo
|
||||
{
|
||||
int64_t id; //自增主键(写入时固定填 0)
|
||||
long long point_id;
|
||||
long long track_id;
|
||||
long long circle_id;
|
||||
|
||||
double d1;
|
||||
double d2;
|
||||
double d3;
|
||||
double d_min;
|
||||
int point_index;
|
||||
int track_index;
|
||||
|
||||
double delta_T;
|
||||
double delta_h;
|
||||
double R;
|
||||
double VT;
|
||||
|
||||
bool tas_flag; //TAS关联标志 1是 0否
|
||||
|
||||
bool thd_d;
|
||||
bool thd_h;
|
||||
bool thd_r;
|
||||
|
||||
bool min_d_flag;
|
||||
|
||||
RdpRecAssoInfo(){
|
||||
id = 0;
|
||||
}
|
||||
|
||||
RdpRecAssoInfo(Asso_Info& p){
|
||||
id = 0;
|
||||
point_id = p.point_id;
|
||||
track_id = p.track_id;
|
||||
circle_id = p.circle_id;
|
||||
d1 = p.d1;
|
||||
d2 = p.d2;
|
||||
d3 = p.d3;
|
||||
d_min = p.d_min;
|
||||
point_index = p.point_index;
|
||||
track_index = p.track_index;
|
||||
delta_T = p.delta_T;
|
||||
delta_h = p.delta_h;
|
||||
R = p.R;
|
||||
VT = p.VT;
|
||||
tas_flag = p.tas_flag;
|
||||
thd_d = p.thd_d;
|
||||
thd_h = p.thd_h;
|
||||
thd_r = p.thd_r;
|
||||
min_d_flag = p.min_d_flag;
|
||||
}
|
||||
};
|
||||
RDP_RECORD_DECLARE(RdpRecAssoInfo);
|
||||
|
||||
// ============================ 数组转换函数 ============================
|
||||
|
||||
// 基本类型元素转字符串
|
||||
template <typename T>
|
||||
std::string elementToString(const T& value) {
|
||||
std::ostringstream oss;
|
||||
oss << value;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// 前置声明数组版本
|
||||
template <typename T, std::size_t N>
|
||||
std::string arrayToString(const T (&arr)[N]);
|
||||
|
||||
// 非数组元素:直接转字符串
|
||||
template <typename T>
|
||||
std::string arrayToString(const T& value) {
|
||||
return elementToString(value);
|
||||
}
|
||||
|
||||
// 数组版本
|
||||
template <typename T, std::size_t N>
|
||||
std::string arrayToString(const T (&arr)[N]) {
|
||||
std::string result;
|
||||
|
||||
// 如果 T 仍然是数组,说明当前不是最内层,用分号;
|
||||
// 否则当前就是最内层,用逗号。
|
||||
const char sep = std::is_array<T>::value ? ';' : ',';
|
||||
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
if (i > 0) {
|
||||
result += sep;
|
||||
}
|
||||
result += arrayToString(arr[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#endif // RDP_RECORD_ENTITY_H
|
||||
@@ -9,27 +9,29 @@ 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
|
||||
long long circle_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个点
|
||||
};
|
||||
@@ -37,13 +39,21 @@ struct PointRecv
|
||||
// 可靠航迹数据结构
|
||||
struct Trust_Track
|
||||
{
|
||||
long long track_id; //航迹ID
|
||||
long long point_id; //关联上的点迹ID
|
||||
long long circle_id; //处理圈数ID
|
||||
|
||||
int Track_Index; //航迹号
|
||||
|
||||
double Range; //距离
|
||||
double Azimuth; //方位
|
||||
double Elevation; //俯仰
|
||||
double Velocity; //航向速度
|
||||
double Amplitude; //幅度
|
||||
double Height; //高度
|
||||
|
||||
long long T_track; //航迹时间
|
||||
long long GNSS_time; //GNSS时间
|
||||
long long T_track; //航迹时间
|
||||
long long GNSS_time; //GNSS时间
|
||||
|
||||
int Track_Sum; //航迹总数
|
||||
int Point_Index; //该航迹上的第几个点
|
||||
@@ -56,8 +66,8 @@ struct Trust_Track
|
||||
int approach_flag; //接近标志 1接近 0远离
|
||||
|
||||
int manual_delete_flag; //手动航迹删除
|
||||
int manual_tracking_flag; //手动TAS
|
||||
int associate_point_number; //关联的点数
|
||||
int manual_tracking_flag; //手动TAS
|
||||
int associate_point_number; //关联的点数
|
||||
|
||||
int primary_target_flag; //重点目标标记
|
||||
|
||||
@@ -73,7 +83,7 @@ struct Trust_Track
|
||||
double vr_point;
|
||||
double prf_point;
|
||||
int point_type; //TWS TAS
|
||||
double snr_point; //信噪比
|
||||
double snr_point; //信噪比
|
||||
float amp_asv_point; //关联上的点迹信息(归一化幅度)
|
||||
float prn_fftn_point; //点迹速度维FFT点数
|
||||
|
||||
@@ -92,32 +102,34 @@ struct Trust_Track
|
||||
double P1[6][6];
|
||||
double P2[6][6];
|
||||
double P3[6][6];
|
||||
double u[3];
|
||||
|
||||
double u[3];
|
||||
};
|
||||
|
||||
//临时航迹结构体
|
||||
struct Temp_track
|
||||
{
|
||||
long long temp_track_id; //临时航迹ID
|
||||
long long point_id; //关联上的点迹ID
|
||||
long long circle_id; //处理圈数ID
|
||||
|
||||
double X[4]; //状态
|
||||
double X[4]; //状态
|
||||
double P[4][4]; //协方差
|
||||
|
||||
double r; //距离
|
||||
double azi; //方位
|
||||
double vr; //径向速度
|
||||
double height; //高度
|
||||
double snr; //信噪比
|
||||
double SNR; //信噪比
|
||||
double RCS; //RCS
|
||||
double Amp; //幅度
|
||||
long long T; //时间戳
|
||||
long long GNSS_time; //GNSS时间戳
|
||||
double amplitude; //幅度
|
||||
long long CPI_time; //时间戳
|
||||
long long GNSS_time; //GNSS时间戳
|
||||
|
||||
double d; //关联上的点的d
|
||||
double d; //关联上的点的d(马氏距离)
|
||||
|
||||
int buff_round; //已缓存圈数
|
||||
|
||||
int Temp_track_section_idx; //临时航迹区号 与点迹区的划分相同
|
||||
int temp_track_section_idx; //临时航迹区号 与点迹区的划分相同
|
||||
|
||||
int asso_flag; //关联标志 已关联:1 未关联:0
|
||||
|
||||
@@ -126,6 +138,34 @@ struct Temp_track
|
||||
float range_dim[8]; // 目标的十字星数据,目标点距离维8个点
|
||||
};
|
||||
|
||||
//存储关联信息
|
||||
struct Asso_Info
|
||||
{
|
||||
long long point_id;
|
||||
long long track_id;
|
||||
long long circle_id;
|
||||
|
||||
double d1;
|
||||
double d2;
|
||||
double d3;
|
||||
double d_min;
|
||||
int point_index;
|
||||
int track_index;
|
||||
|
||||
double delta_T;
|
||||
double delta_h;
|
||||
double R;
|
||||
double VT;
|
||||
|
||||
bool tas_flag; //TAS关联标志 1是 0否
|
||||
|
||||
bool thd_d;
|
||||
bool thd_h;
|
||||
bool thd_r;
|
||||
|
||||
bool min_d_flag;
|
||||
};
|
||||
|
||||
// TAS跟踪目标结构体
|
||||
struct Tracking_Target
|
||||
{
|
||||
|
||||
@@ -291,7 +291,7 @@ void TAS_Ctrl::tas_beam_output(std::vector<Trust_Track> *trust_track
|
||||
H_track = (*trust_track)[track_idx].Height;
|
||||
delta_T = (latest_timestamp - CPI_time) / 1000.0f;
|
||||
|
||||
earliest_track_idx = track_idx;
|
||||
earliest_track_idx = static_cast<int>(track_idx);
|
||||
earliest_tas_queue_idx = tas_idx;
|
||||
|
||||
for(int ii=0;ii<6;ii++){
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#include "rdp_record.h"
|
||||
#include "rdp_record_entity.h"
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace std;
|
||||
|
||||
@@ -16,6 +20,7 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
|
||||
std::vector <Trust_Track> *trust_track, //航迹文件
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10], //更新航迹信息
|
||||
int *Trust_track_num_Output, //更新航迹数
|
||||
long long latest_timestamp, //最新时间戳
|
||||
struct RadarPara Work_Parameter //雷达参数
|
||||
)
|
||||
{
|
||||
@@ -24,14 +29,14 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
|
||||
for (size_t i=0;i< point_recv->size();i++)
|
||||
{
|
||||
point_process.push_back((*point_recv)[i]);
|
||||
int n=point_process.size();
|
||||
int n=static_cast<int>(point_process.size());
|
||||
point_process[n-1].point_section_asso = 1;
|
||||
}
|
||||
|
||||
//IMM算法
|
||||
model_interaction(trust_track);
|
||||
|
||||
model_filter(trust_track, Work_Parameter);
|
||||
model_filter(trust_track, Work_Parameter, latest_timestamp);
|
||||
|
||||
model_output(trust_track);
|
||||
|
||||
@@ -39,7 +44,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();
|
||||
@@ -101,6 +106,11 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
|
||||
if(Direction_Angle<0){Direction_Angle=Direction_Angle+2*PI;}
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Direction_Angle=static_cast<float>(Direction_Angle/PI*180);
|
||||
|
||||
(*trust_track)[i].Range = Trust_Track_Output[*Trust_track_num_Output-1][0].Range;
|
||||
(*trust_track)[i].Azimuth = Trust_Track_Output[*Trust_track_num_Output-1][0].Azimuth;
|
||||
(*trust_track)[i].Elevation = Trust_Track_Output[*Trust_track_num_Output-1][0].Elevation;
|
||||
(*trust_track)[i].Velocity = Trust_Track_Output[*Trust_track_num_Output-1][0].Range_V;
|
||||
|
||||
//关联点信息
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].range_point=static_cast<float>((*trust_track)[i].range_point);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].azi_point=static_cast<float>((*trust_track)[i].azi_point);
|
||||
@@ -143,12 +153,13 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Direction=0;
|
||||
}
|
||||
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].track_rcs = static_cast<float>((*trust_track)[i].RCS);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].pitch_num = (*trust_track)[i].pitch_num;
|
||||
|
||||
std::memcpy(Trust_Track_Output[*Trust_track_num_Output-1][0].speed_dim, (*trust_track)[i].speed_dim, sizeof(Trust_Track_Output[*Trust_track_num_Output-1][0].speed_dim));
|
||||
std::memcpy(Trust_Track_Output[*Trust_track_num_Output-1][0].range_dim, (*trust_track)[i].range_dim, sizeof(Trust_Track_Output[*Trust_track_num_Output-1][0].range_dim));
|
||||
|
||||
RDP_RECORD(RdpRecTrustTrack((*trust_track)[i]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,20 +430,11 @@ void Track_Asso::IMM_d_cal(double v_track,
|
||||
|
||||
}
|
||||
|
||||
void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct RadarPara Work_Parameter)
|
||||
void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct RadarPara Work_Parameter, long long latest_timestamp)
|
||||
{
|
||||
|
||||
//存储关联信息
|
||||
struct asso_info
|
||||
{
|
||||
double d1;
|
||||
double d2;
|
||||
double d3;
|
||||
double d_min;
|
||||
int point_index;
|
||||
int track_index;
|
||||
};
|
||||
std::vector <asso_info> associated_info;
|
||||
|
||||
std::vector <Asso_Info> associated_info;
|
||||
|
||||
//遍历所有点迹航迹 计算点迹和航迹的统计距离
|
||||
for (size_t loop_of_track = 0; loop_of_track<trust_track->size();loop_of_track++)
|
||||
@@ -443,7 +445,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 +459,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 +500,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;
|
||||
}
|
||||
@@ -518,32 +520,43 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
thd_r = false;
|
||||
}
|
||||
|
||||
struct Asso_Info associated_info_tmp;
|
||||
associated_info_tmp.d1=d1;
|
||||
associated_info_tmp.d2=d2;
|
||||
associated_info_tmp.d3=d3;
|
||||
associated_info_tmp.point_index=static_cast<int>(loop_of_point)+1;
|
||||
associated_info_tmp.track_index=static_cast<int>(loop_of_track)+1;
|
||||
if(d1<=d2&&d1<=d3)
|
||||
associated_info_tmp.d_min=d1;
|
||||
else if(d2<=d1&& d2<=d3)
|
||||
associated_info_tmp.d_min=d2;
|
||||
else
|
||||
associated_info_tmp.d_min=d3;
|
||||
associated_info_tmp.point_id=point_process[loop_of_point].point_id;
|
||||
associated_info_tmp.track_id=(*trust_track)[loop_of_track].track_id;
|
||||
associated_info_tmp.circle_id=point_process[loop_of_point].circle_id;
|
||||
associated_info_tmp.delta_T=delta_T;
|
||||
associated_info_tmp.delta_h=delta_h;
|
||||
associated_info_tmp.R=R;
|
||||
associated_info_tmp.VT=VT;
|
||||
associated_info_tmp.tas_flag=point_process[loop_of_point].point_type;
|
||||
associated_info_tmp.thd_d=thd_d;
|
||||
associated_info_tmp.thd_h=thd_h;
|
||||
associated_info_tmp.thd_r=thd_r;
|
||||
associated_info_tmp.min_d_flag=false;
|
||||
|
||||
//小于关联门限 保存关联信息
|
||||
if(thd_d && thd_h && thd_r)
|
||||
{
|
||||
//std::cout << "asooooooooooo";
|
||||
|
||||
struct asso_info associated_info_tmp;
|
||||
associated_info_tmp.d1=d1;
|
||||
associated_info_tmp.d2=d2;
|
||||
associated_info_tmp.d3=d3;
|
||||
associated_info_tmp.point_index=static_cast<int>(loop_of_point)+1;
|
||||
associated_info_tmp.track_index=static_cast<int>(loop_of_track)+1;
|
||||
if(d1<=d2&&d1<=d3)
|
||||
associated_info_tmp.d_min=d1;
|
||||
else if(d2<=d1&& d2<=d3)
|
||||
associated_info_tmp.d_min=d2;
|
||||
else
|
||||
associated_info_tmp.d_min=d3;
|
||||
|
||||
associated_info.push_back(associated_info_tmp);
|
||||
}else{
|
||||
RDP_RECORD(RdpRecAssoInfo(associated_info_tmp));
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
//最近邻法关联
|
||||
int associated_num=associated_info.size();
|
||||
int associated_num=static_cast<int>(associated_info.size());
|
||||
for (size_t i=0;i<trust_track->size();i++)
|
||||
{
|
||||
//寻找最小d
|
||||
@@ -567,9 +580,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,15 +591,14 @@ 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);
|
||||
|
||||
if (delta_T <= 0)
|
||||
if (delta_T > 0)
|
||||
{
|
||||
// RDP_LOG<<"delta_T <= 0, track_index: "<<track_index<<", point_index: "<<point_index<<", delta_T: "<<delta_T<<std::endl;
|
||||
// 乱序/重复时间戳:不作为有效关联,不标记点迹已使用、不刷新航迹新鲜度
|
||||
}else{
|
||||
associated_info[min_index-1].min_d_flag=true;
|
||||
|
||||
double X1_filter[6],X2_filter[6],X3_filter[6],P1_filter[6][6],P2_filter[6][6],P3_filter[6][6];
|
||||
double S1[3][3] = {{0}},S2[3][3] = {{0}},S3[3][3] = {{0}};
|
||||
kalman Kalman;
|
||||
@@ -645,24 +657,26 @@ 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].point_id = point_process[point_index-1].point_id;
|
||||
(*trust_track)[track_index-1].circle_id = point_process[point_index-1].circle_id;
|
||||
(*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));
|
||||
@@ -681,27 +695,29 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
{
|
||||
if(associated_info[ii].track_index==track_index)
|
||||
{
|
||||
RDP_RECORD(RdpRecAssoInfo(associated_info[ii]));
|
||||
associated_info.erase(associated_info.begin()+ii);
|
||||
associated_num=associated_info.size();
|
||||
associated_num=static_cast<int>(associated_info.size());
|
||||
}
|
||||
else
|
||||
ii++;
|
||||
}
|
||||
associated_num=associated_info.size();
|
||||
associated_num=static_cast<int>(associated_info.size());
|
||||
for (int ii=0;ii<associated_num;)
|
||||
{
|
||||
if(associated_info[ii].point_index==point_index)
|
||||
{
|
||||
RDP_RECORD(RdpRecAssoInfo(associated_info[ii]));
|
||||
associated_info.erase(associated_info.begin()+ii);
|
||||
associated_num=associated_info.size();
|
||||
associated_num=static_cast<int>(associated_info.size());
|
||||
}
|
||||
else
|
||||
ii++;
|
||||
}
|
||||
associated_num=associated_info.size();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,7 +730,11 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
if((*trust_track)[i].Extrapolate_round <= MAX_EXTRAPOLATE_NUM)
|
||||
{
|
||||
double v_track = sqrt(pow((*trust_track)[i].X[1],2)+pow((*trust_track)[i].X[4],2));
|
||||
double delta_T;
|
||||
double delta_T = 0;
|
||||
// double delta_T = (latest_timestamp - (*trust_track)[i].T_track)/1000.0;
|
||||
// if (delta_T <= 0)
|
||||
// continue;
|
||||
|
||||
if(Work_Parameter.work_mode == 0)
|
||||
{
|
||||
delta_T = Work_Parameter.DATA_RATE_SHORT;
|
||||
@@ -882,7 +902,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;
|
||||
|
||||
@@ -919,7 +939,7 @@ void Track_Asso:: track_hight_update(int updata_
|
||||
else if((*trust_track)[updata_track_index-1].Hight_smooth.size()>=static_cast<size_t>(height_win_length))
|
||||
{
|
||||
|
||||
int N=(*trust_track)[updata_track_index-1].Hight_smooth.size();
|
||||
int N=static_cast<int>((*trust_track)[updata_track_index-1].Hight_smooth.size());
|
||||
for (int i=0;i<height_win_length;i++)
|
||||
{
|
||||
sum=sum+(*trust_track)[updata_track_index-1].Hight_smooth[N-1-i];
|
||||
|
||||
@@ -16,6 +16,7 @@ public:
|
||||
std::vector <Trust_Track> *trust_track, //航迹文件
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10], //更新航迹信息
|
||||
int *Trust_track_num_Output, //更新航迹数
|
||||
long long latest_timestamp, //最新时间戳
|
||||
struct RadarPara Work_Parameter //工作参数
|
||||
);
|
||||
|
||||
@@ -30,7 +31,8 @@ private:
|
||||
void model_interaction(std::vector <Trust_Track> *trust_track); //模型交互
|
||||
|
||||
void model_filter(std::vector <Trust_Track> *trust_track, //滤波
|
||||
struct RadarPara Work_Parameter
|
||||
struct RadarPara Work_Parameter,
|
||||
long long latest_timestamp
|
||||
);
|
||||
|
||||
void model_output(std::vector <Trust_Track> *trust_track ); //模型输出
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#include "rdp_record.h"
|
||||
#include "rdp_record_entity.h"
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace std;
|
||||
|
||||
@@ -109,6 +113,13 @@ long long latest_timestamp //最新时间戳
|
||||
|
||||
std::memcpy(Trust_Track_Output[*Trust_track_num_Output-1][0].speed_dim, (*trust_track)[i].speed_dim, sizeof(Trust_Track_Output[*Trust_track_num_Output-1][0].speed_dim));
|
||||
std::memcpy(Trust_Track_Output[*Trust_track_num_Output-1][0].range_dim, (*trust_track)[i].range_dim, sizeof(Trust_Track_Output[*Trust_track_num_Output-1][0].range_dim));
|
||||
|
||||
(*trust_track)[i].Range = Trust_Track_Output[*Trust_track_num_Output-1][0].Range;
|
||||
(*trust_track)[i].Azimuth = Trust_Track_Output[*Trust_track_num_Output-1][0].Azimuth;
|
||||
(*trust_track)[i].Elevation = Trust_Track_Output[*Trust_track_num_Output-1][0].Elevation;
|
||||
(*trust_track)[i].Velocity = Trust_Track_Output[*Trust_track_num_Output-1][0].Range_V;
|
||||
|
||||
RDP_RECORD(RdpRecTrustTrack((*trust_track)[i]));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -240,16 +251,7 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
long long latest_timestamp //最新时间戳
|
||||
)
|
||||
{
|
||||
//存储关联信息
|
||||
struct asso_info
|
||||
{
|
||||
double d1;
|
||||
double d2;
|
||||
double d3;
|
||||
double d_min;
|
||||
int point_index;
|
||||
};
|
||||
std::vector <asso_info> associated_info;
|
||||
std::vector <Asso_Info> associated_info;
|
||||
|
||||
//航迹信息
|
||||
double X1[6] = {0},X2[6] = {0},X3[6] = {0},P1[6][6] = {{0}},P2[6][6] = {{0}},P3[6][6] = {{0}};
|
||||
@@ -289,19 +291,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;
|
||||
@@ -327,24 +329,38 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
thd_h = true;
|
||||
}
|
||||
|
||||
struct Asso_Info associated_info_tmp;
|
||||
associated_info_tmp.d1=d1;
|
||||
associated_info_tmp.d2=d2;
|
||||
associated_info_tmp.d3=d3;
|
||||
associated_info_tmp.point_index=static_cast<int>(loop_of_point)+1;
|
||||
associated_info_tmp.track_index=tas_track_idx;
|
||||
if(d1<=d2&&d1<=d3)
|
||||
associated_info_tmp.d_min=d1;
|
||||
else if(d2<=d1&& d2<=d3)
|
||||
associated_info_tmp.d_min=d2;
|
||||
else
|
||||
associated_info_tmp.d_min=d3;
|
||||
associated_info_tmp.point_id=point_process[loop_of_point].point_id;
|
||||
associated_info_tmp.track_id=(*trust_track)[tas_track_idx].track_id;
|
||||
associated_info_tmp.circle_id=point_process[loop_of_point].circle_id;
|
||||
associated_info_tmp.delta_T=delta_T;
|
||||
associated_info_tmp.delta_h=delta_h;
|
||||
associated_info_tmp.R=0;
|
||||
associated_info_tmp.VT=0;
|
||||
associated_info_tmp.tas_flag=point_process[loop_of_point].point_type;
|
||||
associated_info_tmp.thd_d=thd_d;
|
||||
associated_info_tmp.thd_h=thd_h;
|
||||
associated_info_tmp.thd_r=true;
|
||||
associated_info_tmp.min_d_flag=false;
|
||||
|
||||
|
||||
//小于关联门限 保存关联信息
|
||||
if(thd_d && thd_h)
|
||||
{
|
||||
|
||||
// std::cout << "TAS asoooooo111" ;
|
||||
struct asso_info associated_info_tmp;
|
||||
associated_info_tmp.d1=d1;
|
||||
associated_info_tmp.d2=d2;
|
||||
associated_info_tmp.d3=d3;
|
||||
associated_info_tmp.point_index=static_cast<int>(loop_of_point)+1;
|
||||
if(d1<=d2&&d1<=d3)
|
||||
associated_info_tmp.d_min=d1;
|
||||
else if(d2<=d1&& d2<=d3)
|
||||
associated_info_tmp.d_min=d2;
|
||||
else
|
||||
associated_info_tmp.d_min=d3;
|
||||
|
||||
associated_info.push_back(associated_info_tmp);
|
||||
}else{
|
||||
RDP_RECORD(RdpRecAssoInfo(associated_info_tmp));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,14 +386,16 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
double d2=associated_info[min_index-1].d2;
|
||||
double d3=associated_info[min_index-1].d3;
|
||||
|
||||
associated_info[min_index-1].min_d_flag=true;
|
||||
|
||||
//卡尔曼滤波
|
||||
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);
|
||||
|
||||
@@ -406,7 +424,7 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
double Possibility3=(det_S3>1e-12)?(1.0/sqrt(pow(2*PI,3)*det_S3)*exp(-0.5*d3)):0.0;
|
||||
|
||||
//更新航迹
|
||||
for(size_t i=0;i<trust_track->size();i++)
|
||||
for(size_t i=0;i<trust_track->size();i++){
|
||||
|
||||
if((*trust_track)[i].Track_Index == tas_track_idx)
|
||||
{
|
||||
@@ -446,25 +464,27 @@ 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].point_id=point_process[point_index-1].point_id;
|
||||
(*trust_track)[i].circle_id=point_process[point_index-1].circle_id;
|
||||
(*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));
|
||||
@@ -472,7 +492,12 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
//更新高度
|
||||
track_hight_update(tas_track_idx,point_index,trust_track);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& info : associated_info)
|
||||
{
|
||||
RDP_RECORD(RdpRecAssoInfo(info));
|
||||
}
|
||||
}
|
||||
//未关联上,航迹外推
|
||||
else
|
||||
@@ -730,7 +755,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)
|
||||
@@ -766,7 +791,7 @@ void Track_Asso_Tas::track_hight_update(int tas_
|
||||
else if((*trust_track)[i].Hight_smooth.size()>=static_cast<size_t>(height_win_length))
|
||||
{
|
||||
|
||||
int N=(*trust_track)[i].Hight_smooth.size();
|
||||
int N=static_cast<int>((*trust_track)[i].Hight_smooth.size());
|
||||
for (int ii=0;ii<height_win_length;ii++)
|
||||
{
|
||||
sum=sum+(*trust_track)[i].Hight_smooth[N-1-ii];
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include<iomanip>
|
||||
#include "rdp_record.h"
|
||||
#include "rdp_record_entity.h"
|
||||
using namespace std;
|
||||
|
||||
int Track_Init::track_init_process_logic( std::vector <PointRecv> *point_recv, //输入点迹
|
||||
@@ -15,27 +17,12 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter
|
||||
)
|
||||
|
||||
{
|
||||
|
||||
// for(int i=0;i<(*point_recv).size();i++)
|
||||
// {
|
||||
// if((*point_recv)[i].CPI_Time == 30675948 || (*point_recv)[i].CPI_Time == 30682320
|
||||
// || (*point_recv)[i].CPI_Time == 30682132 || (*point_recv)[i].CPI_Time == 30688692
|
||||
// || (*point_recv)[i].CPI_Time == 30691692 || (*point_recv)[i].CPI_Time == 30691880)
|
||||
// {
|
||||
// std::cout << "azi=" << (*point_recv)[i].Azimuth
|
||||
// << "dis=" << (*point_recv)[i].Range
|
||||
// << "h=" << (*point_recv)[i].Height
|
||||
// << "v=" << (*point_recv)[i].Velocity
|
||||
// << "wave=" << (*point_recv)[i].beam_index
|
||||
// << "time=" << (*point_recv)[i].CPI_Time;
|
||||
// }
|
||||
// }
|
||||
|
||||
//取出待起航的点迹数据
|
||||
for (size_t i=0;i<(*point_recv).size();i++)
|
||||
for (size_t i=0;i<(*point_recv).size();i++){
|
||||
point_process.push_back((*point_recv)[i]);
|
||||
}
|
||||
|
||||
//临时航迹的buff_round+1
|
||||
for (size_t i=0;i<temp_track->size();i++)
|
||||
@@ -58,7 +45,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,27 +65,30 @@ 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.temp_track_id = temp_track_id++;
|
||||
temp_track_tmp.point_id = point_process[i].point_id;
|
||||
temp_track_tmp.circle_id = point_process[i].circle_id;
|
||||
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.amplitude = 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.CPI_time = 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;
|
||||
temp_track->push_back( std::vector <Temp_track> ());
|
||||
int n=temp_track->size();
|
||||
int n=static_cast<int>(temp_track->size());
|
||||
(*temp_track)[n-1].push_back(temp_track_tmp);
|
||||
|
||||
// if(point_process[i].CPI_Time == 30675948 || point_process[i].CPI_Time == 30682320)
|
||||
@@ -117,6 +107,15 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
|
||||
std::vector<PointRecv>().swap(point_process);
|
||||
std::vector<PointRecv>().swap((*point_recv));
|
||||
|
||||
#ifdef RDP_RECORD_ENABLE
|
||||
//记录临时航迹
|
||||
for(auto& vec_t : *temp_track){
|
||||
for(auto& t : vec_t){
|
||||
RDP_RECORD(RdpRecTempTrack(t));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//临时航迹满足起始长度 转为可靠航迹
|
||||
tmp_track_to_trust_track(trust_track,temp_track,Trust_Track_Output, Trust_track_num_Output,Work_Parameter);
|
||||
|
||||
@@ -125,19 +124,6 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
|
||||
//消亡临时航迹
|
||||
tmp_track_die(temp_track);
|
||||
|
||||
// std::cout << "-----------------------------------------------";
|
||||
// for(int i=0;i<temp_track->size();i++)
|
||||
// {
|
||||
// for(int j=0;j<(*temp_track)[i].size();j++)
|
||||
// {
|
||||
// std::cout << "temp_track_azi=" << (*temp_track)[i][j].azi
|
||||
// << "temp_track_dis=" << (*temp_track)[i][j].r
|
||||
// << "temp_track_h=" << (*temp_track)[i][j].height
|
||||
// << "temp_track_v=" << (*temp_track)[i][j].vr
|
||||
// << "temp_track_time=" << (*temp_track)[i][j].T;
|
||||
// }
|
||||
// }
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
@@ -159,22 +145,22 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
|
||||
{
|
||||
for ( size_t j=0;j<(*temp_track).size();j++)
|
||||
{
|
||||
int L = (*temp_track)[j].size();
|
||||
int L = static_cast<int>((*temp_track)[j].size());
|
||||
|
||||
if( (*temp_track)[j].size() > 1 && (*temp_track)[j][L-1].buff_round >= 2)
|
||||
{
|
||||
|
||||
//点迹信息
|
||||
// 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];
|
||||
@@ -184,7 +170,7 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
|
||||
double v_temp_track=(*temp_track)[j][L-1].vr;
|
||||
double r_temp_track=(*temp_track)[j][L-1].r;
|
||||
double h_temp_track=(*temp_track)[j][L-1].height;
|
||||
double T_track_head = static_cast<double>((*temp_track)[j][L-1].T);
|
||||
double T_track_head = static_cast<double>((*temp_track)[j][L-1].CPI_time);
|
||||
|
||||
double delta_T = (T_point - T_track_head)/1000.0;
|
||||
|
||||
@@ -204,20 +190,6 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
|
||||
double y2=Z[0]*sin(Z[1]);
|
||||
double alpha = alpha_cal_track_init(x0,y0,x1,y1,x2,y2);
|
||||
|
||||
// if(T_track_head == 33855816 && T_point == 33859004 )
|
||||
// {
|
||||
// std::cout << "r_point=" << r_point
|
||||
// << "h_point=" << h_point
|
||||
// << "T_point=" << T_point
|
||||
// << "r_temp_track=" << r_temp_track
|
||||
// << "v_temp_track=" << v_temp_track
|
||||
// << "h_temp_track=" << h_temp_track
|
||||
// << "T_track_head=" << T_track_head
|
||||
// << "d=" << d
|
||||
// << "alpha=" << alpha;
|
||||
|
||||
// }
|
||||
|
||||
if(d*d<=TRACK_START_THRESHOLD*TRACK_START_THRESHOLD && alpha<ALPHA_START && vr_point*v_temp_track>0)// && fabs(h_point-h_temp_track )<= r_point*SIGMA_E&& abs(vr_point-v_temp_track)/abs(v_temp_track)<0.8
|
||||
{
|
||||
struct Asso_info asso_info_tmp;
|
||||
@@ -226,7 +198,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;
|
||||
}
|
||||
}
|
||||
@@ -240,39 +212,43 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
|
||||
(*temp_track).push_back(std::vector <Temp_track> ());
|
||||
|
||||
//前L个点
|
||||
int L = (*temp_track)[asso_info[i].track_idx-1].size();
|
||||
int L = static_cast<int>((*temp_track)[asso_info[i].track_idx-1].size());
|
||||
for (int j=0;j < L;j++ )
|
||||
{
|
||||
(*temp_track)[(*temp_track).size()-1].push_back((*temp_track)[asso_info[i].track_idx-1][j]);
|
||||
(*temp_track)[(*temp_track).size()-1][j].asso_flag = 0;
|
||||
(*temp_track)[(*temp_track).size()-1][j].temp_track_id = temp_track_id;
|
||||
}
|
||||
|
||||
//关联上的点
|
||||
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.temp_track_id = temp_track_id++;
|
||||
asso_track_info_tmp.point_id = point_process[asso_info[i].point_idx-1].point_id;
|
||||
asso_track_info_tmp.circle_id = point_process[asso_info[i].point_idx-1].circle_id;
|
||||
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.amplitude = point_process[asso_info[i].point_idx-1].amplitude;
|
||||
asso_track_info_tmp.CPI_time = 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].CPI_time)/1000.0;
|
||||
|
||||
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
|
||||
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
|
||||
@@ -328,11 +304,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;
|
||||
@@ -340,7 +316,7 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
|
||||
y_track_head=(*temp_track)[j][0].X[2];
|
||||
double v_track_head=(*temp_track)[j][0].vr;
|
||||
double h_track_head = (*temp_track)[j][0].height;
|
||||
double T_track_head = static_cast<double>((*temp_track)[j][0].T);
|
||||
double T_track_head = static_cast<double>((*temp_track)[j][0].CPI_time);
|
||||
|
||||
//距离差
|
||||
double dis = sqrt(pow(x_point-x_track_head,2)+pow(y_point-y_track_head,2));
|
||||
@@ -376,7 +352,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;
|
||||
}
|
||||
|
||||
@@ -392,19 +368,23 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
|
||||
//第一个点
|
||||
(*temp_track)[(*temp_track).size()-1].push_back((*temp_track)[asso_info[i].track_idx-1][0]);
|
||||
(*temp_track)[(*temp_track).size()-1][0].asso_flag = 0;
|
||||
(*temp_track)[(*temp_track).size()-1][0].temp_track_id = temp_track_id;
|
||||
|
||||
//第二个点
|
||||
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.temp_track_id = temp_track_id++;
|
||||
asso_track_info_tmp.point_id = point_process[asso_info[i].point_idx-1].point_id;
|
||||
asso_track_info_tmp.circle_id = point_process[asso_info[i].point_idx-1].circle_id;
|
||||
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.amplitude = 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.CPI_time = 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 +392,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].CPI_time)/1000.0;
|
||||
|
||||
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
|
||||
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
|
||||
@@ -468,7 +448,7 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
|
||||
std::vector <std::vector<Temp_track>>::iterator Iter;
|
||||
for (Iter=temp_track->begin(); Iter!=temp_track->end();)
|
||||
{
|
||||
int L=(*Iter).size();
|
||||
int L=static_cast<int>((*Iter).size());
|
||||
double range=(*Iter)[L-1].r;
|
||||
double azi=(*Iter)[L-1].azi/PI*180;
|
||||
|
||||
@@ -514,9 +494,9 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
|
||||
if (Track_to_start[i].size() < 3 || Track_to_start[j].size() < 3)
|
||||
continue;
|
||||
|
||||
if( (Track_to_start[i][0].T == Track_to_start[j][0].T && Track_to_start[i][0].r == Track_to_start[j][0].r)
|
||||
|| (Track_to_start[i][1].T == Track_to_start[j][1].T && Track_to_start[i][1].r == Track_to_start[j][1].r)
|
||||
|| (Track_to_start[i][2].T == Track_to_start[j][2].T && Track_to_start[i][2].r == Track_to_start[j][2].r)
|
||||
if( (Track_to_start[i][0].CPI_time == Track_to_start[j][0].CPI_time && Track_to_start[i][0].r == Track_to_start[j][0].r)
|
||||
|| (Track_to_start[i][1].CPI_time == Track_to_start[j][1].CPI_time && Track_to_start[i][1].r == Track_to_start[j][1].r)
|
||||
|| (Track_to_start[i][2].CPI_time == Track_to_start[j][2].CPI_time && Track_to_start[i][2].r == Track_to_start[j][2].r)
|
||||
)
|
||||
{
|
||||
if(Track_to_start[i][L-1].d<Track_to_start[j][L-1].d)
|
||||
@@ -565,22 +545,25 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
|
||||
double Z2[2]={(Track_to_start[i][L-1].r)*cos(Track_to_start[i][L-1].azi),(Track_to_start[i][L-1].r)*sin(Track_to_start[i][L-1].azi) };
|
||||
double X[6];
|
||||
double P[6][6];
|
||||
double T1=(Track_to_start[i][L-2].T-Track_to_start[i][L-3].T)/1000.0;
|
||||
double T2=(Track_to_start[i][L-1].T-Track_to_start[i][L-2].T)/1000.0;
|
||||
double T1=(Track_to_start[i][L-2].CPI_time-Track_to_start[i][L-3].CPI_time)/1000.0;
|
||||
double T2=(Track_to_start[i][L-1].CPI_time-Track_to_start[i][L-2].CPI_time)/1000.0;
|
||||
kalman Kalman;
|
||||
Kalman.kalman_filter_init_3dots(Z0, Z1, Z2, T1,T2, X ,P);
|
||||
|
||||
Trust_Track trust_track_tmp = {};
|
||||
memcpy(trust_track_tmp.X, X, 6*sizeof(double));
|
||||
memcpy(trust_track_tmp.P, P, 6*6*sizeof(double));
|
||||
|
||||
trust_track_tmp.Amplitude=Track_to_start[i][L-1].Amp;
|
||||
trust_track_tmp.snr_point = Track_to_start[i][L-1].snr;
|
||||
|
||||
trust_track_tmp.track_id = Track_to_start[i][L-1].temp_track_id;
|
||||
trust_track_tmp.point_id = Track_to_start[i][L-1].point_id;
|
||||
trust_track_tmp.circle_id = Track_to_start[i][L-1].circle_id;
|
||||
trust_track_tmp.Amplitude=Track_to_start[i][L-1].amplitude;
|
||||
trust_track_tmp.snr_point = Track_to_start[i][L-1].SNR;
|
||||
trust_track_tmp.RCS = Track_to_start[i][L-1].RCS;
|
||||
trust_track_tmp.pitch_num = Track_to_start[i][L-1].pitch_num;
|
||||
std::memcpy(trust_track_tmp.speed_dim, Track_to_start[i][L-1].speed_dim, sizeof(trust_track_tmp.speed_dim));
|
||||
std::memcpy(trust_track_tmp.range_dim, Track_to_start[i][L-1].range_dim, sizeof(trust_track_tmp.range_dim));
|
||||
trust_track_tmp.T_track = Track_to_start[i][L-1].T;
|
||||
trust_track_tmp.T_track = Track_to_start[i][L-1].CPI_time;
|
||||
trust_track_tmp.GNSS_time = Track_to_start[i][L-1].GNSS_time;
|
||||
trust_track_tmp.point_flag=1; //实点
|
||||
trust_track_tmp.Track_Mode=0; //跟踪模式 TWS 0
|
||||
@@ -626,8 +609,8 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
|
||||
}
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].Range_V=static_cast<float>(sqrt(pow(Track_to_start[i][j].X[1],2)+pow(Track_to_start[i][j].X[3],2)));
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].z=static_cast<float>(Track_to_start[i][j].height);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].Amplitude=static_cast<float>(Track_to_start[i][j].Amp);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].track_snr = static_cast<float>(Track_to_start[i][j].snr);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].Amplitude=static_cast<float>(Track_to_start[i][j].amplitude);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].track_snr = static_cast<float>(Track_to_start[i][j].SNR);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].track_rcs = static_cast<float>(Track_to_start[i][j].RCS);
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].pitch_num = Track_to_start[i][j].pitch_num;
|
||||
@@ -640,7 +623,7 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
|
||||
Direction_Angle=Direction_Angle+2*PI;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].Direction_Angle=static_cast<float>(Direction_Angle/PI*180);
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].track_time=static_cast<float>((Track_to_start[i][j].T)/1000.0);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].track_time=static_cast<float>((Track_to_start[i][j].CPI_time)/1000.0);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].GNSS_time=Track_to_start[i][j].GNSS_time;
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].x=static_cast<float>(Track_to_start[i][j].X[0]);
|
||||
@@ -676,7 +659,7 @@ void Track_Init::tmp_track_die(std::vector <std::vector<Temp_track>> *temp_t
|
||||
std::vector <std::vector<Temp_track>>::iterator Iter;
|
||||
for (Iter=temp_track->begin(); Iter!=temp_track->end();)
|
||||
{
|
||||
int n=(*Iter).size();
|
||||
int n=static_cast<int>((*Iter).size());
|
||||
if (n <= 0)
|
||||
{
|
||||
temp_track->erase(Iter);
|
||||
|
||||
@@ -27,6 +27,8 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
long long temp_track_id = 0; //临时航迹id
|
||||
|
||||
std::vector<PointRecv> point_process; //要处理的点迹
|
||||
|
||||
void point_temp_track_asso(std::vector <std::vector<Temp_track>> *temp_track,
|
||||
|
||||
@@ -37,22 +37,22 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
|
||||
{
|
||||
for ( size_t j=0;j<(*temp_track).size();j++)
|
||||
{
|
||||
int L = (*temp_track)[j].size();
|
||||
int L = static_cast<int>((*temp_track)[j].size());
|
||||
|
||||
if( (*temp_track)[j].size() > 1 && (*temp_track)[j][L-1].buff_round >= 2)
|
||||
{
|
||||
|
||||
//点迹信息
|
||||
// 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];
|
||||
@@ -62,7 +62,7 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
|
||||
double v_temp_track=(*temp_track)[j][L-1].vr;
|
||||
double r_temp_track=(*temp_track)[j][L-1].r;
|
||||
double h_temp_track=(*temp_track)[j][L-1].height;
|
||||
double T_track_head = static_cast<double>((*temp_track)[j][L-1].T);
|
||||
double T_track_head = static_cast<double>((*temp_track)[j][L-1].CPI_time);
|
||||
|
||||
double delta_T = (T_point - T_track_head)/1000.0;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,7 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
|
||||
(*temp_track).push_back(std::vector <Temp_track> ());
|
||||
|
||||
//前L个点
|
||||
int L = (*temp_track)[asso_info[i].track_idx-1].size();
|
||||
int L = static_cast<int>((*temp_track)[asso_info[i].track_idx-1].size());
|
||||
for (int j=0;j < L;j++ )
|
||||
{
|
||||
(*temp_track)[(*temp_track).size()-1].push_back((*temp_track)[asso_info[i].track_idx-1][j]);
|
||||
@@ -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.amplitude = point_process[asso_info[i].point_idx-1].amplitude;
|
||||
asso_track_info_tmp.CPI_time = 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].CPI_time)/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;
|
||||
@@ -199,7 +199,7 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
|
||||
y_track_head=(*temp_track)[j][0].X[2];
|
||||
double v_track_head=(*temp_track)[j][0].vr;
|
||||
double h_track_head = (*temp_track)[j][0].height;
|
||||
double T_track_head = static_cast<double>((*temp_track)[j][0].T);
|
||||
double T_track_head = static_cast<double>((*temp_track)[j][0].CPI_time);
|
||||
|
||||
//距离差
|
||||
double dis = sqrt(pow(x_point-x_track_head,2)+pow(y_point-y_track_head,2));
|
||||
@@ -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.amplitude = 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.CPI_time = 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].CPI_time)/1000.0;
|
||||
|
||||
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
|
||||
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
|
||||
@@ -306,7 +306,7 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
|
||||
std::vector <std::vector<Temp_track>>::iterator Iter;
|
||||
for (Iter=temp_track->begin(); Iter!=temp_track->end();)
|
||||
{
|
||||
int L=(*Iter).size();
|
||||
int L=static_cast<int>((*Iter).size());
|
||||
if (L < 3)
|
||||
{
|
||||
++Iter;
|
||||
@@ -323,8 +323,8 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
|
||||
double Z2[2]={((*Iter)[L-1].r)*cos((*Iter)[L-1].azi),((*Iter)[L-1].r)*sin((*Iter)[L-1].azi) };
|
||||
double X[6];
|
||||
double P[6][6];
|
||||
double T1=((*Iter)[L-2].T-(*Iter)[L-3].T)/1000.0;
|
||||
double T2=((*Iter)[L-1].T-(*Iter)[L-2].T)/1000.0;
|
||||
double T1=((*Iter)[L-2].CPI_time-(*Iter)[L-3].CPI_time)/1000.0;
|
||||
double T2=((*Iter)[L-1].CPI_time-(*Iter)[L-2].CPI_time)/1000.0;
|
||||
kalman Kalman;
|
||||
Kalman.kalman_filter_init_3dots(Z0, Z1, Z2, T1,T2, X ,P);
|
||||
Trust_Track trust_track_tmp = {};
|
||||
@@ -332,10 +332,10 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
|
||||
memcpy(trust_track_tmp.P, P, 6*6*sizeof(double));
|
||||
|
||||
trust_track_tmp.Track_Index = track_ID;
|
||||
trust_track_tmp.Amplitude=(*Iter)[L-1].Amp;
|
||||
trust_track_tmp.snr_point = (*Iter)[L-1].snr;
|
||||
trust_track_tmp.Amplitude=(*Iter)[L-1].amplitude;
|
||||
trust_track_tmp.snr_point = (*Iter)[L-1].SNR;
|
||||
trust_track_tmp.RCS = (*Iter)[L-1].RCS;
|
||||
trust_track_tmp.T_track = (*Iter)[L-1].T;
|
||||
trust_track_tmp.T_track = (*Iter)[L-1].CPI_time;
|
||||
trust_track_tmp.point_flag=1; //实点
|
||||
trust_track_tmp.Track_Mode=1; //跟踪模式 TWS 0
|
||||
trust_track_tmp.Target_Type=UNCONF_TARGET;
|
||||
@@ -375,8 +375,8 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].Elevation=static_cast<float>(asin((*Iter)[j].height/(*Iter)[j].r)/PI*180);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].Range_V=static_cast<float>(sqrt(pow((*Iter)[j].X[1],2)+pow((*Iter)[j].X[3],2)));
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].z=static_cast<float>((*Iter)[j].height);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].Amplitude=static_cast<float>((*Iter)[j].Amp);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].track_snr = static_cast<float>((*Iter)[j].snr);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].Amplitude=static_cast<float>((*Iter)[j].amplitude);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].track_snr = static_cast<float>((*Iter)[j].SNR);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].track_rcs = static_cast<float>((*Iter)[j].RCS);
|
||||
double Direction_Angle;
|
||||
Direction_Angle=atan2((*Iter)[j].X[3],(*Iter)[j].X[1]);
|
||||
@@ -384,7 +384,7 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
|
||||
Direction_Angle=Direction_Angle+2*PI;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].Direction_Angle=static_cast<float>(Direction_Angle/PI*180);
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].track_time=static_cast<float>(((*Iter)[j].T)/1000.0);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].track_time=static_cast<float>(((*Iter)[j].CPI_time)/1000.0);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].x=static_cast<float>((*Iter)[j].X[0]);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].v_x=static_cast<float>((*Iter)[j].X[1]);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].y=static_cast<float>((*Iter)[j].X[2]);
|
||||
@@ -419,7 +419,7 @@ void Track_Init_Direct_Tracking::tmp_track_die(std::vector <std::vector<Temp_tra
|
||||
std::vector <std::vector<Temp_track>>::iterator Iter;
|
||||
for (Iter=temp_track->begin(); Iter!=temp_track->end();)
|
||||
{
|
||||
int n=(*Iter).size();
|
||||
int n=static_cast<int>((*Iter).size());
|
||||
if (n <= 0)
|
||||
{
|
||||
temp_track->erase(Iter);
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
| ③ 凝聚 | 单 CPI 或相邻两 CPI 点迹 | 同类点合并(保幅度大者) | 凝聚点迹表 |
|
||||
| ④ 关联 | 凝聚点迹 + 已有航迹 | IMM 交互 → 波门筛选 → 最近邻一对一 → EKF 更新 | 更新航迹 + 剩余点迹 |
|
||||
| ⑤ 起始 | 剩余(未关联)点迹 | 逻辑法(每圈顺序固定):点 × ≥2 点临时航迹关联 → 点 × 1 点航迹头关联 → 剩余点转新航迹头 → 达标临时航迹起批 | 新可靠航迹 + 更新后的临时航迹表 |
|
||||
| ⑥ 外推/消亡 | 未关联航迹 | 按数据率预测一帧;超限则消亡 | 补点航迹 / 消亡批号 |
|
||||
| ⑥ 外推/消亡 | 未关联航迹 | 未到外推上限则按数据率预测一帧并输出,超限只累加轮数、不再预测也不再输出(TWS 上限 3 轮;TAS 判据方向相反,相扫 5 / 机扫 48 轮起才预测) | 本圈有更新的航迹(含补点行)/ 消亡批号 |
|
||||
| ⑦ 波束控制 | TAS 队列 + 航迹 | 数据率门控 + 队列调度 → 位置外推 | 跟踪波束指令 |
|
||||
|
||||
**贯穿全流程的三条主线**
|
||||
|
||||
1. **时间主线**:所有时间戳为 64 位毫秒(CPI/GNSS),参与运算时除 1000 转秒;ΔT = 点迹时间 − 航迹时间,ΔT 的正负决定"有效更新 / 只消费不更新 / 跳过"。
|
||||
2. **状态主线**:每条可靠航迹维护一套 IMM(3 个 Singer 模型)状态 [x, vx, ax, y, vy, ay],x/y 两轴解耦,量测为「距离-方位-径向速度」三维 EKF。
|
||||
3. **生命周期主线**:可靠航迹靠"外推轮数"消亡;临时航迹靠"缓存圈数 buff_round"消亡。
|
||||
3. **生命周期主线**:可靠航迹靠"外推轮数"消亡(判据为 `>`:TWS 普通航迹 6 圈、重点目标 10 圈,TAS 相扫 10 / 机扫 96 圈);临时航迹靠"缓存圈数 buff_round"消亡。
|
||||
|
||||
---
|
||||
|
||||
@@ -176,8 +176,11 @@ flowchart TD
|
||||
J --> L{"还有候选?"}
|
||||
K --> L
|
||||
L -- 有 --> G
|
||||
L -- 无 --> M["未关联航迹(point_flag==0 且非手动TAS)按数据率外推"]
|
||||
M --> N["输出全部航迹(更新行 + 补点行)"]
|
||||
L -- 无 --> M{"未关联航迹(point_flag==0 且非手动TAS):<br/>Extrapolate_round ≤ MAX_EXTRAPOLATE_NUM ?"}
|
||||
M -- 是 --> M1["按数据率 Singer 预测一帧<br/>推进航迹时间,Track_Update_Flag=1"]
|
||||
M -- 否 --> M2["不预测、不推进时间<br/>仅 Extrapolate_round+1,Track_Update_Flag=0"]
|
||||
M1 --> N["只输出 Track_Update_Flag==1 的航迹行并清零该标志<br/>(其后由 track_init 追加起批历史点)"]
|
||||
M2 --> N
|
||||
```
|
||||
|
||||
### 4.2 波门(统计距离、高度、距离三类都通过才算候选)
|
||||
@@ -232,7 +235,7 @@ VT > 300 且 R > 1200 → 拒绝(高速目标给固定 1200 m 上限)
|
||||
| IMM/EKF 更新 | **不执行**,X1/X2/X3、P1/P2/P3、模型概率均保持原值 |
|
||||
| 航迹时间/点信息 | **不刷新**(T_track、GNSS_time、关联点信息、幅度/SNR/RCS 都不变) |
|
||||
| 新鲜度标志 | `point_flag` 保持 0、`Extrapolate_round` 不归零、`associate_point_number` 不递增 |
|
||||
| 本圈后续 | 该航迹本圈不再关联其它点迹;由于 point_flag 仍为 0,**在随后的外推环节被推进一帧** |
|
||||
| 本圈后续 | 该航迹本圈不再关联其它点迹;由于 point_flag 仍为 0,**在随后的外推环节被推进一帧**(未超外推上限时;超限后只累加轮数、不再预测也不输出该行) |
|
||||
| 点迹归属 | 该点迹被标记"已使用"消费掉 → **不会再进入航迹起始**,避免生成虚假 TWS 航迹 |
|
||||
|
||||
### 4.5 关联成功后的更新内容
|
||||
@@ -240,9 +243,9 @@ VT > 300 且 R > 1200 → 拒绝(高速目标给固定 1200 m 上限)
|
||||
1. 三个模型的 EKF 状态/协方差更新;
|
||||
2. 模型概率按新息协方差似然更新(见第 5 节);
|
||||
3. 航迹时间 T_track / GNSS 时间跟进到该点迹;
|
||||
4. 关联点信息(距离/方位/俯仰/径向速度/PRI)、幅度、SNR、RCS、俯仰波位、十字星数据刷新;
|
||||
4. 关联点信息(距离/方位/俯仰/径向速度/PRI)、幅度、SNR、RCS、归一化幅度 `amp_asv_point`、速度维 FFT 点数 `prn_fftn_point`、俯仰波位、十字星数据刷新;
|
||||
5. **高度维平滑**(见 4.6);
|
||||
6. `Extrapolate_round = 0`、`point_flag = 1`、`associate_point_number += 1`。
|
||||
6. `Extrapolate_round = 0`、`point_flag = 1`、`associate_point_number += 1`、`Track_Update_Flag = 1`(输出门控,见 4.7 末)。
|
||||
|
||||
### 4.6 高度平滑
|
||||
|
||||
@@ -257,14 +260,26 @@ VT > 300 且 R > 1200 → 拒绝(高速目标给固定 1200 m 上限)
|
||||
|
||||
> 距离越远俯仰测量跳变越大,窗口随之放长;仅 TWS/TAS 关联到实点时才刷新,外推补点不改变高度。
|
||||
|
||||
### 4.7 未关联航迹的外推
|
||||
### 4.7 未关联航迹的外推(含外推圈数上限)
|
||||
|
||||
对 `point_flag == 0` 且 `manual_tracking_flag == 0` 的航迹(含 ΔT≤0 被消费的情形):
|
||||
对 `point_flag == 0` 且 `manual_tracking_flag == 0` 的航迹(含 ΔT≤0 被消费的情形),按 `Extrapolate_round` 分两支(50e0b5c):
|
||||
|
||||
**(1)未到上限**:`Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(机扫/相扫均为 3)时照常外推:
|
||||
|
||||
- 推算步长取运行时数据率:`work_mode` 0/1/2 → `DATA_RATE_SHORT / MIDDLE / FAR`(秒);
|
||||
- 用各子模型的 Singer F、Q 推进一帧(**不做量测更新**);
|
||||
- `T_track`、`GNSS_time` 各推进 ΔT×1000 毫秒;
|
||||
- `Extrapolate_round += 1`,`point_flag = 0`(输出为补点/虚点)。
|
||||
- `Extrapolate_round += 1`,`point_flag = 0`(补点/虚点),`Track_Update_Flag = 1` → 本圈作为补点行输出。
|
||||
|
||||
**(2)已超上限**:判据不再成立时**不再做 Singer 预测、不推进航迹时间**,只执行:
|
||||
|
||||
- `Extrapolate_round += 1`、`point_flag = 0`、`Track_Update_Flag = 0` → 本圈**不输出**该航迹(航迹仍留在航迹表中,等消亡判据命中)。
|
||||
|
||||
**输出门控(50e0b5c)**:TWS 输出航迹的循环改为「`Track_Update_Flag == 1` 才输出,输出后清零」,即每圈只输出**本圈关联到实点或成功外推**的航迹;此前是每圈把航迹表中全部可靠航迹各输出一行。门控只判更新标志、不判 `manual_tracking_flag`,因此手动转 TAS 的航迹若本圈在 TWS 侧关联到实点,仍会经 TWS 通道输出。
|
||||
|
||||
> 由于外推上限(3)小于 TWS 消亡轮数(6),普通航迹在停止预测后还会**连续 3 圈不向宿主输出**任何航迹行(`Extrapolate_round` 依次走到 5、6、7,第 7 圈在 `track_die` 中消亡),只在最后输出一次消亡批号。
|
||||
>
|
||||
> **TAS 通道的判据方向相反**(`track_asso_tas.cpp` L484):`Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS` 时才做预测、推进航迹时间并置 `Track_Update_Flag = 1`;未达该轮数时只累加圈数(不预测、不输出)。即 TWS 是"先外推、到上限后停",TAS 是"先不预测、到轮数后才开始外推",详见 7.2 与第 10 节。
|
||||
|
||||
---
|
||||
|
||||
@@ -375,9 +390,14 @@ dis ≥ |v_r,head| · ΔT 同上,用航迹头的多普勒约
|
||||
|
||||
| 通道 | 条件(满足其一即消亡) |
|
||||
|---|---|
|
||||
| TWS 航迹 | `Track_Mode==0` 且 `Extrapolate_round ≥ 4` |
|
||||
| TAS 航迹 | `Track_Mode==1` 且 `Extrapolate_round ≥ TRACK_DIE_ROUND_TAS`(相扫 5 / 机扫 96) |
|
||||
| 手动删除 | `manual_delete_flag == 1`(两种通道通用,由外部删除接口置位) |
|
||||
| TWS 普通航迹 | `Track_Mode==0` 且 `primary_target_flag==0` 且 `Extrapolate_round > TRACK_DIE_ROUND`(相扫/机扫均为 6) |
|
||||
| TWS 重点航迹 | `Track_Mode==0` 且 `primary_target_flag==1` 且 `Extrapolate_round > TRACK_DIE_ROUND_PRIMARY`(10) |
|
||||
| TAS 航迹 | `Track_Mode==1` 且 `Extrapolate_round > TRACK_DIE_ROUND_TAS`(相扫 10 / 机扫 96) |
|
||||
| 手动删除 | `manual_delete_flag == 1`(各通道单独判定,置位即消亡,由外部删除接口 `track_delete` 置位) |
|
||||
|
||||
三个判据都改为 `die_flag` 组合("或"关系),且比较符由 `>=` 改为 `>`(50e0b5c):即允许的外推圈数比旧版各多 1 圈,且**重点航迹与普通航迹走不同门限**。由于每圈未关联只自增 1,判据为 `>` 时实际是"连续 N+1 圈没有实点关联"才消亡:普通 TWS 航迹 7 圈、重点航迹 11 圈、TAS 相扫 11 圈 / 机扫 97 圈。
|
||||
|
||||
> `primary_target_flag`(重点目标标记)由外部接口 `Data_Process::primary_target_add / primary_target_cancel` 按批号置 1 / 清 0(`data_process.cpp` L268-297);新起批航迹在 `track_init.cpp` 中以 `Trust_Track trust_track_tmp = {}` 零初始化,故恒为 0。该标记目前只影响上表的 TWS 消亡轮数。
|
||||
|
||||
消亡时把批号写入消亡输出数组并从航迹表删除;**临时航迹消亡不对外输出**(起批失败的中间态对宿主不可见)。
|
||||
|
||||
@@ -412,7 +432,8 @@ flowchart TD
|
||||
| 俯仰/高度门限 | 俯仰门限已注释停用;**高度门限生效**(按距离分段,e0bf227 起) | **高度门限生效**(按距离分段,取值与 TWS 相同) |
|
||||
| ΔT ≤ 0 | 参与选取但只消费点迹、不更新 | **直接跳过该点**(并记告警日志) |
|
||||
| 外推时间 | 按 work_mode 数据率固定步长 | 按 `最新时间戳 − 航迹时间` |
|
||||
| 输出 | 全部航迹 | 仅该批号航迹 |
|
||||
| 外推触发轮数 | `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(3)才预测 | `Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)才预测(方向与 TWS 相反) |
|
||||
| 输出 | 本圈有更新的航迹(关联到实点或成功外推,输出后清 `Track_Update_Flag`) | 该批号航迹且本圈有更新 |
|
||||
|
||||
### 7.2 TAS 关联与更新
|
||||
|
||||
@@ -428,10 +449,13 @@ flowchart TD
|
||||
F -- 否 --> H["丢弃"]
|
||||
G --> I["取 d_min 最小的一点"]
|
||||
I --> J["三模型 EKF 更新 → 概率更新 → 融合<br/>刷新时间/点信息/高度平滑"]
|
||||
J --> K["输出该批号航迹"]
|
||||
J --> K["输出该批号航迹(Track_Update_Flag==1 才输出,输出后清零)"]
|
||||
E --> L{"候选表为空?"}
|
||||
L -- 是 --> M["按 ΔT = 最新时间戳 − 航迹时间 外推一帧<br/>(ΔT ≤ 0 则跳过)"]
|
||||
L -- 是 --> L1{"Extrapolate_round ≥ MAX_EXTRAPOLATE_NUM_TAS ?"}
|
||||
L1 -- 是 --> M["按 ΔT = 最新时间戳 − 航迹时间 外推一帧<br/>(ΔT ≤ 0 则跳过),Track_Update_Flag=1"]
|
||||
L1 -- 否 --> M2["不预测、不推进时间<br/>仅 Extrapolate_round+1,Track_Update_Flag=0"]
|
||||
M --> K
|
||||
M2 --> K
|
||||
```
|
||||
|
||||
**高度门限**(|h_航迹 − h_点迹| 上限,按航迹距离分段):
|
||||
@@ -446,6 +470,8 @@ flowchart TD
|
||||
|
||||
**TAS 后续处理**:外推不受 work_mode 数据率约束,而是直接推进到"本帧最新时间戳",保证 TAS 航迹时间与波束调度时刻同步。
|
||||
|
||||
**TAS 外推轮数门控(50e0b5c)**:与 TWS **方向相反**——只有 `Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)时才做预测、推进航迹时间并置 `Track_Update_Flag = 1`;未达该轮数时只 `Extrapolate_round += 1`、`point_flag = 0`、`Track_Update_Flag = 0`,既不预测也不输出。因此 TAS 航迹在刚丢失点迹时并不会立刻按"最新时间戳"外推:相扫体制下前 5 次未关联不产生任何输出行,第 6 次起才开始外推输出;机扫体制下前 48 次不输出。该不对称仅如实记录(成因不明,不作缺陷判定),连同 TWS/TAS 输出门控一并见第 10 节。
|
||||
|
||||
---
|
||||
|
||||
## 8. TAS 波束控制(外部接口 Beam_Ctrl)
|
||||
@@ -510,8 +536,11 @@ flowchart TD
|
||||
| `TRACK_START_THRESHOLD` | 8 | 同左 | 起始统计距离波门(d ≤ 8) |
|
||||
| `ALPHA_START` | 60 | 同左 | 起始折角上限(°) |
|
||||
| `ASSO_THORD` | 4 | 同左 | 关联波门(判据 d*d < ASSO_THORD²,d 已是马氏距离平方,等效 d < 4) |
|
||||
| `TRACK_DIE_ROUND` | 4 | 4 | TWS 消亡外推轮数 |
|
||||
| `TRACK_DIE_ROUND_TAS` | 5 | 96 | TAS 消亡外推轮数 |
|
||||
| `MAX_EXTRAPOLATE_NUM` | 3 | 同左 | TWS 未关联航迹最大外推轮数(`Extrapolate_round <= 该值` 才预测并输出) |
|
||||
| `MAX_EXTRAPOLATE_NUM_TAS` | 5 | 48 | TAS 未关联航迹开始外推的轮数(判据 `>=`,方向与 TWS 相反) |
|
||||
| `TRACK_DIE_ROUND` | 6 | 6 | TWS 普通航迹消亡轮数(判据 `Extrapolate_round > 该值`) |
|
||||
| `TRACK_DIE_ROUND_PRIMARY` | 10 | 10 | TWS 重点航迹(`primary_target_flag==1`)消亡轮数 |
|
||||
| `TRACK_DIE_ROUND_TAS` | 10 | 96 | TAS 航迹消亡轮数(判据 `Extrapolate_round > 该值`) |
|
||||
| `MAX_TRACK_NUM / MAX_TRACK_INDEX` | 500 / 500 | 同左 | 航迹数上限 / 航迹号上限 |
|
||||
| `TAS_QUEUE_LENGTH / MAX_TAS_NUM` | 4 / 4 | 1 / 1 | TAS 队列长度 / 最大 TAS 目标数 |
|
||||
| `H_F_WIN_LEN` | 3 | 同左 | 高度平滑窗长基数(实际 +2/+3/+4/+6) |
|
||||
@@ -564,6 +593,15 @@ flowchart TD
|
||||
- 屏蔽区/禁止区的组数按 30 组数组定义,配置数量未做上限校验;
|
||||
- 高度平滑缓存只增不减,长时运行下每条航迹的内存占用会持续增长。
|
||||
|
||||
**外推、消亡与输出门控(50e0b5c / fc49707 / 42b598e / e8c56f9)**
|
||||
|
||||
- **外推圈数上限**:TWS 只在 `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(3)时预测并输出;TAS 相反,`Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)时才预测并输出。两处比较方向不一致,此处仅如实记录,**不判定为缺陷**;
|
||||
- **消亡判据**由 `>=` 改为 `>`,并区分重点目标:普通 TWS 航迹 `> TRACK_DIE_ROUND`(6)、重点航迹(`primary_target_flag==1`)`> TRACK_DIE_ROUND_PRIMARY`(10)、TAS 航迹 `> TRACK_DIE_ROUND_TAS`(相扫 10 / 机扫 96);`manual_delete_flag == 1` 单独判为消亡;因外推上限(3)小于消亡轮数(6),普通 TWS 航迹停止预测后还会连续 3 圈不向宿主输出;
|
||||
- **输出门控**:TWS 与 TAS 的输出航迹循环都新增 `Track_Update_Flag == 1` 判定并在输出后清零,只输出**本圈关联到实点或成功外推**的航迹(此前 TWS 每圈把航迹表中全部可靠航迹各输出一行);
|
||||
- **接近/远离判据**(fc49707 + 42b598e):`Flag_Direction` 不再用实测径向速度 `vr_point > 0`(受多普勒速度模糊影响符号可能取反),改为用 IMM 融合速度方向角判定——`Direction_Angle = atan2(X[4], X[1])`(弧度转度后输出到 `Track.Direction_Angle`),`x`、`y` 为经 `Work_Parameter.Sys_delay` 补偿后的位置、`r` 为其模,`acos((-x·cosθ - y·sinθ)/r) < π/2` 判为靠近(1)、否则远离(0),等价于"速度方向与雷达→目标位置方向夹角 > 90°"。中间版本 fc49707 曾写为 `acos((-x·vx - y·vy)/r/Range_V) < π/4`(按合速度归一化、门限 45°),42b598e 改为上式;
|
||||
- **归一化幅度 / 速度维 FFT 点数(e8c56f9 新增,输入侧已打通)**:`DataRev` 新增 `amp_asv`(归一化幅度 dB)、`prn_fftn`(速度维 FFT 点数),`Track`/内部 `PointRecv`/`Trust_Track` 也新增同名成对字段;`data_preprocess()` 现已在 TWS 与 TAS 两条分支都把这两个字段搬入 `PointRecv`(并随 `RdpRecPoint` 落库),因此 `track_asso.cpp` 写入航迹并在 TWS 输出行写回的 `amp_asv_point` / `prn_fftn_point` 有有效数据来源;**TAS 的关联与输出路径(`track_asso_tas.cpp`)仍未涉及这两个字段**,TAS 输出行中它们保持宿主原值。
|
||||
- **版本号与记录标识(b227ae1 / c3c7e66)**:`parameters.h` 新增版本宏并拼装为 `VERSION_STRING`(当前 `V1.5.6.PS` / `V1.5.6.MS`),实例创建后可由基类公开成员 `version_info` 读取;`Data_Process` 新增 `point_id` / `frame_id` / `circle_id` 自增计数(`circle_id` 在 TWS 满一圈、TAS 每帧末自增),`PointRecv`/`Temp_track`/`Trust_Track` 新增对应的标识字段,记录功能据此把「输入点迹 → 凝聚点迹 → 临时航迹 → 可靠航迹 → 关联信息」串成同一条链路。
|
||||
|
||||
**未启用**
|
||||
|
||||
- 引导跟踪(direct tracking)、手动打跟踪波束接口为空实现;
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
# 数据记录调试功能(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` | |
|
||||
| `std::string` | `TEXT` | 用于把数组类状态(如 `X[6]`、`P[6][6]`、`u[3]`)经 `rdp_record_entity.h` 的 `arrayToString()` 先转成文本再入库——这是「禁止数组字段」的替代写法,见第 5 条 |
|
||||
|
||||
5. **禁止数组字段**(如 `float speed_dim[129]`、`char[N]`):ormpp 对 C 数组按 C 字符串语义(`strlen`)绑定,会读越界或写入错误数据。需要记录大批量数组时,按下列三种方式之一处理:① 用一个 `std::string` 字段存 `arrayToString(数组)` 的结果(`RdpRecTempTrack`/`RdpRecTrustTrack` 的 `X`/`P`/`X1`~`X3`/`P2`/`P3`/`u` 即此法,多维数组以 `;` 分隔内层、`,` 分隔元素);② 另建「元素展开表」(一行一个元素);③ 改用 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. 现有记录表与记录点
|
||||
|
||||
当前共 **7 个记录实体定义**,其中 **6 张表在用**(第 ⑦ 项已停用)。下表逐项给出记录点、埋点位置、表名与用途;完整字段清单见本节末的「字段速查」。
|
||||
|
||||
| # | 记录点 | 位置 | 表 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| ① | 输入点迹(TWS + TAS) | `data_process.cpp::data_preprocess()`,TWS 分支(`point_type == 0`)与 TAS 分支(`point_type == 1`)的逐点循环内 | `RdpRecPoint` | TWS/TAS **共用一张表**,用 `point_type` 区分;每个输入点迹一行,含 `point_id` / `frame_id` / `circle_id` 三个链路标识 |
|
||||
| ② | TWS 凝聚后点迹 | `dot_coh.cpp::dot_coh_process_buff()`,输出到 `point_recv` 的循环内 | `RdpRecPointCohesionTWS` | 两帧滑窗凝聚后进入关联的点迹 |
|
||||
| ③ | TAS 凝聚后点迹 | `dot_coh_tas.cpp::dot_coh_tas_process()`,输出到 `point_recv_tas` 的循环内 | `RdpRecPointCohesionTAS` | 单帧凝聚后的 TAS 点迹 |
|
||||
| ④ | 临时航迹 | `track_init.cpp::track_init_process_logic()` 末尾(**消亡临时航迹之后**),双层遍历 `temp_track` | `RdpRecTempTrack` | 每条临时航迹的每个点一行,含 `temp_track_id` / `point_id` / `circle_id`;因在消亡之后记录,表中是「本圈存活下来 + 本圈新建」的临时航迹 |
|
||||
| ⑤ | 可靠航迹 | `track_asso.cpp::track_asso_process()` 的**输出循环内**(`Track_Update_Flag==1` 的分支末尾) | `RdpRecTrustTrack` | 内部 `Trust_Track` 全量落库(含 IMM 的 `X`/`P`/`X1`~`X3`/`P2`/`P3`/`u`,数组经 `arrayToString()` 存为文本列)。**只记录本圈有输出的航迹,且仅 TWS 通道**——`track_asso_tas.cpp`(TAS)与 `track_init.cpp`(起批)均未埋此点 |
|
||||
| ⑥ | 点航关联信息 | `track_asso.cpp::model_filter()` 三处:未通过门限的候选、被最近邻取中的候选、因航迹/点迹已被消费而删表的候选 | `RdpRecAssoInfo` | 每个候选一行(`d1`/`d2`/`d3`/`d_min`、`delta_T`/`delta_h`/`R`/`VT`、`thd_d`/`thd_h`/`thd_r`),并以 `min_d_flag` 标记是否被最近邻取中,可用于复现关联决策 |
|
||||
| ⑦ | (已停用)本圈输出航迹 | 原 `data_process.cpp::track_process()` 的 `model == 1/3` 分支 | ~~`RdpRecTrackOut`~~ | 该实体与记录点已在 b227ae1 中整体注释停用,改由 ⑤ 记录内部航迹;`rdp_record.cpp` 与 `rdp_record_entity.h` 中保留注释代码以备参考 |
|
||||
|
||||
> 表清单即上表 ①②③④⑤⑥ 六张在用表(`rdp_record_entity.h` 中共 7 个实体定义,其中第 ⑦ 项已停用)。
|
||||
|
||||
**链路串联**:①→②/③(点迹 → 凝聚后点迹)按 `point_id` 关联;②/③→④/⑤ 按 `point_id` 与 `circle_id` 关联;④→⑤ 通过起批时透传的 `track_id`(取自临时航迹末点的 `temp_track_id`)关联;⑤→⑥ 按 `track_id` 与 `point_id` 关联。`circle_id` 在 TWS 满一圈(`Beam_index_aiz == 0`)、TAS 每帧处理结束时自增,可用来划分「第几圈/第几帧」。
|
||||
|
||||
**字段速查**(字段顺序即建表列顺序;首列均为 `int64_t id` 自增主键):
|
||||
|
||||
- `RdpRecPoint` / `RdpRecPointCohesionTWS` / `RdpRecPointCohesionTAS`(三者字段一致):`point_id`、`frame_id`、`circle_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`;
|
||||
- `RdpRecTempTrack`:`temp_track_id`、`point_id`、`circle_id`、`X`、`P`(文本)、`r`、`azi`、`vr`、`height`、`SNR`、`RCS`、`amplitude`、`CPI_time`、`GNSS_time`、`d`、`buff_round`、`temp_track_section_idx`、`asso_flag`、`pitch_num`;
|
||||
- `RdpRecTrustTrack`:`track_id`、`point_id`、`circle_id`、`Track_Index`、`Range`、`Azimuth`、`Elevation`、`Velocity`、`Amplitude`、`Height`、`T_track`、`GNSS_time`、`Track_Sum`、`Point_Index`、`Track_Mode`、`Track_Update_Flag`、`Extrapolate_round`、`point_flag`、`approach_flag`、`manual_delete_flag`、`manual_tracking_flag`、`associate_point_number`、`primary_target_flag`、`Target_Type`、`Track_section_idx`、`range_point`、`azi_point`、`elev_point`、`vr_point`、`prf_point`、`point_type`、`snr_point`、`amp_asv_point`、`prn_fftn_point`、`RCS`、`pitch_num`、`X`、`P`、`X1`、`X2`、`X3`、`P2`、`P3`、`u`(后 8 项为文本);
|
||||
- `RdpRecAssoInfo`:`point_id`、`track_id`、`circle_id`、`d1`、`d2`、`d3`、`d_min`、`point_index`、`track_index`、`delta_T`、`delta_h`、`R`、`VT`、`thd_d`、`thd_h`、`thd_r`、`min_d_flag`。
|
||||
|
||||
各表的字段为**调试用途**,按第 5 节增删即可,不影响框架。注意记录点中的字段错误只在启用记录功能的构建中才会被检查(关闭时宏丢弃实参)。
|
||||
|
||||
**两点使用提醒**:
|
||||
|
||||
- `RdpRecTrustTrack::Track_Update_Flag` **恒为 0**:写库发生在输出循环内,而该循环入口已先把标志清零(`track_asso.cpp` 先置 0、再输出、最后记录)。判断「某航迹本圈是否更新」应改用该表是否出现对应 `track_id` / `circle_id` 的记录,或改用 `RdpRecAssoInfo::min_d_flag`。
|
||||
- `Range` / `Azimuth` / `Elevation` / `Velocity` 四个内部快照**只在航迹当圈有输出时被回填**,长期未输出(外推超限)的航迹在表中为 0 或旧值。
|
||||
|
||||
## 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(RdpRecPoint)").fetchall())
|
||||
print(con.execute("select count(*) from RdpRecPoint").fetchone())
|
||||
print(con.execute("select * from RdpRecPoint order by id desc limit 3").fetchall())
|
||||
print(con.execute("pragma integrity_check").fetchone())
|
||||
|
||||
# 按点迹链路串联:输入点迹 -> 凝聚后点迹 -> 临时航迹
|
||||
print(con.execute("""
|
||||
select p.circle_id, p.point_id, p.range, c.range, t.temp_track_id, t.buff_round
|
||||
from RdpRecPoint p
|
||||
left join RdpRecPointCohesionTWS c on c.point_id = p.point_id
|
||||
left join RdpRecTempTrack t on t.point_id = p.point_id
|
||||
order by p.id desc limit 10
|
||||
""").fetchall())
|
||||
|
||||
# 查看某圈的关联决策(min_d_flag=1 为最终取中的候选)
|
||||
print(con.execute("""
|
||||
select track_id, point_id, d_min, delta_T, thd_d, thd_h, thd_r, min_d_flag
|
||||
from RdpRecAssoInfo where circle_id = (select max(circle_id) from RdpRecAssoInfo)
|
||||
order by min_d_flag desc, d_min asc limit 20
|
||||
""").fetchall())
|
||||
```
|
||||
|
||||
也可用 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`。
|
||||
+17
-12
@@ -77,20 +77,23 @@ track_init.track_init_process_logic(&point_recv, &trust_track, &temp_track,
|
||||
|
||||
## 2. 数据结构与状态容器
|
||||
|
||||
### 2.1 Temp_track(临时航迹点,`struct.h` L94-121)
|
||||
### 2.1 Temp_track(临时航迹点,`struct.h`)
|
||||
|
||||
| 字段 | 含义 | 起始流程中的作用 |
|
||||
|---|---|---|
|
||||
| `X[4]` | 4 维状态 **[x, vx, y, vy]** | EKF 一步预测的状态;由两点卡尔曼初始化填充 |
|
||||
| `P[4][4]` | 状态协方差 | 同上的协方差 |
|
||||
| `r / azi / vr / height / snr / RCS / Amp` | 该点的极坐标测量 | 初始化、输出、条件判据(`azi` 为**弧度**) |
|
||||
| `T` / `GNSS_time` | CPI 时间 / GNSS 时间(ms) | ΔT 计算(除 1000 转秒) |
|
||||
| `r / azi / vr / height / SNR / RCS / amplitude` | 该点的极坐标测量 | 初始化、输出、条件判据(`azi` 为**弧度**) |
|
||||
| `CPI_time` / `GNSS_time` | CPI 时间 / GNSS 时间(ms) | ΔT 计算(除 1000 转秒) |
|
||||
| `d` | 该点关联时的马氏统计距离 | 去重阶段用于「保留更优者」 |
|
||||
| `buff_round` | **已缓存圈数** | 生命周期与关联时机控制(见 4.2) |
|
||||
| `asso_flag` | 0 未关联 / 1 已关联(待删除)/ 2 去重淘汰 | 临时状态标记 |
|
||||
| `pitch_num`、`speed_dim[129]`、`range_dim[8]` | 俯仰波位、十字星数据 | 纯透传 |
|
||||
| `Temp_track_section_idx` | 临时航迹区号 | 相关赋值已被注释停用,当前不参与逻辑 |
|
||||
| `temp_track_id` / `point_id` / `circle_id` | 临时航迹 ID / 末关联点迹 ID / 处理圈 ID(c3c7e66 新增) | 数据记录链路标识;起批时透传给 `Trust_Track` |
|
||||
| `temp_track_section_idx` | 临时航迹区号 | 相关赋值已被注释停用,当前不参与逻辑 |
|
||||
|
||||
> 字段更名提示(c3c7e66):`snr`→`SNR`、`Amp`→`amplitude`、`T`→`CPI_time`、`Temp_track_section_idx`→`temp_track_section_idx`。阅读早期提交或旧文档时注意对应关系。
|
||||
>
|
||||
> ⚠ **下标陷阱**:`Temp_track::X` 是 4 维 `[x, vx, y, vy]`(y 在 index 2),而 `Trust_Track::X` 是 6 维 `[x, vx, ax, y, vy, ay]`(y 在 index 3)。两套结构混用下标必错。
|
||||
|
||||
### 2.2 容器语义
|
||||
@@ -112,7 +115,7 @@ track_init.track_init_process_logic(&point_recv, &trust_track, &temp_track,
|
||||
| `Trust_track_num_Output` | 输出 | 累计行数(track_asso 输出 N 行后,track_init 在其后继续追加) |
|
||||
| `Work_Parameter` | 输入 | 运行时雷达参数(`RadarPara`) |
|
||||
|
||||
> **输出行数核算**:`track_asso` 先为当圈全部可靠航迹写 N 行;`track_die` 随后删除 D 条(已写出的行与计数不变);起始流程每次起批前判 `trust_track->size() < MAX_TRACK_INDEX(500)`,故最多再追加 `499 − N + D` 行,**合计最多 499 + D 行**。正常情况下 D 很小,远小于 500;但理论上当 D ≥ 2 且当圈起批数量恰好占满配额(需数百条同时起批)时,行号会超过第一维容量 500,存在越界写入风险(极端场景,见 15.14)。第二维 `[j]`(j < L ≤ 10)始终安全。代价是可靠航迹数达 500 时不再允许新起批。
|
||||
> **输出行数核算**:`track_asso` 先为当圈**有更新**的可靠航迹写 N 行(输出循环判 `Track_Update_Flag==1` 并清零,即只含关联到实点或成功外推的航迹);`track_die` 随后删除 D 条(已写出的行与计数不变);起始流程每次起批前判 `trust_track->size() < MAX_TRACK_INDEX(500)`,故最多再追加 `499 − N + D` 行,**合计最多 499 + D 行**。正常情况下 D 很小,远小于 500;但理论上当 D ≥ 2 且当圈起批数量恰好占满配额(需数百条同时起批)时,行号会超过第一维容量 500,存在越界写入风险(极端场景,见 15.14)。第二维 `[j]`(j < L ≤ 10)始终安全。代价是可靠航迹数达 500 时不再允许新起批。
|
||||
|
||||
---
|
||||
|
||||
@@ -356,10 +359,11 @@ for (...) (*point_recv).push_back(point_process[i]); // 回写未被使用的
|
||||
|
||||
| 字段 | 取值 |
|
||||
|---|---|
|
||||
| `r / azi / height / Amp / vr / snr / RCS` | 点迹对应量(`azi` 为弧度) |
|
||||
| `r / azi / height / amplitude / vr / SNR / RCS` | 点迹对应量(`azi` 为弧度) |
|
||||
| `pitch_num / speed_dim / range_dim` | 透传 |
|
||||
| `X[0] = Range·cos(Azimuth)`、`X[1]=0`、`X[2] = Range·sin(Azimuth)`、`X[3]=0` | 位置赋值,速度置 0(**注意 4 维布局**) |
|
||||
| `T`、`GNSS_time` | 点迹时间 |
|
||||
| `CPI_time`、`GNSS_time` | 点迹时间 |
|
||||
| `temp_track_id = temp_track_id++`、`point_id`、`circle_id` | 取自该点迹(记录链路标识,c3c7e66 新增) |
|
||||
| `asso_flag = 0`、`buff_round = 1` | 生命周期起点 |
|
||||
| `d` | 未赋值 → 0 |
|
||||
|
||||
@@ -440,14 +444,15 @@ Kalman.kalman_filter_init_3dots(Z0, Z1, Z2, T1, T2, X, P); // 6 维 [x,vx,ax,y
|
||||
| `X1/X2/X3 = X`、`P1/P2/P3 = P` | 三个 IMM 子模型同状态、同协方差初始化 |
|
||||
| `u[0..2] = 0.3333` | 模型概率均分 |
|
||||
| `Track_Index` | `track_ind_get()` 分配的航迹号 |
|
||||
| `T_track` / `GNSS_time` / `Height` | 取**末点**的对应值 |
|
||||
| `T_track` / `GNSS_time` / `Height` | 取**末点**的对应值(`CPI_time` 字段) |
|
||||
| `Amplitude / snr_point / RCS / pitch_num / speed_dim / range_dim` | 末点透传 |
|
||||
| `track_id / point_id / circle_id` | 取末点的 `temp_track_id / point_id / circle_id`(记录链路标识,b227ae1 新增) |
|
||||
| `point_flag = 1` | 实点 |
|
||||
| `Track_Mode = 0` | TWS |
|
||||
| `Target_Type = UNCONF_TARGET(0)` | 未确认目标 |
|
||||
| `Extrapolate_round = 0` | 外推计数清零 |
|
||||
| `approach_flag = -1` | 接近标志复位 |
|
||||
| `manual_delete_flag / manual_tracking_flag / associate_point_number / point_type = 0` | 各类标志复位 |
|
||||
| `manual_delete_flag / manual_tracking_flag / associate_point_number / point_type / primary_target_flag = 0` | 各类标志复位(`Trust_Track trust_track_tmp = {}` 零初始化,故新航迹的重点目标标记为 0) |
|
||||
|
||||
> L606-608 有一处**死代码**:`coor_trans::cart2polar(X[0], X[3], &r, &azi)`(把 `X[3]=vy` 当 y 用),计算结果 `r/azi` 从未被使用。
|
||||
|
||||
@@ -664,13 +669,13 @@ $$
|
||||
| `Direction_Angle` | `atan2(vy, vx)`(→度, `[0,360)`) | 航向角 |
|
||||
| `x / v_x / y / v_y` | 点的 `X[0..3]` | 注意 4 维布局 |
|
||||
| `range_point / azi_point / elev_point / vr_point` | 点的原始测量 | `point_type=0` |
|
||||
| `Amplitude / track_snr / track_rcs` | 点的 `Amp / snr / RCS` | |
|
||||
| `Amplitude / track_snr / track_rcs` | 点的 `amplitude / SNR / RCS` | |
|
||||
| `pitch_num / speed_dim / range_dim` | 点透传 | |
|
||||
| `track_time` | `T/1000`(秒) | |
|
||||
| `track_time` | `CPI_time/1000`(秒) | |
|
||||
| `GNSS_time` | 点透传 | |
|
||||
| `Flag_Point` | 恒为 **1**(实点) | |
|
||||
| `Track_Mode` | 恒为 **0**(TWS) | |
|
||||
| **未赋值** | `Flag_Direction`、`x_Predict/y_Predict/z_Predict`、`Range_Predict/Azimuth_Predict/Elevation_Predict`、`Target_Type`、`prf_point`、`v_z` | DLL 不清零该数组,宿主读到的是其自身缓存的旧值/未初始化值。注意 `Flag_Direction` 只在关联输出路径赋值(`track_asso.cpp` / `track_asso_tas.cpp`,f249fdc:`vr_point > 0 ? 1 : 0`),起批输出不写该字段 |
|
||||
| **未赋值** | `Flag_Direction`、`x_Predict/y_Predict/z_Predict`、`Range_Predict/Azimuth_Predict/Elevation_Predict`、`Target_Type`、`prf_point`、`v_z`、`amp_asv_point`、`prn_fftn_point` | DLL 不清零该数组,宿主读到的是其自身缓存的旧值/未初始化值。注意 `Flag_Direction` 只在关联输出路径赋值(`track_asso.cpp` L129 / `track_asso_tas.cpp` L93),起批输出不写该字段。该字段的判据已由 f249fdc 的 `vr_point > 0 ? 1 : 0`(实测径向速度,受多普勒速度模糊影响符号可能取反)改为 fc49707 + 42b598e 的**航向-位置夹角法**:`Direction_Angle = atan2(X[4], X[1])`(弧度转度后同时输出到 `Direction_Angle`),`x`、`y` 为经 `Work_Parameter.Sys_delay` 补偿后的位置、`r` 为其模,`acos((-x·cos(Direction_Angle) - y·sin(Direction_Angle))/r) < π/2` 判为靠近(1)、否则远离(0),即滤波速度方向与"雷达→目标"位置方向夹角超 90° 判为接近。另 `amp_asv_point` / `prn_fftn_point` 属 e8c56f9 新增,`data_preprocess()` **现已**把 `DataRev.amp_asv / prn_fftn` 搬入 `PointRecv`(TWS 与 TAS 分支均赋值,并随 `RdpRecPoint` 落库),因此 TWS 关联/输出路径的该字段已有有效来源;TAS 关联与输出路径(`track_asso_tas.cpp`)仍未涉及这两个字段,TAS 输出行中它们保持宿主原值 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user